<commit_before>//! A library for parsing and processing COLLADA documents.
//!
//! [COLLADA][COLLADA] is a COLLAborative Design Activity that defines an XML-based schema to
//! enable 3D authoring applications to freely exchange digital assets. It supports a vast array of
//! features used in 3D modeling, animation, and VFX work, and provides and open, non-proprietary
//! alternative to common formats like [FBX][FBX].
//!
//! This provides functionality for parsing a COLLADA document and utilities for processing the
//! contained data, with the intention of enable direct usage of COLLADA data as well as
//! interchange of document data into other formats.
//!
//! # Quick Start
//!
//! The easiest way to parse a COLLADA document is to load it from a file and use
//! [`Collada::read()`][Collada::read]:
//!
//! ```
//! # #![allow(unused_variables)]
//! use std::fs::File;
//! use parse_collada::Collada;
//!
//! let file = File::open("resources/blender_cube.dae").unwrap();
//! let collada = Collada::read(file).unwrap();
//! ```
//!
//! The resulting [`Collada`][Collada] object provides direct access to all data in the document,
//! directly recreating the logical structure of the document as a Rust type.
//!
//! # COLLADA Versions
//!
//! Currently there are 3 COLLADA versions supported by this library: `1.4.0`, `1.4.1`, and
//! `1.5.0`. Older versions are not supported, but may be added if there is reason to do so. This
//! library attempts to normalize data across versions by "upgrading" all documents to match the
//! `1.5.0` specification. This removes the need for client code to be aware of the specification
//! version used by documents it handles. This conversion is done transparently without the need
//! for user specification.
//!
//! # 3rd Party Extensions
//!
//! The COLLADA format allows for semi-arbitrary extensions to the standard, allowing applications
//! to include application-specific data. This extra data is considered "optional", but may allow
//! applications consuming the COLLADA document to more accurately recreate the scene contained
//! in the document. This library attempts to directly support common 3rd party extensions,
//! primarily those for Blender and Maya. In the case that the 3rd party extension is not
//! directly supported, the underlying XML will be preserved so that the client code can attempt
//! to still use the data.
//!
//! [COLLADA]: https://www.khronos.org/collada/
//! [FBX]: https://en.wikipedia.org/wiki/FBX
//! [Collada]: struct.Collada.html
//! [Collada::read]: struct.Collada.html#method.read

extern crate xml;

pub use xml::common::TextPosition;
pub use xml::reader::Error as XmlError;

use std::io::Read;
use std::fmt::{self, Display, Formatter};
use xml::common::Position;
use xml::EventReader;
use xml::reader::XmlEvent::*;

pub static COLLADA_ATTRIBS: &'static [&'static str] = &["version", "xmlns", "base"];

/// Represents a parsed COLLADA document.
#[derive(Debug, Clone)]
pub struct Collada {
    /// The version string for the COLLADA specification used by the document.
    ///
    /// Only "1.4.0", "1.4.1", and "1.5.0" are supported currently.
    pub version: String,

    /// The base uri for any relative URIs in the document.
    ///
    /// Specified by the `base` attribute on the root `<COLLADA>` element.
    base_uri: Option<AnyUri>,
}

impl Collada {
    /// Read a COLLADA document from a string.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![allow(unused_variables)]
    /// use parse_collada::Collada;
    ///
    /// static DOCUMENT: &'static str = r#"
    ///     <?xml version="1.0" encoding="utf-8"?>
    ///     <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
    ///     </COLLADA>
    /// "#;
    ///
    /// let collada = Collada::from_str(DOCUMENT).unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `Err` if the document is invalid or malformed in some way. For details about
    /// COLLADA versions, 3rd party extensions, and any other details that could influence how
    /// a document is parsed see the [crate-level documentation][crate].
    ///
    /// [crate]: index.html
    pub fn from_str(source: &str) -> Result<Collada> {
        let reader = EventReader::from_str(source);
        Collada::parse(reader)
    }

    /// Attempts to parse the contents of a COLLADA document.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![allow(unused_variables)]
    /// use std::fs::File;
    /// use parse_collada::Collada;
    ///
    /// let file = File::open("resources/blender_cube.dae").unwrap();
    /// let collada = Collada::read(file).unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `Err` if the document is invalid or malformed in some way. For details about
    /// COLLADA versions, 3rd party extensions, and any other details that could influence how
    /// a document is parsed see the [crate-level documentation][crate].
    ///
    /// [crate]: index.html
    pub fn read<R: Read>(reader: R) -> Result<Collada> {
        let reader = EventReader::new(reader);
        Collada::parse(reader)
    }

    /// The logic behind parsing the COLLADA document.
    ///
    /// `from_str()` and `read()` just create the `xml::EventReader` and then defer to `parse()`.
    fn parse<R: Read>(mut reader: EventReader<R>) -> Result<Collada> {
        let mut collada = Collada {
            version: String::new(),
            base_uri: None,
        };

        // Eat the `StartDocument` event. It has no useful information for our purposes, but it
        // will always be the first event emitted, even if there's no XML declaration at the
        // beginning of the document. This is defined as part of the xml-rs API as of v0.3.5,
        // but it's possible this can will change in the future.
        match reader.next()? {
            StartDocument { .. } => {},
            _ => panic!("First event from EventReader wasn't StartDocument"),
        }

        // The next element will always be the `<COLLADA>` tag. This will specify what version of
        // the COLLADA spec is being used, which is how we'll determine our sub-parser.
        match reader.next()? {
            StartElement { name, attributes, namespace: _ } => {
                // If the element isn't the `<COLLADA>` tag then the document is malformed,
                // return an error.
                if name.local_name != "COLLADA" {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::UnexpectedRootElement {
                            element: name.local_name,
                        }
                    })
                }

                // Valide the attributes on the `<COLLADA>` tag.
                // Use boolean flags to track if specific attributes have been encountered.
                let mut has_found_version = false;
                let mut has_found_base = false;

                for attribute in attributes {
                    // NOTE: I'm using `if` blocks instead of `match` here because using `match`
                    // won't allow for the name to be moved out of `attribute`. Using `if` saves
                    // some unnecessary allocations. I expect at some point Rust will get smart
                    // enough that this will no longer be an issue, at which point we should
                    // change this to use `match`, as that keeps better with Rust best practices.
                    if attribute.name.local_name == "version" {
                        if !has_found_version {
                            collada.version = attribute.value;
                            has_found_version = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "version".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                },
                            })
                        }
                    } else if attribute.name.local_name == "base" {
                        if !has_found_base {
                            // TODO: Do we need to validate the URI?
                            collada.base_uri = Some(AnyUri(attribute.value));
                            has_found_base = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "base".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                },
                            })
                        }
                    } else {
                        return Err(Error {
                            position: reader.position(),
                            kind: ErrorKind::UnexpectedAttribute {
                                element: name.local_name,
                                attribute: attribute.name.local_name,
                                expected: COLLADA_ATTRIBS,
                            },
                        })
                    }
                }

                // Verify that all required attributes have been found.
                if !has_found_version {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::MissingAttribute {
                            element: name.local_name,
                            attribute: "version",
                        },
                    })
                }
            }

            // I'm *almost* 100% certain that the only event that can follow the `StartDocument`
            // event is a `StartElement` event. As of v0.3.5, xml-rs doesn't support
            // `<!DOCTYPE>` or processing instructions, and it ignores whitespace and comments
            // (according to how we configure the parser), and those are the only things allowed
            // between `StartDocument` and the first `StartElement`. If xml-rs changes its
            // behavior this will need to be updated.
            event @ _ => panic!("Unexpected event: {:?}", event),
        }

        // TODO: Eat any events until we get to the `</COLLADA>` tag.
        // TODO: Verify the next event is the `EndDocument` event.

        Ok(collada)
    }
}

/// A COLLADA parsing error.
///
/// Contains where in the document the error occurred (i.e. line number and column), and
/// details about the nature of the error.
#[derive(Debug)]
pub struct Error {
    position: TextPosition,
    kind: ErrorKind,
}

impl Error {
    /// Gets the position in the document where the error occurred.
    pub fn position(&self) -> TextPosition { self.position }

    /// Gets detailed information about the error.
    pub fn kind(&self) -> &ErrorKind { &self.kind }
}

impl From<xml::reader::Error> for Error {
    fn from(from: xml::reader::Error) -> Error {
        Error {
            position: from.position(),
            kind: ErrorKind::XmlError(from),
        }
    }
}

impl Display for Error {
    fn fmt(&self, formatter: &mut Formatter) -> ::std::result::Result<(), fmt::Error> {
        write!(formatter, "Error at {}: {}", self.position, self.kind)
    }
}

/// The specific error variant.
#[derive(Debug, PartialEq)]
pub enum ErrorKind {
    /// An element was missing a required attribute.
    MissingAttribute {
        /// The element that was missing an attribute.
        element: String,

        /// The attribute that expected to be present.
        attribute: &'static str,
    },

    /// An element had an attribute that isn't allowed.
    UnexpectedAttribute {
        /// The element that had the unexpected attribute.
        element: String,

        /// The unexpected attribute.
        attribute: String,

        /// The set of attributes allowed for this element.
        expected: &'static [&'static str],
    },

    /// An element had a child element that wasn't allowed.
    UnexpectedElement {
        parent: String,
        element: String,
        expected: &'static [&'static str],
    },

    /// The document started with an element other than `<COLLADA>`.
    UnexpectedRootElement {
        element: String,
    },

    /// The XML in the document was malformed in some way.
    XmlError(XmlError),
}

impl Display for ErrorKind {
    fn fmt(&self, formatter: &mut Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            ErrorKind::MissingAttribute { ref element, ref attribute } => {
                write!(formatter, "<{}> is missing the required attribute \"{}\"", element, attribute)
            }

            ErrorKind::UnexpectedAttribute { ref element, ref attribute, expected } => {
                write!(
                    formatter,
                    "<{}> had an an attribute \"{}\" that is not allowed, only the following attributes are allowed for <{0}>: {}",
                    element,
                    attribute,
                    StringListDisplay(expected),
                )
            }

            ErrorKind::UnexpectedElement { ref parent, ref element, expected } => {
                write!(
                    formatter,
                    "<{}> had a child <{}> which is not allowed, <{0}> may only have the following children: {}",
                    parent,
                    element,
                    StringListDisplay(expected),
                )
            }

            ErrorKind::UnexpectedRootElement { ref element } => {
                write!(formatter, "Document began with <{}> instead of <COLLADA>", element)
            }

            ErrorKind::XmlError(ref error) => {
                write!(formatter, "{}", error.msg())
            }
        }
    }
}

/// A specialized result type for COLLADA parsing.
///
/// Specializes [`std::result::Result`][std::result::Result] to [`Error`][Error] for the purpose
/// of simplifying the signature of any falible COLLADA operation.
///
/// [std::result::Result]: https://doc.rust-lang.org/std/result/enum.Result.html
/// [Error]: struct.Error.html
pub type Result<T> = std::result::Result<T, Error>;

/// A URI in the COLLADA document.
///
/// Represents the [`xs:anyURI`][anyURI] XML data type.
///
/// [anyURI]: http://www.datypic.com/sc/xsd/t-xsd_anyURI.html
#[derive(Debug, Clone)]
pub struct AnyUri(String);

/// Helper struct for pretty-printing lists of strings.
struct StringListDisplay<'a>(&'a [&'a str]);

impl<'a> Display for StringListDisplay<'a> {
    fn fmt(&self, formatter: &mut Formatter) -> ::std::result::Result<(), fmt::Error> {
        if self.0.len() > 0 {
            write!(formatter, "{}", self.0[0])?;

            for string in &self.0[1..] {
                write!(formatter, ", {}", string)?;
            }
        }

        Ok(())
    }
}
<commit_msg>Parse through the end of the document<commit_after>//! A library for parsing and processing COLLADA documents.
//!
//! [COLLADA][COLLADA] is a COLLAborative Design Activity that defines an XML-based schema to
//! enable 3D authoring applications to freely exchange digital assets. It supports a vast array of
//! features used in 3D modeling, animation, and VFX work, and provides and open, non-proprietary
//! alternative to common formats like [FBX][FBX].
//!
//! This provides functionality for parsing a COLLADA document and utilities for processing the
//! contained data, with the intention of enable direct usage of COLLADA data as well as
//! interchange of document data into other formats.
//!
//! # Quick Start
//!
//! The easiest way to parse a COLLADA document is to load it from a file and use
//! [`Collada::read()`][Collada::read]:
//!
//! ```
//! # #![allow(unused_variables)]
//! use std::fs::File;
//! use parse_collada::Collada;
//!
//! let file = File::open("resources/blender_cube.dae").unwrap();
//! let collada = Collada::read(file).unwrap();
//! ```
//!
//! The resulting [`Collada`][Collada] object provides direct access to all data in the document,
//! directly recreating the logical structure of the document as a Rust type.
//!
//! # COLLADA Versions
//!
//! Currently there are 3 COLLADA versions supported by this library: `1.4.0`, `1.4.1`, and
//! `1.5.0`. Older versions are not supported, but may be added if there is reason to do so. This
//! library attempts to normalize data across versions by "upgrading" all documents to match the
//! `1.5.0` specification. This removes the need for client code to be aware of the specification
//! version used by documents it handles. This conversion is done transparently without the need
//! for user specification.
//!
//! # 3rd Party Extensions
//!
//! The COLLADA format allows for semi-arbitrary extensions to the standard, allowing applications
//! to include application-specific data. This extra data is considered "optional", but may allow
//! applications consuming the COLLADA document to more accurately recreate the scene contained
//! in the document. This library attempts to directly support common 3rd party extensions,
//! primarily those for Blender and Maya. In the case that the 3rd party extension is not
//! directly supported, the underlying XML will be preserved so that the client code can attempt
//! to still use the data.
//!
//! [COLLADA]: https://www.khronos.org/collada/
//! [FBX]: https://en.wikipedia.org/wiki/FBX
//! [Collada]: struct.Collada.html
//! [Collada::read]: struct.Collada.html#method.read

extern crate xml;

pub use xml::common::TextPosition;
pub use xml::reader::Error as XmlError;

use std::io::Read;
use std::fmt::{self, Display, Formatter};
use xml::common::Position;
use xml::EventReader;
use xml::reader::XmlEvent::*;

pub static COLLADA_ATTRIBS: &'static [&'static str] = &["version", "xmlns", "base"];

/// Represents a parsed COLLADA document.
#[derive(Debug, Clone)]
pub struct Collada {
    /// The version string for the COLLADA specification used by the document.
    ///
    /// Only "1.4.0", "1.4.1", and "1.5.0" are supported currently.
    pub version: String,

    /// The base uri for any relative URIs in the document.
    ///
    /// Specified by the `base` attribute on the root `<COLLADA>` element.
    base_uri: Option<AnyUri>,
}

impl Collada {
    /// Read a COLLADA document from a string.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![allow(unused_variables)]
    /// use parse_collada::Collada;
    ///
    /// static DOCUMENT: &'static str = r#"
    ///     <?xml version="1.0" encoding="utf-8"?>
    ///     <COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
    ///     </COLLADA>
    /// "#;
    ///
    /// let collada = Collada::from_str(DOCUMENT).unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `Err` if the document is invalid or malformed in some way. For details about
    /// COLLADA versions, 3rd party extensions, and any other details that could influence how
    /// a document is parsed see the [crate-level documentation][crate].
    ///
    /// [crate]: index.html
    pub fn from_str(source: &str) -> Result<Collada> {
        let reader = EventReader::from_str(source);
        Collada::parse(reader)
    }

    /// Attempts to parse the contents of a COLLADA document.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![allow(unused_variables)]
    /// use std::fs::File;
    /// use parse_collada::Collada;
    ///
    /// let file = File::open("resources/blender_cube.dae").unwrap();
    /// let collada = Collada::read(file).unwrap();
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `Err` if the document is invalid or malformed in some way. For details about
    /// COLLADA versions, 3rd party extensions, and any other details that could influence how
    /// a document is parsed see the [crate-level documentation][crate].
    ///
    /// [crate]: index.html
    pub fn read<R: Read>(reader: R) -> Result<Collada> {
        let reader = EventReader::new(reader);
        Collada::parse(reader)
    }

    /// The logic behind parsing the COLLADA document.
    ///
    /// `from_str()` and `read()` just create the `xml::EventReader` and then defer to `parse()`.
    fn parse<R: Read>(mut reader: EventReader<R>) -> Result<Collada> {
        let mut collada = Collada {
            version: String::new(),
            base_uri: None,
        };

        // Eat the `StartDocument` event. It has no useful information for our purposes, but it
        // will always be the first event emitted, even if there's no XML declaration at the
        // beginning of the document. This is defined as part of the xml-rs API as of v0.3.5,
        // but it's possible this can will change in the future.
        match reader.next()? {
            StartDocument { .. } => {},
            _ => panic!("First event from EventReader wasn't StartDocument"),
        }

        // The next element will always be the `<COLLADA>` tag. This will specify what version of
        // the COLLADA spec is being used, which is how we'll determine our sub-parser.
        match reader.next()? {
            StartElement { name, attributes, namespace: _ } => {
                // If the element isn't the `<COLLADA>` tag then the document is malformed,
                // return an error.
                if name.local_name != "COLLADA" {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::UnexpectedRootElement {
                            element: name.local_name,
                        }
                    })
                }

                // Valide the attributes on the `<COLLADA>` tag.
                // Use boolean flags to track if specific attributes have been encountered.
                let mut has_found_version = false;
                let mut has_found_base = false;

                for attribute in attributes {
                    // NOTE: I'm using `if` blocks instead of `match` here because using `match`
                    // won't allow for the name to be moved out of `attribute`. Using `if` saves
                    // some unnecessary allocations. I expect at some point Rust will get smart
                    // enough that this will no longer be an issue, at which point we should
                    // change this to use `match`, as that keeps better with Rust best practices.
                    if attribute.name.local_name == "version" {
                        if !has_found_version {
                            collada.version = attribute.value;
                            has_found_version = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "version".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                },
                            })
                        }
                    } else if attribute.name.local_name == "base" {
                        if !has_found_base {
                            // TODO: Do we need to validate the URI?
                            collada.base_uri = Some(AnyUri(attribute.value));
                            has_found_base = true;
                        } else {
                            return Err(Error {
                                position: reader.position(),
                                kind: ErrorKind::UnexpectedAttribute {
                                    element: name.local_name,
                                    attribute: "base".to_owned(),
                                    expected: COLLADA_ATTRIBS,
                                },
                            })
                        }
                    } else {
                        return Err(Error {
                            position: reader.position(),
                            kind: ErrorKind::UnexpectedAttribute {
                                element: name.local_name,
                                attribute: attribute.name.local_name,
                                expected: COLLADA_ATTRIBS,
                            },
                        })
                    }
                }

                // Verify that all required attributes have been found.
                if !has_found_version {
                    return Err(Error {
                        position: reader.position(),
                        kind: ErrorKind::MissingAttribute {
                            element: name.local_name,
                            attribute: "version",
                        },
                    })
                }
            }

            // I'm *almost* 100% certain that the only event that can follow the `StartDocument`
            // event is a `StartElement` event. As of v0.3.5, xml-rs doesn't support
            // `<!DOCTYPE>` or processing instructions, and it ignores whitespace and comments
            // (according to how we configure the parser), and those are the only things allowed
            // between `StartDocument` and the first `StartElement`. If xml-rs changes its
            // behavior this will need to be updated.
            event @ _ => panic!("Unexpected event: {:?}", event),
        }

        // Eat any events until we get to the `</COLLADA>` tag.
        // TODO: Actually parse the body of the document.
        loop {
            match reader.next()? {
                EndElement { ref name } if name.local_name == "COLLADA" => { break }
                _ => {}
            }
        }

        // TODO: Verify the next event is the `EndDocument` event.
        match reader.next()? {
            EndDocument => {}

            // Same logic here as with the starting event. The only thing that can come after the
            // close tag are comments, white space, and processing instructions, all of which we
            // ignore. This can change with future versions of xml-rs, though.
            event @ _ => { panic!("Unexpected event: {:?}", event); }
        }

        Ok(collada)
    }
}

/// A COLLADA parsing error.
///
/// Contains where in the document the error occurred (i.e. line number and column), and
/// details about the nature of the error.
#[derive(Debug)]
pub struct Error {
    position: TextPosition,
    kind: ErrorKind,
}

impl Error {
    /// Gets the position in the document where the error occurred.
    pub fn position(&self) -> TextPosition { self.position }

    /// Gets detailed information about the error.
    pub fn kind(&self) -> &ErrorKind { &self.kind }
}

impl From<xml::reader::Error> for Error {
    fn from(from: xml::reader::Error) -> Error {
        Error {
            position: from.position(),
            kind: ErrorKind::XmlError(from),
        }
    }
}

impl Display for Error {
    fn fmt(&self, formatter: &mut Formatter) -> ::std::result::Result<(), fmt::Error> {
        write!(formatter, "Error at {}: {}", self.position, self.kind)
    }
}

/// The specific error variant.
#[derive(Debug, PartialEq)]
pub enum ErrorKind {
    /// An element was missing a required attribute.
    MissingAttribute {
        /// The element that was missing an attribute.
        element: String,

        /// The attribute that expected to be present.
        attribute: &'static str,
    },

    /// An element had an attribute that isn't allowed.
    UnexpectedAttribute {
        /// The element that had the unexpected attribute.
        element: String,

        /// The unexpected attribute.
        attribute: String,

        /// The set of attributes allowed for this element.
        expected: &'static [&'static str],
    },

    /// An element had a child element that wasn't allowed.
    UnexpectedElement {
        parent: String,
        element: String,
        expected: &'static [&'static str],
    },

    /// The document started with an element other than `<COLLADA>`.
    UnexpectedRootElement {
        element: String,
    },

    /// The XML in the document was malformed in some way.
    XmlError(XmlError),
}

impl Display for ErrorKind {
    fn fmt(&self, formatter: &mut Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            ErrorKind::MissingAttribute { ref element, ref attribute } => {
                write!(formatter, "<{}> is missing the required attribute \"{}\"", element, attribute)
            }

            ErrorKind::UnexpectedAttribute { ref element, ref attribute, expected } => {
                write!(
                    formatter,
                    "<{}> had an an attribute \"{}\" that is not allowed, only the following attributes are allowed for <{0}>: {}",
                    element,
                    attribute,
                    StringListDisplay(expected),
                )
            }

            ErrorKind::UnexpectedElement { ref parent, ref element, expected } => {
                write!(
                    formatter,
                    "<{}> had a child <{}> which is not allowed, <{0}> may only have the following children: {}",
                    parent,
                    element,
                    StringListDisplay(expected),
                )
            }

            ErrorKind::UnexpectedRootElement { ref element } => {
                write!(formatter, "Document began with <{}> instead of <COLLADA>", element)
            }

            ErrorKind::XmlError(ref error) => {
                write!(formatter, "{}", error.msg())
            }
        }
    }
}

/// A specialized result type for COLLADA parsing.
///
/// Specializes [`std::result::Result`][std::result::Result] to [`Error`][Error] for the purpose
/// of simplifying the signature of any falible COLLADA operation.
///
/// [std::result::Result]: https://doc.rust-lang.org/std/result/enum.Result.html
/// [Error]: struct.Error.html
pub type Result<T> = std::result::Result<T, Error>;

/// A URI in the COLLADA document.
///
/// Represents the [`xs:anyURI`][anyURI] XML data type.
///
/// [anyURI]: http://www.datypic.com/sc/xsd/t-xsd_anyURI.html
#[derive(Debug, Clone)]
pub struct AnyUri(String);

/// Helper struct for pretty-printing lists of strings.
struct StringListDisplay<'a>(&'a [&'a str]);

impl<'a> Display for StringListDisplay<'a> {
    fn fmt(&self, formatter: &mut Formatter) -> ::std::result::Result<(), fmt::Error> {
        if self.0.len() > 0 {
            write!(formatter, "{}", self.0[0])?;

            for string in &self.0[1..] {
                write!(formatter, ", {}", string)?;
            }
        }

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


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::TextEncoderBinding;
use dom::bindings::codegen::Bindings::TextEncoderBinding::TextEncoderMethods;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::{DOMString, USVString};
use encoding::EncoderTrap;
use encoding::label::encoding_from_whatwg_label;
use encoding::types::EncodingRef;
use js::jsapi::{JSContext, JSObject};
use js::jsapi::{JS_GetUint8ArrayData, JS_NewUint8Array};
use libc::uint8_t;
use std::borrow::ToOwned;
use std::ptr;

#[dom_struct]
pub struct TextEncoder {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Defined in rust-encoding"]
    encoder: EncodingRef,
}

impl TextEncoder {
    fn new_inherited(encoder: EncodingRef) -> TextEncoder {
        TextEncoder {
            reflector_: Reflector::new(),
            encoder: encoder,
        }
    }

    pub fn new(global: GlobalRef, encoder: EncodingRef) -> Root<TextEncoder> {
        reflect_dom_object(box TextEncoder::new_inherited(encoder),
                           global,
                           TextEncoderBinding::Wrap)
    }

    // https://encoding.spec.whatwg.org/#dom-textencoder
    pub fn Constructor(global: GlobalRef,
                       label: DOMString) -> Fallible<Root<TextEncoder>> {
        let encoding = match encoding_from_whatwg_label(&label) {
            Some(enc) => enc,
            None => {
                debug!("Encoding Label Not Supported");
                return Err(Error::Range("The given encoding is not supported.".to_owned()))
            }
        };

        match encoding.name() {
            "utf-8" | "utf-16be" | "utf-16le" => {
                Ok(TextEncoder::new(global, encoding))
            }
            _ => {
                debug!("Encoding Not UTF");
                Err(Error::Range("The encoding must be utf-8, utf-16le, or utf-16be.".to_owned()))
            }
        }
    }
}

impl TextEncoderMethods for TextEncoder {
    // https://encoding.spec.whatwg.org/#dom-textencoder-encoding
    fn Encoding(&self) -> DOMString {
        DOMString::from(self.encoder.name())
    }

    #[allow(unsafe_code)]
    // https://encoding.spec.whatwg.org/#dom-textencoder-encode
    fn Encode(&self, cx: *mut JSContext, input: USVString) -> *mut JSObject {
        unsafe {
            let encoded = self.encoder.encode(&input.0, EncoderTrap::Strict).unwrap();
            let length = encoded.len() as u32;
            let js_object: *mut JSObject = JS_NewUint8Array(cx, length);
            let mut is_shared = false;
            let js_object_data: *mut uint8_t = JS_GetUint8ArrayData(js_object, &mut is_shared, ptr::null());
            assert!(!is_shared);
            ptr::copy_nonoverlapping(encoded.as_ptr(), js_object_data, length as usize);
            js_object
        }
    }
}
<commit_msg>Root js_object in TextEncoder::Encode<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::TextEncoderBinding;
use dom::bindings::codegen::Bindings::TextEncoderBinding::TextEncoderMethods;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::{DOMString, USVString};
use encoding::EncoderTrap;
use encoding::label::encoding_from_whatwg_label;
use encoding::types::EncodingRef;
use js::jsapi::{JSContext, JSObject};
use js::jsapi::{JS_GetUint8ArrayData, JS_NewUint8Array};
use libc::uint8_t;
use std::borrow::ToOwned;
use std::ptr;

#[dom_struct]
pub struct TextEncoder {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Defined in rust-encoding"]
    encoder: EncodingRef,
}

impl TextEncoder {
    fn new_inherited(encoder: EncodingRef) -> TextEncoder {
        TextEncoder {
            reflector_: Reflector::new(),
            encoder: encoder,
        }
    }

    pub fn new(global: GlobalRef, encoder: EncodingRef) -> Root<TextEncoder> {
        reflect_dom_object(box TextEncoder::new_inherited(encoder),
                           global,
                           TextEncoderBinding::Wrap)
    }

    // https://encoding.spec.whatwg.org/#dom-textencoder
    pub fn Constructor(global: GlobalRef,
                       label: DOMString) -> Fallible<Root<TextEncoder>> {
        let encoding = match encoding_from_whatwg_label(&label) {
            Some(enc) => enc,
            None => {
                debug!("Encoding Label Not Supported");
                return Err(Error::Range("The given encoding is not supported.".to_owned()))
            }
        };

        match encoding.name() {
            "utf-8" | "utf-16be" | "utf-16le" => {
                Ok(TextEncoder::new(global, encoding))
            }
            _ => {
                debug!("Encoding Not UTF");
                Err(Error::Range("The encoding must be utf-8, utf-16le, or utf-16be.".to_owned()))
            }
        }
    }
}

impl TextEncoderMethods for TextEncoder {
    // https://encoding.spec.whatwg.org/#dom-textencoder-encoding
    fn Encoding(&self) -> DOMString {
        DOMString::from(self.encoder.name())
    }

    #[allow(unsafe_code)]
    // https://encoding.spec.whatwg.org/#dom-textencoder-encode
    fn Encode(&self, cx: *mut JSContext, input: USVString) -> *mut JSObject {
        unsafe {
            let encoded = self.encoder.encode(&input.0, EncoderTrap::Strict).unwrap();
            let length = encoded.len() as u32;
            rooted!(in(cx) let js_object = JS_NewUint8Array(cx, length));
            assert!(!js_object.is_null());
            let mut is_shared = false;
            let js_object_data: *mut uint8_t = JS_GetUint8ArrayData(js_object.get(), &mut is_shared, ptr::null());
            assert!(!is_shared);
            ptr::copy_nonoverlapping(encoded.as_ptr(), js_object_data, length as usize);
            js_object.get()
        }
    }
}
<|endoftext|>


<commit_before>use core::iter::FusedIterator;
use core::ptr::{self, NonNull};
use core::{fmt, mem};

use super::{count, Iter, VecDeque};

/// A draining iterator over the elements of a `VecDeque`.
///
/// This `struct` is created by the [`drain`] method on [`VecDeque`]. See its
/// documentation for more.
///
/// [`drain`]: struct.VecDeque.html#method.drain
/// [`VecDeque`]: struct.VecDeque.html
#[stable(feature = "drain", since = "1.6.0")]
pub struct Drain<'a, T: 'a> {
    pub(crate) after_tail: usize,
    pub(crate) after_head: usize,
    pub(crate) iter: Iter<'a, T>,
    pub(crate) deque: NonNull<VecDeque<T>>,
}

#[stable(feature = "collection_debug", since = "1.17.0")]
impl<T: fmt::Debug> fmt::Debug for Drain<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("Drain")
            .field(&self.after_tail)
            .field(&self.after_head)
            .field(&self.iter)
            .finish()
    }
}

#[stable(feature = "drain", since = "1.6.0")]
unsafe impl<T: Sync> Sync for Drain<'_, T> {}
#[stable(feature = "drain", since = "1.6.0")]
unsafe impl<T: Send> Send for Drain<'_, T> {}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> Drop for Drain<'_, T> {
    fn drop(&mut self) {
        struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>);

        impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> {
            fn drop(&mut self) {
                self.0.for_each(drop);

                let source_deque = unsafe { self.0.deque.as_mut() };

                // T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head
                //
                //        T   t   h   H
                // [. . . o o x x o o . . .]
                //
                let orig_tail = source_deque.tail;
                let drain_tail = source_deque.head;
                let drain_head = self.0.after_tail;
                let orig_head = self.0.after_head;

                let tail_len = count(orig_tail, drain_tail, source_deque.cap());
                let head_len = count(drain_head, orig_head, source_deque.cap());

                // Restore the original head value
                source_deque.head = orig_head;

                match (tail_len, head_len) {
                    (0, 0) => {
                        source_deque.head = 0;
                        source_deque.tail = 0;
                    }
                    (0, _) => {
                        source_deque.tail = drain_head;
                    }
                    (_, 0) => {
                        source_deque.head = drain_tail;
                    }
                    _ => unsafe {
                        if tail_len <= head_len {
                            source_deque.tail = source_deque.wrap_sub(drain_head, tail_len);
                            source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len);
                        } else {
                            source_deque.head = source_deque.wrap_add(drain_tail, head_len);
                            source_deque.wrap_copy(drain_tail, drain_head, head_len);
                        }
                    },
                }
            }
        }

        while let Some(item) = self.next() {
            let guard = DropGuard(self);
            drop(item);
            mem::forget(guard);
        }

        DropGuard(self);
    }
}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> Iterator for Drain<'_, T> {
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<T> {
        self.iter.next().map(|elt| unsafe { ptr::read(elt) })
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> DoubleEndedIterator for Drain<'_, T> {
    #[inline]
    fn next_back(&mut self) -> Option<T> {
        self.iter.next_back().map(|elt| unsafe { ptr::read(elt) })
    }
}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> ExactSizeIterator for Drain<'_, T> {}

#[stable(feature = "fused", since = "1.26.0")]
impl<T> FusedIterator for Drain<'_, T> {}
<commit_msg>Use intra-doc links<commit_after>use core::iter::FusedIterator;
use core::ptr::{self, NonNull};
use core::{fmt, mem};

use super::{count, Iter, VecDeque};

/// A draining iterator over the elements of a `VecDeque`.
///
/// This `struct` is created by the [`drain`] method on [`VecDeque`]. See its
/// documentation for more.
///
/// [`drain`]: VecDeque::drain
#[stable(feature = "drain", since = "1.6.0")]
pub struct Drain<'a, T: 'a> {
    pub(crate) after_tail: usize,
    pub(crate) after_head: usize,
    pub(crate) iter: Iter<'a, T>,
    pub(crate) deque: NonNull<VecDeque<T>>,
}

#[stable(feature = "collection_debug", since = "1.17.0")]
impl<T: fmt::Debug> fmt::Debug for Drain<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("Drain")
            .field(&self.after_tail)
            .field(&self.after_head)
            .field(&self.iter)
            .finish()
    }
}

#[stable(feature = "drain", since = "1.6.0")]
unsafe impl<T: Sync> Sync for Drain<'_, T> {}
#[stable(feature = "drain", since = "1.6.0")]
unsafe impl<T: Send> Send for Drain<'_, T> {}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> Drop for Drain<'_, T> {
    fn drop(&mut self) {
        struct DropGuard<'r, 'a, T>(&'r mut Drain<'a, T>);

        impl<'r, 'a, T> Drop for DropGuard<'r, 'a, T> {
            fn drop(&mut self) {
                self.0.for_each(drop);

                let source_deque = unsafe { self.0.deque.as_mut() };

                // T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head
                //
                //        T   t   h   H
                // [. . . o o x x o o . . .]
                //
                let orig_tail = source_deque.tail;
                let drain_tail = source_deque.head;
                let drain_head = self.0.after_tail;
                let orig_head = self.0.after_head;

                let tail_len = count(orig_tail, drain_tail, source_deque.cap());
                let head_len = count(drain_head, orig_head, source_deque.cap());

                // Restore the original head value
                source_deque.head = orig_head;

                match (tail_len, head_len) {
                    (0, 0) => {
                        source_deque.head = 0;
                        source_deque.tail = 0;
                    }
                    (0, _) => {
                        source_deque.tail = drain_head;
                    }
                    (_, 0) => {
                        source_deque.head = drain_tail;
                    }
                    _ => unsafe {
                        if tail_len <= head_len {
                            source_deque.tail = source_deque.wrap_sub(drain_head, tail_len);
                            source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len);
                        } else {
                            source_deque.head = source_deque.wrap_add(drain_tail, head_len);
                            source_deque.wrap_copy(drain_tail, drain_head, head_len);
                        }
                    },
                }
            }
        }

        while let Some(item) = self.next() {
            let guard = DropGuard(self);
            drop(item);
            mem::forget(guard);
        }

        DropGuard(self);
    }
}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> Iterator for Drain<'_, T> {
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<T> {
        self.iter.next().map(|elt| unsafe { ptr::read(elt) })
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> DoubleEndedIterator for Drain<'_, T> {
    #[inline]
    fn next_back(&mut self) -> Option<T> {
        self.iter.next_back().map(|elt| unsafe { ptr::read(elt) })
    }
}

#[stable(feature = "drain", since = "1.6.0")]
impl<T> ExactSizeIterator for Drain<'_, T> {}

#[stable(feature = "fused", since = "1.26.0")]
impl<T> FusedIterator for Drain<'_, T> {}
<|endoftext|>


<commit_before>// Fake module for documentation
//! Documentation about the various scripts contained herein
//!
//! - [check-graphite](#check-graphite)
//! - [check-cpu](#check-cpu)
//! - [check-fs-writeable](#check-fs-writeable)
//! - [check-load](#check-load)
//! - [check-ram](#check-ram)
//!
//! check-graphite
//! ==============
//!
//! Usage looks like:
//!
//! ```plain
//! $ check-graphite -h
//! check-graphite 0.1.0
//! Brandon W Maister <quodlibetor@gmail.com>
//! Query graphite and exit based on predicates
//!
//! USAGE:
//!         check-graphite [FLAGS] [OPTIONS] <URL> <PATH> <ASSERTION>
//!
//! FLAGS:
//!     -h, --help       Prints help information
//!     -V, --version    Prints version information
//!
//! OPTIONS:
//!         --no-data <NO_DATA_STATUS>    What to do with no data. Choices: ok, warn, critical, unknown. Default: warn. [values: critical ok unknown warn]
//!     -w, --window <WINDOW>             How many minutes of data to test. Default 10.
//!
//! ARGS:
//!     URL          The domain to query graphite. Must include scheme (http/s)
//!     PATH         The graphite path to query. For example: "collectd.*.cpu"
//!     ASSERTION    The assertion to make against the PATH. See Below.
//!
//! About Assertions:
//!
//!     Assertions look like "critical if any point in any series is > 5".
//!
//!     They describe what you care about in your graphite data. The structure of
//!     an assertion is as follows:
//!
//!         <errorkind> if <point spec> [in <series spec>] is|are [not] <operator> <threshold>
//!
//!     Where:
//!
//!     - `errorkind` is either `critical` or `warning`
//!     - `point spec` can be one of:
//!         - `any point`
//!         - `all points`
//!         - `at least <N>% of points`
//!         - `most recent point`
//!     - `series spec` (optional) can be one of:
//!         - `any series`
//!         - `all series`
//!         - `at least <N>% of series`
//!     - `not` (optional) inverts the following operator
//!     - `operator` is one of: `==` `!=` `<` `>` `<=` `>=`
//!     - `threshold` is a floating-point value (e.g. 100, 78.0)
//!
//!     Here are some example assertions:
//!
//!     - `critical if any point is > 0`
//!     - `warning if any point is == 9`
//!     - `critical if all points are > 100.0`
//!     - `critical if at least 20% of points are > 100`
//!     - `critical if most recent point is > 5`
//!     - `critical if most recent point in all series == 0`
//! ```
//!
//! check-cpu
//! =========
//!
//! ```plain
//! $ check-cpu -h
//! Usage: check-cpu [options] [--type=<work-source>...] [--show-hogs=<count>]
//!
//! Options:
//!     -h, --help               Show this help message
//!
//!     -s, --sample=<seconds>   Seconds to spent collecting   [default: 1]
//!     -w, --warn=<percent>     Percent to warn at            [default: 80]
//!     -c, --crit=<percent>     Percent to critical at        [default: 95]
//!     --show-hogs=<count>      Show most cpu-hungry procs    [default: 0]
//!
//! CPU Work Types:
//!
//!     Specifying one of the CPU kinds checks that kind of utilization. The
//!     default is to check total utilization. Specifying this multiple times
//!     alerts if *any* of the CPU usage types are critical.
//!
//!     --type=<usage>           Some of:
//!                                 total user nice system idle
//!                                 iowait irq softirq steal guest [default: total]
//! ```
//!
//! check-fs-writeable
//! ==================
//!
//! ```plain
//! $ check-fs-writeable -h
//! Usage:
//!     check-fs-writeable <filename>
//!     check-fs-writeable -h | --help
//!
//! Check that we can write to a filesystem by writing a byte to a file. Does not
//! try to create the directory, or do anything else. Just writes a single byte to
//! a file.
//!
//! Arguments:
//!
//!     <filename>            The file to write to
//!
//! Options:
//!     -h, --help            Show this message and exit
//! ```
//!
//! check-load
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-load -h
//! Usage: check-load [options]
//!        check-load -h | --help
//!
//! Check the load average of the system
//!
//! Load average is the number of processes *waiting* to do work in a queue, either
//! due to IO or CPU constraints. The numbers used to check are the load averaged
//! over 1, 5 and 15 minutes, respectively
//!
//! Options:
//!     -h, --help              Show this message and exit
//!     -v, --verbose           Print even when things are okay
//!
//! Threshold Behavior:
//!     -w, --warn=<averages>   Averages to warn at         [default: 5,3.5,2.5]
//!     -c, --crit=<averages>   Averages to go critical at  [default: 10,5,3]
//!
//!     --per-cpu=<maybe>      Divide the load average by the number of processors on the
//!                            system.                      [default: true]
//! ```
//!
//! check-ram
//! =========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-ram -h
//! Usage: check-ram [options]
//!        check-ram -h | --help
//!
//! Options:
//!     -h, --help             Show this help message
//!
//!     -w, --warn=<percent>   Percent used to warn at      [default: 80]
//!     -c, --crit=<percent>   Percent used to critical at  [default: 95]
//!
//!     --show-hogs=<count>    Show most RAM-hungry procs   [default: 0]
//!     -v, --verbose          Always show the hogs
//! ```
//!
//! check-disk
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-disk -h
//! Usage:
//!      check-disk [options] [thresholds] [filters]
//!      check-disk -h | --help
//!
//! Check all mounted file systems for disk usage.
//!
//! For some reason this check generally generates values that are between 1% and
//! 3% higher than `df`, even though AFAICT we're both just calling statvfs a bunch
//! of times.
//!
//! Options:
//!     -h, --help            Show this message and exit
//!     --info                Print information of all known filesystems.
//!                           Similar to df.
//!
//! Thresholds:
//!     -w, --warn=<percent>  Percent usage to warn at. [default: 80]
//!     -c, --crit=<percent>  Percent usage to go critical at. [default: 90]
//!     -W, --warn-inodes=<percent>
//!                           Percent of inode usage to warn at. [default: 80]
//!     -C, --crit-inodes=<percent>
//!                           Percent of inode usage to go critical at. [default: 90]
//!
//! Filters:
//!     --pattern=<regex>     Only check filesystems that match this regex.
//!     --exclude-pattern=<regex>  Do not check filesystems that match this regex.
//!     --type=<fs>           Only check filesystems that are of this type, e.g.
//!                           ext4 or tmpfs. See 'man 8 mount' for more examples.
//!     --exclude-type=<fs>   Do not check filesystems that are of this type.
//! ```
<commit_msg>Update `scripts` docs<commit_after>// Fake module for documentation
//! Documentation about the various scripts contained herein
//!
//! - [check-graphite](#check-graphite)
//! - [check-cpu](#check-cpu)
//! - [check-container-cpu](#check-container-cpu)
//! - [check-load](#check-load)
//! - [check-container-ram](#check-container-ram)
//! - [check-ram](#check-ram)
//! - [check-fs-writeable](#check-fs-writeable)
//! - [check-disk](#check-disk)
//!
//! check-graphite
//! ==============
//!
//! Usage looks like:
//!
//! ```plain
//! $ target/osx/debug/check-graphite -h
//! check-graphite 0.1.0
//! Brandon W Maister <quodlibetor@gmail.com>
//! Query graphite and exit based on predicates
//!
//! USAGE:
//!         check-graphite [FLAGS] [OPTIONS] <URL> <PATH> <ASSERTION>... [--]
//!
//! FLAGS:
//!     -h, --help         Prints help information
//!         --print-url    Unconditionally print the graphite url queried
//!     -V, --version      Prints version information
//!
//! OPTIONS:
//!         --retries <COUNT>     How many times to retry reaching graphite. Default 4
//!         --graphite-error <GRAPHITE_ERROR_STATUS>    What to do with no data.
//!                               Choices: ok, warn, critical, unknown.
//!                               No data includes 'all nulls' and error connecting.
//!                               Default: unknown. [values: ok warning critical unknown]
//!     -w, --window <MINUTES>              How many minutes of data to test. Default 10.
//!         --no-data <NO_DATA_STATUS>      What to do with no data.
//!                               Choices: ok, warn, critical, unknown.
//!                               No data includes 'all nulls' and error connecting.
//!                               Default: warn. [values: ok warning critical unknown]
//!
//! ARGS:
//!     URL             The domain to query graphite. Must include scheme (http/s)
//!     PATH            The graphite path to query. For example: "collectd.*.cpu"
//!     ASSERTION...    The assertions to make against the PATH. See Below.
//!
//! About Assertions:
//!
//!     Assertions look like 'critical if any point in any series is > 5'.
//!
//!     They describe what you care about in your graphite data. The structure of
//!     an assertion is as follows:
//!
//!         <errorkind> if <point spec> [in <series spec>] is|are [not] <operator> <threshold>
//!
//!     Where:
//!
//!         - `errorkind` is either `critical` or `warning`
//!         - `point spec` can be one of:
//!             - `any point`
//!             - `all points`
//!             - `at least <N>% of points`
//!             - `most recent point`
//!         - `series spec` (optional) can be one of:
//!             - `any series`
//!             - `all series`
//!             - `at least <N>% of series`
//!         - `not` is optional, and inverts the following operator
//!         - `operator` is one of: `==` `!=` `<` `>` `<=` `>=`
//!         - `threshold` is a floating-point value (e.g. 100, 78.0)
//!
//!     Here are some example assertions:
//!
//!         - `critical if any point is > 0`
//!         - `critical if any point in at least 40% of series is > 0`
//!         - `critical if any point is not > 0`
//!         - `warning if any point is == 9`
//!         - `critical if all points are > 100.0`
//!         - `critical if at least 20% of points are > 100`
//!         - `critical if most recent point is > 5`
//!         - `critical if most recent point in all series are == 0`
//! ```
//!
//! check-cpu
//! =========
//!
//! Linux-only.
//!
//! ```plain
//! Usage:
//!     check-cpu [options] [--type=<work-source>...] [--show-hogs=<count>]
//!     check-cpu (-h | --help)
//!
//! Options:
//!     -h, --help               Show this help message
//!
//!     -s, --sample=<seconds>   Seconds to spent collecting   [default: 1]
//!     -w, --warn=<percent>     Percent to warn at            [default: 80]
//!     -c, --crit=<percent>     Percent to critical at        [default: 95]
//!
//!     --per-cpu                Gauge values per-cpu instead of across the
//!                              entire machine
//!     --cpu-count=<num>        If --per-cpu is specified, this is how many
//!                              CPUs need to be at a threshold to trigger.
//!                              [default: 1]
//!
//!     --show-hogs=<count>      Show most cpu-hungry procs    [default: 0]
//!
//! CPU Work Types:
//!
//!     Specifying one of the CPU kinds checks that kind of utilization. The
//!     default is to check total utilization. Specifying this multiple times
//!     alerts if *any* of the CPU usage types are critical.
//!
//!     There are three CPU type groups: `active` `activeplusiowait` and
//!     `activeminusnice`. `activeplusiowait` considers time spent waiting for IO
//!     to be busy time, this gets alerts to be more aligned with the overall
//!     system load, but is different from CPU usage reported by `top` since the
//!     CPU isn't actually *busy* during this time.
//!
//!     --type=<usage>           Some of:
//!                                 active activeplusiowait activeminusnice
//!                                 user nice system irq softirq steal guest
//!                                 idle iowait [default: active]
//! ```
//!
//! check-container-cpu
//! ===================
//!
//! Linux-only. Can only be run from inside a cgroup.
//!
//! ```plain
//! $ target/osx/debug/check-container-cpu -h
//! Usage:
//!     check-container-cpu [options]
//!     check-cpu (-h | --help)
//!
//! Check the cpu usage of the currently-running container. This must be run from
//! inside the container to be checked.
//!
//! Options:
//!     -h, --help            Show this help message
//!
//!     -w, --warn=<percent>  Percent to warn at           [default: 80]
//!     -c, --crit=<percent>  Percent to critical at       [default: 80]
//!
//!     -s, --sample=<secs>   Seconds to take sample over  [default: 5]
//!
//!     --show-hogs=<count>   Show <count> most cpu-intensive processes in this
//!                           container.                   [default: 0]
//!
//!     --shares-per-cpu=<shares>
//!                           The number of CPU shares given to a cgroup when
//!                           it has exactly one CPU allocated to it.
//!
//! About usage percentages:
//!
//!     If you don't specify '--shares-per-cpu', percentages should be specified
//!     relative to a single CPU's usage. So if you have a process that you want to
//!     be allowed to use 4 CPUs worth of processor time, and you were planning on
//!     going critical at 90%, you should specify something like '--crit 360'
//!
//!     However, if you are using a container orchestrator such as Mesos, you often
//!     tell it that you want this container to have "2 CPUs" worth of hardware.
//!     Your scheduler is responsible for deciding how many cgroup cpu shares 1
//!     CPU's worth of time is, and keeping track of how many shares it has doled
//!     out, and then schedule your containers to run with 2 CPUs worth of CPU
//!     shares. Assuming that your scheduler uses the default number of shares
//!     (1024) as "one cpu", this will mean that you have given that cgroup 2048
//!     shares.
//!
//!     If you do specify --shares-per-cpu then the percentage that you give will
//!     be scaled by the number of CPUs worth of shares that this container has
//!     been given, and CPU usage will be compared to the total percent of the CPUs
//!     that it has been allocated.
//!
//!     Which is to say, if you specify --shares-per-cpu, you should always specify
//!     your warn/crit percentages out of 100%, because this script will correctly
//!     scale it for your process.
//!
//!     Here are some examples, where 'shares granted' is the value in
//!     /sys/fs/cgroup/cpu/cpu.shares:
//!
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 1024
//!           percent of one CPU to alert at: 90
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 2024
//!           percent of one CPU to alert at: 180
//!         * args: --shares-per-cpu 1024 --crit 90
//!           shares granted: 102
//!           percent of one CPU to alert at: 9
//! ```
//!
//! check-load
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-load -h
//! Usage: check-load [options]
//!        check-load -h | --help
//!
//! Check the load average of the system
//!
//! Load average is the number of processes *waiting* to do work in a queue, either
//! due to IO or CPU constraints. The numbers used to check are the load averaged
//! over 1, 5 and 15 minutes, respectively
//!
//! Options:
//!     -h, --help              Show this message and exit
//!     -v, --verbose           Print even when things are okay
//!
//! Threshold Behavior:
//!     -w, --warn=<averages>   Averages to warn at         [default: 5,3.5,2.5]
//!     -c, --crit=<averages>   Averages to go critical at  [default: 10,5,3]
//!
//!     --per-cpu               Divide the load average by the number of processors on the
//!                             system.
//! ```
//!
//! check-ram
//! =========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-ram -h
//! Usage: check-ram [options]
//!        check-ram -h | --help
//!
//! Options:
//!     -h, --help             Show this help message
//!
//!     -w, --warn=<percent>   Percent used to warn at      [default: 80]
//!     -c, --crit=<percent>   Percent used to critical at  [default: 95]
//!
//!     --show-hogs=<count>    Show most RAM-hungry procs   [default: 0]
//!     -v, --verbose          Always show the hogs
//! ```
//!
//! check-container-ram
//! ===================
//!
//! Linux-only. Can only be run from inside a container.
//!
//! ```plain
//! $ target/osx/debug/check-container-ram -h
//! Usage:
//!     check-container-ram [--show-hogs=<count>] [--invalid-limit=<status>] [options]
//!     check-container-ram (-h | --help)
//!
//! Check the RAM usage of the currently-running container. This must be run from
//! inside the container to be checked.
//!
//! This checks as a ratio of the limit specified in the cgroup memory limit, and
//! if there is no limit set (or the limit is greater than the total memory
//! available on the system) this checks against the total system memory.
//!
//! Options:
//!     -h, --help                 Show this message and exit
//!
//!     -w, --warn=<percent>       Warn at this percent used           [default: 85]
//!     -c, --crit=<percent>       Critical at this percent used       [default: 95]
//!
//!     --invalid-limit=<status>   Status to consider this check if the CGroup limit
//!                                is greater than the system ram      [default: ok]
//!
//!     --show-hogs=<count>        Show the most ram-hungry procs      [default: 0]
//! ```
//!
//! check-disk
//! ==========
//!
//! Linux-only.
//!
//! ```plain
//! $ check-disk -h
//! Usage:
//!      check-disk [options] [thresholds] [filters]
//!      check-disk -h | --help
//!
//! Check all mounted file systems for disk usage.
//!
//! For some reason this check generally generates values that are between 1% and
//! 3% higher than `df`, even though AFAICT we're both just calling statvfs a bunch
//! of times.
//!
//! Options:
//!     -h, --help            Show this message and exit
//!     --info                Print information of all known filesystems.
//!                           Similar to df.
//!
//! Thresholds:
//!     -w, --warn=<percent>  Percent usage to warn at. [default: 80]
//!     -c, --crit=<percent>  Percent usage to go critical at. [default: 90]
//!     -W, --warn-inodes=<percent>
//!                           Percent of inode usage to warn at. [default: 80]
//!     -C, --crit-inodes=<percent>
//!                           Percent of inode usage to go critical at. [default: 90]
//!
//! Filters:
//!     --pattern=<regex>     Only check filesystems that match this regex.
//!     --exclude-pattern=<regex>  Do not check filesystems that match this regex.
//!     --type=<fs>           Only check filesystems that are of this type, e.g.
//!                           ext4 or tmpfs. See 'man 8 mount' for more examples.
//!     --exclude-type=<fs>   Do not check filesystems that are of this type.
//! ```
//!
//! check-fs-writeable
//! ==================
//!
//! ```plain
//! $ check-fs-writeable -h
//! Usage:
//!     check-fs-writeable <filename>
//!     check-fs-writeable -h | --help
//!
//! Check that we can write to a filesystem by writing a byte to a file. Does not
//! try to create the directory, or do anything else. Just writes a single byte to
//! a file.
//!
//! Arguments:
//!
//!     <filename>            The file to write to
//!
//! Options:
//!     -h, --help            Show this message and exit
//! ```
//!
<|endoftext|>


<commit_before>use nitro::component::Component;
use nitro::component::Message;
use nitro::game_object::GameObject;

pub struct Spinny {

}

impl Component for Spinny {
    fn receive_message(&mut self, game_object : &mut GameObject, message : &Message) {
        match *message {
            Message::Update{delta_time} => {
                game_object.transform.add_rotation(1.0 * delta_time);
                game_object.transform.add_x(10.0 * delta_time);
            }
            _ => {}
        }
    }
}
<commit_msg>Removed duplicate spinny<commit_after><|endoftext|>


<commit_before>extern crate orbclient;

use orbclient::Color;

use std::env;
use std::io::{self, Read, Write};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};
use std::thread;

use window::{ConsoleEvent, ConsoleWindow};

mod window;

fn main() {
    let shell = env::args().nth(1).unwrap_or("sh".to_string());
    match Command::new(&shell)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
    {
        Ok(process) => {
            let output_mutex = Arc::new(Mutex::new(Vec::new()));

            {
                let mut stdout = process.stdout.unwrap();
                let stdout_output_mutex = output_mutex.clone();
                thread::spawn(move || {
                    'stdout: loop {
                        let mut buf = [0; 4096];
                        match stdout.read(&mut buf) {
                            Ok(0) => break 'stdout,
                            Ok(count) => {
                                match stdout_output_mutex.lock() {
                                    Ok(mut stdout_output) => stdout_output.extend_from_slice(&buf[..count]),
                                    Err(_) => {
                                        println!("failed to lock stdout output mutex");
                                        break 'stdout;
                                    }
                                }
                            },
                            Err(err) => {
                                println!("failed to read stdout: {}", err);
                                break 'stdout;
                            }
                        }
                    }
                });
            }

            {
                let mut stderr = process.stderr.unwrap();
                let stderr_output_mutex = output_mutex.clone();
                thread::spawn(move || {
                    'stderr: loop {
                        let mut buf = [0; 4096];
                        match stderr.read(&mut buf) {
                            Ok(0) => break 'stderr,
                            Ok(count) => {
                                match stderr_output_mutex.lock() {
                                    Ok(mut stderr_output) => stderr_output.extend_from_slice(&buf[..count]),
                                    Err(_) => {
                                        println!("failed to lock stderr output mutex");
                                        break 'stderr;
                                    }
                                }
                            },
                            Err(err) => {
                                println!("failed to read stderr: {}", err);
                                break 'stderr;
                            }
                        }
                    }
                });
            }

            let mut stdin = process.stdin.unwrap();
            let mut window = ConsoleWindow::new(-1, -1, 576, 400, "Terminal");
            'events: loop {
                match output_mutex.lock() {
                    Ok(mut output) => {
                        let mut string = String::new();
                        for byte in output.drain(..) {
                            string.push(byte as char);
                        }
                        window.print(&string, Color::rgb(255, 255, 255));
                    },
                    Err(_) => {
                        println!("failed to lock print output mutex");
                        break 'events;
                    }
                }

                for console_event in window.read() {
                    match console_event {
                        ConsoleEvent::Line(mut line) => {
                            line.push('\n');
                            match stdin.write(&line.as_bytes()) {
                                Ok(_) => (),
                                Err(err) => {
                                    println!("failed to write stdin: {}", err);
                                    break 'events;
                                }
                            }
                        },
                        ConsoleEvent::Quit => break 'events
                    }
                }

                thread::sleep_ms(30);
            }
        },
        Err(err) => println!("failed to execute '{}': {}\n", shell, err)
    }
}
<commit_msg>Do not print on empty string<commit_after>extern crate orbclient;

use orbclient::Color;

use std::env;
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex};
use std::thread;

use window::{ConsoleEvent, ConsoleWindow};

mod window;

fn main() {
    let shell = env::args().nth(1).unwrap_or("sh".to_string());
    match Command::new(&shell)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
    {
        Ok(process) => {
            let output_mutex = Arc::new(Mutex::new(Vec::new()));

            {
                let mut stdout = process.stdout.unwrap();
                let stdout_output_mutex = output_mutex.clone();
                thread::spawn(move || {
                    'stdout: loop {
                        let mut buf = [0; 4096];
                        match stdout.read(&mut buf) {
                            Ok(0) => break 'stdout,
                            Ok(count) => {
                                match stdout_output_mutex.lock() {
                                    Ok(mut stdout_output) => stdout_output.extend_from_slice(&buf[..count]),
                                    Err(_) => {
                                        println!("failed to lock stdout output mutex");
                                        break 'stdout;
                                    }
                                }
                            },
                            Err(err) => {
                                println!("failed to read stdout: {}", err);
                                break 'stdout;
                            }
                        }
                    }
                });
            }

            {
                let mut stderr = process.stderr.unwrap();
                let stderr_output_mutex = output_mutex.clone();
                thread::spawn(move || {
                    'stderr: loop {
                        let mut buf = [0; 4096];
                        match stderr.read(&mut buf) {
                            Ok(0) => break 'stderr,
                            Ok(count) => {
                                match stderr_output_mutex.lock() {
                                    Ok(mut stderr_output) => stderr_output.extend_from_slice(&buf[..count]),
                                    Err(_) => {
                                        println!("failed to lock stderr output mutex");
                                        break 'stderr;
                                    }
                                }
                            },
                            Err(err) => {
                                println!("failed to read stderr: {}", err);
                                break 'stderr;
                            }
                        }
                    }
                });
            }

            let mut stdin = process.stdin.unwrap();
            let mut window = ConsoleWindow::new(-1, -1, 576, 400, "Terminal");
            'events: loop {
                match output_mutex.lock() {
                    Ok(mut output) => {
                        let mut string = String::new();
                        for byte in output.drain(..) {
                            string.push(byte as char);
                        }
                        if ! string.is_empty() {
                            window.print(&string, Color::rgb(255, 255, 255));
                        }
                    },
                    Err(_) => {
                        println!("failed to lock print output mutex");
                        break 'events;
                    }
                }

                for console_event in window.read() {
                    match console_event {
                        ConsoleEvent::Line(mut line) => {
                            line.push('\n');
                            match stdin.write(&line.as_bytes()) {
                                Ok(_) => (),
                                Err(err) => {
                                    println!("failed to write stdin: {}", err);
                                    break 'events;
                                }
                            }
                        },
                        ConsoleEvent::Quit => break 'events
                    }
                }

                thread::sleep_ms(30);
            }
        },
        Err(err) => println!("failed to execute '{}': {}\n", shell, err)
    }
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use geo;
use serde;
use std::rc::Rc;
use std::cell::Cell;
use serde::ser::Serializer;
use std::cmp::Ordering;

// Note: this file is needed to use serde in rust stable
// cf mimirsbrunn/build.rs for explanations

pub trait Incr: Clone {
    fn id(&self) -> &str;
    fn incr(&mut self);
}

/// Object stored in elastic search
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Place {
    Admin(Admin),
    Street(Street),
    Addr(Addr),
    Poi(Poi),
}

impl Place{
    pub fn is_admin(&self) -> bool {
        match *self {
            Place::Admin(_) => true,
            _ => false
        }
    }
    pub fn is_street(&self) -> bool {
        match *self {
            Place::Street(_) => true,
            _ => false
        }
    }
    pub fn is_addr(&self) -> bool {
        match *self {
            Place::Addr(_) => true,
            _ => false
        }
    }
    pub fn is_poi(&self) -> bool {
        match *self {
            Place::Poi(_) => true,
            _ => false
        }
    }
    pub fn label(&self) -> &str {
        match *self {
            Place::Admin(ref o) => o.label(),
            Place::Street(ref o) => o.label(),
            Place::Addr(ref o) => o.label(),
            Place::Poi(ref o) => o.label(),
        }
    }

    pub fn admins(&self) -> Vec<Rc<Admin>> {
        match *self {
            Place::Admin(ref o) => o.admins(),
            Place::Street(ref o) => o.admins(),
            Place::Addr(ref o) => o.admins(),
            Place::Poi(ref o) => o.admins(),
        }
    }
}

pub trait DocType {
    fn doc_type() -> &'static str; // provides the elasticsearch type name
}

pub trait EsId {
    fn es_id(&self) -> Option<String>; // provides the elasticsearch id
}

pub trait Members{
    fn label(&self) -> &str;
    fn admins(&self) -> Vec<Rc<Admin>>;
}

impl<'a, T: DocType> DocType for &'a T {
    fn doc_type() -> &'static str {
        T::doc_type()
    }
}
impl<'a, T: EsId> EsId for &'a T {
    fn es_id(&self) -> Option<String> {
        T::es_id(self)
    }
}
impl<'a, T: DocType> DocType for Rc<T> {
    fn doc_type() -> &'static str {
        T::doc_type()
    }
}
impl<'a, T: EsId> EsId for Rc<T> {
    fn es_id(&self) -> Option<String> {
        T::es_id(self)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Poi {
    pub id: String,
    pub label: String,
    pub name: String,
    pub coord: Coord,
    pub administrative_regions: Vec<Rc<Admin>>,
    pub weight: u32,
    pub zip_codes: Vec<String>,
}

impl DocType for Poi {
    fn doc_type() -> &'static str {
        "poi"
    }
}

impl EsId for Poi {
    fn es_id(&self) -> Option<String> {
        Some(self.id.clone())
    }
}

impl Members for Poi {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        self.administrative_regions.clone()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Admin {
    pub id: String,
    pub insee: String,
    pub level: u32,
    pub label: String,
    pub zip_codes: Vec<String>,
    #[serde(serialize_with="custom_cell_serialize", skip_deserializing)]
    //Attribut weight is used in elastic search to sort the result. It is absent in the response
    //of navitia (jormungandr) and hence deserializing is not necessary
    pub weight: Cell<u32>,
    pub coord: Coord,
    #[serde(skip_serializing, skip_deserializing)]
    pub boundary: Option<geo::MultiPolygon<f64>>,
}

impl Ord for Admin {
    fn cmp(&self, other: &Self) -> Ordering {
        self.id.cmp(&other.id)
    }
}

impl PartialOrd for Admin {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Admin {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Members for Admin {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        vec![Rc::new(self.clone())]
    }
}

impl Eq for Admin {}

fn custom_cell_serialize<S>(cell: &Cell<u32>, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
	// we can serialize the cell as a u32, since the reference is important only while loading the data
	// in ES but not in bragi
	serializer.serialize_u32(cell.get())
}

impl EsId for Admin {
    fn es_id(&self) -> Option<String> {
        Some(self.id.clone())
    }
}

impl DocType for Admin {
    fn doc_type() -> &'static str {
        "admin"
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Street {
    pub id: String,
    pub street_name: String,
    pub administrative_regions: Vec<Rc<Admin>>,
    pub label: String,
    pub weight: u32,
    pub coord: Coord,
    pub zip_codes: Vec<String>,
}
impl Incr for Street {
    fn id(&self) -> &str {
        &self.id
    }
    fn incr(&mut self) {
        self.weight += 1;
    }
}
impl DocType for Street {
    fn doc_type() -> &'static str {
        "street"
    }
}

impl EsId for Street {
    fn es_id(&self) -> Option<String> {
        None
    }
}

impl Members for Street {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        self.administrative_regions.clone()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Addr {
    pub id: String,
    pub house_number: String,
    pub street: Street,
    pub label: String,
    pub coord: Coord,
    pub weight: u32,
    pub zip_codes: Vec<String>,
}

impl DocType for Addr {
    fn doc_type() -> &'static str {
        "addr"
    }
}

impl EsId for Addr {
    fn es_id(&self) -> Option<String> {
        None
    }
}

impl Members for Addr {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        self.street.admins()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AliasOperations {
    pub actions: Vec<AliasOperation>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AliasOperation {
    #[serde(skip_serializing_if="Option::is_none")]
    pub add: Option<AliasParameter>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub remove: Option<AliasParameter>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AliasParameter{
    pub index: String,
    pub alias: String,

}

// we want a custom serialization for coords, and so far the cleanest way
// to do this that has been found is to wrap the coord in another struct
#[derive(Debug, Clone)]
pub struct Coord(pub geo::Coordinate<f64>);
impl Coord {
    pub fn new(lat: f64, lon: f64) -> Coord {
        Coord(geo::Coordinate {x: lat, y: lon})
    }
    pub fn lat(&self) -> f64 {
        self.x
    }
    pub fn lon(&self) -> f64 {
        self.y
    }
    pub fn default() -> Coord {
        Coord(geo::Coordinate {x: 0., y: 0.})
    }
    pub fn is_default(&self) -> bool {
        self.lat() == 0. && self.lon() == 0.
    }
}

impl ::std::ops::Deref for Coord {
    type Target = geo::Coordinate<f64>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl serde::Serialize for Coord {
  fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer {
      let mut state = try!(serializer.serialize_struct("Coord", 2));
      try!(serializer.serialize_struct_elt(&mut state, "lat", &self.0.x));
      try!(serializer.serialize_struct_elt(&mut state, "lon", &self.0.y));
      serializer.serialize_struct_end(state)
  }
}

enum GeoCoordField {
    X,
    Y,
}

impl serde::Deserialize for GeoCoordField {
    fn deserialize<D>(deserializer: &mut D) -> Result<GeoCoordField, D::Error>
        where D: serde::de::Deserializer
    {
        struct GeoCoordFieldVisitor;

        impl serde::de::Visitor for GeoCoordFieldVisitor {
            type Value = GeoCoordField;

            fn visit_str<E>(&mut self, value: &str) -> Result<GeoCoordField, E>
                where E: serde::de::Error
            {
                match value {
                    "lat" => Ok(GeoCoordField::X),
                    "lon" => Ok(GeoCoordField::Y),
                    _ => Err(serde::de::Error::custom("expected lon or lat")),
                }
            }
        }

        deserializer.deserialize(GeoCoordFieldVisitor)
    }
}

impl serde::Deserialize for Coord {
  fn deserialize<D>(deserializer: &mut D) -> Result<Coord, D::Error>
      where D: serde::de::Deserializer
  {
      static FIELDS: &'static [&'static str] = &["lat", "lon"];
      deserializer.deserialize_struct("Coord", FIELDS, GeoCoordDeserializerVisitor)
  }
}
struct GeoCoordDeserializerVisitor;

impl serde::de::Visitor for GeoCoordDeserializerVisitor {
    type Value = Coord;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Coord, V::Error>
        where V: serde::de::MapVisitor
    {
        let mut x = None;
        let mut y = None;

        loop {
            match try!(visitor.visit_key()) {
                Some(GeoCoordField::X) => { x = Some(try!(visitor.visit_value())); }
                Some(GeoCoordField::Y) => { y = Some(try!(visitor.visit_value())); }
                None => { break; }
            }
        }

        let x = match x {
            Some(x) => x,
            None => try!(visitor.missing_field("x")),
        };

        let y = match y {
            Some(y) => y,
            None => try!(visitor.missing_field("y")),
        };

        try!(visitor.end());

        Ok(Coord(geo::Coordinate{ x: x, y: y }))
    }
}
<commit_msg>stop object added<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

use geo;
use serde;
use std::rc::Rc;
use std::cell::Cell;
use serde::ser::Serializer;
use std::cmp::Ordering;

// Note: this file is needed to use serde in rust stable
// cf mimirsbrunn/build.rs for explanations

pub trait Incr: Clone {
    fn id(&self) -> &str;
    fn incr(&mut self);
}

/// Object stored in elastic search
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Place {
    Admin(Admin),
    Street(Street),
    Addr(Addr),
    Poi(Poi),
}

impl Place{
    pub fn is_admin(&self) -> bool {
        match *self {
            Place::Admin(_) => true,
            _ => false
        }
    }
    pub fn is_street(&self) -> bool {
        match *self {
            Place::Street(_) => true,
            _ => false
        }
    }
    pub fn is_addr(&self) -> bool {
        match *self {
            Place::Addr(_) => true,
            _ => false
        }
    }
    pub fn is_poi(&self) -> bool {
        match *self {
            Place::Poi(_) => true,
            _ => false
        }
    }
    pub fn label(&self) -> &str {
        match *self {
            Place::Admin(ref o) => o.label(),
            Place::Street(ref o) => o.label(),
            Place::Addr(ref o) => o.label(),
            Place::Poi(ref o) => o.label(),
        }
    }

    pub fn admins(&self) -> Vec<Rc<Admin>> {
        match *self {
            Place::Admin(ref o) => o.admins(),
            Place::Street(ref o) => o.admins(),
            Place::Addr(ref o) => o.admins(),
            Place::Poi(ref o) => o.admins(),
        }
    }
}

pub trait DocType {
    fn doc_type() -> &'static str; // provides the elasticsearch type name
}

pub trait EsId {
    fn es_id(&self) -> Option<String>; // provides the elasticsearch id
}

pub trait Members{
    fn label(&self) -> &str;
    fn admins(&self) -> Vec<Rc<Admin>>;
}

impl<'a, T: DocType> DocType for &'a T {
    fn doc_type() -> &'static str {
        T::doc_type()
    }
}
impl<'a, T: EsId> EsId for &'a T {
    fn es_id(&self) -> Option<String> {
        T::es_id(self)
    }
}
impl<'a, T: DocType> DocType for Rc<T> {
    fn doc_type() -> &'static str {
        T::doc_type()
    }
}
impl<'a, T: EsId> EsId for Rc<T> {
    fn es_id(&self) -> Option<String> {
        T::es_id(self)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Poi {
    pub id: String,
    pub label: String,
    pub name: String,
    pub coord: Coord,
    pub administrative_regions: Vec<Rc<Admin>>,
    pub weight: u32,
    pub zip_codes: Vec<String>,
}

impl DocType for Poi {
    fn doc_type() -> &'static str {
        "poi"
    }
}

impl EsId for Poi {
    fn es_id(&self) -> Option<String> {
        Some(self.id.clone())
    }
}

impl Members for Poi {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        self.administrative_regions.clone()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Stop {
    pub id: String,
    pub label: String,
    pub name: String,
    pub coord: Coord,
    pub administrative_regions: Vec<Rc<Admin>>,
    pub weight: u32,
    pub zip_codes: Vec<String>,
}

impl EsId for Stop {
    fn es_id(&self) -> Option<String> {
        Some(self.id.clone())
    }
}

impl DocType for Stop {
    fn doc_type() -> &'static str {
        "stop"
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Admin {
    pub id: String,
    pub insee: String,
    pub level: u32,
    pub label: String,
    pub zip_codes: Vec<String>,
    #[serde(serialize_with="custom_cell_serialize", skip_deserializing)]
    //Attribut weight is used in elastic search to sort the result. It is absent in the response
    //of navitia (jormungandr) and hence deserializing is not necessary
    pub weight: Cell<u32>,
    pub coord: Coord,
    #[serde(skip_serializing, skip_deserializing)]
    pub boundary: Option<geo::MultiPolygon<f64>>,
}

impl Ord for Admin {
    fn cmp(&self, other: &Self) -> Ordering {
        self.id.cmp(&other.id)
    }
}

impl PartialOrd for Admin {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Admin {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Members for Admin {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        vec![Rc::new(self.clone())]
    }
}

impl Eq for Admin {}

fn custom_cell_serialize<S>(cell: &Cell<u32>, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
	// we can serialize the cell as a u32, since the reference is important only while loading the data
	// in ES but not in bragi
	serializer.serialize_u32(cell.get())
}

impl EsId for Admin {
    fn es_id(&self) -> Option<String> {
        Some(self.id.clone())
    }
}

impl DocType for Admin {
    fn doc_type() -> &'static str {
        "admin"
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Street {
    pub id: String,
    pub street_name: String,
    pub administrative_regions: Vec<Rc<Admin>>,
    pub label: String,
    pub weight: u32,
    pub coord: Coord,
    pub zip_codes: Vec<String>,
}
impl Incr for Street {
    fn id(&self) -> &str {
        &self.id
    }
    fn incr(&mut self) {
        self.weight += 1;
    }
}
impl DocType for Street {
    fn doc_type() -> &'static str {
        "street"
    }
}

impl EsId for Street {
    fn es_id(&self) -> Option<String> {
        None
    }
}

impl Members for Street {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        self.administrative_regions.clone()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Addr {
    pub id: String,
    pub house_number: String,
    pub street: Street,
    pub label: String,
    pub coord: Coord,
    pub weight: u32,
    pub zip_codes: Vec<String>,
}

impl DocType for Addr {
    fn doc_type() -> &'static str {
        "addr"
    }
}

impl EsId for Addr {
    fn es_id(&self) -> Option<String> {
        None
    }
}

impl Members for Addr {
    fn label(&self) -> &str {
        &self.label
    }
    fn admins(&self) -> Vec<Rc<Admin>> {
        self.street.admins()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AliasOperations {
    pub actions: Vec<AliasOperation>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AliasOperation {
    #[serde(skip_serializing_if="Option::is_none")]
    pub add: Option<AliasParameter>,
    #[serde(skip_serializing_if="Option::is_none")]
    pub remove: Option<AliasParameter>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AliasParameter{
    pub index: String,
    pub alias: String,

}

// we want a custom serialization for coords, and so far the cleanest way
// to do this that has been found is to wrap the coord in another struct
#[derive(Debug, Clone)]
pub struct Coord(pub geo::Coordinate<f64>);
impl Coord {
    pub fn new(lat: f64, lon: f64) -> Coord {
        Coord(geo::Coordinate {x: lat, y: lon})
    }
    pub fn lat(&self) -> f64 {
        self.x
    }
    pub fn lon(&self) -> f64 {
        self.y
    }
    pub fn default() -> Coord {
        Coord(geo::Coordinate {x: 0., y: 0.})
    }
    pub fn is_default(&self) -> bool {
        self.lat() == 0. && self.lon() == 0.
    }
}

impl ::std::ops::Deref for Coord {
    type Target = geo::Coordinate<f64>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl serde::Serialize for Coord {
  fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer {
      let mut state = try!(serializer.serialize_struct("Coord", 2));
      try!(serializer.serialize_struct_elt(&mut state, "lat", &self.0.x));
      try!(serializer.serialize_struct_elt(&mut state, "lon", &self.0.y));
      serializer.serialize_struct_end(state)
  }
}

enum GeoCoordField {
    X,
    Y,
}

impl serde::Deserialize for GeoCoordField {
    fn deserialize<D>(deserializer: &mut D) -> Result<GeoCoordField, D::Error>
        where D: serde::de::Deserializer
    {
        struct GeoCoordFieldVisitor;

        impl serde::de::Visitor for GeoCoordFieldVisitor {
            type Value = GeoCoordField;

            fn visit_str<E>(&mut self, value: &str) -> Result<GeoCoordField, E>
                where E: serde::de::Error
            {
                match value {
                    "lat" => Ok(GeoCoordField::X),
                    "lon" => Ok(GeoCoordField::Y),
                    _ => Err(serde::de::Error::custom("expected lon or lat")),
                }
            }
        }

        deserializer.deserialize(GeoCoordFieldVisitor)
    }
}

impl serde::Deserialize for Coord {
  fn deserialize<D>(deserializer: &mut D) -> Result<Coord, D::Error>
      where D: serde::de::Deserializer
  {
      static FIELDS: &'static [&'static str] = &["lat", "lon"];
      deserializer.deserialize_struct("Coord", FIELDS, GeoCoordDeserializerVisitor)
  }
}
struct GeoCoordDeserializerVisitor;

impl serde::de::Visitor for GeoCoordDeserializerVisitor {
    type Value = Coord;

    fn visit_map<V>(&mut self, mut visitor: V) -> Result<Coord, V::Error>
        where V: serde::de::MapVisitor
    {
        let mut x = None;
        let mut y = None;

        loop {
            match try!(visitor.visit_key()) {
                Some(GeoCoordField::X) => { x = Some(try!(visitor.visit_value())); }
                Some(GeoCoordField::Y) => { y = Some(try!(visitor.visit_value())); }
                None => { break; }
            }
        }

        let x = match x {
            Some(x) => x,
            None => try!(visitor.missing_field("x")),
        };

        let y = match y {
            Some(y) => y,
            None => try!(visitor.missing_field("y")),
        };

        try!(visitor.end());

        Ok(Coord(geo::Coordinate{ x: x, y: y }))
    }
}
<|endoftext|>