<commit_before>
use super::{Notice, NoticeBuilder};

pub struct ContextBuilder {
    pub operating_system: Option<String>,
    pub hostname: Option<String>,
    pub language: Option<String>,
    pub environment: Option<String>,
    pub severity: Option<String>,
    pub version: Option<String>,
    pub url: Option<String>,
    pub root_directory: Option<String>,
    pub user: Option<ContextUser>,
    pub route: Option<String>,
    pub http_method: Option<String>,
}

impl ContextBuilder {
    pub fn new() -> ContextBuilder {
        ContextBuilder {
            operating_system: None,
            hostname: None,
            language: None,
            environment: None,
            severity: None,
            version: None,
            url: None,
            root_directory: None,
            user: None,
            route: None,
            http_method: None
        }
    }

    /// Set the operating system on the ContextBuilder
    pub fn operating_system(mut self, os: String) -> ContextBuilder {
        self.operating_system = Some(os);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn hostname(mut self, hostname: String) -> ContextBuilder {
        self.hostname = Some(hostname);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn language(mut self, language: String) -> ContextBuilder {
        self.language = Some(language);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn environment(mut self, environment: String) -> ContextBuilder {
        self.environment = Some(environment);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn severity(mut self, severity: String) -> ContextBuilder {
        self.severity = Some(severity);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn version(mut self, version: String) -> ContextBuilder {
        self.version = Some(version);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn url(mut self, url: String) -> ContextBuilder {
        self.url = Some(url);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn root_directory(mut self, root_directory: String) -> ContextBuilder {
        self.root_directory = Some(root_directory);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn user(mut self, user: ContextUser) -> ContextBuilder {
        self.user = Some(user);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn route(mut self, route: String) -> ContextBuilder {
        self.route = Some(route);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn http_method(mut self, http_method: String) -> ContextBuilder {
        self.http_method = Some(http_method);
        self
    }

    pub fn build(self) -> Context {
        Context {
            notifier: &CONTEXT_NOTIFIER,
            operating_system: self.operating_system,
            hostname: self.hostname,
            language: self.language,
            environment: self.environment,
            severity: self.severity,
            version: self.version,
            url: self.url,
            root_directory: self.root_directory,
            user: self.user,
            route: self.route,
            http_method: self.http_method
        }
    }

}

#[derive(Debug, Serialize, Clone)]
pub struct Context {
    // Builtin notifier
    pub notifier: &'static ContextNotifier,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename="os")]
    pub operating_system: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub hostname: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub language: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub environment: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub severity: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    #[serde(rename="rootDirectory")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub root_directory: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub user: Option<ContextUser>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub route: Option<String>,

    #[serde(rename="httpMethod")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub http_method: Option<String>
}

impl Context {
    pub fn builder() -> ContextBuilder {
        ContextBuilder::new()
    }

    pub fn new_notice_builder(&self) -> NoticeBuilder {
        Notice::builder().context(self.clone())
    }
}

/// This type is not intended to be used beyond the const CONTEXT_NOTIFIER
#[derive(Debug, Serialize, Clone)]
pub struct ContextNotifier {
    name: &'static str,
    version: &'static str,
    url: &'static str,
}

const NOTIFIER_NAME: &'static str = "airbrake-rust";
const NOTIFIER_URL: &'static str = "https://github.com/airbrake/airbrake-rust";
const NOTIFIER_VERSION: &'static str = "0.2.0";

pub const CONTEXT_NOTIFIER: ContextNotifier = ContextNotifier {
    name: NOTIFIER_NAME,
    version: NOTIFIER_VERSION,
    url: NOTIFIER_URL
};

#[derive(Debug, Serialize, Clone)]
pub struct ContextUser {
    #[serde(skip_serializing_if = "Option::is_none")]
    id: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    email: Option<String>
}

impl ContextUser {
    /// Get a blank ContextUser, whose values are all None
    pub fn empty() -> ContextUser {
        ContextUser {
            id: None,
            name: None,
            email: None
        }
    }

    /// Set the id on the ContextUser
    pub fn id(mut self, id: String) -> ContextUser {
        self.id = Some(id);
        self
    }

    /// Set the name on the ContextUser
    pub fn name(mut self, name: String) -> ContextUser {
        self.name = Some(name);
        self
    }

    /// Set the email on the ContextUser
    pub fn email(mut self, email: String) -> ContextUser {
        self.email = Some(email);
        self
    }
}

#[cfg(test)]
mod context_user_tests {
    use std::str::FromStr;
    use std::collections::HashMap;
    use serde_json::{self, Value};
    use hyper::body::Body;
    use super::{Context, ContextUser};

    #[test]
    fn context_user_default() {
        let context = ContextUser::empty();
        let expected_json = r#"
        {}
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_id() {
        let context = ContextUser::empty().id("foo".to_string());
        let expected_json = r#"
        {
            "id": "foo"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_name() {
        let context = ContextUser::empty().name("foo".to_string());
        let expected_json = r#"
        {
            "name": "foo"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_email() {
        let context = ContextUser::empty().email("foo".to_string());
        let expected_json = r#"
        {
            "email": "foo"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_all_fields() {
        let context = ContextUser::empty()
            .id("foo".to_string())
            .email("bar".to_string())
            .name("baz".to_string());
        let expected_json = r#"
        {
            "id": "foo",
            "email": "bar",
            "name": "baz"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }
}

#[cfg(test)]
mod context_tests {
    use std::str::FromStr;
    use std::collections::HashMap;
    use serde_json::{self, Value};
    use hyper::body::Body;
    use super::{Context, ContextUser};

    #[test]
    fn context_default_has_notifier() {
        let context = Context::builder().build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            }
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_operating_system() {
        let context = Context::builder()
            .operating_system("SolarOS".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "os": "SolarOS"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_hostname() {
        let context = Context::builder()
            .hostname("usw2.swa.foobar.com".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "hostname": "usw2.swa.foobar.com"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_language() {
        let context = Context::builder()
            .language("klingon".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "language": "klingon"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_environment() {
        let context = Context::builder()
            .environment("production".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "environment": "production"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_severity() {
        let context = Context::builder()
            .severity("critical".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "severity": "critical"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_version() {
        let context = Context::builder()
            .version("9000.0.1".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "version": "9000.0.1"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_url() {
        let context = Context::builder()
            .url("http://localhost/my/foobar".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "url": "http://localhost/my/foobar"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_root_directory() {
        let context = Context::builder()
            .root_directory("/dev/null".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "rootDirectory": "/dev/null"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user() {
        let context_user = ContextUser::empty()
            .id("foo".to_string())
            .email("bar".to_string())
            .name("baz".to_string());
        let context = Context::builder()
            .user(context_user)
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "user": {
                "id": "foo",
                "email": "bar",
                "name": "baz"
            }
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_route() {
        let context = Context::builder()
            .route("/foo/bar/baz".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "route": "/foo/bar/baz"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_http_method() {
        let context = Context::builder()
            .http_method("post".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "httpMethod": "post"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }
}

<commit_msg>ContextBuilder uses non-consuming builder pattern<commit_after>
use super::{Notice, NoticeBuilder};

pub struct ContextBuilder {
    pub operating_system: Option<String>,
    pub hostname: Option<String>,
    pub language: Option<String>,
    pub environment: Option<String>,
    pub severity: Option<String>,
    pub version: Option<String>,
    pub url: Option<String>,
    pub root_directory: Option<String>,
    pub user: Option<ContextUser>,
    pub route: Option<String>,
    pub http_method: Option<String>,
}

impl ContextBuilder {
    pub fn new() -> ContextBuilder {
        ContextBuilder {
            operating_system: None,
            hostname: None,
            language: None,
            environment: None,
            severity: None,
            version: None,
            url: None,
            root_directory: None,
            user: None,
            route: None,
            http_method: None
        }
    }

    /// Set the operating system on the ContextBuilder
    pub fn operating_system<'a>(&'a mut self, os: String) -> &'a mut ContextBuilder {
        self.operating_system = Some(os);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn hostname<'a>(&'a mut self, hostname: String) -> &'a mut ContextBuilder {
        self.hostname = Some(hostname);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn language<'a>(&'a mut self, language: String) -> &'a mut ContextBuilder {
        self.language = Some(language);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn environment<'a>(&'a mut self, environment: String) -> &'a mut ContextBuilder {
        self.environment = Some(environment);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn severity<'a>(&'a mut self, severity: String) -> &'a mut ContextBuilder {
        self.severity = Some(severity);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn version<'a>(&'a mut self, version: String) -> &'a mut ContextBuilder {
        self.version = Some(version);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn url<'a>(&'a mut self, url: String) -> &'a mut ContextBuilder {
        self.url = Some(url);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn root_directory<'a>(&'a mut self, root_directory: String) -> &'a mut ContextBuilder {
        self.root_directory = Some(root_directory);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn user<'a>(&'a mut self, user: ContextUser) -> &'a mut ContextBuilder {
        self.user = Some(user);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn route<'a>(&'a mut self, route: String) -> &'a mut ContextBuilder {
        self.route = Some(route);
        self
    }

    /// Set the operating system on the ContextBuilder
    pub fn http_method<'a>(&'a mut self, http_method: String) -> &'a mut ContextBuilder {
        self.http_method = Some(http_method);
        self
    }

    pub fn build(&self) -> Context {
        Context {
            notifier: &CONTEXT_NOTIFIER,
            operating_system: self.operating_system.clone(),
            hostname: self.hostname.clone(),
            language: self.language.clone(),
            environment: self.environment.clone(),
            severity: self.severity.clone(),
            version: self.version.clone(),
            url: self.url.clone(),
            root_directory: self.root_directory.clone(),
            user: self.user.clone(),
            route: self.route.clone(),
            http_method: self.http_method.clone()
        }
    }

}

#[derive(Debug, Serialize, Clone)]
pub struct Context {
    // Builtin notifier
    pub notifier: &'static ContextNotifier,

    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename="os")]
    pub operating_system: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub hostname: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub language: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub environment: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub severity: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    #[serde(rename="rootDirectory")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub root_directory: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub user: Option<ContextUser>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub route: Option<String>,

    #[serde(rename="httpMethod")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub http_method: Option<String>
}

impl Context {
    pub fn builder() -> ContextBuilder {
        ContextBuilder::new()
    }

    pub fn new_notice_builder(&self) -> NoticeBuilder {
        Notice::builder().context(self.clone())
    }
}

/// This type is not intended to be used beyond the const CONTEXT_NOTIFIER
#[derive(Debug, Serialize, Clone)]
pub struct ContextNotifier {
    name: &'static str,
    version: &'static str,
    url: &'static str,
}

const NOTIFIER_NAME: &'static str = "airbrake-rust";
const NOTIFIER_URL: &'static str = "https://github.com/airbrake/airbrake-rust";
const NOTIFIER_VERSION: &'static str = "0.2.0";

pub const CONTEXT_NOTIFIER: ContextNotifier = ContextNotifier {
    name: NOTIFIER_NAME,
    version: NOTIFIER_VERSION,
    url: NOTIFIER_URL
};

#[derive(Debug, Serialize, Clone)]
pub struct ContextUser {
    #[serde(skip_serializing_if = "Option::is_none")]
    id: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    email: Option<String>
}

impl ContextUser {
    /// Get a blank ContextUser, whose values are all None
    pub fn empty() -> ContextUser {
        ContextUser {
            id: None,
            name: None,
            email: None
        }
    }

    /// Set the id on the ContextUser
    pub fn id(mut self, id: String) -> ContextUser {
        self.id = Some(id);
        self
    }

    /// Set the name on the ContextUser
    pub fn name(mut self, name: String) -> ContextUser {
        self.name = Some(name);
        self
    }

    /// Set the email on the ContextUser
    pub fn email(mut self, email: String) -> ContextUser {
        self.email = Some(email);
        self
    }
}

#[cfg(test)]
mod context_user_tests {
    use std::str::FromStr;
    use std::collections::HashMap;
    use serde_json::{self, Value};
    use hyper::body::Body;
    use super::{Context, ContextUser};

    #[test]
    fn context_user_default() {
        let context = ContextUser::empty();
        let expected_json = r#"
        {}
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_id() {
        let context = ContextUser::empty().id("foo".to_string());
        let expected_json = r#"
        {
            "id": "foo"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_name() {
        let context = ContextUser::empty().name("foo".to_string());
        let expected_json = r#"
        {
            "name": "foo"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_email() {
        let context = ContextUser::empty().email("foo".to_string());
        let expected_json = r#"
        {
            "email": "foo"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user_with_all_fields() {
        let context = ContextUser::empty()
            .id("foo".to_string())
            .email("bar".to_string())
            .name("baz".to_string());
        let expected_json = r#"
        {
            "id": "foo",
            "email": "bar",
            "name": "baz"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }
}

#[cfg(test)]
mod context_tests {
    use std::str::FromStr;
    use std::collections::HashMap;
    use serde_json::{self, Value};
    use hyper::body::Body;
    use super::{Context, ContextUser};

    #[test]
    fn context_default_has_notifier() {
        let context = Context::builder().build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            }
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_operating_system() {
        let context = Context::builder()
            .operating_system("SolarOS".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "os": "SolarOS"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_hostname() {
        let context = Context::builder()
            .hostname("usw2.swa.foobar.com".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "hostname": "usw2.swa.foobar.com"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_language() {
        let context = Context::builder()
            .language("klingon".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "language": "klingon"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_environment() {
        let context = Context::builder()
            .environment("production".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "environment": "production"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_severity() {
        let context = Context::builder()
            .severity("critical".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "severity": "critical"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_version() {
        let context = Context::builder()
            .version("9000.0.1".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "version": "9000.0.1"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_url() {
        let context = Context::builder()
            .url("http://localhost/my/foobar".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "url": "http://localhost/my/foobar"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_root_directory() {
        let context = Context::builder()
            .root_directory("/dev/null".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "rootDirectory": "/dev/null"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_user() {
        let context_user = ContextUser::empty()
            .id("foo".to_string())
            .email("bar".to_string())
            .name("baz".to_string());
        let context = Context::builder()
            .user(context_user)
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "user": {
                "id": "foo",
                "email": "bar",
                "name": "baz"
            }
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_route() {
        let context = Context::builder()
            .route("/foo/bar/baz".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "route": "/foo/bar/baz"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }

    #[test]
    fn context_http_method() {
        let context = Context::builder()
            .http_method("post".to_string())
            .build();
        let expected_json = r#"
        {
            "notifier": {
                "name": "airbrake-rust",
                "version": "0.2.0",
                "url": "https://github.com/airbrake/airbrake-rust"
            },
            "httpMethod": "post"
        }
        "#;
        assert_eq!(
            Value::from_str(expected_json).unwrap(),
            serde_json::json!(context)
        );
    }
}

<|endoftext|>


<commit_before>// Copyright 2015 Bruno Medeiros
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use ::util::core::*;
use ::util::string::*;
use ::source_model::*;

use ::syntex_syntax::syntax::ast;
use ::syntex_syntax::parse::{ self, ParseSess };
use ::syntex_syntax::visit;
use ::syntex_syntax::codemap:: { self, Span, CodeMap};
use ::syntex_syntax::errors:: { Handler, RenderSpan, Level, emitter };

use std::boxed::Box;
use std::path::Path;

use ::token_writer::TokenWriter;

use std::cell::RefCell;
use std::rc::*;
use std::io;
use std::io::Write;
use std::fmt;

/* -----------------  ----------------- */

pub fn parse_analysis_forStdout(source : &str) {
	parse_analysis(source, StdoutWrite(io::stdout())).ok();
	println!("");
	io::stdout().flush().ok();
}

pub fn parse_analysis<T : fmt::Write + 'static>(source : &str, out : T) -> GResult<T> {
	let (messages, elements) = parse_crate_with_messages(source);

	let outRc = Rc::new(RefCell::new(out));
	try!(write_parse_analysis_do(messages, elements, outRc.clone()));
	let res = unwrap_Rc_RefCell(outRc);
	return Ok(res);
}

use std::thread;
use std::sync::{Arc, Mutex};

pub fn parse_crate_with_messages(source: &str) -> (Vec<SourceMessage>, Vec<StructureElement>) {

	let messages = Arc::new(Mutex::new(vec![]));
	let elements =
	{
		let source = String::from(source);
		let messages = messages.clone();

		let worker_thread = thread::Builder::new().name("parser_thread".to_string()).spawn(move || {
			parse_crate_with_messages_do(&source, messages)
		}).unwrap();

		worker_thread.join().unwrap_or(vec![])
	};

	let messages : Mutex<Vec<SourceMessage>> = Arc::try_unwrap(messages).ok().unwrap();
	let messages : Vec<SourceMessage> = messages.into_inner().unwrap();

	return (messages, elements);
}

pub fn parse_crate_with_messages_do(source: &str, messages: Arc<Mutex<Vec<SourceMessage>>>)
	-> Vec<StructureElement>
{
	use ::structure_visitor::StructureVisitor;

	let mut elements = vec![];

	let fileLoader = Box::new(DummyFileLoader::new());
	let codemap = Rc::new(CodeMap::with_file_loader(fileLoader));

	let krate = parse_crate(source, codemap.clone(), messages.clone());

	if let Some(krate) = krate {
		let mut visitor : StructureVisitor = StructureVisitor::new(&codemap);
		visit::walk_crate(&mut visitor, &krate);

		elements = visitor.elements;
	}
	return elements;
}

/* -----------------  ----------------- */

use std::ffi::OsStr;

/// A FileLoader that loads any file successfully
pub struct DummyFileLoader {
   	modName : &'static OsStr,
}

impl DummyFileLoader {
	fn new() -> DummyFileLoader {
		DummyFileLoader { modName : OsStr::new("mod.rs") }
	}
}

impl codemap::FileLoader for DummyFileLoader {
    fn file_exists(&self, path: &Path) -> bool {
    	return path.file_name() == Some(self.modName);
    }

    fn read_file(&self, _path: &Path) -> io::Result<String> {
        Ok(String::new())
    }
}

struct MessagesHandler {
	codemap : Rc<CodeMap>,
	messages : Arc<Mutex<Vec<SourceMessage>>>,
}

fn parse_crate<'a>(source: &str, codemap: Rc<CodeMap>, messages: Arc<Mutex<Vec<SourceMessage>>>) -> Option<ast::Crate>
{
	let emitter = MessagesHandler::new(codemap.clone(), messages.clone());

	let handler = Handler::with_emitter(true, false, Box::new(emitter));
	let sess = ParseSess::with_span_handler(handler, codemap.clone());

	let krate_result = parse_crate_do(source, &sess);

	return match krate_result {
		Ok(_krate) => {
			Some(_krate)
		}
		Err(mut db) => {
			db.emit();
			None
		}
	}
}

pub fn parse_crate_do<'a>(source : &str, sess : &'a ParseSess) -> parse::PResult<'a, ast::Crate>
{
	let source = source.to_string();

	let cfg = vec![];
	let name = "_file_module_".to_string();

//	We inlined: let mut parser = parse::new_parser_from_source_str(&sess, cfg, name, source);

	let filemap = sess.codemap().new_filemap(name, source);

	// filemap_to_tts but without a panic
	let tts =
	{
	    let cfg = Vec::new();
	    let srdr = parse::lexer::StringReader::new(&sess.span_diagnostic, filemap);
	    let mut p1 = parse::parser::Parser::new(sess, cfg, Box::new(srdr));

	    try!(p1.parse_all_token_trees())
	};

    let trdr = parse::lexer::new_tt_reader(&sess.span_diagnostic, None, None, tts);
    let mut parser = parse::parser::Parser::new(sess, cfg, Box::new(trdr));

	return parser.parse_crate_mod();
}

impl MessagesHandler {

	fn new(codemap: Rc<CodeMap>, messages: Arc<Mutex<Vec<SourceMessage>>>) -> MessagesHandler {
		MessagesHandler { codemap : codemap, messages : messages }
	}

	fn writeMessage_handled(&mut self, sourcerange : Option<SourceRange>, msg: &str, severity: Severity) {

		let msg = SourceMessage{ severity : severity , sourcerange : sourcerange,  message : String::from(msg) };

		let mut messages = self.messages.lock().unwrap();
		messages.push(msg);
	}

}

impl emitter::Emitter for MessagesHandler {

    fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, lvl: Level) {

    	if let Some(code) = code {
   			io::stderr().write_fmt(format_args!("Code: {}\n", code)).unwrap();
   			panic!("What is code: Option<&str>??");
    	}

		let sourcerange = match span {
			Some(span) => Some(SourceRange::new(&self.codemap, span)),
			None => None,
		};

		self.writeMessage_handled(sourcerange, msg, level_to_status_level(lvl));
    }

    fn custom_emit(&mut self, _: RenderSpan, msg: &str, lvl: Level) {
    	match lvl { Level::Help | Level::Note => return, _ => () }

    	self.writeMessage_handled(None, msg, level_to_status_level(lvl));
    }

}

fn level_to_status_level(lvl: Level) -> Severity {
	match lvl {
		Level::Bug => panic!("Level::BUG"),
		Level::Cancelled => panic!("Level::CANCELLED"),
		Level::Help | Level::Note => Severity::INFO,
		Level::Warning => Severity::WARNING,
		Level::Error | Level::Fatal => Severity::ERROR,
	}
}

impl MessagesHandler {
}

/* ----------------- describe writting ----------------- */

pub fn write_parse_analysis_do(messages: Vec<SourceMessage>, elements: Vec<StructureElement>,
	out : Rc<RefCell<fmt::Write>>) -> Void {

	let mut tokenWriter = TokenWriter { out : out };

	try!(tokenWriter.writeRaw("RUST_PARSE_DESCRIBE 1.0 {\n"));
	try!(write_parse_analysis_contents(messages, elements, &mut tokenWriter));
	try!(tokenWriter.writeRaw("\n}"));

	Ok(())
}

pub fn write_parse_analysis_contents(messages: Vec<SourceMessage>, elements: Vec<StructureElement>,
	tokenWriter : &mut TokenWriter) -> Void {

	try!(tokenWriter.writeRaw("MESSAGES {\n"));
	for msg in messages {
		try!(output_message(tokenWriter, msg.sourcerange, &msg.message, &msg.severity));
	}
	try!(tokenWriter.writeRaw("}\n"));

	for element in elements {
		try!(write_structure_element(tokenWriter, &element, 0));
	}

	Ok(())
}

fn output_message(tokenWriter: &mut TokenWriter, opt_sr : Option<SourceRange>, msg: & str, lvl: &Severity)
	-> Void
{

	try!(tokenWriter.writeRaw("{ "));

	try!(outputString_Level(&lvl, tokenWriter));

	try!(outputString_optSourceRange(&opt_sr, tokenWriter));

	try!(tokenWriter.writeStringToken(msg));

	try!(tokenWriter.writeRaw("}\n"));

	Ok(())
}

pub fn outputString_Level(lvl : &Severity, writer : &mut TokenWriter) -> Void {

	try!(writer.writeRawToken(lvl.to_string()));

	Ok(())
}

pub fn outputString_SourceRange(sr : &SourceRange, tw : &mut TokenWriter) -> Void {
	try!(tw.writeRaw("{ "));
	{
		let mut out = tw.getCharOut();
		try!(out.write_fmt(format_args!("{}:{} {}:{} ",
			sr.start_pos.line-1, sr.start_pos.col.0,
			sr.end_pos.line-1, sr.end_pos.col.0,
		)));
	}
	try!(tw.writeRaw("}"));

	Ok(())
}

pub fn outputString_optSourceRange(sr : &Option<SourceRange>, writer : &mut TokenWriter) -> Void {

	match sr {
		&None => try!(writer.writeRaw("{ }")) ,
		&Some(ref sr) => try!(outputString_SourceRange(sr, writer)) ,
	}

	try!(writer.writeRaw(" "));

	Ok(())
}

pub fn write_indent(tokenWriter : &mut TokenWriter, level : u32) -> Void {
	try!(writeNTimes(&mut *tokenWriter.getCharOut(), ' ', level * 2));
	Ok(())
}

pub fn write_structure_element(tw : &mut TokenWriter, element: &StructureElement, level: u32) -> Void
{
	try!(tw.writeRawToken(element.kind.to_String()));

	try!(tw.writeRaw("{ "));

	try!(tw.writeStringToken(&element.name));

	try!(outputString_SourceRange(&element.sourcerange, tw));

	try!(tw.getCharOut().write_str(" {}")); // name source range, Not Supported

	try!(tw.getCharOut().write_str(" "));
	try!(tw.writeStringToken(&element.type_desc));

	try!(tw.getCharOut().write_str("{}")); // attribs, Not Supported

	if element.children.is_empty() {
		try!(tw.getCharOut().write_str(" "));
	} else {
		let level = level + 1;

		for child in &element.children {
			try!(tw.getCharOut().write_str("\n"));
			try!(write_indent(tw, level));
			try!(write_structure_element(tw, child, level));
		}

		try!(tw.getCharOut().write_str("\n"));
		try!(write_indent(tw, level-1));
	}

	try!(tw.getCharOut().write_str("}"));

	Ok(())
}

#[cfg(test)]
mod parse_describe_tests {

    use parse_describe::*;
    use source_model::*;
    use token_writer::TokenWriter;
    use util::core::*;

	use std::rc::Rc;
	use std::cell::RefCell;

    #[test]
    fn tests_write_structure_element() {

    	fn test_writeStructureElement(name : &str, kind : StructureElementKind, sr: SourceRange, type_desc : String,
    		expected : &str,
    	) {
    		let stringRc = Rc::new(RefCell::new(String::new()));
    		{
    			let name = String::from(name);
    			let element = StructureElement { name: name, kind: kind, sourcerange: sr, type_desc: type_desc,
    				 children: vec![]};
    			let mut tw = TokenWriter { out : stringRc.clone() };

    			write_structure_element(&mut tw, &element, 0).ok();
    		}

    		assert_eq!(unwrap_Rc_RefCell(stringRc).trim(), expected);
    	}

    	test_writeStructureElement("blah", StructureElementKind::Var, sourceRange(1, 0, 2, 5), "desc".to_string(),
    		r#"Var { "blah" { 0:0 1:5 } {} "desc" {} }"#);
    }

    #[test]
    fn parse_analysis_tests() {
    	test_parse_analysis("", "");

    	test_parse_analysis(" #blah ", r#"{ ERROR { 0:2 0:6 } "expected `[`, found `blah`" }"#);

    	test_parse_analysis("fn foo(\n  blah", r#"
    { ERROR { 1:6 1:6 } "this file contains an un-closed delimiter" }
    { INFO { 0:6 0:7 } "did you mean to close this delimiter?" }"#);

    	// Test a lexer panic
    	test_parse_analysis("const a = '",
    		r#"{ ERROR { 0:10 0:11 } "character literal may only contain one codepoint: '" }"#);

//    	test_parse_analysis("pub extern crate futures;",
//    		r#""#);
    }

    fn test_parse_analysis(source : &str, expected_msgs : &str) {
    	let result = parse_analysis(source, String::new()).ok().unwrap();
    	let mut result : &str = &result;

    	result = assert_surrounding_string("RUST_PARSE_DESCRIBE 1.0 {", result, "}");

    	result = assert_starts_with("MESSAGES {", result.trim());
    	result = assert_starts_with(expected_msgs.trim(), result.trim());
    	result = assert_starts_with("}", result.trim());

    	assert_eq!(result, "");
    }

    fn assert_surrounding_string<'a> (start : &str, string : &'a str, end : &str) -> &'a str {
    	let mut string : &str = string;

    	string = assert_starts_with(start, string);
    	string = assert_ends_with(string, end);

    	return string;
    }

    fn assert_starts_with<'a> (start : &str, string : &'a str) -> &'a str {
    	assert!(string.starts_with(start), "`{}` does not start with `{}`", string, start);
    	return &string[start.len() .. ];
    }

    fn assert_ends_with<'a> (string : &'a str, end : &str) -> &'a str {
    	assert!(string.ends_with(end), "`{}` does not end with `{}`", string, end);
    	return &string[0 .. string.len() - end.len()];
    }

}
<commit_msg>normalize whitespace<commit_after>// Copyright 2015 Bruno Medeiros
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use ::util::core::*;
use ::util::string::*;
use ::source_model::*;

use ::syntex_syntax::syntax::ast;
use ::syntex_syntax::parse::{ self, ParseSess };
use ::syntex_syntax::visit;
use ::syntex_syntax::codemap:: { self, Span, CodeMap};
use ::syntex_syntax::errors:: { Handler, RenderSpan, Level, emitter };

use std::boxed::Box;
use std::path::Path;

use ::token_writer::TokenWriter;

use std::cell::RefCell;
use std::rc::*;
use std::io;
use std::io::Write;
use std::fmt;

/* -----------------  ----------------- */

pub fn parse_analysis_forStdout(source : &str) {
    parse_analysis(source, StdoutWrite(io::stdout())).ok();
    println!("");
    io::stdout().flush().ok();
}

pub fn parse_analysis<T : fmt::Write + 'static>(source : &str, out : T) -> GResult<T> {
    let (messages, elements) = parse_crate_with_messages(source);

    let outRc = Rc::new(RefCell::new(out));
    try!(write_parse_analysis_do(messages, elements, outRc.clone()));
    let res = unwrap_Rc_RefCell(outRc);
    return Ok(res);
}

use std::thread;
use std::sync::{Arc, Mutex};

pub fn parse_crate_with_messages(source: &str) -> (Vec<SourceMessage>, Vec<StructureElement>) {

    let messages = Arc::new(Mutex::new(vec![]));
    let elements =
    {
        let source = String::from(source);
        let messages = messages.clone();

        let worker_thread = thread::Builder::new().name("parser_thread".to_string()).spawn(move || {
            parse_crate_with_messages_do(&source, messages)
        }).unwrap();

        worker_thread.join().unwrap_or(vec![])
    };

    let messages : Mutex<Vec<SourceMessage>> = Arc::try_unwrap(messages).ok().unwrap();
    let messages : Vec<SourceMessage> = messages.into_inner().unwrap();

    return (messages, elements);
}

pub fn parse_crate_with_messages_do(source: &str, messages: Arc<Mutex<Vec<SourceMessage>>>)
    -> Vec<StructureElement>
{
    use ::structure_visitor::StructureVisitor;

    let mut elements = vec![];

    let fileLoader = Box::new(DummyFileLoader::new());
    let codemap = Rc::new(CodeMap::with_file_loader(fileLoader));

    let krate = parse_crate(source, codemap.clone(), messages.clone());

    if let Some(krate) = krate {
        let mut visitor : StructureVisitor = StructureVisitor::new(&codemap);
        visit::walk_crate(&mut visitor, &krate);

        elements = visitor.elements;
    }
    return elements;
}

/* -----------------  ----------------- */

use std::ffi::OsStr;

/// A FileLoader that loads any file successfully
pub struct DummyFileLoader {
       modName : &'static OsStr,
}

impl DummyFileLoader {
    fn new() -> DummyFileLoader {
        DummyFileLoader { modName : OsStr::new("mod.rs") }
    }
}

impl codemap::FileLoader for DummyFileLoader {
    fn file_exists(&self, path: &Path) -> bool {
        return path.file_name() == Some(self.modName);
    }

    fn read_file(&self, _path: &Path) -> io::Result<String> {
        Ok(String::new())
    }
}

struct MessagesHandler {
    codemap : Rc<CodeMap>,
    messages : Arc<Mutex<Vec<SourceMessage>>>,
}

fn parse_crate<'a>(source: &str, codemap: Rc<CodeMap>, messages: Arc<Mutex<Vec<SourceMessage>>>) -> Option<ast::Crate>
{
    let emitter = MessagesHandler::new(codemap.clone(), messages.clone());

    let handler = Handler::with_emitter(true, false, Box::new(emitter));
    let sess = ParseSess::with_span_handler(handler, codemap.clone());

    let krate_result = parse_crate_do(source, &sess);

    return match krate_result {
        Ok(_krate) => {
            Some(_krate)
        }
        Err(mut db) => {
            db.emit();
            None
        }
    }
}

pub fn parse_crate_do<'a>(source : &str, sess : &'a ParseSess) -> parse::PResult<'a, ast::Crate>
{
    let source = source.to_string();

    let cfg = vec![];
    let name = "_file_module_".to_string();

//    We inlined: let mut parser = parse::new_parser_from_source_str(&sess, cfg, name, source);

    let filemap = sess.codemap().new_filemap(name, source);

    // filemap_to_tts but without a panic
    let tts =
    {
        let cfg = Vec::new();
        let srdr = parse::lexer::StringReader::new(&sess.span_diagnostic, filemap);
        let mut p1 = parse::parser::Parser::new(sess, cfg, Box::new(srdr));

        try!(p1.parse_all_token_trees())
    };

    let trdr = parse::lexer::new_tt_reader(&sess.span_diagnostic, None, None, tts);
    let mut parser = parse::parser::Parser::new(sess, cfg, Box::new(trdr));

    return parser.parse_crate_mod();
}

impl MessagesHandler {

    fn new(codemap: Rc<CodeMap>, messages: Arc<Mutex<Vec<SourceMessage>>>) -> MessagesHandler {
        MessagesHandler { codemap : codemap, messages : messages }
    }

    fn writeMessage_handled(&mut self, sourcerange : Option<SourceRange>, msg: &str, severity: Severity) {

        let msg = SourceMessage{ severity : severity , sourcerange : sourcerange,  message : String::from(msg) };

        let mut messages = self.messages.lock().unwrap();
        messages.push(msg);
    }

}

impl emitter::Emitter for MessagesHandler {

    fn emit(&mut self, span: Option<Span>, msg: &str, code: Option<&str>, lvl: Level) {

        if let Some(code) = code {
               io::stderr().write_fmt(format_args!("Code: {}\n", code)).unwrap();
               panic!("What is code: Option<&str>??");
        }

        let sourcerange = match span {
            Some(span) => Some(SourceRange::new(&self.codemap, span)),
            None => None,
        };

        self.writeMessage_handled(sourcerange, msg, level_to_status_level(lvl));
    }

    fn custom_emit(&mut self, _: RenderSpan, msg: &str, lvl: Level) {
        match lvl { Level::Help | Level::Note => return, _ => () }

        self.writeMessage_handled(None, msg, level_to_status_level(lvl));
    }

}

fn level_to_status_level(lvl: Level) -> Severity {
    match lvl {
        Level::Bug => panic!("Level::BUG"),
        Level::Cancelled => panic!("Level::CANCELLED"),
        Level::Help | Level::Note => Severity::INFO,
        Level::Warning => Severity::WARNING,
        Level::Error | Level::Fatal => Severity::ERROR,
    }
}

impl MessagesHandler {
}

/* ----------------- describe writting ----------------- */

pub fn write_parse_analysis_do(messages: Vec<SourceMessage>, elements: Vec<StructureElement>,
    out : Rc<RefCell<fmt::Write>>) -> Void {

    let mut tokenWriter = TokenWriter { out : out };

    try!(tokenWriter.writeRaw("RUST_PARSE_DESCRIBE 1.0 {\n"));
    try!(write_parse_analysis_contents(messages, elements, &mut tokenWriter));
    try!(tokenWriter.writeRaw("\n}"));

    Ok(())
}

pub fn write_parse_analysis_contents(messages: Vec<SourceMessage>, elements: Vec<StructureElement>,
    tokenWriter : &mut TokenWriter) -> Void {

    try!(tokenWriter.writeRaw("MESSAGES {\n"));
    for msg in messages {
        try!(output_message(tokenWriter, msg.sourcerange, &msg.message, &msg.severity));
    }
    try!(tokenWriter.writeRaw("}\n"));

    for element in elements {
        try!(write_structure_element(tokenWriter, &element, 0));
    }

    Ok(())
}

fn output_message(tokenWriter: &mut TokenWriter, opt_sr : Option<SourceRange>, msg: & str, lvl: &Severity)
    -> Void
{

    try!(tokenWriter.writeRaw("{ "));

    try!(outputString_Level(&lvl, tokenWriter));

    try!(outputString_optSourceRange(&opt_sr, tokenWriter));

    try!(tokenWriter.writeStringToken(msg));

    try!(tokenWriter.writeRaw("}\n"));

    Ok(())
}

pub fn outputString_Level(lvl : &Severity, writer : &mut TokenWriter) -> Void {

    try!(writer.writeRawToken(lvl.to_string()));

    Ok(())
}

pub fn outputString_SourceRange(sr : &SourceRange, tw : &mut TokenWriter) -> Void {
    try!(tw.writeRaw("{ "));
    {
        let mut out = tw.getCharOut();
        try!(out.write_fmt(format_args!("{}:{} {}:{} ",
            sr.start_pos.line-1, sr.start_pos.col.0,
            sr.end_pos.line-1, sr.end_pos.col.0,
        )));
    }
    try!(tw.writeRaw("}"));

    Ok(())
}

pub fn outputString_optSourceRange(sr : &Option<SourceRange>, writer : &mut TokenWriter) -> Void {

    match sr {
        &None => try!(writer.writeRaw("{ }")) ,
        &Some(ref sr) => try!(outputString_SourceRange(sr, writer)) ,
    }

    try!(writer.writeRaw(" "));

    Ok(())
}

pub fn write_indent(tokenWriter : &mut TokenWriter, level : u32) -> Void {
    try!(writeNTimes(&mut *tokenWriter.getCharOut(), ' ', level * 2));
    Ok(())
}

pub fn write_structure_element(tw : &mut TokenWriter, element: &StructureElement, level: u32) -> Void
{
    try!(tw.writeRawToken(element.kind.to_String()));

    try!(tw.writeRaw("{ "));

    try!(tw.writeStringToken(&element.name));

    try!(outputString_SourceRange(&element.sourcerange, tw));

    try!(tw.getCharOut().write_str(" {}")); // name source range, Not Supported

    try!(tw.getCharOut().write_str(" "));
    try!(tw.writeStringToken(&element.type_desc));

    try!(tw.getCharOut().write_str("{}")); // attribs, Not Supported

    if element.children.is_empty() {
        try!(tw.getCharOut().write_str(" "));
    } else {
        let level = level + 1;

        for child in &element.children {
            try!(tw.getCharOut().write_str("\n"));
            try!(write_indent(tw, level));
            try!(write_structure_element(tw, child, level));
        }

        try!(tw.getCharOut().write_str("\n"));
        try!(write_indent(tw, level-1));
    }

    try!(tw.getCharOut().write_str("}"));

    Ok(())
}

#[cfg(test)]
mod parse_describe_tests {

    use parse_describe::*;
    use source_model::*;
    use token_writer::TokenWriter;
    use util::core::*;

    use std::rc::Rc;
    use std::cell::RefCell;

    #[test]
    fn tests_write_structure_element() {

        fn test_writeStructureElement(name : &str, kind : StructureElementKind, sr: SourceRange, type_desc : String,
            expected : &str,
        ) {
            let stringRc = Rc::new(RefCell::new(String::new()));
            {
                let name = String::from(name);
                let element = StructureElement { name: name, kind: kind, sourcerange: sr, type_desc: type_desc,
                     children: vec![]};
                let mut tw = TokenWriter { out : stringRc.clone() };

                write_structure_element(&mut tw, &element, 0).ok();
            }

            assert_eq!(unwrap_Rc_RefCell(stringRc).trim(), expected);
        }

        test_writeStructureElement("blah", StructureElementKind::Var, sourceRange(1, 0, 2, 5), "desc".to_string(),
            r#"Var { "blah" { 0:0 1:5 } {} "desc" {} }"#);
    }

    #[test]
    fn parse_analysis_tests() {
        test_parse_analysis("", "");

        test_parse_analysis(" #blah ", r#"{ ERROR { 0:2 0:6 } "expected `[`, found `blah`" }"#);

        test_parse_analysis("fn foo(\n  blah", r#"
    { ERROR { 1:6 1:6 } "this file contains an un-closed delimiter" }
    { INFO { 0:6 0:7 } "did you mean to close this delimiter?" }"#);

        // Test a lexer panic
        test_parse_analysis("const a = '",
            r#"{ ERROR { 0:10 0:11 } "character literal may only contain one codepoint: '" }"#);

//        test_parse_analysis("pub extern crate futures;",
//            r#""#);
    }

    fn test_parse_analysis(source : &str, expected_msgs : &str) {
        let result = parse_analysis(source, String::new()).ok().unwrap();
        let mut result : &str = &result;

        result = assert_surrounding_string("RUST_PARSE_DESCRIBE 1.0 {", result, "}");

        result = assert_starts_with("MESSAGES {", result.trim());
        result = assert_starts_with(expected_msgs.trim(), result.trim());
        result = assert_starts_with("}", result.trim());

        assert_eq!(result, "");
    }

    fn assert_surrounding_string<'a> (start : &str, string : &'a str, end : &str) -> &'a str {
        let mut string : &str = string;

        string = assert_starts_with(start, string);
        string = assert_ends_with(string, end);

        return string;
    }

    fn assert_starts_with<'a> (start : &str, string : &'a str) -> &'a str {
        assert!(string.starts_with(start), "`{}` does not start with `{}`", string, start);
        return &string[start.len() .. ];
    }

    fn assert_ends_with<'a> (string : &'a str, end : &str) -> &'a str {
        assert!(string.ends_with(end), "`{}` does not end with `{}`", string, end);
        return &string[0 .. string.len() - end.len()];
    }

}
<|endoftext|>


<commit_before>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use serde_generate::{cpp, java, python3, rust, test_utils, SourceInstaller};
use std::fs::File;
use std::io::Write;
use std::process::Command;
use tempfile::tempdir;

#[test]
fn test_that_python_code_parses() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let source_path = dir.path().join("test.py");
    let mut source = File::create(&source_path).unwrap();
    python3::output(&mut source, &registry).unwrap();

    let python_path = format!(
        "{}:runtime/python",
        std::env::var("PYTHONPATH").unwrap_or_default()
    );
    let status = Command::new("python3")
        .arg(source_path)
        .env("PYTHONPATH", python_path)
        .status()
        .unwrap();
    assert!(status.success());
}

#[test]
fn test_that_installed_python_code_passes_pyre_check() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();

    let installer = python3::Installer::new(dir.path().join("src"), /* serde package */ None);
    installer.install_module("test", &registry).unwrap();
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    // Sadly, we have to manage numpy typeshed manually for now until the next release of numpy.
    let status = Command::new("cp")
        .arg("-r")
        .arg("runtime/python/typeshed")
        .arg(dir.path())
        .status()
        .unwrap();
    assert!(status.success());

    let mut pyre_config = File::create(dir.path().join(".pyre_configuration")).unwrap();
    writeln!(
        &mut pyre_config,
        r#"{{
  "source_directories": [
    "src"
  ],
  "search_path": [
    "typeshed"
  ]
}}"#,
    )
    .unwrap();

    let status = Command::new("pyre")
        .current_dir(dir.path())
        .arg("check")
        .status()
        .unwrap();
    assert!(status.success());
}

#[test]
fn test_that_cpp_code_compiles() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let header_path = dir.path().join("test.hpp");
    let mut header = File::create(&header_path).unwrap();
    cpp::output(&mut header, &registry, None).unwrap();

    let source_path = dir.path().join("test.cpp");
    let mut source = File::create(&source_path).unwrap();
    writeln!(
        source,
        r#"
#include "bincode.hpp"
#include "test.hpp"
"#
    )
    .unwrap();

    let output = Command::new("clang++")
        .arg("--std=c++17")
        .arg("-c")
        .arg("-o")
        .arg(dir.path().join("test.o"))
        .arg("-I")
        .arg("runtime/cpp")
        .arg(source_path)
        .output()
        .unwrap();
    assert_eq!(String::new(), String::from_utf8_lossy(&output.stderr));
    assert!(output.status.success());
}

#[test]
fn test_that_cpp_code_links() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let header_path = dir.path().join("test.hpp");
    let mut header = File::create(&header_path).unwrap();
    cpp::output(&mut header, &registry, None).unwrap();

    let source_path = dir.path().join("lib.cpp");
    let mut source = File::create(&source_path).unwrap();
    writeln!(
        source,
        r#"
#include "lcs.hpp"
#include "test.hpp"

using namespace serde;

std::vector<uint8_t> serialize_data(SerdeData data) {{
    auto serializer = LcsSerializer();
    Serializable<SerdeData>::serialize(data, serializer);
    return std::move(serializer).bytes();
}}

SerdeData deserialize_data(const std::vector<uint8_t> &input) {{
    auto deserializer = LcsDeserializer(input);
    return Deserializable<SerdeData>::deserialize(deserializer);
}}
"#
    )
    .unwrap();

    let source_path = dir.path().join("main.cpp");
    let mut source = File::create(&source_path).unwrap();
    writeln!(
        source,
        r#"
#include "test.hpp"

using namespace serde;

extern std::vector<uint8_t> serialize_data(SerdeData data);

extern SerdeData deserialize_data(const std::vector<uint8_t> &bytes);

bool test(const std::vector<uint8_t>& input) {{
    auto output = serialize_data(deserialize_data(input));
    return input == output;
}}

int main() {{
    // dummy
    return test({{}});
}}
"#
    )
    .unwrap();

    let status = Command::new("clang++")
        .arg("--std=c++17")
        .arg("-I")
        .arg("runtime/cpp")
        .arg("-o")
        .arg(dir.path().join("main"))
        .arg(dir.path().join("lib.cpp"))
        .arg(dir.path().join("main.cpp"))
        .status()
        .unwrap();
    assert!(status.success());
}

// Quick test using rustc directly.
#[test]
fn test_that_rust_code_compiles() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let source_path = dir.path().join("test.rs");
    let mut source = File::create(&source_path).unwrap();
    rust::output(&mut source, /* with_derive_macros */ false, &registry).unwrap();

    let output = Command::new("rustc")
        .current_dir(dir.path())
        .arg("--crate-type")
        .arg("lib")
        .arg("--edition")
        .arg("2018")
        .arg(source_path)
        .output()
        .unwrap();
    assert_eq!(String::new(), String::from_utf8_lossy(&output.stderr));
    assert!(output.status.success());
}

// Full test using cargo. This may take a while.
#[test]
fn test_that_rust_code_compiles_with_derive_macros() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    std::fs::write(
        dir.path().join("Cargo.toml"),
        r#"[package]
name = "testing"
version = "0.1.0"
edition = "2018"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_bytes = "0.11"

[workspace]
"#,
    )
    .unwrap();
    std::fs::create_dir(dir.path().join("src")).unwrap();
    let source_path = dir.path().join("src/lib.rs");
    let mut source = File::create(&source_path).unwrap();
    rust::output(&mut source, /* with_derive_macros */ true, &registry).unwrap();
    // Use a stable `target` dir to avoid downloading and recompiling crates everytime.
    let target_dir = std::env::current_dir().unwrap().join("../target");
    let output = Command::new("cargo")
        .current_dir(dir.path())
        .arg("build")
        .arg("--target-dir")
        .arg(target_dir)
        .output()
        .unwrap();
    assert!(output.status.success());
}

#[test]
fn test_that_java_code_compiles() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();

    let source_path = dir.path().join("Test.java");
    let mut source = File::create(&source_path).unwrap();
    java::output(&mut source, &registry, "Test").unwrap();

    let paths = std::iter::empty()
        .chain(std::fs::read_dir("runtime/java/com/facebook/serde").unwrap())
        .chain(std::fs::read_dir("runtime/java/com/facebook/bincode").unwrap())
        .map(|e| e.unwrap().path());
    let status = Command::new("javac")
        .arg("-Xlint")
        .arg("-d")
        .arg(dir.path())
        .args(paths)
        .status()
        .unwrap();
    assert!(status.success());

    let status = Command::new("javac")
        .arg("-Xlint")
        .arg("-cp")
        .arg(dir.path())
        .arg("-d")
        .arg(dir.path())
        .arg(source_path)
        .status()
        .unwrap();
    assert!(status.success());
}
<commit_msg>[serde generate] add test for comments and definitions + clean up<commit_after>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use serde_generate::{cpp, java, python3, rust, test_utils, SourceInstaller};
use std::fs::File;
use std::io::Write;
use std::process::Command;
use tempfile::tempdir;

#[test]
fn test_that_python_code_parses() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let source_path = dir.path().join("test.py");
    let mut source = File::create(&source_path).unwrap();
    python3::output(&mut source, &registry).unwrap();

    let python_path = format!(
        "{}:runtime/python",
        std::env::var("PYTHONPATH").unwrap_or_default()
    );
    let status = Command::new("python3")
        .arg(source_path)
        .env("PYTHONPATH", python_path)
        .status()
        .unwrap();
    assert!(status.success());
}

#[test]
fn test_that_installed_python_code_passes_pyre_check() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();

    let installer = python3::Installer::new(dir.path().join("src"), /* serde package */ None);
    installer.install_module("test", &registry).unwrap();
    installer.install_serde_runtime().unwrap();
    installer.install_bincode_runtime().unwrap();
    installer.install_lcs_runtime().unwrap();

    // Sadly, we have to manage numpy typeshed manually for now until the next release of numpy.
    let status = Command::new("cp")
        .arg("-r")
        .arg("runtime/python/typeshed")
        .arg(dir.path())
        .status()
        .unwrap();
    assert!(status.success());

    let mut pyre_config = File::create(dir.path().join(".pyre_configuration")).unwrap();
    writeln!(
        &mut pyre_config,
        r#"{{
  "source_directories": [
    "src"
  ],
  "search_path": [
    "typeshed"
  ]
}}"#,
    )
    .unwrap();

    let status = Command::new("pyre")
        .current_dir(dir.path())
        .arg("check")
        .status()
        .unwrap();
    assert!(status.success());
}

#[test]
fn test_that_cpp_code_compiles() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let header_path = dir.path().join("test.hpp");
    let mut header = File::create(&header_path).unwrap();
    cpp::output(&mut header, &registry, None).unwrap();

    let source_path = dir.path().join("test.cpp");
    let mut source = File::create(&source_path).unwrap();
    writeln!(
        source,
        r#"
#include "bincode.hpp"
#include "test.hpp"
"#
    )
    .unwrap();

    let status = Command::new("clang++")
        .arg("--std=c++17")
        .arg("-c")
        .arg("-o")
        .arg(dir.path().join("test.o"))
        .arg("-I")
        .arg("runtime/cpp")
        .arg(source_path)
        .status()
        .unwrap();
    assert!(status.success());
}

#[test]
fn test_that_cpp_code_links() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let header_path = dir.path().join("test.hpp");
    let mut header = File::create(&header_path).unwrap();
    cpp::output(&mut header, &registry, None).unwrap();

    let source_path = dir.path().join("lib.cpp");
    let mut source = File::create(&source_path).unwrap();
    writeln!(
        source,
        r#"
#include "lcs.hpp"
#include "test.hpp"

using namespace serde;

std::vector<uint8_t> serialize_data(SerdeData data) {{
    auto serializer = LcsSerializer();
    Serializable<SerdeData>::serialize(data, serializer);
    return std::move(serializer).bytes();
}}

SerdeData deserialize_data(const std::vector<uint8_t> &input) {{
    auto deserializer = LcsDeserializer(input);
    return Deserializable<SerdeData>::deserialize(deserializer);
}}
"#
    )
    .unwrap();

    let source_path = dir.path().join("main.cpp");
    let mut source = File::create(&source_path).unwrap();
    writeln!(
        source,
        r#"
#include "test.hpp"

using namespace serde;

extern std::vector<uint8_t> serialize_data(SerdeData data);

extern SerdeData deserialize_data(const std::vector<uint8_t> &bytes);

bool test(const std::vector<uint8_t>& input) {{
    auto output = serialize_data(deserialize_data(input));
    return input == output;
}}

int main() {{
    // dummy
    return test({{}});
}}
"#
    )
    .unwrap();

    let status = Command::new("clang++")
        .arg("--std=c++17")
        .arg("-I")
        .arg("runtime/cpp")
        .arg("-o")
        .arg(dir.path().join("main"))
        .arg(dir.path().join("lib.cpp"))
        .arg(dir.path().join("main.cpp"))
        .status()
        .unwrap();
    assert!(status.success());
}

// Quick test using rustc directly.
#[test]
fn test_that_rust_code_compiles() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let source_path = dir.path().join("test.rs");
    let mut source = File::create(&source_path).unwrap();
    rust::output(&mut source, /* with_derive_macros */ false, &registry).unwrap();

    let status = Command::new("rustc")
        .current_dir(dir.path())
        .arg("--crate-type")
        .arg("lib")
        .arg("--edition")
        .arg("2018")
        .arg(source_path)
        .status()
        .unwrap();
    assert!(status.success());
}

// Quick test using rustc directly.
#[test]
fn test_that_rust_code_with_comments_compiles() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    let source_path = dir.path().join("test.rs");
    let mut source = File::create(&source_path).unwrap();
    let comments = vec![(vec!["SerdeData".to_string()], "Some\ncomments".to_string())]
        .into_iter()
        .collect();
    let definitions = vec![("foo".to_string(), vec!["Map".to_string(), "Bytes".into()])]
        .into_iter()
        .collect();
    rust::output_with_external_dependencies_and_comments(
        &mut source,
        /* with_derive_macros */ false,
        &registry,
        &definitions,
        &comments,
    )
    .unwrap();

    // Comment was correctly generated.
    let content = std::fs::read_to_string(&source_path).unwrap();
    assert!(content.contains("/// Some\n/// comments\n"));

    let output = Command::new("rustc")
        .current_dir(dir.path())
        .arg("--crate-type")
        .arg("lib")
        .arg("--edition")
        .arg("2018")
        .arg(source_path.clone())
        .output()
        .unwrap();
    assert!(!output.status.success());

    // Externally defined names "Map" and "Bytes" have caused the usual imports to be
    // replaced by `use foo::{Map, Bytes}`, so we must add the definitions.
    writeln!(
        &mut source,
        r#"
mod foo {{
    pub type Map<K, V> = std::collections::BTreeMap<K, V>;
    pub type Bytes = Vec<u8>;
}}
"#
    )
    .unwrap();

    let status = Command::new("rustc")
        .current_dir(dir.path())
        .arg("--crate-type")
        .arg("lib")
        .arg("--edition")
        .arg("2018")
        .arg(source_path)
        .status()
        .unwrap();
    assert!(status.success());
}

// Full test using cargo. This may take a while.
#[test]
fn test_that_rust_code_compiles_with_derive_macros() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();
    std::fs::write(
        dir.path().join("Cargo.toml"),
        r#"[package]
name = "testing"
version = "0.1.0"
edition = "2018"

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_bytes = "0.11"

[workspace]
"#,
    )
    .unwrap();
    std::fs::create_dir(dir.path().join("src")).unwrap();
    let source_path = dir.path().join("src/lib.rs");
    let mut source = File::create(&source_path).unwrap();
    rust::output(&mut source, /* with_derive_macros */ true, &registry).unwrap();
    // Use a stable `target` dir to avoid downloading and recompiling crates everytime.
    let target_dir = std::env::current_dir().unwrap().join("../target");
    let status = Command::new("cargo")
        .current_dir(dir.path())
        .arg("build")
        .arg("--target-dir")
        .arg(target_dir)
        .status()
        .unwrap();
    assert!(status.success());
}

#[test]
fn test_that_java_code_compiles() {
    let registry = test_utils::get_registry().unwrap();
    let dir = tempdir().unwrap();

    let source_path = dir.path().join("Test.java");
    let mut source = File::create(&source_path).unwrap();
    java::output(&mut source, &registry, "Test").unwrap();

    let paths = std::iter::empty()
        .chain(std::fs::read_dir("runtime/java/com/facebook/serde").unwrap())
        .chain(std::fs::read_dir("runtime/java/com/facebook/bincode").unwrap())
        .map(|e| e.unwrap().path());
    let status = Command::new("javac")
        .arg("-Xlint")
        .arg("-d")
        .arg(dir.path())
        .args(paths)
        .status()
        .unwrap();
    assert!(status.success());

    let status = Command::new("javac")
        .arg("-Xlint")
        .arg("-cp")
        .arg(dir.path())
        .arg("-d")
        .arg(dir.path())
        .arg(source_path)
        .status()
        .unwrap();
    assert!(status.success());
}
<|endoftext|>


<commit_before>use std::path;
use std::result;
use std::cmp;

use sdl2::VideoSubsystem;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};
use sdl2::image::{LoadTexture, LoadSurface};
use sdl2::pixels::{Color, Palette, PixelFormatEnum};
use sdl2::ttf::Font;
use sdl2::surface::Surface;

use game::*;
use game::frontends::sdl::{Tileset, ExtraTileType};

use coord::Coord;
use colour::Rgb24;

const RENDERING_FAILED_MSG: &'static str = "Rendering failed";
const MESSAGE_LOG_NUM_LINES: usize = 4;
const MESSAGE_LOG_LINE_HEIGHT_PX: usize = 16;
const MESSAGE_LOG_HEIGHT_PX: usize = MESSAGE_LOG_LINE_HEIGHT_PX * MESSAGE_LOG_NUM_LINES;
const MESSAGE_LOG_PLAIN_COLOUR: Rgb24 = Rgb24 { red: 255, green: 255, blue: 255 };
const MESSAGE_LOG_PADDING_TOP_PX: usize = 16;

struct SdlCellInfo {
    fg: Option<Rect>,
    bg: Option<Rect>,
    moon: bool,
    visible: bool,
}

pub struct SdlKnowledgeRenderer<'a> {
    buffer: TileBuffer,
    sdl_renderer: Renderer<'static>,
    tile_texture: Texture,
    greyscale_tile_texture: Texture,
    font: Font<'a>,
    width: usize,
    height: usize,
    game_width_px: usize,
    game_height_px: usize,
    tileset: Tileset,
    message_log_position: Coord,
    clear_colour: Color,
    game_rect: Rect,
    message_log: Vec<Message>,
}

#[derive(Debug)]
pub enum SdlKnowledgeRendererError {
    WindowCreationFailure,
    RendererInitialisationFailure,
    TileLoadFailure,
}

impl<'a> SdlKnowledgeRenderer<'a> {

    fn index_to_rgb(index: u8) -> (u8, u8, u8) {
        let r = index >> 5;
        let g = (index >> 2) & 7;
        let b_small = index & 3;
        let b = if b_small == 0 {
            0
        } else {
            (b_small * 2) + 1
        };

        (r, g, b)
    }

    fn create_greyscale_tile_texture(renderer: &Renderer, tile_path: &path::PathBuf) -> result::Result<Texture, String> {
        let mut rgb332_colours = Vec::new();
        for i in 0u32..256 {

            let (r, g, b) = Self::index_to_rgb(i as u8);

            rgb332_colours.push(Color::RGB(r as u8 * 36, g as u8 * 36, b as u8 * 36));
        }
        let rgb332_palette = Palette::with_colors(&rgb332_colours)?;

        let mut greyscale_colours = Vec::new();
        for i in 0i32..256 {

            let (r, g, b) = Self::index_to_rgb(i as u8);

            let max = cmp::max(r, cmp::max(g, b));

            let normalised = if max == 0 {
                0
            } else {
                32 + 6 * max
            };

            let r = normalised;
            let g = normalised;
            let b = normalised;

            greyscale_colours.push(Color::RGB(r as u8, g as u8, b as u8));
        }
        let greyscale_palette = Palette::with_colors(&greyscale_colours)?;

        let mut dummy_surface = Surface::new(1, 1, PixelFormatEnum::Index8)?;
        dummy_surface.set_palette(&rgb332_palette)?;

        let pixel_format = dummy_surface.pixel_format();

        let tile_surface = Surface::from_file(&tile_path)?;
        let mut greyscale_tile_surface = tile_surface.convert(&pixel_format)?;
        greyscale_tile_surface.set_palette(&greyscale_palette)?;

        renderer.create_texture_from_surface(greyscale_tile_surface).map_err(|e| format!("{}", e))
    }

    pub fn new(video: &VideoSubsystem,
               title: &str,
               game_width: usize,
               game_height: usize,
               tile_path: path::PathBuf,
               tileset: Tileset,
               font: Font<'a>) -> result::Result<Self, SdlKnowledgeRendererError> {

        let game_width_px = (game_width * tileset.tile_width()) as usize;
        let game_height_px = (game_height * tileset.tile_height()) as usize;
        let width_px = game_width_px as u32;
        let height_px = (game_height_px + MESSAGE_LOG_HEIGHT_PX + MESSAGE_LOG_PADDING_TOP_PX) as u32;
        let window = video.window(title, width_px, height_px)
            .build()
            .map_err(|_| SdlKnowledgeRendererError::WindowCreationFailure)?;

        let renderer = window.renderer()
            .build()
            .map_err(|_| SdlKnowledgeRendererError::RendererInitialisationFailure)?;

        let tile_texture = renderer.load_texture(&tile_path).map_err(|_| SdlKnowledgeRendererError::TileLoadFailure)?;
        let greyscale_tile_texture = Self::create_greyscale_tile_texture(&renderer, &tile_path).unwrap();

        let mut message_log = Vec::new();
        for _ in 0..MESSAGE_LOG_NUM_LINES {
            message_log.push(Message::new());
        }

        Ok(SdlKnowledgeRenderer {
            buffer: TileBuffer::new(game_width, game_height),
            sdl_renderer: renderer,
            tile_texture: tile_texture,
            greyscale_tile_texture: greyscale_tile_texture,
            font: font,
            width: game_width,
            height: game_height,
            game_width_px: width_px as usize,
            game_height_px: game_height_px,
            tileset: tileset,
            clear_colour: Color::RGB(0, 0, 0),
            game_rect: Rect::new(0, 0, game_width_px as u32, game_height_px as u32),
            message_log: message_log,
            message_log_position: Coord::new(0, (game_height_px + MESSAGE_LOG_PADDING_TOP_PX) as isize),
        })
    }

    fn tile_width(&self) -> usize {
        self.tileset.tile_width()
    }

    fn tile_height(&self) -> usize {
        self.tileset.tile_height()
    }

    fn screen_rect(&self, coord: Coord) -> Rect {
        let width = self.tile_width() as i32;
        let height = self.tile_height() as i32;

        Rect::new(coord.x as i32 * width, coord.y as i32 * height, width as u32, height as u32)
    }

    fn simple_tile(tile: frontends::sdl::ComplexTile, is_front: bool) -> frontends::sdl::SimpleTile {
        match tile {
            frontends::sdl::ComplexTile::Simple(s) => s,
            frontends::sdl::ComplexTile::Wall { front, back } => {
                if is_front {
                    front
                } else {
                    back
                }
            }
        }
    }

    fn to_sdl_info(&self, cell: &CellDrawInfo) -> SdlCellInfo {
        let mut info = SdlCellInfo {
            moon: cell.moon,
            visible: cell.visible,
            fg: None,
            bg: None,
        };

        if let Some(bg_type) = cell.background {
            let complex_tile = self.tileset.resolve(bg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            info.bg = tile.background();
            info.fg = tile.foreground();
        }

        if let Some(fg_type) = cell.foreground {
            let complex_tile = self.tileset.resolve(fg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            if let Some(fg) = tile.foreground() {
                info.fg = Some(fg);
            }
        }

        info
    }

    fn clear_internal(&mut self) {
        self.sdl_renderer.set_draw_color(self.clear_colour);
        self.sdl_renderer.draw_rect(self.game_rect).expect("Failed to clear screen");
    }

    fn draw_internal(&mut self) {

        let blank = *self.tileset.resolve_extra(ExtraTileType::Blank);
        let moon = *self.tileset.resolve_extra(ExtraTileType::Moon);

        for (coord, cell) in izip!(self.buffer.coord_iter(), self.buffer.iter()) {
            let rect = self.screen_rect(coord);
            let info = self.to_sdl_info(cell);

            self.sdl_renderer.copy(&self.tile_texture, Some(blank), Some(rect)).expect(RENDERING_FAILED_MSG);

            let texture = if info.visible {
                &self.tile_texture
            } else {
                &self.greyscale_tile_texture
            };

            if let Some(bg_rect) = info.bg {
                self.sdl_renderer.copy(texture, Some(bg_rect), Some(rect)).expect(RENDERING_FAILED_MSG);
            }
            if let Some(fg_rect) = info.fg {
                self.sdl_renderer.copy(texture, Some(fg_rect), Some(rect)).expect(RENDERING_FAILED_MSG);
            }

            if info.moon && info.visible{
                self.sdl_renderer.copy(&self.tile_texture, Some(moon), Some(rect)).expect(RENDERING_FAILED_MSG);
            }
        }
    }

    fn draw_overlay_internal(&mut self, overlay: &RenderOverlay) {
        let aim_line_bg = *self.tileset.resolve_extra(ExtraTileType::AimLine);
        if let Some(ref aim_line) = overlay.aim_line {
            for coord in aim_line.iter() {
                if let Some(cell) = self.buffer.get(coord) {
                    let rect = self.screen_rect(coord);
                    let info = self.to_sdl_info(cell);

                    self.sdl_renderer.copy(&self.tile_texture, Some(aim_line_bg), Some(rect)).expect(RENDERING_FAILED_MSG);
                    if let Some(fg_rect) = info.fg {
                        self.sdl_renderer.copy(&self.tile_texture, Some(fg_rect), Some(rect)).expect(RENDERING_FAILED_MSG);
                    }
                }
            }
        }
    }

    fn draw_message_log_internal(&mut self) {

        let mut cursor = self.message_log_position;

        for line in &self.message_log {
            for part in line {
                let (colour, string) = match part {
                    &MessagePart::Plain(ref s) => (MESSAGE_LOG_PLAIN_COLOUR, s),
                    &MessagePart::Colour(c, ref s) => (c, s),
                };

                let sdl_colour = rgb24_to_sdl_colour(colour);
                let surface = self.font.render(string.as_ref()).solid(sdl_colour).expect("Failed to create text surface");
                let texture = self.sdl_renderer.create_texture_from_surface(&surface).expect("Failed to create text texture");

                // assume fixed-width, square font
                let text_width = string.len() * MESSAGE_LOG_LINE_HEIGHT_PX;
                let text_rect = Rect::new(cursor.x as i32, cursor.y as i32, text_width as u32,
                                          MESSAGE_LOG_LINE_HEIGHT_PX as u32);
                self.sdl_renderer.copy(&texture, None, Some(text_rect)).expect("Failed to render text");
                cursor.x += text_width as isize;
            }
            cursor.x = self.message_log_position.x;
            cursor.y += MESSAGE_LOG_LINE_HEIGHT_PX as isize;
        }
    }
}

impl<'a> KnowledgeRenderer for SdlKnowledgeRenderer<'a> {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn world_offset(&self) -> Coord {
        Coord::new(0, 0)
    }

    fn update(&mut self, knowledge: &DrawableKnowledgeLevel, turn_id: u64, _position: Coord) {
        // TODO handle scrolling
        self.buffer.update(knowledge, turn_id, None);
    }

    fn draw(&mut self) {
        self.clear_internal();
        self.draw_internal();
        self.draw_message_log_internal();
        self.sdl_renderer.present();
    }

    fn draw_with_overlay(&mut self, overlay: &RenderOverlay) {
        self.clear_internal();
        self.draw_internal();
        self.draw_overlay_internal(overlay);
        self.draw_message_log_internal();
        self.sdl_renderer.present();
    }

    fn update_log(&mut self, messages: &MessageLog, language: &Box<Language>) {
        for (log_entry, message) in izip!(messages.tail(MESSAGE_LOG_NUM_LINES), &mut self.message_log) {
            language.translate_repeated(log_entry.message, log_entry.repeated, message);
        }
    }
}

fn rgb24_to_sdl_colour(rgb24: Rgb24) -> Color {
    Color::RGB(rgb24.red, rgb24.green, rgb24.blue)
}
<commit_msg>Clearing the message area before printing<commit_after>use std::path;
use std::result;
use std::cmp;

use sdl2::VideoSubsystem;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};
use sdl2::image::{LoadTexture, LoadSurface};
use sdl2::pixels::{Color, Palette, PixelFormatEnum};
use sdl2::ttf::Font;
use sdl2::surface::Surface;

use game::*;
use game::frontends::sdl::{Tileset, ExtraTileType};

use coord::Coord;
use colour::Rgb24;

const RENDERING_FAILED_MSG: &'static str = "Rendering failed";
const MESSAGE_LOG_NUM_LINES: usize = 4;
const MESSAGE_LOG_LINE_HEIGHT_PX: usize = 16;
const MESSAGE_LOG_HEIGHT_PX: usize = MESSAGE_LOG_LINE_HEIGHT_PX * MESSAGE_LOG_NUM_LINES;
const MESSAGE_LOG_PLAIN_COLOUR: Rgb24 = Rgb24 { red: 255, green: 255, blue: 255 };
const MESSAGE_LOG_PADDING_TOP_PX: usize = 16;

struct SdlCellInfo {
    fg: Option<Rect>,
    bg: Option<Rect>,
    moon: bool,
    visible: bool,
}

pub struct SdlKnowledgeRenderer<'a> {
    buffer: TileBuffer,
    sdl_renderer: Renderer<'static>,
    tile_texture: Texture,
    greyscale_tile_texture: Texture,
    font: Font<'a>,
    width: usize,
    height: usize,
    game_width_px: usize,
    game_height_px: usize,
    tileset: Tileset,
    clear_colour: Color,
    game_rect: Rect,
    message_log_position: Coord,
    message_log_rect: Rect,
    message_log: Vec<Message>,
}

#[derive(Debug)]
pub enum SdlKnowledgeRendererError {
    WindowCreationFailure,
    RendererInitialisationFailure,
    TileLoadFailure,
}

impl<'a> SdlKnowledgeRenderer<'a> {

    fn index_to_rgb(index: u8) -> (u8, u8, u8) {
        let r = index >> 5;
        let g = (index >> 2) & 7;
        let b_small = index & 3;
        let b = if b_small == 0 {
            0
        } else {
            (b_small * 2) + 1
        };

        (r, g, b)
    }

    fn create_greyscale_tile_texture(renderer: &Renderer, tile_path: &path::PathBuf) -> result::Result<Texture, String> {
        let mut rgb332_colours = Vec::new();
        for i in 0u32..256 {

            let (r, g, b) = Self::index_to_rgb(i as u8);

            rgb332_colours.push(Color::RGB(r as u8 * 36, g as u8 * 36, b as u8 * 36));
        }
        let rgb332_palette = Palette::with_colors(&rgb332_colours)?;

        let mut greyscale_colours = Vec::new();
        for i in 0i32..256 {

            let (r, g, b) = Self::index_to_rgb(i as u8);

            let max = cmp::max(r, cmp::max(g, b));

            let normalised = if max == 0 {
                0
            } else {
                32 + 6 * max
            };

            let r = normalised;
            let g = normalised;
            let b = normalised;

            greyscale_colours.push(Color::RGB(r as u8, g as u8, b as u8));
        }
        let greyscale_palette = Palette::with_colors(&greyscale_colours)?;

        let mut dummy_surface = Surface::new(1, 1, PixelFormatEnum::Index8)?;
        dummy_surface.set_palette(&rgb332_palette)?;

        let pixel_format = dummy_surface.pixel_format();

        let tile_surface = Surface::from_file(&tile_path)?;
        let mut greyscale_tile_surface = tile_surface.convert(&pixel_format)?;
        greyscale_tile_surface.set_palette(&greyscale_palette)?;

        renderer.create_texture_from_surface(greyscale_tile_surface).map_err(|e| format!("{}", e))
    }

    pub fn new(video: &VideoSubsystem,
               title: &str,
               game_width: usize,
               game_height: usize,
               tile_path: path::PathBuf,
               tileset: Tileset,
               font: Font<'a>) -> result::Result<Self, SdlKnowledgeRendererError> {

        let game_width_px = (game_width * tileset.tile_width()) as usize;
        let game_height_px = (game_height * tileset.tile_height()) as usize;
        let width_px = game_width_px as u32;
        let height_px = (game_height_px + MESSAGE_LOG_HEIGHT_PX + MESSAGE_LOG_PADDING_TOP_PX) as u32;
        let window = video.window(title, width_px, height_px)
            .build()
            .map_err(|_| SdlKnowledgeRendererError::WindowCreationFailure)?;

        let renderer = window.renderer()
            .build()
            .map_err(|_| SdlKnowledgeRendererError::RendererInitialisationFailure)?;

        let tile_texture = renderer.load_texture(&tile_path).map_err(|_| SdlKnowledgeRendererError::TileLoadFailure)?;
        let greyscale_tile_texture = Self::create_greyscale_tile_texture(&renderer, &tile_path).unwrap();

        let mut message_log = Vec::new();
        for _ in 0..MESSAGE_LOG_NUM_LINES {
            message_log.push(Message::new());
        }

        let message_log_position = Coord::new(0, (game_height_px + MESSAGE_LOG_PADDING_TOP_PX) as isize);
        let message_log_rect = Rect::new(message_log_position.x as i32,
                                         message_log_position.y as i32,
                                         width_px,
                                         MESSAGE_LOG_HEIGHT_PX as u32);

        Ok(SdlKnowledgeRenderer {
            buffer: TileBuffer::new(game_width, game_height),
            sdl_renderer: renderer,
            tile_texture: tile_texture,
            greyscale_tile_texture: greyscale_tile_texture,
            font: font,
            width: game_width,
            height: game_height,
            game_width_px: width_px as usize,
            game_height_px: game_height_px,
            tileset: tileset,
            clear_colour: Color::RGB(0, 0, 0),
            game_rect: Rect::new(0, 0, game_width_px as u32, game_height_px as u32),
            message_log_position: message_log_position,
            message_log_rect: message_log_rect,
            message_log: message_log,
        })
    }

    fn tile_width(&self) -> usize {
        self.tileset.tile_width()
    }

    fn tile_height(&self) -> usize {
        self.tileset.tile_height()
    }

    fn screen_rect(&self, coord: Coord) -> Rect {
        let width = self.tile_width() as i32;
        let height = self.tile_height() as i32;

        Rect::new(coord.x as i32 * width, coord.y as i32 * height, width as u32, height as u32)
    }

    fn simple_tile(tile: frontends::sdl::ComplexTile, is_front: bool) -> frontends::sdl::SimpleTile {
        match tile {
            frontends::sdl::ComplexTile::Simple(s) => s,
            frontends::sdl::ComplexTile::Wall { front, back } => {
                if is_front {
                    front
                } else {
                    back
                }
            }
        }
    }

    fn to_sdl_info(&self, cell: &CellDrawInfo) -> SdlCellInfo {
        let mut info = SdlCellInfo {
            moon: cell.moon,
            visible: cell.visible,
            fg: None,
            bg: None,
        };

        if let Some(bg_type) = cell.background {
            let complex_tile = self.tileset.resolve(bg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            info.bg = tile.background();
            info.fg = tile.foreground();
        }

        if let Some(fg_type) = cell.foreground {
            let complex_tile = self.tileset.resolve(fg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            if let Some(fg) = tile.foreground() {
                info.fg = Some(fg);
            }
        }

        info
    }

    fn clear_internal(&mut self) {
        self.sdl_renderer.set_draw_color(self.clear_colour);
        self.sdl_renderer.fill_rect(self.game_rect).expect("Failed to clear screen");
    }

    fn clear_message_log(&mut self) {
        self.sdl_renderer.set_draw_color(self.clear_colour);
        self.sdl_renderer.fill_rect(self.message_log_rect).expect("Failed to clear message_log");
    }

    fn draw_internal(&mut self) {

        let blank = *self.tileset.resolve_extra(ExtraTileType::Blank);
        let moon = *self.tileset.resolve_extra(ExtraTileType::Moon);

        for (coord, cell) in izip!(self.buffer.coord_iter(), self.buffer.iter()) {
            let rect = self.screen_rect(coord);
            let info = self.to_sdl_info(cell);

            self.sdl_renderer.copy(&self.tile_texture, Some(blank), Some(rect)).expect(RENDERING_FAILED_MSG);

            let texture = if info.visible {
                &self.tile_texture
            } else {
                &self.greyscale_tile_texture
            };

            if let Some(bg_rect) = info.bg {
                self.sdl_renderer.copy(texture, Some(bg_rect), Some(rect)).expect(RENDERING_FAILED_MSG);
            }
            if let Some(fg_rect) = info.fg {
                self.sdl_renderer.copy(texture, Some(fg_rect), Some(rect)).expect(RENDERING_FAILED_MSG);
            }

            if info.moon && info.visible{
                self.sdl_renderer.copy(&self.tile_texture, Some(moon), Some(rect)).expect(RENDERING_FAILED_MSG);
            }
        }
    }

    fn draw_overlay_internal(&mut self, overlay: &RenderOverlay) {
        let aim_line_bg = *self.tileset.resolve_extra(ExtraTileType::AimLine);
        if let Some(ref aim_line) = overlay.aim_line {
            for coord in aim_line.iter() {
                if let Some(cell) = self.buffer.get(coord) {
                    let rect = self.screen_rect(coord);
                    let info = self.to_sdl_info(cell);

                    self.sdl_renderer.copy(&self.tile_texture, Some(aim_line_bg), Some(rect)).expect(RENDERING_FAILED_MSG);
                    if let Some(fg_rect) = info.fg {
                        self.sdl_renderer.copy(&self.tile_texture, Some(fg_rect), Some(rect)).expect(RENDERING_FAILED_MSG);
                    }
                }
            }
        }
    }

    fn draw_message_log_internal(&mut self) {

        self.clear_message_log();
        let mut cursor = self.message_log_position;

        for line in &self.message_log {
            for part in line {
                let (colour, string) = match part {
                    &MessagePart::Plain(ref s) => (MESSAGE_LOG_PLAIN_COLOUR, s),
                    &MessagePart::Colour(c, ref s) => (c, s),
                };

                let sdl_colour = rgb24_to_sdl_colour(colour);
                let surface = self.font.render(string.as_ref()).solid(sdl_colour).expect("Failed to create text surface");
                let texture = self.sdl_renderer.create_texture_from_surface(&surface).expect("Failed to create text texture");

                // assume fixed-width, square font
                let text_width = string.len() * MESSAGE_LOG_LINE_HEIGHT_PX;
                let text_rect = Rect::new(cursor.x as i32, cursor.y as i32, text_width as u32,
                                          MESSAGE_LOG_LINE_HEIGHT_PX as u32);
                self.sdl_renderer.copy(&texture, None, Some(text_rect)).expect("Failed to render text");
                cursor.x += text_width as isize;
            }
            cursor.x = self.message_log_position.x;
            cursor.y += MESSAGE_LOG_LINE_HEIGHT_PX as isize;
        }
    }
}

impl<'a> KnowledgeRenderer for SdlKnowledgeRenderer<'a> {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn world_offset(&self) -> Coord {
        Coord::new(0, 0)
    }

    fn update(&mut self, knowledge: &DrawableKnowledgeLevel, turn_id: u64, _position: Coord) {
        // TODO handle scrolling
        self.buffer.update(knowledge, turn_id, None);
    }

    fn draw(&mut self) {
        self.clear_internal();
        self.draw_internal();
        self.draw_message_log_internal();
        self.sdl_renderer.present();
    }

    fn draw_with_overlay(&mut self, overlay: &RenderOverlay) {
        self.clear_internal();
        self.draw_internal();
        self.draw_overlay_internal(overlay);
        self.draw_message_log_internal();
        self.sdl_renderer.present();
    }

    fn update_log(&mut self, messages: &MessageLog, language: &Box<Language>) {
        for (log_entry, message) in izip!(messages.tail(MESSAGE_LOG_NUM_LINES), &mut self.message_log) {
            language.translate_repeated(log_entry.message, log_entry.repeated, message);
        }
    }
}

fn rgb24_to_sdl_colour(rgb24: Rgb24) -> Color {
    Color::RGB(rgb24.red, rgb24.green, rgb24.blue)
}
<|endoftext|>