<commit_before>//! Defines custom types and structs primarily that composite the JSON:API
//! document
use serde_json;
use std::collections::HashMap;
use crate::errors::*;
use std::str::FromStr;
use std;

/// Permitted JSON-API values (all JSON Values)
pub type JsonApiValue = serde_json::Value;

/// Vector of `Resource`
pub type Resources = Vec<Resource>;
/// Vector of `ResourceIdentifiers`
pub type ResourceIdentifiers = Vec<ResourceIdentifier>;
pub type Links = HashMap<String, JsonApiValue>;
/// Meta-data object, can contain any data
pub type Meta = HashMap<String, JsonApiValue>;
/// Resource Attributes, can be any JSON value
pub type ResourceAttributes = HashMap<String, JsonApiValue>;
/// Map of relationships with other objects
pub type Relationships = HashMap<String, Relationship>;
/// Side-loaded Resources
pub type Included = Vec<Resource>;
/// Data-related errors
pub type JsonApiErrors = Vec<JsonApiError>;

pub type JsonApiId = String;
pub type JsonApiIds<'a> = Vec<&'a JsonApiId>;

/// Resource Identifier
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ResourceIdentifier {
    #[serde(rename = "type")]
    pub _type: String,
    pub id: JsonApiId,
}

/// Representation of a JSON:API resource. This is a struct that contains
/// attributes that map to the JSON:API specification of `id`, `type`,
/// `attributes`, `relationships`, `links`, and `meta`
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct Resource {
    #[serde(rename = "type")]
    pub _type: String,
    pub id: JsonApiId,
    #[serde(default)]
    pub attributes: ResourceAttributes,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub relationships: Option<Relationships>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
}

/// Relationship with another object
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Relationship {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<IdentifierData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
}

/// Valid data Resource (can be None)
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum PrimaryData {
    None,
    Single(Box<Resource>),
    Multiple(Resources),
}

/// Valid Resource Identifier (can be None)
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum IdentifierData {
    None,
    Single(ResourceIdentifier),
    Multiple(ResourceIdentifiers),
}

/// A struct that defines an error state for a document
/// Per the v1.0 spec a document may not contain `errors` and `data`
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct DocumentError {
    pub errors: JsonApiErrors,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jsonapi: Option<JsonApiInfo>,
}

/// A struct that defines the valid state when the document does not contain errors
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct DocumentData {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<PrimaryData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub included: Option<Resources>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jsonapi: Option<JsonApiInfo>,
}

/// Use an enum to create the primitive types of JSONAPI documents. Errors and data cannot mutually
/// co-exist. Rely on Rust's type system to handle this validation instead of testing attributes in
/// the JsonApiDocument
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum JsonApiDocument {
    Error(DocumentError),
    Data(DocumentData),
}

/// Error location
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct ErrorSource {
    pub pointer: Option<String>,
    pub parameter: Option<String>,
}

/// JSON-API Error
/// All fields are optional
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct JsonApiError {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub code: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub detail: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source: Option<ErrorSource>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
}

/// Optional `JsonApiDocument` payload identifying the JSON-API version the
/// server implements
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct JsonApiInfo {
    pub version: Option<String>,
    pub meta: Option<Meta>,
}

/// Pagination links
#[derive(Serialize, Deserialize, Debug)]
pub struct Pagination {
    pub first: Option<String>,
    pub prev: Option<String>,
    pub next: Option<String>,
    pub last: Option<String>,
}

#[derive(Debug)]
pub struct Patch {
    pub patch_type: PatchType,
    pub subject: String,
    pub previous: JsonApiValue,
    pub next: JsonApiValue,
}

#[derive(Debug)]
pub struct PatchSet {
    pub resource_type: String,
    pub resource_id: String,
    pub patches: Vec<Patch>,
}

impl PatchSet {
    pub fn new_for(resource: &Resource) -> Self {
        PatchSet {
            resource_type: resource._type.clone(),
            resource_id: resource.id.clone(),
            patches: Vec::<Patch>::new(),
        }
    }

    pub fn push(&mut self, patch: Patch) {
        self.patches.push(patch);
    }
}

impl DocumentData {
    fn has_meta(&self) -> bool {
        self.meta.is_some()
    }
    fn has_included(&self) -> bool {
        self.included.is_some()
    }
    fn has_data(&self) -> bool {
        self.data.is_some()
    }
}

/// Top-level JSON-API Document
/// An "error" document can be valid, just as a "data" document can be valid
impl JsonApiDocument {
    /// This function returns `false` if the `JsonApiDocument` contains any violations of the
    /// specification. See `DocumentValidationError`
    ///
    /// The spec dictates that the document must have least one of `data`, `errors` or `meta`.
    /// Of these, `data` and `errors` must not co-exist.
    /// The optional field `included` may only be present if the `data` field is present too.
    ///
    /// ```
    /// use jsonapi::api::{JsonApiDocument, PrimaryData, JsonApiErrors};
    /// let doc = JsonApiDocument {
    ///     data: Some(PrimaryData::None),
    ///     errors: Some(JsonApiErrors::new()),
    ///     ..Default::default()
    /// };
    ///
    /// assert_eq!(doc.is_valid(), false);
    /// ```
    pub fn is_valid(&self) -> bool {
        self.validate().is_none()
    }

    /// This function returns a `Vec` with identified specification violations enumerated in
    /// `DocumentValidationError`
    ///
    /// ```
    /// use jsonapi::api::{JsonApiDocument, PrimaryData, JsonApiErrors, DocumentValidationError};
    ///
    /// let doc = JsonApiDocument {
    ///     data: Some(PrimaryData::None),
    ///     errors: Some(JsonApiErrors::new()),
    ///     ..Default::default()
    /// };
    ///
    /// match doc.validate() {
    ///   Some(errors) => {
    ///     assert!(
    ///       errors.contains(
    ///         &DocumentValidationError::DataWithErrors
    ///       )
    ///     )
    ///   }
    ///   None => assert!(false)
    /// }
    /// ```
    pub fn validate(&self) -> Option<Vec<DocumentValidationError>> {
        let mut errors = Vec::<DocumentValidationError>::new();
        println!("{:#?}", self);

        match self {
            JsonApiDocument::Error(_x) => None,
            JsonApiDocument::Data(doc) => {
                if doc.has_included() && !doc.has_data() {
                    errors.push(DocumentValidationError::IncludedWithoutData);
                }

                if !(doc.has_data() || doc.has_meta()) {
                    errors.push(DocumentValidationError::MissingContent);
                }
                match errors.len() {
                    0 => None,
                    _ => Some(errors),
                }
            }
        }
    }
}

impl FromStr for JsonApiDocument {
    type Err = Error;

    /// Instantiate from string
    ///
    /// ```
    /// use jsonapi::api::JsonApiDocument;
    /// use std::str::FromStr;
    ///
    /// let serialized = r#"{
    ///   "data" : [
    ///     { "id":"1", "type":"post", "attributes":{}, "relationships":{}, "links" :{} },
    ///     { "id":"2", "type":"post", "attributes":{}, "relationships":{}, "links" :{} },
    ///     { "id":"3", "type":"post", "attributes":{}, "relationships":{}, "links" :{} }
    ///   ]
    /// }"#;
    /// let doc = JsonApiDocument::from_str(&serialized);
    /// assert_eq!(doc.is_ok(), true);
    /// ```
    fn from_str(s: &str) -> Result<Self> {
        serde_json::from_str(s).chain_err(|| "Error parsing Document")
    }
}

impl Resource {
    pub fn get_relationship(&self, name: &str) -> Option<&Relationship> {
        match self.relationships {
            None => None,
            Some(ref relationships) => {
                match relationships.get(name) {
                    None => None,
                    Some(rel) => Some(rel),
                }
            }
        }
    }

    /// Get an attribute `JsonApiValue`
    ///
    /// ```
    /// use jsonapi::api::Resource;
    /// use std::str::FromStr;
    ///
    /// let serialized = r#"{
    ///   "id":"1",
    ///   "type":"post",
    ///   "attributes":{
    ///     "title": "Rails is Omakase",
    ///     "likes": 250
    ///   },
    ///   "relationships":{},
    ///   "links" :{}
    /// }"#;
    ///
    /// match Resource::from_str(&serialized) {
    ///   Err(_)=> assert!(false),
    ///   Ok(resource)=> {
    ///     match resource.get_attribute("title") {
    ///       None => assert!(false),
    ///       Some(attr) => {
    ///         match attr.as_str() {
    ///           None => assert!(false),
    ///           Some(s) => {
    ///               assert_eq!(s, "Rails is Omakase");
    ///           }
    ///         }
    ///       }
    ///     }
    ///   }
    /// }
    pub fn get_attribute(&self, name: &str) -> Option<&JsonApiValue> {
        match self.attributes.get(name) {
            None => None,
            Some(val) => Some(val),
        }
    }

    pub fn diff(&self, other: Resource) -> std::result::Result<PatchSet, DiffPatchError> {
        if self._type != other._type {
            Err(DiffPatchError::IncompatibleTypes(
                self._type.clone(),
                other._type.clone(),
            ))
        } else {

            let mut self_keys: Vec<String> =
                self.attributes.iter().map(|(key, _)| key.clone()).collect();

            self_keys.sort();

            let mut other_keys: Vec<String> = other
                .attributes
                .iter()
                .map(|(key, _)| key.clone())
                .collect();

            other_keys.sort();

            let matching = self_keys
                .iter()
                .zip(other_keys.iter())
                .filter(|&(a, b)| a == b)
                .count();

            if matching != self_keys.len() {
                Err(DiffPatchError::DifferentAttributeKeys)
            } else {
                let mut patchset = PatchSet::new_for(self);

                for (attr, self_value) in &self.attributes {
                    match other.attributes.get(attr) {
                        None => {
                            error!(
                                "Resource::diff unable to find attribute {:?} in {:?}",
                                attr,
                                other
                            );
                        }
                        Some(other_value) => {
                            if self_value != other_value {
                                patchset.push(Patch {
                                    patch_type: PatchType::Attribute,
                                    subject: attr.clone(),
                                    previous: self_value.clone(),
                                    next: other_value.clone(),
                                });
                            }
                        }
                    }

                }

                Ok(patchset)
            }
        }
    }

    pub fn patch(&mut self, patchset: PatchSet) -> Result<Resource> {
        let mut res = self.clone();
        for patch in &patchset.patches {
            res.attributes.insert(
                patch.subject.clone(),
                patch.next.clone(),
            );
        }
        Ok(res)
    }
}

impl FromStr for Resource {
    type Err = Error;

    /// Instantiate from string
    ///
    /// ```
    /// use jsonapi::api::Resource;
    /// use std::str::FromStr;
    ///
    /// let serialized = r#"{
    ///   "id":"1",
    ///   "type":"post",
    ///   "attributes":{
    ///     "title": "Rails is Omakase",
    ///     "likes": 250
    ///   },
    ///   "relationships":{},
    ///   "links" :{}
    /// }"#;
    ///
    /// let data = Resource::from_str(&serialized);
    /// assert_eq!(data.is_ok(), true);
    /// ```
    fn from_str(s: &str) -> Result<Self> {
        serde_json::from_str(s).chain_err(|| "Error parsing resource")
    }
}

impl Relationship {
    pub fn as_id(&self) -> std::result::Result<Option<&JsonApiId>, RelationshipAssumptionError> {
        match self.data {
            Some(IdentifierData::None) => Ok(None),
            Some(IdentifierData::Multiple(_)) => Err(RelationshipAssumptionError::RelationshipIsAList),
            Some(IdentifierData::Single(ref data)) => Ok(Some(&data.id)),
            None => Ok(None),
        }
    }

    pub fn as_ids(&self) -> std::result::Result<Option<JsonApiIds>, RelationshipAssumptionError> {
        match self.data {
            Some(IdentifierData::None) => Ok(None),
            Some(IdentifierData::Single(_)) => Err(RelationshipAssumptionError::RelationshipIsNotAList),
            Some(IdentifierData::Multiple(ref data)) => Ok(Some(data.iter().map(|x| &x.id).collect())),
            None => Ok(None),
        }
    }
}

/// Top-level (Document) JSON-API specification violations
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum DocumentValidationError {
    IncludedWithoutData,
    MissingContent,
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum JsonApiDataError {
    AttributeNotFound,
    IncompatibleAttributeType,
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum RelationshipAssumptionError {
    RelationshipIsAList,
    RelationshipIsNotAList,
}

#[derive(Debug, Clone, PartialEq)]
pub enum DiffPatchError {
    IncompatibleTypes(String, String),
    DifferentAttributeKeys,
    NonExistentProperty(String),
    IncorrectPropertyValue(String),
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum PatchType {
    Relationship,
    Attribute,
}
<commit_msg>refactor: Fix broken inline documentation tests<commit_after>//! Defines custom types and structs primarily that composite the JSON:API
//! document
use serde_json;
use std::collections::HashMap;
use crate::errors::*;
use std::str::FromStr;
use std;

/// Permitted JSON-API values (all JSON Values)
pub type JsonApiValue = serde_json::Value;

/// Vector of `Resource`
pub type Resources = Vec<Resource>;
/// Vector of `ResourceIdentifiers`
pub type ResourceIdentifiers = Vec<ResourceIdentifier>;
pub type Links = HashMap<String, JsonApiValue>;
/// Meta-data object, can contain any data
pub type Meta = HashMap<String, JsonApiValue>;
/// Resource Attributes, can be any JSON value
pub type ResourceAttributes = HashMap<String, JsonApiValue>;
/// Map of relationships with other objects
pub type Relationships = HashMap<String, Relationship>;
/// Side-loaded Resources
pub type Included = Vec<Resource>;
/// Data-related errors
pub type JsonApiErrors = Vec<JsonApiError>;

pub type JsonApiId = String;
pub type JsonApiIds<'a> = Vec<&'a JsonApiId>;

/// Resource Identifier
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ResourceIdentifier {
    #[serde(rename = "type")]
    pub _type: String,
    pub id: JsonApiId,
}

/// Representation of a JSON:API resource. This is a struct that contains
/// attributes that map to the JSON:API specification of `id`, `type`,
/// `attributes`, `relationships`, `links`, and `meta`
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct Resource {
    #[serde(rename = "type")]
    pub _type: String,
    pub id: JsonApiId,
    #[serde(default)]
    pub attributes: ResourceAttributes,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub relationships: Option<Relationships>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
}

/// Relationship with another object
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Relationship {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<IdentifierData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
}

/// Valid data Resource (can be None)
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum PrimaryData {
    None,
    Single(Box<Resource>),
    Multiple(Resources),
}

/// Valid Resource Identifier (can be None)
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum IdentifierData {
    None,
    Single(ResourceIdentifier),
    Multiple(ResourceIdentifiers),
}

/// A struct that defines an error state for a document
/// Per the v1.0 spec a document may not contain `errors` and `data`
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct DocumentError {
    pub errors: JsonApiErrors,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jsonapi: Option<JsonApiInfo>,
}

/// A struct that defines the valid state when the document does not contain errors
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct DocumentData {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<PrimaryData>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub included: Option<Resources>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jsonapi: Option<JsonApiInfo>,
}

/// Use an enum to create the primitive types of JSONAPI documents. Errors and data cannot mutually
/// co-exist. Rely on Rust's type system to handle this validation instead of testing attributes in
/// the JsonApiDocument
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum JsonApiDocument {
    Error(DocumentError),
    Data(DocumentData),
}

/// Error location
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct ErrorSource {
    pub pointer: Option<String>,
    pub parameter: Option<String>,
}

/// JSON-API Error
/// All fields are optional
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct JsonApiError {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub links: Option<Links>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub code: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub detail: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source: Option<ErrorSource>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub meta: Option<Meta>,
}

/// Optional `JsonApiDocument` payload identifying the JSON-API version the
/// server implements
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct JsonApiInfo {
    pub version: Option<String>,
    pub meta: Option<Meta>,
}

/// Pagination links
#[derive(Serialize, Deserialize, Debug)]
pub struct Pagination {
    pub first: Option<String>,
    pub prev: Option<String>,
    pub next: Option<String>,
    pub last: Option<String>,
}

#[derive(Debug)]
pub struct Patch {
    pub patch_type: PatchType,
    pub subject: String,
    pub previous: JsonApiValue,
    pub next: JsonApiValue,
}

#[derive(Debug)]
pub struct PatchSet {
    pub resource_type: String,
    pub resource_id: String,
    pub patches: Vec<Patch>,
}

impl PatchSet {
    pub fn new_for(resource: &Resource) -> Self {
        PatchSet {
            resource_type: resource._type.clone(),
            resource_id: resource.id.clone(),
            patches: Vec::<Patch>::new(),
        }
    }

    pub fn push(&mut self, patch: Patch) {
        self.patches.push(patch);
    }
}

impl DocumentData {
    fn has_meta(&self) -> bool {
        self.meta.is_some()
    }
    fn has_included(&self) -> bool {
        self.included.is_some()
    }
    fn has_data(&self) -> bool {
        self.data.is_some()
    }
}

/// Top-level JSON-API Document
/// An "error" document can be valid, just as a "data" document can be valid
impl JsonApiDocument {
    /// This function returns `false` if the `JsonApiDocument` contains any violations of the
    /// specification. See `DocumentValidationError`
    ///
    /// The spec dictates that the document must have least one of `data`, `errors` or `meta`.
    /// Of these, `data` and `errors` must not co-exist.
    /// The optional field `included` may only be present if the `data` field is present too.
    pub fn is_valid(&self) -> bool {
        self.validate().is_none()
    }

    /// This function returns a `Vec` with identified specification violations enumerated in
    /// `DocumentValidationError`
    ///
    /// ```
    /// // Simulate an error where `included` has data but `data` does not
    /// use jsonapi::api::*;
    /// use std::str::FromStr;
    ///
    /// let serialized = r#"{
    ///   "id":"1",
    ///   "type":"post",
    ///   "attributes":{
    ///     "title": "Rails is Omakase",
    ///     "likes": 250
    ///   },
    ///   "relationships":{},
    ///   "links" :{}
    /// }"#;
    ///
    /// let resource = Resource::from_str(&serialized);
    ///
    /// let data = DocumentData {
    ///     data: None,
    ///     included: Some(vec![resource.unwrap()]),
    ///     ..Default::default()
    /// };
    ///
    /// let doc = JsonApiDocument::Data(data);
    ///
    /// match doc.validate() {
    ///   Some(errors) => {
    ///     assert!(
    ///       errors.contains(
    ///         &DocumentValidationError::IncludedWithoutData
    ///       )
    ///     )
    ///   }
    ///   None => assert!(false)
    /// }
    /// ```
    pub fn validate(&self) -> Option<Vec<DocumentValidationError>> {
        let mut errors = Vec::<DocumentValidationError>::new();

        match self {
            JsonApiDocument::Error(_x) => None,
            JsonApiDocument::Data(doc) => {
                if doc.has_included() && !doc.has_data() {
                    errors.push(DocumentValidationError::IncludedWithoutData);
                }

                if !(doc.has_data() || doc.has_meta()) {
                    errors.push(DocumentValidationError::MissingContent);
                }
                match errors.len() {
                    0 => None,
                    _ => Some(errors),
                }
            }
        }
    }
}

impl FromStr for JsonApiDocument {
    type Err = Error;

    /// Instantiate from string
    ///
    /// ```
    /// use jsonapi::api::JsonApiDocument;
    /// use std::str::FromStr;
    ///
    /// let serialized = r#"{
    ///   "data" : [
    ///     { "id":"1", "type":"post", "attributes":{}, "relationships":{}, "links" :{} },
    ///     { "id":"2", "type":"post", "attributes":{}, "relationships":{}, "links" :{} },
    ///     { "id":"3", "type":"post", "attributes":{}, "relationships":{}, "links" :{} }
    ///   ]
    /// }"#;
    /// let doc = JsonApiDocument::from_str(&serialized);
    /// assert_eq!(doc.is_ok(), true);
    /// ```
    fn from_str(s: &str) -> Result<Self> {
        serde_json::from_str(s).chain_err(|| "Error parsing Document")
    }
}

impl Resource {
    pub fn get_relationship(&self, name: &str) -> Option<&Relationship> {
        match self.relationships {
            None => None,
            Some(ref relationships) => {
                match relationships.get(name) {
                    None => None,
                    Some(rel) => Some(rel),
                }
            }
        }
    }

    /// Get an attribute `JsonApiValue`
    ///
    /// ```
    /// use jsonapi::api::Resource;
    /// use std::str::FromStr;
    ///
    /// let serialized = r#"{
    ///   "id":"1",
    ///   "type":"post",
    ///   "attributes":{
    ///     "title": "Rails is Omakase",
    ///     "likes": 250
    ///   },
    ///   "relationships":{},
    ///   "links" :{}
    /// }"#;
    ///
    /// match Resource::from_str(&serialized) {
    ///   Err(_)=> assert!(false),
    ///   Ok(resource)=> {
    ///     match resource.get_attribute("title") {
    ///       None => assert!(false),
    ///       Some(attr) => {
    ///         match attr.as_str() {
    ///           None => assert!(false),
    ///           Some(s) => {
    ///               assert_eq!(s, "Rails is Omakase");
    ///           }
    ///         }
    ///       }
    ///     }
    ///   }
    /// }
    pub fn get_attribute(&self, name: &str) -> Option<&JsonApiValue> {
        match self.attributes.get(name) {
            None => None,
            Some(val) => Some(val),
        }
    }

    pub fn diff(&self, other: Resource) -> std::result::Result<PatchSet, DiffPatchError> {
        if self._type != other._type {
            Err(DiffPatchError::IncompatibleTypes(
                self._type.clone(),
                other._type.clone(),
            ))
        } else {

            let mut self_keys: Vec<String> =
                self.attributes.iter().map(|(key, _)| key.clone()).collect();

            self_keys.sort();

            let mut other_keys: Vec<String> = other
                .attributes
                .iter()
                .map(|(key, _)| key.clone())
                .collect();

            other_keys.sort();

            let matching = self_keys
                .iter()
                .zip(other_keys.iter())
                .filter(|&(a, b)| a == b)
                .count();

            if matching != self_keys.len() {
                Err(DiffPatchError::DifferentAttributeKeys)
            } else {
                let mut patchset = PatchSet::new_for(self);

                for (attr, self_value) in &self.attributes {
                    match other.attributes.get(attr) {
                        None => {
                            error!(
                                "Resource::diff unable to find attribute {:?} in {:?}",
                                attr,
                                other
                            );
                        }
                        Some(other_value) => {
                            if self_value != other_value {
                                patchset.push(Patch {
                                    patch_type: PatchType::Attribute,
                                    subject: attr.clone(),
                                    previous: self_value.clone(),
                                    next: other_value.clone(),
                                });
                            }
                        }
                    }

                }

                Ok(patchset)
            }
        }
    }

    pub fn patch(&mut self, patchset: PatchSet) -> Result<Resource> {
        let mut res = self.clone();
        for patch in &patchset.patches {
            res.attributes.insert(
                patch.subject.clone(),
                patch.next.clone(),
            );
        }
        Ok(res)
    }
}

impl FromStr for Resource {
    type Err = Error;

    /// Instantiate from string
    ///
    /// ```
    /// use jsonapi::api::Resource;
    /// use std::str::FromStr;
    ///
    /// let serialized = r#"{
    ///   "id":"1",
    ///   "type":"post",
    ///   "attributes":{
    ///     "title": "Rails is Omakase",
    ///     "likes": 250
    ///   },
    ///   "relationships":{},
    ///   "links" :{}
    /// }"#;
    ///
    /// let data = Resource::from_str(&serialized);
    /// assert_eq!(data.is_ok(), true);
    /// ```
    fn from_str(s: &str) -> Result<Self> {
        serde_json::from_str(s).chain_err(|| "Error parsing resource")
    }
}

impl Relationship {
    pub fn as_id(&self) -> std::result::Result<Option<&JsonApiId>, RelationshipAssumptionError> {
        match self.data {
            Some(IdentifierData::None) => Ok(None),
            Some(IdentifierData::Multiple(_)) => Err(RelationshipAssumptionError::RelationshipIsAList),
            Some(IdentifierData::Single(ref data)) => Ok(Some(&data.id)),
            None => Ok(None),
        }
    }

    pub fn as_ids(&self) -> std::result::Result<Option<JsonApiIds>, RelationshipAssumptionError> {
        match self.data {
            Some(IdentifierData::None) => Ok(None),
            Some(IdentifierData::Single(_)) => Err(RelationshipAssumptionError::RelationshipIsNotAList),
            Some(IdentifierData::Multiple(ref data)) => Ok(Some(data.iter().map(|x| &x.id).collect())),
            None => Ok(None),
        }
    }
}

/// Top-level (Document) JSON-API specification violations
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum DocumentValidationError {
    IncludedWithoutData,
    MissingContent,
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum JsonApiDataError {
    AttributeNotFound,
    IncompatibleAttributeType,
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum RelationshipAssumptionError {
    RelationshipIsAList,
    RelationshipIsNotAList,
}

#[derive(Debug, Clone, PartialEq)]
pub enum DiffPatchError {
    IncompatibleTypes(String, String),
    DifferentAttributeKeys,
    NonExistentProperty(String),
    IncorrectPropertyValue(String),
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub enum PatchType {
    Relationship,
    Attribute,
}
<|endoftext|>


<commit_before>use git2::Error as GitErr;
use serde_json::Error as SerdeJsonError;
use toml::de::Error as TomlDeError;
use std::{fmt, io::Error as IoErr};

/// Oops
#[derive(Debug)]
pub enum Error {
    /// git2 library failed. If problems persist, delete `~/.cargo/registry`
    Git(GitErr),
    /// `Index::from_url` got a bogus URL
    Url(String),
    /// Filesystem error
    Io(IoErr),
    /// If this happens, the registry is seriously corrupted. Delete `~/.cargo/registry`.
    Json(SerdeJsonError),
    /// Cargo config.toml deserialization error
    Toml(TomlDeError),
}

impl fmt::Display for Error {
    #[cold]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Git(e) => fmt::Display::fmt(&e, f),
            Self::Url(u) => f.write_str(u),
            Self::Io(e) => fmt::Display::fmt(&e, f),
            Self::Json(e) => fmt::Display::fmt(&e, f),
            Self::Toml(e) => fmt::Display::fmt(&e, f),
        }
    }
}

impl std::error::Error for Error {
    #[cold]
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Git(e) => Some(e),
            Self::Io(e) => Some(e),
            _ => None,
        }
    }
}

impl From<GitErr> for Error {
    #[cold]
    fn from(e: GitErr) -> Self {
        Self::Git(e)
    }
}

#[test]
fn error_is_send() {
    fn is_send<T: Send>() {}
    is_send::<Error>();
}

/// Unknown error from [`crate::Index::crates_parallel`]
#[derive(Debug)]
pub struct CratesIterError;

impl std::error::Error for CratesIterError {}

impl fmt::Display for CratesIterError {
    #[cold]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("error while iterating git repository")
    }
}
<commit_msg>From for io::Error<commit_after>use git2::Error as GitErr;
use serde_json::Error as SerdeJsonError;
use toml::de::Error as TomlDeError;
use std::{fmt, io};

/// Oops
#[derive(Debug)]
pub enum Error {
    /// git2 library failed. If problems persist, delete `~/.cargo/registry`
    Git(GitErr),
    /// `Index::from_url` got a bogus URL
    Url(String),
    /// Filesystem error
    Io(io::Error),
    /// If this happens, the registry is seriously corrupted. Delete `~/.cargo/registry`.
    Json(SerdeJsonError),
    /// Cargo config.toml deserialization error
    Toml(TomlDeError),
}

impl fmt::Display for Error {
    #[cold]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Git(e) => fmt::Display::fmt(&e, f),
            Self::Url(u) => f.write_str(u),
            Self::Io(e) => fmt::Display::fmt(&e, f),
            Self::Json(e) => fmt::Display::fmt(&e, f),
            Self::Toml(e) => fmt::Display::fmt(&e, f),
        }
    }
}

impl std::error::Error for Error {
    #[cold]
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Self::Git(e) => Some(e),
            Self::Io(e) => Some(e),
            _ => None,
        }
    }
}

impl From<GitErr> for Error {
    #[cold]
    fn from(e: GitErr) -> Self {
        Self::Git(e)
    }
}

impl From<io::Error> for Error {
    #[cold]
    fn from(e: io::Error) -> Self {
        Self::Io(e)
    }
}

#[test]
fn error_is_send() {
    fn is_send<T: Send>() {}
    is_send::<Error>();
}

/// Unknown error from [`crate::Index::crates_parallel`]
#[derive(Debug)]
pub struct CratesIterError;

impl std::error::Error for CratesIterError {}

impl fmt::Display for CratesIterError {
    #[cold]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("error while iterating git repository")
    }
}
<|endoftext|>


<commit_before>#![feature(globs)]

extern crate conduit;

use std::fmt::Show;

use conduit::{Request, Response, Handler};

pub trait Middleware {
    fn before(&self, _: &mut Request) -> Result<(), Box<Show>> {
        Ok(())
    }

    fn after(&self, _: &mut Request, res: Result<Response, Box<Show>>)
             -> Result<Response, Box<Show>>
    {
        res
    }
}

pub trait AroundMiddleware : Handler {
    fn with_handler(&mut self, handler: Box<Handler + 'static + Share>);
}

pub struct MiddlewareBuilder {
    middlewares: Vec<Box<Middleware + 'static + Share>>,
    handler: Option<Box<Handler + 'static + Share>>
}

impl MiddlewareBuilder {
    pub fn new<H: Handler + 'static + Share>(handler: H) -> MiddlewareBuilder {
        MiddlewareBuilder {
            middlewares: vec!(),
            handler: Some(box handler as Box<Handler + 'static + Share>)
        }
    }

    pub fn add<M: Middleware + 'static + Share>(&mut self, middleware: M) {
        self.middlewares.push(box middleware as Box<Middleware + 'static + Share>);
    }

    pub fn around<M: AroundMiddleware + 'static + Share>(&mut self,
                                                         mut middleware: M) {
        let handler = self.handler.take_unwrap();
        middleware.with_handler(handler);
        self.handler = Some(box middleware as Box<Handler + 'static + Share>);
    }
}

impl Handler for MiddlewareBuilder {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
        let mut error = None;

        for (i, middleware) in self.middlewares.iter().enumerate() {
            match middleware.before(req) {
                Ok(_) => (),
                Err(err) => {
                    error = Some((err, i));
                    break;
                }
            }
        }

        match error {
            Some((err, i)) => {
                let middlewares = self.middlewares.slice_to(i);
                run_afters(middlewares, req, Err(err))
            },
            None => {
                let res = { self.handler.get_ref().call(req) };
                let middlewares = self.middlewares.as_slice();

                run_afters(middlewares, req, res)
            }
        }
    }
}

fn run_afters(middleware: &[Box<Middleware>],
                  req: &mut Request,
                  res: Result<Response, Box<Show>>)
                  -> Result<Response, Box<Show>>
{
    middleware.iter().rev().fold(res, |res, m| m.after(req, res))
}

#[cfg(test)]
mod tests {
    extern crate semver;

    use super::*;

    use std::any::{Any, AnyRefExt};
    use std::io::net::ip::IpAddr;
    use std::io::MemReader;
    use std::fmt;
    use std::fmt::{Show, Formatter};
    use std::collections::HashMap;

    use conduit;
    use conduit::{Request, Response, Host, Headers, Method, Scheme, Extensions, Handler};

    struct RequestSentinel {
        path: String,
        extensions: HashMap<&'static str, Box<Any>>,
        method: Method
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_str(),
                extensions: HashMap::new(),
                method: method
            }
        }
    }

    impl conduit::Request for RequestSentinel {
        fn http_version(&self) -> semver::Version { unimplemented!() }
        fn conduit_version(&self) -> semver::Version { unimplemented!() }
        fn method(&self) -> Method { self.method }
        fn scheme(&self) -> Scheme { unimplemented!() }
        fn host<'a>(&'a self) -> Host<'a> { unimplemented!() }
        fn virtual_root<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn path<'a>(&'a self) -> &'a str {
            self.path.as_slice()
        }
        fn query_string<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn remote_ip(&self) -> IpAddr { unimplemented!() }
        fn content_length(&self) -> Option<uint> { unimplemented!() }
        fn headers<'a>(&'a self) -> &'a Headers { unimplemented!() }
        fn body<'a>(&'a mut self) -> &'a mut Reader { unimplemented!() }
        fn extensions<'a>(&'a self) -> &'a Extensions {
            &self.extensions
        }
        fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
            &mut self.extensions
        }
    }

    struct MyMiddleware;

    impl Middleware for MyMiddleware {
        fn before<'a>(&self, req: &'a mut Request) -> Result<(), Box<Show>> {
            req.mut_extensions().insert("test.middleware", box "hello".to_str() as Box<Any>);
            Ok(())
        }
    }

    struct ErrorRecovery;

    impl Middleware for ErrorRecovery {
        fn after(&self, _: &mut Request, res: Result<Response, Box<Show>>)
                     -> Result<Response, Box<Show>>
        {
            res.or_else(|e| {
                Ok(Response {
                    status: (500, "Internal Server Error"),
                    headers: HashMap::new(),
                    body: box MemReader::new(show(e).to_str().into_bytes())
                })
            })
        }
    }

    struct ProducesError;

    impl Middleware for ProducesError {
        fn before(&self, _: &mut Request) -> Result<(), Box<Show>> {
            Err(box "Nope".to_str() as Box<Show>)
        }
    }

    struct NotReached;

    impl Middleware for NotReached {
        fn after(&self, _: &mut Request, _: Result<Response, Box<Show>>)
                     -> Result<Response, Box<Show>>
        {
            Ok(Response {
                status: (200, "OK"),
                headers: HashMap::new(),
                body: box MemReader::new(vec!())
            })
        }
    }

    struct MyAroundMiddleware {
        handler: Option<Box<Handler + 'static + Share>>
    }

    impl MyAroundMiddleware {
        fn new() -> MyAroundMiddleware {
            MyAroundMiddleware { handler: None }
        }
    }

    impl Middleware for MyAroundMiddleware {}

    impl AroundMiddleware for MyAroundMiddleware {
        fn with_handler(&mut self, handler: Box<Handler + 'static + Share>) {
            self.handler = Some(handler)
        }
    }

    impl Handler for MyAroundMiddleware {
        fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
            req.mut_extensions().insert("test.round-and-round", box "hello".to_str() as Box<Any>);
            self.handler.get_ref().call(req)
        }
    }

    struct Shower<'a> {
        inner: &'a Show
    }

    impl<'a> Show for Shower<'a> {
        fn fmt(&self, f: &mut Formatter) -> fmt::Result {
            self.inner.fmt(f)
        }
    }

    fn show<'a>(s: &'a Show) -> Shower<'a> {
        Shower { inner: s }
    }

    fn get_extension<'a, T: 'static>(req: &'a Request, key: &'static str) -> &'a T {
        req.extensions().find(&key).and_then(|s| s.as_ref::<T>())
            .expect(format!("No {} key found in extensions", key).as_slice())
    }

    fn response(string: String) -> Response {
        Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: box MemReader::new(string.into_bytes())
        }
    }

    fn handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        Ok(response(hello.clone()))
    }

    fn error_handler(_: &mut Request) -> Result<Response, String> {
        Err("Error in handler".to_str())
    }

    fn middle_handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        let middle = get_extension::<String>(req, "test.round-and-round");

        Ok(response(format!("{} {}", hello, middle)))
    }

    #[test]
    fn test_simple_middleware() {
        let mut builder = MiddlewareBuilder::new(handler);
        builder.add(MyMiddleware);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello".to_str());
    }

    #[test]
    fn test_error_recovery() {
        let mut builder = MiddlewareBuilder::new(handler);
        builder.add(ErrorRecovery);
        builder.add(ProducesError);
        // the error bubbles up from ProducesError and shouldn't reach here
        builder.add(NotReached);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let res = builder.call(&mut req).ok().expect("Error not handled");

        assert_eq!(res.status, (500, "Internal Server Error"));
    }

    #[test]
    fn test_error_recovery_in_handlers() {
        let mut builder = MiddlewareBuilder::new(error_handler);
        builder.add(ErrorRecovery);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("Error not handled");

        assert_eq!(res.status, (500, "Internal Server Error"));
        assert_eq!(res.body.read_to_str().ok().expect("No body"), "Error in handler".to_str());
    }

    #[test]
    fn test_around_middleware() {
        let mut builder = MiddlewareBuilder::new(middle_handler);
        builder.add(MyMiddleware);
        builder.around(MyAroundMiddleware::new());

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello hello".to_str());
    }
}
<commit_msg>Require Send instead of 'static<commit_after>#![feature(globs)]

extern crate conduit;

use std::fmt::Show;

use conduit::{Request, Response, Handler};

pub trait Middleware {
    fn before(&self, _: &mut Request) -> Result<(), Box<Show>> {
        Ok(())
    }

    fn after(&self, _: &mut Request, res: Result<Response, Box<Show>>)
             -> Result<Response, Box<Show>>
    {
        res
    }
}

pub trait AroundMiddleware : Handler {
    fn with_handler(&mut self, handler: Box<Handler + Send + Share>);
}

pub struct MiddlewareBuilder {
    middlewares: Vec<Box<Middleware + Send + Share>>,
    handler: Option<Box<Handler + Send + Share>>
}

impl MiddlewareBuilder {
    pub fn new<H: Handler + Send + Share>(handler: H) -> MiddlewareBuilder {
        MiddlewareBuilder {
            middlewares: vec!(),
            handler: Some(box handler as Box<Handler + Send + Share>)
        }
    }

    pub fn add<M: Middleware + Send + Share>(&mut self, middleware: M) {
        self.middlewares.push(box middleware as Box<Middleware + Send + Share>);
    }

    pub fn around<M: AroundMiddleware + Send + Share>(&mut self,
                                                         mut middleware: M) {
        let handler = self.handler.take_unwrap();
        middleware.with_handler(handler);
        self.handler = Some(box middleware as Box<Handler + Send + Share>);
    }
}

impl Handler for MiddlewareBuilder {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
        let mut error = None;

        for (i, middleware) in self.middlewares.iter().enumerate() {
            match middleware.before(req) {
                Ok(_) => (),
                Err(err) => {
                    error = Some((err, i));
                    break;
                }
            }
        }

        match error {
            Some((err, i)) => {
                let middlewares = self.middlewares.slice_to(i);
                run_afters(middlewares, req, Err(err))
            },
            None => {
                let res = { self.handler.get_ref().call(req) };
                let middlewares = self.middlewares.as_slice();

                run_afters(middlewares, req, res)
            }
        }
    }
}

fn run_afters(middleware: &[Box<Middleware>],
                  req: &mut Request,
                  res: Result<Response, Box<Show>>)
                  -> Result<Response, Box<Show>>
{
    middleware.iter().rev().fold(res, |res, m| m.after(req, res))
}

#[cfg(test)]
mod tests {
    extern crate semver;

    use super::*;

    use std::any::{Any, AnyRefExt};
    use std::io::net::ip::IpAddr;
    use std::io::MemReader;
    use std::fmt;
    use std::fmt::{Show, Formatter};
    use std::collections::HashMap;

    use conduit;
    use conduit::{Request, Response, Host, Headers, Method, Scheme, Extensions, Handler};

    struct RequestSentinel {
        path: String,
        extensions: HashMap<&'static str, Box<Any>>,
        method: Method
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_str(),
                extensions: HashMap::new(),
                method: method
            }
        }
    }

    impl conduit::Request for RequestSentinel {
        fn http_version(&self) -> semver::Version { unimplemented!() }
        fn conduit_version(&self) -> semver::Version { unimplemented!() }
        fn method(&self) -> Method { self.method }
        fn scheme(&self) -> Scheme { unimplemented!() }
        fn host<'a>(&'a self) -> Host<'a> { unimplemented!() }
        fn virtual_root<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn path<'a>(&'a self) -> &'a str {
            self.path.as_slice()
        }
        fn query_string<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn remote_ip(&self) -> IpAddr { unimplemented!() }
        fn content_length(&self) -> Option<uint> { unimplemented!() }
        fn headers<'a>(&'a self) -> &'a Headers { unimplemented!() }
        fn body<'a>(&'a mut self) -> &'a mut Reader { unimplemented!() }
        fn extensions<'a>(&'a self) -> &'a Extensions {
            &self.extensions
        }
        fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
            &mut self.extensions
        }
    }

    struct MyMiddleware;

    impl Middleware for MyMiddleware {
        fn before<'a>(&self, req: &'a mut Request) -> Result<(), Box<Show>> {
            req.mut_extensions().insert("test.middleware", box "hello".to_str() as Box<Any>);
            Ok(())
        }
    }

    struct ErrorRecovery;

    impl Middleware for ErrorRecovery {
        fn after(&self, _: &mut Request, res: Result<Response, Box<Show>>)
                     -> Result<Response, Box<Show>>
        {
            res.or_else(|e| {
                Ok(Response {
                    status: (500, "Internal Server Error"),
                    headers: HashMap::new(),
                    body: box MemReader::new(show(e).to_str().into_bytes())
                })
            })
        }
    }

    struct ProducesError;

    impl Middleware for ProducesError {
        fn before(&self, _: &mut Request) -> Result<(), Box<Show>> {
            Err(box "Nope".to_str() as Box<Show>)
        }
    }

    struct NotReached;

    impl Middleware for NotReached {
        fn after(&self, _: &mut Request, _: Result<Response, Box<Show>>)
                     -> Result<Response, Box<Show>>
        {
            Ok(Response {
                status: (200, "OK"),
                headers: HashMap::new(),
                body: box MemReader::new(vec!())
            })
        }
    }

    struct MyAroundMiddleware {
        handler: Option<Box<Handler + Send + Share>>
    }

    impl MyAroundMiddleware {
        fn new() -> MyAroundMiddleware {
            MyAroundMiddleware { handler: None }
        }
    }

    impl Middleware for MyAroundMiddleware {}

    impl AroundMiddleware for MyAroundMiddleware {
        fn with_handler(&mut self, handler: Box<Handler + Send + Share>) {
            self.handler = Some(handler)
        }
    }

    impl Handler for MyAroundMiddleware {
        fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
            req.mut_extensions().insert("test.round-and-round", box "hello".to_str() as Box<Any>);
            self.handler.get_ref().call(req)
        }
    }

    struct Shower<'a> {
        inner: &'a Show
    }

    impl<'a> Show for Shower<'a> {
        fn fmt(&self, f: &mut Formatter) -> fmt::Result {
            self.inner.fmt(f)
        }
    }

    fn show<'a>(s: &'a Show) -> Shower<'a> {
        Shower { inner: s }
    }

    fn get_extension<'a, T: 'static>(req: &'a Request, key: &'static str) -> &'a T {
        req.extensions().find(&key).and_then(|s| s.as_ref::<T>())
            .expect(format!("No {} key found in extensions", key).as_slice())
    }

    fn response(string: String) -> Response {
        Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: box MemReader::new(string.into_bytes())
        }
    }

    fn handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        Ok(response(hello.clone()))
    }

    fn error_handler(_: &mut Request) -> Result<Response, String> {
        Err("Error in handler".to_str())
    }

    fn middle_handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        let middle = get_extension::<String>(req, "test.round-and-round");

        Ok(response(format!("{} {}", hello, middle)))
    }

    #[test]
    fn test_simple_middleware() {
        let mut builder = MiddlewareBuilder::new(handler);
        builder.add(MyMiddleware);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello".to_str());
    }

    #[test]
    fn test_error_recovery() {
        let mut builder = MiddlewareBuilder::new(handler);
        builder.add(ErrorRecovery);
        builder.add(ProducesError);
        // the error bubbles up from ProducesError and shouldn't reach here
        builder.add(NotReached);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let res = builder.call(&mut req).ok().expect("Error not handled");

        assert_eq!(res.status, (500, "Internal Server Error"));
    }

    #[test]
    fn test_error_recovery_in_handlers() {
        let mut builder = MiddlewareBuilder::new(error_handler);
        builder.add(ErrorRecovery);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("Error not handled");

        assert_eq!(res.status, (500, "Internal Server Error"));
        assert_eq!(res.body.read_to_str().ok().expect("No body"), "Error in handler".to_str());
    }

    #[test]
    fn test_around_middleware() {
        let mut builder = MiddlewareBuilder::new(middle_handler);
        builder.add(MyMiddleware);
        builder.around(MyAroundMiddleware::new());

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello hello".to_str());
    }
}
<|endoftext|>


<commit_before>//! Provides support for sending events to Sentry.
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::{BufRead, BufReader};
use std::process::Command;
use std::time::UNIX_EPOCH;

use anylog::LogEntry;
use chrono::Utc;
use failure::{Error, ResultExt};
use hostname::get_hostname;
use regex::Regex;
use serde_json::Value;
#[cfg(not(windows))]
use uname::uname;
use username::get_user_name;

use constants::{ARCH, PLATFORM};
use utils::releases::detect_release_name;
use utils::system::{get_family, get_model, to_timestamp};

lazy_static! {
    static ref COMPONENT_RE: Regex = Regex::new(r#"^([^:]+): (.*)$"#).unwrap();
}

#[derive(Serialize)]
pub struct Message {
    pub message: String,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub params: Vec<String>,
}

#[derive(Serialize)]
pub struct Exception {
    pub values: Vec<SingleException>,
}

#[derive(Serialize, Default)]
pub struct Frame {
    pub filename: String,
    pub abs_path: Option<String>,
    pub function: String,
    pub lineno: Option<u32>,
    pub context_line: Option<String>,
    pub pre_context: Option<Vec<String>>,
    pub post_context: Option<Vec<String>>,
}

#[derive(Serialize)]
pub struct Stacktrace {
    pub frames: Vec<Frame>,
}

#[derive(Serialize)]
pub struct SingleException {
    #[serde(rename = "type")]
    pub ty: String,
    pub value: String,
    pub stacktrace: Option<Stacktrace>,
}

#[derive(Serialize, Debug)]
pub struct Breadcrumb {
    pub timestamp: f64,
    #[serde(rename = "type")]
    pub ty: String,
    pub message: String,
    pub category: String,
}

/// Represents a Sentry event.
#[derive(Serialize)]
pub struct Event {
    pub tags: HashMap<String, String>,
    pub extra: HashMap<String, Value>,
    pub level: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fingerprint: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none", rename = "sentry.interfaces.Message")]
    pub message: Option<Message>,
    pub platform: String,
    pub timestamp: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub server_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub release: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dist: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub environment: Option<String>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub user: HashMap<String, String>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub contexts: HashMap<String, HashMap<String, String>>,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub breadcrumbs: Vec<Breadcrumb>,
    pub exception: Option<Exception>,
}

fn get_server_name() -> Result<String, Error> {
    let p = Command::new("uname").arg("-n").output()?;
    Ok(String::from_utf8(p.stdout)?.trim().to_owned())
}

impl Event {
    pub fn new() -> Event {
        Event {
            tags: HashMap::new(),
            extra: HashMap::new(),
            level: "error".into(),
            fingerprint: None,
            message: None,
            platform: "other".into(),
            timestamp: to_timestamp(Utc::now()),
            server_name: get_server_name().ok(),
            release: None,
            dist: None,
            environment: None,
            user: HashMap::new(),
            contexts: HashMap::new(),
            breadcrumbs: Vec::new(),
            exception: None,
        }
    }

    pub fn new_prefilled() -> Result<Event, Error> {
        let mut event = Event::new();

        event.extra.insert(
            "environ".into(),
            Value::Object(env::vars().map(|(k, v)| (k, Value::String(v))).collect()),
        );

        event.user.insert(
            "username".into(),
            get_user_name().unwrap_or("unknown".into()),
        );
        event
            .user
            .insert("ip_address".into(), String::from("{{auto}}"));

        let mut device = HashMap::new();
        if let Some(hostname) = get_hostname() {
            device.insert("name".into(), hostname);
        }
        if let Some(model) = get_model() {
            device.insert("model".into(), model);
        }
        if let Some(family) = get_family() {
            device.insert("family".into(), family);
        }
        device.insert("arch".into(), ARCH.into());
        event.contexts.insert("device".into(), device);

        let mut os = HashMap::new();
        #[cfg(not(windows))]
        {
            if let Ok(info) = uname() {
                os.insert("name".into(), info.sysname);
                os.insert("kernel_version".into(), info.version);
                os.insert("version".into(), info.release);
            }
        }
        if !os.contains_key("name") {
            os.insert("name".into(), PLATFORM.into());
        }
        event.contexts.insert("os".into(), os);
        Ok(event)
    }

    pub fn detect_release(&mut self) {
        self.release = detect_release_name().ok();
    }

    pub fn attach_logfile(&mut self, logfile: &str, with_component: bool) -> Result<(), Error> {
        let f = fs::File::open(logfile).context("Could not open logfile")?;

        // sentry currently requires timestamps for breadcrumbs at all times.
        // Because we might not be able to parse a timestamp from the log file
        // we fall back to either the modified time of the file or if that does
        // not work we use the current timestamp.
        let fallback_timestamp = fs::metadata(logfile)
            .context("Could not get metadata for logfile")?
            .modified()
            .map(|ts| ts.duration_since(UNIX_EPOCH).unwrap().as_secs() as f64)
            .unwrap_or_else(|_| Utc::now().timestamp() as f64);

        let reader = BufReader::new(f);
        for line in reader.lines() {
            let line = line?;
            let rec = LogEntry::parse(line.as_bytes());
            let component;
            let message;

            if_chain! {
                if with_component;
                if let Some(caps) = COMPONENT_RE.captures(&rec.message());
                then {
                    component = caps.get(1).map(|x| x.as_str().to_string()).unwrap();
                    message = caps.get(2).map(|x| x.as_str().to_string()).unwrap();
                } else {
                    component = "log".to_string();
                    message = rec.message().to_string();
                }
            }

            self.breadcrumbs.push(Breadcrumb {
                timestamp: rec.utc_timestamp()
                    .map(|x| x.timestamp() as f64)
                    .unwrap_or(fallback_timestamp),
                message: message,
                ty: "default".to_string(),
                category: component.to_string(),
            })
        }

        if self.breadcrumbs.len() > 100 {
            let skip = self.breadcrumbs.len() - 100;
            self.breadcrumbs.drain(..skip);
        }

        Ok(())
    }
}
<commit_msg>ref: move to logrecord to support relay for event sending<commit_after>//! Provides support for sending events to Sentry.
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::{BufRead, BufReader};
use std::process::Command;
use std::time::UNIX_EPOCH;

use anylog::LogEntry;
use chrono::Utc;
use failure::{Error, ResultExt};
use hostname::get_hostname;
use regex::Regex;
use serde_json::Value;
#[cfg(not(windows))]
use uname::uname;
use username::get_user_name;

use constants::{ARCH, PLATFORM};
use utils::releases::detect_release_name;
use utils::system::{get_family, get_model, to_timestamp};

lazy_static! {
    static ref COMPONENT_RE: Regex = Regex::new(r#"^([^:]+): (.*)$"#).unwrap();
}

#[derive(Serialize)]
pub struct Message {
    pub message: String,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub params: Vec<String>,
}

#[derive(Serialize)]
pub struct Exception {
    pub values: Vec<SingleException>,
}

#[derive(Serialize, Default)]
pub struct Frame {
    pub filename: String,
    pub abs_path: Option<String>,
    pub function: String,
    pub lineno: Option<u32>,
    pub context_line: Option<String>,
    pub pre_context: Option<Vec<String>>,
    pub post_context: Option<Vec<String>>,
}

#[derive(Serialize)]
pub struct Stacktrace {
    pub frames: Vec<Frame>,
}

#[derive(Serialize)]
pub struct SingleException {
    #[serde(rename = "type")]
    pub ty: String,
    pub value: String,
    pub stacktrace: Option<Stacktrace>,
}

#[derive(Serialize, Debug)]
pub struct Breadcrumb {
    pub timestamp: f64,
    #[serde(rename = "type")]
    pub ty: String,
    pub message: String,
    pub category: String,
}

/// Represents a Sentry event.
#[derive(Serialize)]
pub struct Event {
    pub tags: HashMap<String, String>,
    pub extra: HashMap<String, Value>,
    pub level: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fingerprint: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none", rename = "logentry")]
    pub message: Option<Message>,
    pub platform: String,
    pub timestamp: f64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub server_name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub release: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dist: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub environment: Option<String>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub user: HashMap<String, String>,
    #[serde(skip_serializing_if = "HashMap::is_empty")]
    pub contexts: HashMap<String, HashMap<String, String>>,
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub breadcrumbs: Vec<Breadcrumb>,
    pub exception: Option<Exception>,
}

fn get_server_name() -> Result<String, Error> {
    let p = Command::new("uname").arg("-n").output()?;
    Ok(String::from_utf8(p.stdout)?.trim().to_owned())
}

impl Event {
    pub fn new() -> Event {
        Event {
            tags: HashMap::new(),
            extra: HashMap::new(),
            level: "error".into(),
            fingerprint: None,
            message: None,
            platform: "other".into(),
            timestamp: to_timestamp(Utc::now()),
            server_name: get_server_name().ok(),
            release: None,
            dist: None,
            environment: None,
            user: HashMap::new(),
            contexts: HashMap::new(),
            breadcrumbs: Vec::new(),
            exception: None,
        }
    }

    pub fn new_prefilled() -> Result<Event, Error> {
        let mut event = Event::new();

        event.extra.insert(
            "environ".into(),
            Value::Object(env::vars().map(|(k, v)| (k, Value::String(v))).collect()),
        );

        event.user.insert(
            "username".into(),
            get_user_name().unwrap_or("unknown".into()),
        );
        event
            .user
            .insert("ip_address".into(), String::from("{{auto}}"));

        let mut device = HashMap::new();
        if let Some(hostname) = get_hostname() {
            device.insert("name".into(), hostname);
        }
        if let Some(model) = get_model() {
            device.insert("model".into(), model);
        }
        if let Some(family) = get_family() {
            device.insert("family".into(), family);
        }
        device.insert("arch".into(), ARCH.into());
        event.contexts.insert("device".into(), device);

        let mut os = HashMap::new();
        #[cfg(not(windows))]
        {
            if let Ok(info) = uname() {
                os.insert("name".into(), info.sysname);
                os.insert("kernel_version".into(), info.version);
                os.insert("version".into(), info.release);
            }
        }
        if !os.contains_key("name") {
            os.insert("name".into(), PLATFORM.into());
        }
        event.contexts.insert("os".into(), os);
        Ok(event)
    }

    pub fn detect_release(&mut self) {
        self.release = detect_release_name().ok();
    }

    pub fn attach_logfile(&mut self, logfile: &str, with_component: bool) -> Result<(), Error> {
        let f = fs::File::open(logfile).context("Could not open logfile")?;

        // sentry currently requires timestamps for breadcrumbs at all times.
        // Because we might not be able to parse a timestamp from the log file
        // we fall back to either the modified time of the file or if that does
        // not work we use the current timestamp.
        let fallback_timestamp = fs::metadata(logfile)
            .context("Could not get metadata for logfile")?
            .modified()
            .map(|ts| ts.duration_since(UNIX_EPOCH).unwrap().as_secs() as f64)
            .unwrap_or_else(|_| Utc::now().timestamp() as f64);

        let reader = BufReader::new(f);
        for line in reader.lines() {
            let line = line?;
            let rec = LogEntry::parse(line.as_bytes());
            let component;
            let message;

            if_chain! {
                if with_component;
                if let Some(caps) = COMPONENT_RE.captures(&rec.message());
                then {
                    component = caps.get(1).map(|x| x.as_str().to_string()).unwrap();
                    message = caps.get(2).map(|x| x.as_str().to_string()).unwrap();
                } else {
                    component = "log".to_string();
                    message = rec.message().to_string();
                }
            }

            self.breadcrumbs.push(Breadcrumb {
                timestamp: rec.utc_timestamp()
                    .map(|x| x.timestamp() as f64)
                    .unwrap_or(fallback_timestamp),
                message: message,
                ty: "default".to_string(),
                category: component.to_string(),
            })
        }

        if self.breadcrumbs.len() > 100 {
            let skip = self.breadcrumbs.len() - 100;
            self.breadcrumbs.drain(..skip);
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! This abstract syntax tree is *really* abstract.
//! By necessity, it's not tied to any specific language.
//! "Normal" language forms all correspond to `Node`, and their meaning comes from their `Form`.

#![macro_use]

use beta::{Beta, ExportBeta};
use name::*;
use std::{fmt, iter};
use util::mbe::EnvMBE;

// TODO: This really ought to be an `Rc` around an `enum`
#[derive(Clone, PartialEq)]
pub enum Ast {
    Trivial,
    /// Typically, a binder
    Atom(Name),
    VariableReference(Name),

    /// Shift environment to quote (a pos/neg piece of syntax) more
    QuoteMore(Box<Ast>, bool),
    /// Shift environment (by some amount) to quote less
    QuoteLess(Box<Ast>, u8),

    /// A meaningful chunk of syntax, governed by a form, containing an environment,
    ///  potentially exporting some names.
    Node(::std::rc::Rc<::form::Form>, EnvMBE<Ast>, ExportBeta),

    /// For parsing purposes.
    IncompleteNode(EnvMBE<Ast>),
    /// For parsing purposes. Is this used for anything other than writing simple tests?
    Shape(Vec<Ast>),

    /// Variable binding
    ExtendEnv(Box<Ast>, Beta),
}

// Reification macros would totally work for this,
//  but it's worth having a special case in `Value` in order to make this faster.
impl ::runtime::reify::Reifiable for Ast {
    fn ty_name() -> Name { n("Ast") }

    fn reify(&self) -> ::runtime::eval::Value {
        ::runtime::eval::Value::AbstractSyntax(self.clone())
    }

    fn reflect(v: &::runtime::eval::Value) -> Ast {
        extract!((v) ::runtime::eval::Value::AbstractSyntax = (ref ast) => (ast.clone()))
    }
}

pub use self::Ast::*;

impl fmt::Debug for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Trivial => write!(f, "⨉"),
            Atom(ref n) => write!(f, "∘{:#?}∘", n),
            VariableReference(ref v) => write!(f, "{:#?}", v),
            Shape(ref v) => {
                write!(f, "(")?;
                let mut first = true;
                for elt in v {
                    if !first {
                        write!(f, " ")?
                    }
                    elt.fmt(f)?;
                    first = false;
                }
                write!(f, ")")
            }
            Node(ref form, ref body, ref export) => {
                write!(f, "{{ ({}); {:#?}", form.name.sp(), body)?;
                match *export {
                    ::beta::ExportBeta::Nothing => {}
                    _ => write!(f, " ⇑{:#?}", export)?,
                }
                write!(f, "}}")
            }
            QuoteMore(ref body, pos) => {
                if pos {
                    write!(f, "pos``{:#?}``", body)
                } else {
                    write!(f, "neg``{:#?}``", body)
                }
            }
            QuoteLess(ref body, depth) => write!(f, ",,({}){:#?},,", depth, body),
            IncompleteNode(ref body) => write!(f, "{{ INCOMPLETE; {:#?} }}", body),
            ExtendEnv(ref body, ref beta) => write!(f, "{:#?}↓{:#?}", body, beta),
        }
    }
}

// Warning: this assumes the core language! To properly display an `Ast`, you need the `SynEnv`.
impl fmt::Display for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Atom(ref n) => write!(f, "{}", n.print()),
            VariableReference(ref v) => write!(f, "{}", v.print()),
            Node(ref form, ref body, _) => {
                let s = ::unparse::unparse_mbe(
                    &form.grammar,
                    self,
                    body,
                    &::core_forms::get_core_forms(),
                );
                write!(f, "{}", s)
            }
            ExtendEnv(ref body, _) => write!(f, "{}↓", body),
            _ => write!(f, "{:#?}", self),
        }
    }
}

impl Ast {
    // TODO: this ought to at least warn if we're losing anything other than `Shape`
    pub fn flatten(&self) -> EnvMBE<Ast> {
        match *self {
            Trivial | Atom(_) => EnvMBE::new(),
            VariableReference(_) => EnvMBE::new(),
            Shape(ref v) => {
                let mut accum = EnvMBE::new();
                for sub_a in v {
                    accum = accum.combine_overriding(&sub_a.flatten())
                }
                accum
            }
            IncompleteNode(ref env) => env.clone(),
            Node(ref _f, ref _body, ref _export) => {
                // TODO: think about what should happen when
                //  `Scope` contains a `Scope` without an intervening `Named`
                panic!("I don't know what to do here!")
            }
            QuoteMore(ref body, _) => body.flatten(),
            QuoteLess(ref body, _) => body.flatten(),
            ExtendEnv(ref body, _) => body.flatten(),
        }
    }

    // TODO: use this more
    pub fn destructure(
        &self,
        expd_form: ::std::rc::Rc<::form::Form>,
    ) -> Option<::util::mbe::EnvMBE<Ast>>
    {
        if let Node(ref f, ref parts, _) = self {
            if f == &expd_form {
                return Some(parts.clone());
            }
        }
        None
    }

    // TODO: I think we have a lot of places where we ought to use this function:
    pub fn node_parts(&self) -> &EnvMBE<Ast> {
        match *self {
            Node(_, ref body, _) => body,
            _ => icp!(),
        }
    }
    pub fn node_form(&self) -> &::form::Form {
        match *self {
            Node(ref form, _, _) => form,
            _ => icp!(),
        }
    }

    pub fn free_vrs(&self) -> Vec<Name> {
        match *self {
            Trivial | Atom(_) => vec![],
            VariableReference(v) => vec![v],
            Shape(_) | IncompleteNode(_) => unimplemented!("TODO"),
            QuoteLess(_, _) | QuoteMore(_, _) => unimplemented!("TODO"),
            // This one is actually encounterable by real-world code
            //  (if a ∀ somehow ends up underneath a `*` in syntax.)
            // And we need to take a LazyWalkReses to do this right.
            ExtendEnv(_, _) => unimplemented!("TODO"),
            Node(_, ref body, _) => body.map_reduce(
                &|a| a.free_vrs(),
                &|v0, v1| {
                    let mut res = v0.clone();
                    res.append(&mut v1.clone());
                    res
                },
                vec![],
            ),
        }
    }
}

// This is used by combine::many, which is used by the Star parser
impl iter::FromIterator<Ast> for Ast {
    fn from_iter<I: IntoIterator<Item = Ast>>(i: I) -> Self {
        IncompleteNode(EnvMBE::new_from_anon_repeat(i.into_iter().map(|a| a.flatten()).collect()))
    }
}

// This is also sort of a test of MBE, since we need `Ast`s to make them with the macros
//
// Suppose we have the following series of `Ast`s:
// [b = 8] [a = [1 2], b = 8] [a = [3 4 5], b = 8]
//
// We should turn them into the following `Ast`
// [a = [[] [1 2] [3 4 5]], b = [8 8 8]]
#[test]
fn combine_from_kleene_star() {
    use std::iter::FromIterator;

    let parse_parts = vec![
        ast!({ - "b" => "8.0"}),
        ast!({ - "a" => ["1", "2"], "b" => "8.1"}),
        ast!({ - "a" => ["1", "2", "3"], "b" => "8.2"}),
    ];
    let parsed = Ast::from_iter(parse_parts);

    let mut expected_mbe = mbe!("a" => [@"triple" [], ["1", "2"], ["1", "2", "3"]],
             "b" => [@"triple" "8.0", "8.1", "8.2"]);
    expected_mbe.anonimize_repeat(n("triple"));

    assert_eq!(parsed, IncompleteNode(expected_mbe));
}

#[test]
fn star_construction() {
    let env = mbe!( "a" => ["1", "2"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a") env : (, env.get_leaf_or_panic(&n("a")).clone())]} ),
        ast!( { - "x" => ["1", "2"] })
    );

    let env = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a", "b") env :
                            ((, env.get_leaf_or_panic(&n("b")).clone())
                             (, env.get_leaf_or_panic(&n("a")).clone()))]} ),
        ast!( { - "x" => [("11" "1"), ("22" "2")] })
    );
}

#[test]
fn mbe_r_and_r_roundtrip() {
    use runtime::reify::Reifiable;
    let mbe1 = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);
    assert_eq!(mbe1, EnvMBE::<Ast>::reflect(&mbe1.reify()));
}
<commit_msg>Always use `.print()` for `Name`<commit_after>//! This abstract syntax tree is *really* abstract.
//! By necessity, it's not tied to any specific language.
//! "Normal" language forms all correspond to `Node`, and their meaning comes from their `Form`.

#![macro_use]

use beta::{Beta, ExportBeta};
use name::*;
use std::{fmt, iter};
use util::mbe::EnvMBE;

// TODO: This really ought to be an `Rc` around an `enum`
#[derive(Clone, PartialEq)]
pub enum Ast {
    Trivial,
    /// Typically, a binder
    Atom(Name),
    VariableReference(Name),

    /// Shift environment to quote (a pos/neg piece of syntax) more
    QuoteMore(Box<Ast>, bool),
    /// Shift environment (by some amount) to quote less
    QuoteLess(Box<Ast>, u8),

    /// A meaningful chunk of syntax, governed by a form, containing an environment,
    ///  potentially exporting some names.
    Node(::std::rc::Rc<::form::Form>, EnvMBE<Ast>, ExportBeta),

    /// For parsing purposes.
    IncompleteNode(EnvMBE<Ast>),
    /// For parsing purposes. Is this used for anything other than writing simple tests?
    Shape(Vec<Ast>),

    /// Variable binding
    ExtendEnv(Box<Ast>, Beta),
}

// Reification macros would totally work for this,
//  but it's worth having a special case in `Value` in order to make this faster.
impl ::runtime::reify::Reifiable for Ast {
    fn ty_name() -> Name { n("Ast") }

    fn reify(&self) -> ::runtime::eval::Value {
        ::runtime::eval::Value::AbstractSyntax(self.clone())
    }

    fn reflect(v: &::runtime::eval::Value) -> Ast {
        extract!((v) ::runtime::eval::Value::AbstractSyntax = (ref ast) => (ast.clone()))
    }
}

pub use self::Ast::*;

impl fmt::Debug for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Trivial => write!(f, "⨉"),
            Atom(ref n) => write!(f, "∘{}∘", n.print()),
            VariableReference(ref v) => write!(f, "{}", v.print()),
            Shape(ref v) => {
                write!(f, "(")?;
                let mut first = true;
                for elt in v {
                    if !first {
                        write!(f, " ")?
                    }
                    elt.fmt(f)?;
                    first = false;
                }
                write!(f, ")")
            }
            Node(ref form, ref body, ref export) => {
                write!(f, "{{ ({}); {:#?}", form.name.sp(), body)?;
                match *export {
                    ::beta::ExportBeta::Nothing => {}
                    _ => write!(f, " ⇑{:#?}", export)?,
                }
                write!(f, "}}")
            }
            QuoteMore(ref body, pos) => {
                if pos {
                    write!(f, "pos``{:#?}``", body)
                } else {
                    write!(f, "neg``{:#?}``", body)
                }
            }
            QuoteLess(ref body, depth) => write!(f, ",,({}){:#?},,", depth, body),
            IncompleteNode(ref body) => write!(f, "{{ INCOMPLETE; {:#?} }}", body),
            ExtendEnv(ref body, ref beta) => write!(f, "{:#?}↓{:#?}", body, beta),
        }
    }
}

// Warning: this assumes the core language! To properly display an `Ast`, you need the `SynEnv`.
impl fmt::Display for Ast {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Atom(ref n) => write!(f, "{}", n.print()),
            VariableReference(ref v) => write!(f, "{}", v.print()),
            Node(ref form, ref body, _) => {
                let s = ::unparse::unparse_mbe(
                    &form.grammar,
                    self,
                    body,
                    &::core_forms::get_core_forms(),
                );
                write!(f, "{}", s)
            }
            ExtendEnv(ref body, _) => write!(f, "{}↓", body),
            _ => write!(f, "{:#?}", self),
        }
    }
}

impl Ast {
    // TODO: this ought to at least warn if we're losing anything other than `Shape`
    pub fn flatten(&self) -> EnvMBE<Ast> {
        match *self {
            Trivial | Atom(_) => EnvMBE::new(),
            VariableReference(_) => EnvMBE::new(),
            Shape(ref v) => {
                let mut accum = EnvMBE::new();
                for sub_a in v {
                    accum = accum.combine_overriding(&sub_a.flatten())
                }
                accum
            }
            IncompleteNode(ref env) => env.clone(),
            Node(ref _f, ref _body, ref _export) => {
                // TODO: think about what should happen when
                //  `Scope` contains a `Scope` without an intervening `Named`
                panic!("I don't know what to do here!")
            }
            QuoteMore(ref body, _) => body.flatten(),
            QuoteLess(ref body, _) => body.flatten(),
            ExtendEnv(ref body, _) => body.flatten(),
        }
    }

    // TODO: use this more
    pub fn destructure(
        &self,
        expd_form: ::std::rc::Rc<::form::Form>,
    ) -> Option<::util::mbe::EnvMBE<Ast>>
    {
        if let Node(ref f, ref parts, _) = self {
            if f == &expd_form {
                return Some(parts.clone());
            }
        }
        None
    }

    // TODO: I think we have a lot of places where we ought to use this function:
    pub fn node_parts(&self) -> &EnvMBE<Ast> {
        match *self {
            Node(_, ref body, _) => body,
            _ => icp!(),
        }
    }
    pub fn node_form(&self) -> &::form::Form {
        match *self {
            Node(ref form, _, _) => form,
            _ => icp!(),
        }
    }

    pub fn free_vrs(&self) -> Vec<Name> {
        match *self {
            Trivial | Atom(_) => vec![],
            VariableReference(v) => vec![v],
            Shape(_) | IncompleteNode(_) => unimplemented!("TODO"),
            QuoteLess(_, _) | QuoteMore(_, _) => unimplemented!("TODO"),
            // This one is actually encounterable by real-world code
            //  (if a ∀ somehow ends up underneath a `*` in syntax.)
            // And we need to take a LazyWalkReses to do this right.
            ExtendEnv(_, _) => unimplemented!("TODO"),
            Node(_, ref body, _) => body.map_reduce(
                &|a| a.free_vrs(),
                &|v0, v1| {
                    let mut res = v0.clone();
                    res.append(&mut v1.clone());
                    res
                },
                vec![],
            ),
        }
    }
}

// This is used by combine::many, which is used by the Star parser
impl iter::FromIterator<Ast> for Ast {
    fn from_iter<I: IntoIterator<Item = Ast>>(i: I) -> Self {
        IncompleteNode(EnvMBE::new_from_anon_repeat(i.into_iter().map(|a| a.flatten()).collect()))
    }
}

// This is also sort of a test of MBE, since we need `Ast`s to make them with the macros
//
// Suppose we have the following series of `Ast`s:
// [b = 8] [a = [1 2], b = 8] [a = [3 4 5], b = 8]
//
// We should turn them into the following `Ast`
// [a = [[] [1 2] [3 4 5]], b = [8 8 8]]
#[test]
fn combine_from_kleene_star() {
    use std::iter::FromIterator;

    let parse_parts = vec![
        ast!({ - "b" => "8.0"}),
        ast!({ - "a" => ["1", "2"], "b" => "8.1"}),
        ast!({ - "a" => ["1", "2", "3"], "b" => "8.2"}),
    ];
    let parsed = Ast::from_iter(parse_parts);

    let mut expected_mbe = mbe!("a" => [@"triple" [], ["1", "2"], ["1", "2", "3"]],
             "b" => [@"triple" "8.0", "8.1", "8.2"]);
    expected_mbe.anonimize_repeat(n("triple"));

    assert_eq!(parsed, IncompleteNode(expected_mbe));
}

#[test]
fn star_construction() {
    let env = mbe!( "a" => ["1", "2"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a") env : (, env.get_leaf_or_panic(&n("a")).clone())]} ),
        ast!( { - "x" => ["1", "2"] })
    );

    let env = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);

    assert_eq!(
        ast!( { - "x" => [* env =>("a", "b") env :
                            ((, env.get_leaf_or_panic(&n("b")).clone())
                             (, env.get_leaf_or_panic(&n("a")).clone()))]} ),
        ast!( { - "x" => [("11" "1"), ("22" "2")] })
    );
}

#[test]
fn mbe_r_and_r_roundtrip() {
    use runtime::reify::Reifiable;
    let mbe1 = mbe!( "a" => [@"duo" "1", "2"], "b" => [@"duo" "11", "22"]);
    assert_eq!(mbe1, EnvMBE::<Ast>::reflect(&mbe1.reify()));
}
<|endoftext|>