<commit_before>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use std::io::{Read, Write};
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};
use chrono::{DateTime, Duration, TimeZone, UTC};
use num::traits::FromPrimitive;

use {Error, Result};
use information_element::{SessionStatus, InformationElementType};
use message::Message;

/// A mobile originated (MO) message;
#[derive(Debug)]
pub struct MobileOriginated {
    cdr_reference: u32,
    imei: [u8; 15],
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time: DateTime<UTC>,
    payload: Vec<u8>,
}

impl Default for MobileOriginated {
    fn default() -> MobileOriginated {
        MobileOriginated {
            cdr_reference: 0,
            imei: [0; 15],
            session_status: SessionStatus::Unknown,
            momsn: 0,
            mtmsn: 0,
            time: UTC.ymd(1970, 1, 1).and_hms(0, 0, 0),
            payload: Vec::new(),
        }
    }
}

impl MobileOriginated {
    /// Reads a mobile originated message from a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// MobileOriginated::from_path("data/0-mo.sbd");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::from_path(path)).into_mobile_originated()))
    }

    /// Reads a mobile originated message from a `Read`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// use std::fs::File;
    /// let file = File::open("data/0-mo.sbd");
    /// MobileOriginated::read_from(&mut file);
    pub fn read_from<R: Read>(readable: &mut R) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::read_from(readable)).into_mobile_originated()))
    }

    /// Writes this mobile originated to a object that can `Write`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use sbd::mobile_originated::MobileOriginated;
    /// let mobile_originated = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    /// let cursor = Cursor::new(Vec::new());
    /// mobile_originated.write_to(&mut cursor);
    /// ```
    pub fn write_to<W: Write>(&self, writeable: &mut W) -> Result<()> {
        Ok(())
    }

    /// Returns the cdr reference, also called the auto ID.
    ///
    /// This is a unique value given each call data record.
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }

    /// Returns the IMEI number.
    ///
    /// This is unique to each unit, and is always 15 characters long.
    pub fn imei(&self) -> &[u8] {
        &self.imei
    }

    /// Returns the status of the mobile originated session.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }

    /// Returns the mobile originated message sequence number.
    ///
    /// This value is set by the IMEI, and is incremented for every successful SBD session.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }

    /// Returns the mobile terminated message squence number.
    ///
    /// This value is set by the Gateway when the message is queued for delivery. The MTMSN is
    /// tranferred to the IMEI as part of the MT payload transfer, regardless of the session
    /// success.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }

    /// Returns the UTC timestamp in the form of an epoch integer.
    ///
    /// This is the time of the session between the IMEI and the Gateway, in seconds since the
    /// start of the epoch: 1/1/1970 00:00:00.
    pub fn time(&self) -> DateTime<UTC> {
        self.time
    }

    /// Returns the MO payload.
    pub fn payload(&self) -> &Vec<u8> {
        &self.payload
    }
}

impl Message {
    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        let mut information_elements = self.into_information_elements();

        // TODO should we fail if the payload is absent?
        let header = match information_elements.remove(&InformationElementType::MobileOriginatedHeader) {
            Some(header) => header,
            None => return Err(Error::NoMobileOriginatedHeader),
        };
        let payload = match information_elements.remove(&InformationElementType::MobileOriginatedPayload) {
            Some(payload) => payload,
            None => return Err(Error::NoMobileOriginatedPayload),
        };

        let mut readable = header.as_contents_reader();
        let mut mobile_originated: MobileOriginated = Default::default();
        mobile_originated.cdr_reference = try!(readable.read_u32::<BigEndian>());
        let bytes_read = try!(readable.read(&mut mobile_originated.imei));
        if bytes_read != mobile_originated.imei.len() {
            return Err(Error::Undersized(bytes_read));
        }
        mobile_originated.session_status = match SessionStatus::from_u8(try!(readable.read_u8())) {
            Some(status) => status,
            None => SessionStatus::Unknown,
        };
        mobile_originated.momsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.mtmsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.time = UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) +
            Duration::seconds(try!(readable.read_u32::<BigEndian>()) as i64);

        mobile_originated.payload = payload.into_contents();

        Ok(mobile_originated)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::io::Cursor;
    use std::str;

    use chrono::{TimeZone, UTC};

    use information_element::SessionStatus;
    use message::Message;

    #[test]
    fn into_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }

    #[test]
    fn from_path() {
        MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn mo_header_values() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1894516585, mo.cdr_reference());
        assert_eq!("300234063904190", str::from_utf8(mo.imei()).unwrap());
        assert_eq!(SessionStatus::Ok, mo.session_status());
        assert_eq!(75, mo.momsn());
        assert_eq!(0, mo.mtmsn());
        assert_eq!(UTC.ymd(2015, 7, 9).and_hms(18, 15, 08), mo.time());
    }

    #[test]
    fn mo_payload() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", str::from_utf8(mo.payload()).unwrap());
    }

    #[test]
    fn write_to() {
        let mobile_originated = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        let mut cursor = Cursor::new(Vec::new());
        mobile_originated.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        MobileOriginated::read_from(&mut cursor).unwrap();
    }
}
<commit_msg>Revert "Stash the beginnings of MO write_to"<commit_after>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};
use chrono::{DateTime, Duration, TimeZone, UTC};
use num::traits::FromPrimitive;

use {Error, Result};
use information_element::{SessionStatus, InformationElementType};
use message::Message;

/// A mobile originated (MO) message;
#[derive(Debug)]
pub struct MobileOriginated {
    cdr_reference: u32,
    imei: [u8; 15],
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time: DateTime<UTC>,
    payload: Vec<u8>,
}

impl Default for MobileOriginated {
    fn default() -> MobileOriginated {
        MobileOriginated {
            cdr_reference: 0,
            imei: [0; 15],
            session_status: SessionStatus::Unknown,
            momsn: 0,
            mtmsn: 0,
            time: UTC.ymd(1970, 1, 1).and_hms(0, 0, 0),
            payload: Vec::new(),
        }
    }
}

impl MobileOriginated {
    /// Reads a mobile originated message from a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// MobileOriginated::from_path("data/0-mo.sbd");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::from_path(path)).into_mobile_originated()))
    }

    /// Returns the cdr reference, also called the auto ID.
    ///
    /// This is a unique value given each call data record.
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }

    /// Returns the IMEI number.
    ///
    /// This is unique to each unit, and is always 15 characters long.
    pub fn imei(&self) -> &[u8] {
        &self.imei
    }

    /// Returns the status of the mobile originated session.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }

    /// Returns the mobile originated message sequence number.
    ///
    /// This value is set by the IMEI, and is incremented for every successful SBD session.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }

    /// Returns the mobile terminated message squence number.
    ///
    /// This value is set by the Gateway when the message is queued for delivery. The MTMSN is
    /// tranferred to the IMEI as part of the MT payload transfer, regardless of the session
    /// success.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }

    /// Returns the UTC timestamp in the form of an epoch integer.
    ///
    /// This is the time of the session between the IMEI and the Gateway, in seconds since the
    /// start of the epoch: 1/1/1970 00:00:00.
    pub fn time(&self) -> DateTime<UTC> {
        self.time
    }

    /// Returns the MO payload.
    pub fn payload(&self) -> &Vec<u8> {
        &self.payload
    }
}

impl Message {
    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        let mut information_elements = self.into_information_elements();

        // TODO should we fail if the payload is absent?
        let header = match information_elements.remove(&InformationElementType::MobileOriginatedHeader) {
            Some(header) => header,
            None => return Err(Error::NoMobileOriginatedHeader),
        };
        let payload = match information_elements.remove(&InformationElementType::MobileOriginatedPayload) {
            Some(payload) => payload,
            None => return Err(Error::NoMobileOriginatedPayload),
        };

        let mut readable = header.as_contents_reader();
        let mut mobile_originated: MobileOriginated = Default::default();
        mobile_originated.cdr_reference = try!(readable.read_u32::<BigEndian>());
        let bytes_read = try!(readable.read(&mut mobile_originated.imei));
        if bytes_read != mobile_originated.imei.len() {
            return Err(Error::Undersized(bytes_read));
        }
        mobile_originated.session_status = match SessionStatus::from_u8(try!(readable.read_u8())) {
            Some(status) => status,
            None => SessionStatus::Unknown,
        };
        mobile_originated.momsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.mtmsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.time = UTC.ymd(1970, 1, 1).and_hms(0, 0, 0) +
            Duration::seconds(try!(readable.read_u32::<BigEndian>()) as i64);

        mobile_originated.payload = payload.into_contents();

        Ok(mobile_originated)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::str;

    use chrono::{TimeZone, UTC};

    use information_element::SessionStatus;
    use message::Message;

    #[test]
    fn into_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }

    #[test]
    fn from_path() {
        MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn mo_header_values() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1894516585, mo.cdr_reference());
        assert_eq!("300234063904190", str::from_utf8(mo.imei()).unwrap());
        assert_eq!(SessionStatus::Ok, mo.session_status());
        assert_eq!(75, mo.momsn());
        assert_eq!(0, mo.mtmsn());
        assert_eq!(UTC.ymd(2015, 7, 9).and_hms(18, 15, 08), mo.time());
    }

    #[test]
    fn mo_payload() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", str::from_utf8(mo.payload()).unwrap());
    }
}
<|endoftext|>


<commit_before>//! FBX binary parser.

use std::io;
use std::io::Read;

pub use self::error::{Result, Error, Warning};
pub use self::event::{Event, FbxHeader, FbxFooter, StartNode};
pub use self::event::{Attributes, Attribute, SpecialAttributeType};
pub use self::event::{PrimitiveAttribute, ArrayAttribute, SpecialAttribute};
pub use self::event::ArrayAttributeReader;
use self::event::{EventBuilder, NodeHeader, StartNodeBuilder};
use self::event::read_fbx_header;
pub use self::reader::{ParserSource, BasicSource, SeekableSource, LimitedSeekReader};

mod error;
mod event;
mod reader;
pub mod utils;

/// Parser of a FBX binary node.
pub trait Parser<R: ParserSource> {
    /// Returns the root parser.
    fn root_parser(&self) -> &RootParser<R>;
    /// Parses FBX from the given stream and returns the next event.
    fn next_event(&mut self) -> Result<Event<R>>;
    /// Skips to the end of the current node.
    ///
    /// Returns `Ok(true)` if the current node is skipped and closed,
    /// `Ok(false)` if no nodes are open (i.e. the parser is reading under implicit root node),
    /// `Err(err)` if error happened.
    fn skip_current_node(&mut self) -> Result<bool>;
}

/// Parser state without error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
    /// Reading file header.
    Header,
    /// A node started.
    ///
    /// This status means:
    ///
    /// - there might be some attributes remain unread, or
    /// - the most recent opened node might close without no null node header.
    NodeStarted,
    /// A node ended.
    ///
    /// This status means:
    ///
    /// - if the next event is `NodeEnd`, there must be a null node header, and
    /// - if the parser got an extra null header, it indicates end of implicit root node.
    NodeEnded,
}

/// Information about opened (but not yet closed) node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct OpenNode {
    /// Start offset of the node attribute.
    ///
    /// Note that this doesn't mean start offset of node header.
    begin: u64,
    /// End offset of the node.
    end: u64,
    /// End offset of attributes of the node.
    attributes_end: u64,
}

/// Pull parser for whole FBX with binary format.
#[derive(Debug)]
pub struct RootParser<R> {
    /// Source reader.
    source: R,
    /// Parser state.
    ///
    /// `Ok(State)` if the parser is working without critical error,
    /// `Err(Error)` if the parsing failed and cannot be continued.
    state: Result<State>,
    /// Warnings.
    warnings: Vec<Warning>,
    /// FBX version.
    fbx_version: Option<u32>,
    /// Open nodes stack.
    open_nodes: Vec<OpenNode>,
}

impl<R: Read> RootParser<BasicSource<R>> {
    /// Creates a new binary parser.
    pub fn new(source: R) -> Self {
        RootParser {
            source: BasicSource::new(source),
            state: Ok(State::Header),
            warnings: Vec::new(),
            fbx_version: None,
            open_nodes: Vec::new(),
        }
    }
}

impl<R: Read + io::Seek> RootParser<SeekableSource<R>> {
    /// Creates a new binary parser.
    pub fn from_seekable(source: R) -> Self {
        RootParser {
            source: SeekableSource::new(source),
            state: Ok(State::Header),
            warnings: Vec::new(),
            fbx_version: None,
            open_nodes: Vec::new(),
        }
    }
}

impl<R: ParserSource> RootParser<R> {
    /// Returns FBX version of the reading input.
    ///
    /// Returns `None` if unknown yet.
    pub fn fbx_version(&self) -> Option<u32> {
        self.fbx_version
    }

    /// Returns the parser error if available.
    pub fn error(&self) -> Option<&Error> {
        self.state.as_ref().err()
    }

    /// Returns reference to the warnings.
    pub fn warnings(&self) -> &Vec<Warning> {
        &self.warnings
    }

    /// Returns the number of the opened (and not closed) node.
    pub fn num_open_nodes(&self) -> usize {
        self.open_nodes.len()
    }

    /// Set the parser state as finished parsing.
    fn set_finish(&mut self) {
        self.state = Err(Error::Finished);
    }

    /// Set the parser state as error.
    fn set_error(&mut self, err: &Error) {
        error!("FBX binary parser error: {}", err);
        debug!("Parser: {:#?}", self);
        self.state = Err(err.clone());
    }

    /// Add warning.
    fn warn(&mut self, warning: Warning) {
        warn!("FBX binary parser warning: {}", warning);
        debug!("Parser: {:#?}", self);
        self.warnings.push(warning);
    }

    /// Reads FBX header.
    fn read_fbx_header(&mut self) -> Result<EventBuilder> {
        let header = read_fbx_header(self)?;
        self.fbx_version = Some(header.version);
        self.state = Ok(State::NodeEnded);
        Ok(header.into())
    }

    /// Gets event after node start.
    fn read_after_node_start(&mut self) -> Result<EventBuilder> {
        // Attributes of recent opened node might remain partially unread.
        // They should be skipped before getting a next node event.
        self.skip_attributes()?;

        // Most recent opened node might ends here without a null node header.
        if let Some(end) = self.open_nodes.last().map(|v| v.end) {
            if self.source.position() == end {
                // Most recent opened node ends here (without a null node header).
                self.state = Ok(State::NodeEnded);
                self.open_nodes.pop();
                return Ok(EventBuilder::EndNode);
            }
        }

        let builder = self.read_node_event()?;
        Ok(builder)
    }

    /// Gets event after node end.
    fn read_after_node_end(&mut self) -> Result<EventBuilder> {
        self.read_node_event()
    }

    /// Reads a next node-related event from the source.
    ///
    /// This always returns `Ok(EventBuilder::StartNode)`, `Ok(EventBuilder::EndNode)`,
    /// `Ok(EventBuilder::EndFbx)` or `Err(_)`.
    fn read_node_event(&mut self) -> Result<EventBuilder> {
        let header = NodeHeader::read_from_parser(self)?;
        if header.is_node_end() {
            if let Some(last_node) = self.open_nodes.pop() {
                // There is open nodes, so this is not end of the FBX.
                let current_pos = self.source.position();
                if current_pos != last_node.end {
                    // Invalid node header.
                    return Err(Error::WrongNodeEndOffset {
                        begin: last_node.begin,
                        expected_end: last_node.end,
                        real_end: current_pos,
                    });
                }
            } else {
                assert_eq!(self.state.as_ref().ok(),
                           Some(&State::NodeEnded),
                           "End of implicit root node is read with unexpected parser state {:?}",
                           self.state);
                // No open nodes, so this `EndNode` event indicates the end of
                // the implicit root node.
                // FBX file has no more nodes.
                let footer = self.read_fbx_footer();
                return Ok(footer.into());
            }

            self.state = Ok(State::NodeEnded);
            Ok(EventBuilder::EndNode)
        } else {
            let mut name_vec = vec![0u8; header.bytelen_name as usize];
            self.source.read_exact(&mut name_vec)?;
            let name = String::from_utf8(name_vec).map_err(Error::node_name_invalid_utf8)?;
            let current_pos = self.source.position();
            self.open_nodes.push(OpenNode {
                begin: current_pos,
                end: header.end_offset,
                attributes_end: current_pos + header.bytelen_attributes,
            });

            // Zero or more attributes come after node start.
            self.state = Ok(State::NodeStarted);
            Ok(StartNodeBuilder {
                name: name,
                header: header,
            }
            .into())
        }
    }

    /// Reads an FBX footer.
    fn read_fbx_footer(&mut self) -> Result<FbxFooter> {
        self.set_finish();
        FbxFooter::read_from_parser(self)
    }

    /// Skip attributes of the most recent opened node.
    fn skip_attributes(&mut self) -> io::Result<()> {
        let attributes_end = self.open_nodes
            .last()
            .expect("`RootParser::skip_attributes()` is called but no nodes are open")
            .attributes_end;
        self.source.skip_to(attributes_end)
    }
}

impl<R: ParserSource> Parser<R> for RootParser<R> {
    fn root_parser(&self) -> &RootParser<R> {
        self
    }

    fn next_event(&mut self) -> Result<Event<R>> {
        let builder = match self.state.clone()? {
            State::Header => self.read_fbx_header(),
            State::NodeStarted => self.read_after_node_start(),
            State::NodeEnded => self.read_after_node_end(),
        };
        if let Err(ref err) = builder {
            self.set_error(err);
        }
        Ok(builder?.build(self))
    }

    fn skip_current_node(&mut self) -> Result<bool> {
        if let Some(end) = self.open_nodes.pop().map(|v| v.end) {
            self.source.skip_to(end)?;
            self.state = Ok(State::NodeEnded);
            Ok(true)
        } else {
            Ok(false)
        }
    }
}

/// Pull parser for a subtree of the FBX binary.
pub struct SubtreeParser<'a, R: 'a> {
    /// Root parser.
    root_parser: &'a mut RootParser<R>,
    /// Initial depth.
    ///
    /// Depth of the implicit root node is `0`.
    initial_depth: usize,
}

impl<'a, R: 'a + ParserSource> SubtreeParser<'a, R> {
    /// Creates a new `SubtreeParser`.
    pub fn new(root_parser: &'a mut RootParser<R>) -> Self {
        let initial_depth = root_parser.num_open_nodes();
        SubtreeParser {
            root_parser: root_parser,
            initial_depth: initial_depth,
        }
    }

    /// Checks if the subtree parser can emit more events.
    ///
    /// Returns `Ok(())` if more events can be read,
    /// `Err(Error::Finished)` if the subtree is all read,
    /// `Err(_)` if error happened.
    fn check_finished(&self) -> Result<()> {
        if let Some(err) = self.root_parser.error() {
            return Err(err.clone());
        }
        if self.root_parser.num_open_nodes() < self.initial_depth {
            return Err(Error::Finished);
        }
        Ok(())
    }
}

impl<'a, R: 'a + ParserSource> Parser<R> for SubtreeParser<'a, R> {
    fn root_parser(&self) -> &RootParser<R> {
        self.root_parser
    }

    fn next_event(&mut self) -> Result<Event<R>> {
        self.check_finished()?;
        self.root_parser.next_event()
    }

    fn skip_current_node(&mut self) -> Result<bool> {
        self.check_finished()?;
        self.root_parser.skip_current_node()
    }
}
<commit_msg>Add a convenience method `RootParser::subtree_parser()`<commit_after>//! FBX binary parser.

use std::io;
use std::io::Read;

pub use self::error::{Result, Error, Warning};
pub use self::event::{Event, FbxHeader, FbxFooter, StartNode};
pub use self::event::{Attributes, Attribute, SpecialAttributeType};
pub use self::event::{PrimitiveAttribute, ArrayAttribute, SpecialAttribute};
pub use self::event::ArrayAttributeReader;
use self::event::{EventBuilder, NodeHeader, StartNodeBuilder};
use self::event::read_fbx_header;
pub use self::reader::{ParserSource, BasicSource, SeekableSource, LimitedSeekReader};

mod error;
mod event;
mod reader;
pub mod utils;

/// Parser of a FBX binary node.
pub trait Parser<R: ParserSource> {
    /// Returns the root parser.
    fn root_parser(&self) -> &RootParser<R>;
    /// Parses FBX from the given stream and returns the next event.
    fn next_event(&mut self) -> Result<Event<R>>;
    /// Skips to the end of the current node.
    ///
    /// Returns `Ok(true)` if the current node is skipped and closed,
    /// `Ok(false)` if no nodes are open (i.e. the parser is reading under implicit root node),
    /// `Err(err)` if error happened.
    fn skip_current_node(&mut self) -> Result<bool>;
}

/// Parser state without error.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
    /// Reading file header.
    Header,
    /// A node started.
    ///
    /// This status means:
    ///
    /// - there might be some attributes remain unread, or
    /// - the most recent opened node might close without no null node header.
    NodeStarted,
    /// A node ended.
    ///
    /// This status means:
    ///
    /// - if the next event is `NodeEnd`, there must be a null node header, and
    /// - if the parser got an extra null header, it indicates end of implicit root node.
    NodeEnded,
}

/// Information about opened (but not yet closed) node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct OpenNode {
    /// Start offset of the node attribute.
    ///
    /// Note that this doesn't mean start offset of node header.
    begin: u64,
    /// End offset of the node.
    end: u64,
    /// End offset of attributes of the node.
    attributes_end: u64,
}

/// Pull parser for whole FBX with binary format.
#[derive(Debug)]
pub struct RootParser<R> {
    /// Source reader.
    source: R,
    /// Parser state.
    ///
    /// `Ok(State)` if the parser is working without critical error,
    /// `Err(Error)` if the parsing failed and cannot be continued.
    state: Result<State>,
    /// Warnings.
    warnings: Vec<Warning>,
    /// FBX version.
    fbx_version: Option<u32>,
    /// Open nodes stack.
    open_nodes: Vec<OpenNode>,
}

impl<R: Read> RootParser<BasicSource<R>> {
    /// Creates a new binary parser.
    pub fn new(source: R) -> Self {
        RootParser {
            source: BasicSource::new(source),
            state: Ok(State::Header),
            warnings: Vec::new(),
            fbx_version: None,
            open_nodes: Vec::new(),
        }
    }
}

impl<R: Read + io::Seek> RootParser<SeekableSource<R>> {
    /// Creates a new binary parser.
    pub fn from_seekable(source: R) -> Self {
        RootParser {
            source: SeekableSource::new(source),
            state: Ok(State::Header),
            warnings: Vec::new(),
            fbx_version: None,
            open_nodes: Vec::new(),
        }
    }
}

impl<R: ParserSource> RootParser<R> {
    /// Returns FBX version of the reading input.
    ///
    /// Returns `None` if unknown yet.
    pub fn fbx_version(&self) -> Option<u32> {
        self.fbx_version
    }

    /// Returns the parser error if available.
    pub fn error(&self) -> Option<&Error> {
        self.state.as_ref().err()
    }

    /// Returns reference to the warnings.
    pub fn warnings(&self) -> &Vec<Warning> {
        &self.warnings
    }

    /// Returns the number of the opened (and not closed) node.
    pub fn num_open_nodes(&self) -> usize {
        self.open_nodes.len()
    }

    /// Creates subtree parser for the current node.
    pub fn subtree_parser(&mut self) -> SubtreeParser<R> {
        SubtreeParser::new(self)
    }

    /// Set the parser state as finished parsing.
    fn set_finish(&mut self) {
        self.state = Err(Error::Finished);
    }

    /// Set the parser state as error.
    fn set_error(&mut self, err: &Error) {
        error!("FBX binary parser error: {}", err);
        debug!("Parser: {:#?}", self);
        self.state = Err(err.clone());
    }

    /// Add warning.
    fn warn(&mut self, warning: Warning) {
        warn!("FBX binary parser warning: {}", warning);
        debug!("Parser: {:#?}", self);
        self.warnings.push(warning);
    }

    /// Reads FBX header.
    fn read_fbx_header(&mut self) -> Result<EventBuilder> {
        let header = read_fbx_header(self)?;
        self.fbx_version = Some(header.version);
        self.state = Ok(State::NodeEnded);
        Ok(header.into())
    }

    /// Gets event after node start.
    fn read_after_node_start(&mut self) -> Result<EventBuilder> {
        // Attributes of recent opened node might remain partially unread.
        // They should be skipped before getting a next node event.
        self.skip_attributes()?;

        // Most recent opened node might ends here without a null node header.
        if let Some(end) = self.open_nodes.last().map(|v| v.end) {
            if self.source.position() == end {
                // Most recent opened node ends here (without a null node header).
                self.state = Ok(State::NodeEnded);
                self.open_nodes.pop();
                return Ok(EventBuilder::EndNode);
            }
        }

        let builder = self.read_node_event()?;
        Ok(builder)
    }

    /// Gets event after node end.
    fn read_after_node_end(&mut self) -> Result<EventBuilder> {
        self.read_node_event()
    }

    /// Reads a next node-related event from the source.
    ///
    /// This always returns `Ok(EventBuilder::StartNode)`, `Ok(EventBuilder::EndNode)`,
    /// `Ok(EventBuilder::EndFbx)` or `Err(_)`.
    fn read_node_event(&mut self) -> Result<EventBuilder> {
        let header = NodeHeader::read_from_parser(self)?;
        if header.is_node_end() {
            if let Some(last_node) = self.open_nodes.pop() {
                // There is open nodes, so this is not end of the FBX.
                let current_pos = self.source.position();
                if current_pos != last_node.end {
                    // Invalid node header.
                    return Err(Error::WrongNodeEndOffset {
                        begin: last_node.begin,
                        expected_end: last_node.end,
                        real_end: current_pos,
                    });
                }
            } else {
                assert_eq!(self.state.as_ref().ok(),
                           Some(&State::NodeEnded),
                           "End of implicit root node is read with unexpected parser state {:?}",
                           self.state);
                // No open nodes, so this `EndNode` event indicates the end of
                // the implicit root node.
                // FBX file has no more nodes.
                let footer = self.read_fbx_footer();
                return Ok(footer.into());
            }

            self.state = Ok(State::NodeEnded);
            Ok(EventBuilder::EndNode)
        } else {
            let mut name_vec = vec![0u8; header.bytelen_name as usize];
            self.source.read_exact(&mut name_vec)?;
            let name = String::from_utf8(name_vec).map_err(Error::node_name_invalid_utf8)?;
            let current_pos = self.source.position();
            self.open_nodes.push(OpenNode {
                begin: current_pos,
                end: header.end_offset,
                attributes_end: current_pos + header.bytelen_attributes,
            });

            // Zero or more attributes come after node start.
            self.state = Ok(State::NodeStarted);
            Ok(StartNodeBuilder {
                name: name,
                header: header,
            }
            .into())
        }
    }

    /// Reads an FBX footer.
    fn read_fbx_footer(&mut self) -> Result<FbxFooter> {
        self.set_finish();
        FbxFooter::read_from_parser(self)
    }

    /// Skip attributes of the most recent opened node.
    fn skip_attributes(&mut self) -> io::Result<()> {
        let attributes_end = self.open_nodes
            .last()
            .expect("`RootParser::skip_attributes()` is called but no nodes are open")
            .attributes_end;
        self.source.skip_to(attributes_end)
    }
}

impl<R: ParserSource> Parser<R> for RootParser<R> {
    fn root_parser(&self) -> &RootParser<R> {
        self
    }

    fn next_event(&mut self) -> Result<Event<R>> {
        let builder = match self.state.clone()? {
            State::Header => self.read_fbx_header(),
            State::NodeStarted => self.read_after_node_start(),
            State::NodeEnded => self.read_after_node_end(),
        };
        if let Err(ref err) = builder {
            self.set_error(err);
        }
        Ok(builder?.build(self))
    }

    fn skip_current_node(&mut self) -> Result<bool> {
        if let Some(end) = self.open_nodes.pop().map(|v| v.end) {
            self.source.skip_to(end)?;
            self.state = Ok(State::NodeEnded);
            Ok(true)
        } else {
            Ok(false)
        }
    }
}

/// Pull parser for a subtree of the FBX binary.
pub struct SubtreeParser<'a, R: 'a> {
    /// Root parser.
    root_parser: &'a mut RootParser<R>,
    /// Initial depth.
    ///
    /// Depth of the implicit root node is `0`.
    initial_depth: usize,
}

impl<'a, R: 'a + ParserSource> SubtreeParser<'a, R> {
    /// Creates a new `SubtreeParser`.
    pub fn new(root_parser: &'a mut RootParser<R>) -> Self {
        let initial_depth = root_parser.num_open_nodes();
        SubtreeParser {
            root_parser: root_parser,
            initial_depth: initial_depth,
        }
    }

    /// Checks if the subtree parser can emit more events.
    ///
    /// Returns `Ok(())` if more events can be read,
    /// `Err(Error::Finished)` if the subtree is all read,
    /// `Err(_)` if error happened.
    fn check_finished(&self) -> Result<()> {
        if let Some(err) = self.root_parser.error() {
            return Err(err.clone());
        }
        if self.root_parser.num_open_nodes() < self.initial_depth {
            return Err(Error::Finished);
        }
        Ok(())
    }
}

impl<'a, R: 'a + ParserSource> Parser<R> for SubtreeParser<'a, R> {
    fn root_parser(&self) -> &RootParser<R> {
        self.root_parser
    }

    fn next_event(&mut self) -> Result<Event<R>> {
        self.check_finished()?;
        self.root_parser.next_event()
    }

    fn skip_current_node(&mut self) -> Result<bool> {
        self.check_finished()?;
        self.root_parser.skip_current_node()
    }
}
<|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::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{namespace_from_domstring, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers, ElementHelpers};
use dom::node::{Node, NodeHelpers, TreeIterator};
use dom::window::Window;
use util::str::{DOMString, split_html_space_chars};

use std::ascii::AsciiExt;
use std::iter::{FilterMap, Skip};
use string_cache::{Atom, Namespace};

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

#[derive(JSTraceable)]
#[must_root]
pub enum CollectionTypeId {
    Static(Vec<JS<Element>>),
    Live(JS<Node>, Box<CollectionFilter + 'static>)
}

#[dom_struct]
#[derive(HeapSizeOf)]
pub struct HTMLCollection {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    collection: CollectionTypeId,
}

impl HTMLCollection {
    fn new_inherited(collection: CollectionTypeId) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            collection: collection,
        }
    }

    pub fn new(window: &Window, collection: CollectionTypeId) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(collection),
                           GlobalRef::Window(window), HTMLCollectionBinding::Wrap)
    }
}

impl HTMLCollection {
    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, CollectionTypeId::Live(JS::from_ref(root), filter))
    }

    fn all_elements(window: &Window, root: &Node,
                    namespace_filter: Option<Namespace>) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct AllElementFilter {
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for AllElementFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                match self.namespace_filter {
                    None => true,
                    Some(ref namespace) => *elem.namespace() == *namespace
                }
            }
        }
        let filter = AllElementFilter {namespace_filter: namespace_filter};
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name(window: &Window, root: &Node, tag: DOMString)
                       -> Root<HTMLCollection> {
        if tag == "*" {
            return HTMLCollection::all_elements(window, root, None);
        }

        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameFilter {
            tag: Atom,
            ascii_lower_tag: Atom,
        }
        impl CollectionFilter for TagNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                if elem.html_element_in_html_document() {
                    *elem.local_name() == self.ascii_lower_tag
                } else {
                    *elem.local_name() == self.tag
                }
            }
        }
        let filter = TagNameFilter {
            tag: Atom::from_slice(&tag),
            ascii_lower_tag: Atom::from_slice(&tag.to_ascii_lowercase()),
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let namespace_filter = match maybe_ns {
            Some(ref namespace) if namespace == &"*" => None,
            ns => Some(namespace_from_domstring(ns)),
        };

        if tag == "*" {
            return HTMLCollection::all_elements(window, root, namespace_filter);
        }
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            tag: Atom,
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                let ns_match = match self.namespace_filter {
                    Some(ref namespace) => {
                        *elem.namespace() == *namespace
                    },
                    None => true
                };
                ns_match && *elem.local_name() == self.tag
            }
        }
        let filter = TagNameNSFilter {
            tag: Atom::from_slice(&tag),
            namespace_filter: namespace_filter
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                self.classes.iter().all(|class| elem.has_class(class))
            }
        }
        let filter = ClassNameFilter {
            classes: split_html_space_chars(&classes).map(|class| {
                         Atom::from_slice(class)
                     }).collect()
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(NodeCast::from_ref(elem))
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    fn traverse(root: &Node)
                -> FilterMap<Skip<TreeIterator>,
                             fn(Root<Node>) -> Option<Root<Element>>> {
        fn to_temporary(node: Root<Node>) -> Option<Root<Element>> {
            ElementCast::to_root(node)
        }
        root.traverse_preorder()
            .skip(1)
            .filter_map(to_temporary)
    }
}

impl<'a> HTMLCollectionMethods for &'a HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    fn Length(self) -> u32 {
        match self.collection {
            CollectionTypeId::Static(ref elems) => elems.len() as u32,
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .count() as u32
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn Item(self, index: u32) -> Option<Root<Element>> {
        let index = index as usize;
        match self.collection {
            CollectionTypeId::Static(ref elems) => elems
                .get(index).map(|t| t.root()),
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .nth(index)
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    fn NamedItem(self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        match self.collection {
            CollectionTypeId::Static(ref elems) => elems.iter()
                .map(|elem| elem.root())
                .find(|elem| {
                    elem.r().get_string_attribute(&atom!("name")) == key ||
                    elem.r().get_string_attribute(&atom!("id")) == key }),
            CollectionTypeId::Live(ref root, ref filter) => {
                let root = root.root();
                HTMLCollection::traverse(root.r())
                    .filter(|element| filter.filter(element.r(), root.r()))
                    .find(|elem| {
                        elem.r().get_string_attribute(&atom!("name")) == key ||
                        elem.r().get_string_attribute(&atom!("id")) == key })
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(self, index: u32, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.Item(index);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // check-tidy: no specs after this line
    fn NamedGetter(self, name: DOMString, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.NamedItem(name);
        *found = maybe_elem.is_some();
        maybe_elem
    }
}

<commit_msg>Auto merge of #7219 - vectorijk:issue#7042, r=Ms2ger<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::HTMLCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{namespace_from_domstring, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers, ElementHelpers};
use dom::node::{Node, NodeHelpers, TreeIterator};
use dom::window::Window;
use util::str::{DOMString, split_html_space_chars};

use std::ascii::AsciiExt;
use std::iter::{FilterMap, Skip};
use string_cache::{Atom, Namespace};

pub trait CollectionFilter : JSTraceable {
    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool;
}

#[derive(JSTraceable)]
#[must_root]
pub struct Collection(JS<Node>, Box<CollectionFilter + 'static>);

#[dom_struct]
#[derive(HeapSizeOf)]
pub struct HTMLCollection {
    reflector_: Reflector,
    #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"]
    collection: Collection,
}

impl HTMLCollection {
    fn new_inherited(collection: Collection) -> HTMLCollection {
        HTMLCollection {
            reflector_: Reflector::new(),
            collection: collection,
        }
    }

    pub fn new(window: &Window, collection: Collection) -> Root<HTMLCollection> {
        reflect_dom_object(box HTMLCollection::new_inherited(collection),
                           GlobalRef::Window(window), HTMLCollectionBinding::Wrap)
    }
}

impl HTMLCollection {
    pub fn create(window: &Window, root: &Node,
                  filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> {
        HTMLCollection::new(window, Collection(JS::from_ref(root), filter))
    }

    fn all_elements(window: &Window, root: &Node,
                    namespace_filter: Option<Namespace>) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct AllElementFilter {
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for AllElementFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                match self.namespace_filter {
                    None => true,
                    Some(ref namespace) => *elem.namespace() == *namespace
                }
            }
        }
        let filter = AllElementFilter {namespace_filter: namespace_filter};
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name(window: &Window, root: &Node, tag: DOMString)
                       -> Root<HTMLCollection> {
        if tag == "*" {
            return HTMLCollection::all_elements(window, root, None);
        }

        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameFilter {
            tag: Atom,
            ascii_lower_tag: Atom,
        }
        impl CollectionFilter for TagNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                if elem.html_element_in_html_document() {
                    *elem.local_name() == self.ascii_lower_tag
                } else {
                    *elem.local_name() == self.tag
                }
            }
        }
        let filter = TagNameFilter {
            tag: Atom::from_slice(&tag),
            ascii_lower_tag: Atom::from_slice(&tag.to_ascii_lowercase()),
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                          maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
        let namespace_filter = match maybe_ns {
            Some(ref namespace) if namespace == &"*" => None,
            ns => Some(namespace_from_domstring(ns)),
        };

        if tag == "*" {
            return HTMLCollection::all_elements(window, root, namespace_filter);
        }
        #[derive(JSTraceable, HeapSizeOf)]
        struct TagNameNSFilter {
            tag: Atom,
            namespace_filter: Option<Namespace>
        }
        impl CollectionFilter for TagNameNSFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                let ns_match = match self.namespace_filter {
                    Some(ref namespace) => {
                        *elem.namespace() == *namespace
                    },
                    None => true
                };
                ns_match && *elem.local_name() == self.tag
            }
        }
        let filter = TagNameNSFilter {
            tag: Atom::from_slice(&tag),
            namespace_filter: namespace_filter
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn by_class_name(window: &Window, root: &Node, classes: DOMString)
                         -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ClassNameFilter {
            classes: Vec<Atom>
        }
        impl CollectionFilter for ClassNameFilter {
            fn filter(&self, elem: &Element, _root: &Node) -> bool {
                self.classes.iter().all(|class| elem.has_class(class))
            }
        }
        let filter = ClassNameFilter {
            classes: split_html_space_chars(&classes).map(|class| {
                         Atom::from_slice(class)
                     }).collect()
        };
        HTMLCollection::create(window, root, box filter)
    }

    pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> {
        #[derive(JSTraceable, HeapSizeOf)]
        struct ElementChildFilter;
        impl CollectionFilter for ElementChildFilter {
            fn filter(&self, elem: &Element, root: &Node) -> bool {
                root.is_parent_of(NodeCast::from_ref(elem))
            }
        }
        HTMLCollection::create(window, root, box ElementChildFilter)
    }

    fn traverse(root: &Node)
                -> FilterMap<Skip<TreeIterator>,
                             fn(Root<Node>) -> Option<Root<Element>>> {
        fn to_temporary(node: Root<Node>) -> Option<Root<Element>> {
            ElementCast::to_root(node)
        }
        root.traverse_preorder()
            .skip(1)
            .filter_map(to_temporary)
    }
}

impl<'a> HTMLCollectionMethods for &'a HTMLCollection {
    // https://dom.spec.whatwg.org/#dom-htmlcollection-length
    #[allow(unrooted_must_root)]
    fn Length(self) -> u32 {
        let Collection(ref root, ref filter) = self.collection;
        let root = root.root();
        HTMLCollection::traverse(root.r())
            .filter(|element| filter.filter(element.r(), root.r()))
            .count() as u32
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    #[allow(unrooted_must_root)]
    fn Item(self, index: u32) -> Option<Root<Element>> {
        let index = index as usize;
        let Collection(ref root, ref filter) = self.collection;
        let root = root.root();
        HTMLCollection::traverse(root.r())
            .filter(|element| filter.filter(element.r(), root.r()))
            .nth(index)
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem
    #[allow(unrooted_must_root)]
    fn NamedItem(self, key: DOMString) -> Option<Root<Element>> {
        // Step 1.
        if key.is_empty() {
            return None;
        }

        // Step 2.
        let Collection(ref root, ref filter) = self.collection;
        let root = root.root();
        HTMLCollection::traverse(root.r())
            .filter(|element| filter.filter(element.r(), root.r()))
            .find(|elem| {
                elem.r().get_string_attribute(&atom!("name")) == key ||
                elem.r().get_string_attribute(&atom!("id")) == key})
    }

    // https://dom.spec.whatwg.org/#dom-htmlcollection-item
    fn IndexedGetter(self, index: u32, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.Item(index);
        *found = maybe_elem.is_some();
        maybe_elem
    }

    // check-tidy: no specs after this line
    fn NamedGetter(self, name: DOMString, found: &mut bool) -> Option<Root<Element>> {
        let maybe_elem = self.NamedItem(name);
        *found = maybe_elem.is_some();
        maybe_elem
    }
}

<|endoftext|>


<commit_before>#![feature(test, custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate test;

include!("../../testing/tests/test.rs.in");

mod compile_tests;
<commit_msg>fix nightly<commit_after>#![feature(test, custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate test;

include!("../../testing/tests/test.rs.in");

mod compile_tests;
<|endoftext|>


<commit_before>extern crate kite;
extern crate kite_rocksdb;
extern crate chrono;
#[macro_use]
extern crate router;
extern crate url;
#[macro_use]
extern crate log;
#[macro_use(o, b)]
extern crate slog;
extern crate slog_term;
#[macro_use]
extern crate maplit;
extern crate unicode_segmentation;
extern crate uuid;
#[macro_use]
extern crate serde_json;
extern crate atomicwrites;

pub mod analysis;
pub mod query_parser;
pub mod mapping;
pub mod document;
pub mod index;
pub mod system;
mod api;
mod logger;

use std::path::Path;
use std::sync::Arc;
use std::thread;
use std::time::Duration;

use slog::Logger;

use system::System;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let log = Logger::new_root(o!());
    log.set_drain(slog_term::async_stderr());

    log.info("[sys] starting rusticsearch", b!("version" => VERSION));

    logger::init().unwrap();

    let system = Arc::new(System::new(log, Path::new("data/").to_path_buf()));

    system.log.info("[sys] loading indices", b!());
    system.load_indices();

    {
        let system = system.clone();
        thread::spawn(move || {
            loop {
                {
                    let indices = system.indices.read().unwrap();
                    for index in indices.values() {
                        // TODO: Catch panic
                        index.run_maintenance_task().unwrap();
                    }
                }

                thread::sleep(Duration::new(1, 0));
            }
        });
    }

    system.log.info("[sys] starting api server", b!());
    api::api_main(system);
}
<commit_msg>Catch panics raised from maintenance tasks<commit_after>extern crate kite;
extern crate kite_rocksdb;
extern crate chrono;
#[macro_use]
extern crate router;
extern crate url;
#[macro_use]
extern crate log;
#[macro_use(o, b)]
extern crate slog;
extern crate slog_term;
#[macro_use]
extern crate maplit;
extern crate unicode_segmentation;
extern crate uuid;
#[macro_use]
extern crate serde_json;
extern crate atomicwrites;

pub mod analysis;
pub mod query_parser;
pub mod mapping;
pub mod document;
pub mod index;
pub mod system;
mod api;
mod logger;

use std::path::Path;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use std::panic;

use slog::Logger;

use system::System;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let log = Logger::new_root(o!());
    log.set_drain(slog_term::async_stderr());

    log.info("[sys] starting rusticsearch", b!("version" => VERSION));

    logger::init().unwrap();

    let system = Arc::new(System::new(log, Path::new("data/").to_path_buf()));

    system.log.info("[sys] loading indices", b!());
    system.load_indices();

    {
        let system = system.clone();
        thread::spawn(move || {
            loop {
                {
                    let indices = system.indices.read().unwrap();
                    for index in indices.values() {
                        let result = panic::catch_unwind(|| {
                            index.run_maintenance_task().unwrap();
                        });

                        if let Err(error) = result {
                            system.log.error("[sys] maintenance task panicked", b!("index" => index.canonical_name(), "error" => format!("{:?}", error)));
                        }

                    }
                }

                thread::sleep(Duration::new(1, 0));
            }
        });
    }

    system.log.info("[sys] starting api server", b!());
    api::api_main(system);
}
<|endoftext|>


<commit_before>const CHUNKSIZE: usize = 128;
const HASHSIZE: usize = 32;

pub trait TreeHash {
    fn tree_hash(&self) -> Vec<u8>;
}

/// Returns a 32 byte hash of 'list' - a vector of byte vectors.
/// Note that this will consume 'list'.
pub fn merkle_hash(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
    // flatten list
    let data = &mut list_to_blob(list);

    // get data_len as bytes. It will hashed will the merkle root
    let dlen = data.len() as u64;
    let data_len_bytes = &mut dlen.tree_hash();
    data_len_bytes.resize(32, 0);

    // merklize
    let mut mhash = hash_level(data, CHUNKSIZE);
    while mhash.len() > HASHSIZE {
        mhash = hash_level(&mut mhash, HASHSIZE);
    }

    mhash.append(data_len_bytes);
    mhash.as_slice().tree_hash()
}

/// Takes a flat vector of bytes. It then hashes 'chunk_size * 2' slices into
/// a byte vector of hashes, divisible by HASHSIZE
fn hash_level(data: &mut Vec<u8>, chunk_size: usize) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    for two_chunks in data.chunks(chunk_size * 2) {
        if two_chunks.len() == chunk_size && data.len() > chunk_size {
            // if there is only one chunk here, hash it with a zero-byte
            // CHUNKSIZE vector
            let mut c = two_chunks.to_vec();
            c.append(&mut vec![0; CHUNKSIZE]);
            result.append(&mut c.as_slice().tree_hash());
        } else {
            result.append(&mut two_chunks.tree_hash());
        }
    }

    result
}

fn list_to_blob(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
    if list[0].len().is_power_of_two() == false {
        for x in list.iter_mut() {
            extend_to_power_of_2(x);
        }
    }

    let mut data_len = list[0].len() * list.len();

    // do we need padding?
    let extend_by = if data_len % CHUNKSIZE > 0 {
        CHUNKSIZE - (data_len % CHUNKSIZE)
    } else {
        0
    };

    println!("data_len {}, extend_by {}", data_len, extend_by);

    // allocate buffer and append each list element (flatten the vec of vecs)
    data_len += extend_by;
    let mut data: Vec<u8> = Vec::with_capacity(data_len);
    for x in list.iter_mut() {
        data.append(x);
    }

    // add padding
    let mut i = 0;
    while i < extend_by {
        data.push(0);
        i += 1;
    }

    data
}

/// Extends data length to a power of 2 by minimally right-zero-padding
fn extend_to_power_of_2(data: &mut Vec<u8>) {
    let len = data.len();
    let new_len = len.next_power_of_two();
    if new_len > len {
        data.resize(new_len, 0);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_merkle_hash() {
        let data1 = vec![1; 100];
        let data2 = vec![2; 100];
        let data3 = vec![3; 100];
        let mut list = vec![data1, data2, data3];
        let result = merkle_hash(&mut list);

        //note: should test againt a known test hash value
        assert_eq!(HASHSIZE, result.len());
        println!("merkle_hash: {:?}", result);
    }

}
<commit_msg>removed a debugging println statement<commit_after>const CHUNKSIZE: usize = 128;
const HASHSIZE: usize = 32;

pub trait TreeHash {
    fn tree_hash(&self) -> Vec<u8>;
}

/// Returns a 32 byte hash of 'list' - a vector of byte vectors.
/// Note that this will consume 'list'.
pub fn merkle_hash(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
    // flatten list
    let data = &mut list_to_blob(list);

    // get data_len as bytes. It will hashed will the merkle root
    let dlen = data.len() as u64;
    let data_len_bytes = &mut dlen.tree_hash();
    data_len_bytes.resize(32, 0);

    // merklize
    let mut mhash = hash_level(data, CHUNKSIZE);
    while mhash.len() > HASHSIZE {
        mhash = hash_level(&mut mhash, HASHSIZE);
    }

    mhash.append(data_len_bytes);
    mhash.as_slice().tree_hash()
}

/// Takes a flat vector of bytes. It then hashes 'chunk_size * 2' slices into
/// a byte vector of hashes, divisible by HASHSIZE
fn hash_level(data: &mut Vec<u8>, chunk_size: usize) -> Vec<u8> {
    let mut result: Vec<u8> = Vec::new();
    for two_chunks in data.chunks(chunk_size * 2) {
        if two_chunks.len() == chunk_size && data.len() > chunk_size {
            // if there is only one chunk here, hash it with a zero-byte
            // CHUNKSIZE vector
            let mut c = two_chunks.to_vec();
            c.append(&mut vec![0; CHUNKSIZE]);
            result.append(&mut c.as_slice().tree_hash());
        } else {
            result.append(&mut two_chunks.tree_hash());
        }
    }

    result
}

fn list_to_blob(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
    if list[0].len().is_power_of_two() == false {
        for x in list.iter_mut() {
            extend_to_power_of_2(x);
        }
    }

    let mut data_len = list[0].len() * list.len();

    // do we need padding?
    let extend_by = if data_len % CHUNKSIZE > 0 {
        CHUNKSIZE - (data_len % CHUNKSIZE)
    } else {
        0
    };

    // allocate buffer and append each list element (flatten the vec of vecs)
    data_len += extend_by;
    let mut data: Vec<u8> = Vec::with_capacity(data_len);
    for x in list.iter_mut() {
        data.append(x);
    }

    // add padding
    let mut i = 0;
    while i < extend_by {
        data.push(0);
        i += 1;
    }

    data
}

/// Extends data length to a power of 2 by minimally right-zero-padding
fn extend_to_power_of_2(data: &mut Vec<u8>) {
    let len = data.len();
    let new_len = len.next_power_of_two();
    if new_len > len {
        data.resize(new_len, 0);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_merkle_hash() {
        let data1 = vec![1; 100];
        let data2 = vec![2; 100];
        let data3 = vec![3; 100];
        let mut list = vec![data1, data2, data3];
        let result = merkle_hash(&mut list);

        //note: should test againt a known test hash value
        assert_eq!(HASHSIZE, result.len());
        println!("merkle_hash: {:?}", result);
    }

}
<|endoftext|>


<commit_before>use nom::{IResult, is_space};
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::FromIterator;
use std::str::FromStr;

use util::parsing::*;

/// Structure containing the elements of type `A` in a equivalence class of type `B`
#[derive(Debug, Eq, PartialEq)]
pub struct EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    label: B,
    set: Option<HashSet<A>>,
}

impl<A, B> From<(B, Option<Vec<A>>)> for EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    fn from((label, set): (B, Option<Vec<A>>)) -> EquivalenceClass<A, B> {
        EquivalenceClass {
            label,
            set: set.and_then(|elements| Some(HashSet::from_iter(elements))),
        }
    }
}

/// A struct containing a remapping of elements of type `A` into their respective equivalence classes of type `B`.
#[derive(Clone, Debug)]
pub struct EquivalenceRelation<A, B>
    where A: Eq + Hash,
{
    map: HashMap<A, B>,
    default: B,
}

impl<A, B> PartialEq for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.default == other.default && self.map == other.map
    }
}

impl<A, B> EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    pub fn new(map: HashMap<B, HashSet<A>>, default: B) -> Self {
        match Self::new_safe(map, default) {
            Ok(relation) => relation,
            Err(error) => panic!(error),
        }
    }

    pub fn new_safe(map: HashMap<B, HashSet<A>>, default: B) -> Result<Self, String> {
        let mut relation_map = HashMap::new();
        for (class_name, members) in map {
            if class_name == default {
                return Err(String::from(
                    "There can only be one default class in the equivalence relation!"
                ));
            }

            for value in members {
                match relation_map.entry(value) {
                    Entry::Vacant(v) => {
                        v.insert(class_name.clone());
                    },
                    Entry::Occupied(_) => {
                        return Err(String::from(
                            "All classes of the equivalence relation must be disjoint!"
                        ));
                    },
                }
            }
        }

        Ok(EquivalenceRelation { map: relation_map, default })
    }

    // returns the equivalence class of a given value
    pub fn project(&self, key: &A) -> B {
        match self.map.get(key) {
            None => self.default.clone(),
            Some(e) => e.clone(),
        }
    }
}

impl<A, B> From<Vec<EquivalenceClass<A, B>>> for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    fn from(classes: Vec<EquivalenceClass<A, B>>) -> EquivalenceRelation<A, B> {
        let mut map = HashMap::new();
        let mut default = None;

        for EquivalenceClass { label, set } in classes {
            if let Some(elements) = set {
                for element in elements {
                    map.insert(element, label.clone());
                }
            } else {
                default = Some(label);
            }
        }

        EquivalenceRelation { map, default: default.unwrap() }
    }
}

impl<A, B> FromStr for EquivalenceRelation<A, B>
    where A: Clone + Eq + Hash + FromStr,
          A::Err: Debug,
          B: Clone + Eq + Hash + FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut map = HashMap::new();
        let mut default = None;

        for l in s.lines() {
            if !l.is_empty() {
                match l.trim().parse()? {
                    EquivalenceClass { label, set: Some(elements) } => {
                        map.insert(label, elements);
                    },
                    EquivalenceClass { label, set: None } => {
                        default = Some(label);
                    },
                }
            }
        }

        if let Some(label) = default {
            if let Ok(relation) = EquivalenceRelation::new_safe(map, label) {
                return Ok(relation);
            }
        }

        Err(format!("Could not parse {}", s))
    }
}

impl <A, B> FromStr for EquivalenceClass<A, B>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match parse_class(s.as_bytes()) {
            IResult::Done(_, result) => Ok(result),
            _                        => Err(format!("Could not parse {}", s))
        }
    }
}

fn parse_class<A, B>(input: &[u8]) -> IResult<&[u8], EquivalenceClass<A, B>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    do_parse!(
        input,
        label: parse_token >>
            take_while!(is_space) >>
            set: alt!(
                do_parse!(
                    tag!("*") >> (None)
                )
                    |
                do_parse!(
                    the_set: parse_set >> (Some(the_set))
                )
            ) >>
            (EquivalenceClass {
                label,
                set,
            })
    )
}

fn parse_set<A>(input: &[u8]) -> IResult<&[u8], HashSet<A>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
{
    match parse_vec(input, parse_token, "[", "]", ",") {
        IResult::Done(rest, parsed) => IResult::Done(rest, HashSet::from_iter(parsed)),
        IResult::Incomplete(needed) => IResult::Incomplete(needed),
        IResult::Error(error) => IResult::Error(error),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_equivalence_class_from_str_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [1, 0]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [0, 1, 1]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0 *", EquivalenceClass::from((0, None))),
        ];

        for (legal_input, correct_class) in legal_inputs {
            assert_eq!(correct_class,
                       EquivalenceClass::from_str(legal_input).unwrap());
        }
    }

    #[test]
    fn test_equivalence_class_from_str_illegal_input() {
        let illegal_inputs = vec![
            " 0 [0, 1]",
            "[0, 1]",
            "*",
        ];

        for illegal_input in illegal_inputs {
            match EquivalenceClass::<u8, u8>::from_str(illegal_input) {
                Ok(parsed) =>
                    panic!("Was able to parse the illegal input \'{}\' as \'{:?}\'",
                           illegal_input, parsed),
                Err(_) => (),
            }
        }
    }

    #[test]
    fn test_equivalence_relation_project() {
        let rel: EquivalenceRelation<u8, u8> = String::from("0 [0, 1]\n1 [2, 4]\n2 *").parse().unwrap();
        assert_eq!(0, rel.project(&1));
        assert_eq!(1, rel.project(&2));
        assert_eq!(2, rel.project(&3));
        assert_eq!(1, rel.project(&4));
    }

    #[test]
    #[should_panic(
        expected="There can only be one default class in the equivalence relation!"
    )]
    fn test_equivalence_relation_new_colliding_default() {
        let mut map = HashMap::new();
        let classes = vec![
            (0, HashSet::from_iter(vec![0, 1])),
            (1, HashSet::from_iter(vec![2, 3])),
        ];

        for (label, elements) in classes {
            map.insert(label, elements);
        }

        EquivalenceRelation::new(map, 1);
    }

    #[test]
    #[should_panic(
        expected="All classes of the equivalence relation must be disjoint!"
    )]
    fn test_equivalence_relation_new_intersecting_classes() {
        let mut map = HashMap::new();
        let classes = vec![
            (0, HashSet::from_iter(vec![0, 1])),
            (1, HashSet::from_iter(vec![1, 2])),
        ];

        for (label, elements) in classes {
            map.insert(label, elements);
        }

        EquivalenceRelation::new(map, 2);
    }

    #[test]
    fn test_equivalence_relation_from_str_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]\n1 [2, 3]\n2 *", EquivalenceRelation::from(vec![
                EquivalenceClass::from((0, Some(vec![0, 1]))),
                EquivalenceClass::from((1, Some(vec![2, 3]))),
                EquivalenceClass::from((2, None)),
            ])),
            (" 0 [0, 1]\n 1 [2, 3]  \n2 *  ", EquivalenceRelation::from(vec![
                EquivalenceClass::from((0, Some(vec![0, 1]))),
                EquivalenceClass::from((1, Some(vec![2, 3]))),
                EquivalenceClass::from((2, None))
            ])),
        ];

        for (legal_input, correct_relation) in legal_inputs {
            assert_eq!(correct_relation,
                       EquivalenceRelation::from_str(legal_input).unwrap());
        }
    }

    #[test]
    fn test_equivalence_relation_from_str_illegal_input() {
        let illegal_inputs = vec![
            "0 [0, 1]1 [2, 3]2 *",
            "0 [0, 1]\n1 [1, 2]\n2 *",
            "0 [0, 1]\n1 [0, 1]\n2 *",
            "0 [0, 1]\n1 [2, 3]\n2 [*]",
            "0 [0, 1]\n1 [a, 3]\n2 *",
            "0 [0, 1]\na [2, 3]\n2 *",
        ];

        for illegal_input in illegal_inputs {
            match EquivalenceRelation::<u8, u8>::from_str(illegal_input) {
                Ok(parsed) =>
                    panic!("Was able to parse the illegal input \'{}\' as \'{:?}\'",
                           illegal_input, parsed),
                Err(_) => (),
            }
        }
    }
}
<commit_msg>src/approximation/equivalence_classes.rs: now parse_class() is tested instead of from_str()<commit_after>use nom::{IResult, is_space};
use std::collections::{HashMap, HashSet};
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::FromIterator;
use std::str::FromStr;

use util::parsing::*;

/// Structure containing the elements of type `A` in a equivalence class of type `B`
#[derive(Debug, Eq, PartialEq)]
pub struct EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    label: B,
    set: Option<HashSet<A>>,
}

impl<A, B> From<(B, Option<Vec<A>>)> for EquivalenceClass<A, B>
    where A: Eq + Hash,
{
    fn from((label, set): (B, Option<Vec<A>>)) -> EquivalenceClass<A, B> {
        EquivalenceClass {
            label,
            set: set.and_then(|elements| Some(HashSet::from_iter(elements))),
        }
    }
}

/// A struct containing a remapping of elements of type `A` into their respective equivalence classes of type `B`.
#[derive(Clone, Debug)]
pub struct EquivalenceRelation<A, B>
    where A: Eq + Hash,
{
    map: HashMap<A, B>,
    default: B,
}

impl<A, B> PartialEq for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.default == other.default && self.map == other.map
    }
}

impl<A, B> EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    pub fn new(map: HashMap<B, HashSet<A>>, default: B) -> Self {
        match Self::new_safe(map, default) {
            Ok(relation) => relation,
            Err(error) => panic!(error),
        }
    }

    pub fn new_safe(map: HashMap<B, HashSet<A>>, default: B) -> Result<Self, String> {
        let mut relation_map = HashMap::new();
        for (class_name, members) in map {
            if class_name == default {
                return Err(String::from(
                    "There can only be one default class in the equivalence relation!"
                ));
            }

            for value in members {
                match relation_map.entry(value) {
                    Entry::Vacant(v) => {
                        v.insert(class_name.clone());
                    },
                    Entry::Occupied(_) => {
                        return Err(String::from(
                            "All classes of the equivalence relation must be disjoint!"
                        ));
                    },
                }
            }
        }

        Ok(EquivalenceRelation { map: relation_map, default })
    }

    // returns the equivalence class of a given value
    pub fn project(&self, key: &A) -> B {
        match self.map.get(key) {
            None => self.default.clone(),
            Some(e) => e.clone(),
        }
    }
}

impl<A, B> From<Vec<EquivalenceClass<A, B>>> for EquivalenceRelation<A, B>
    where A: Eq + Hash,
          B: Clone + Eq + Hash,
{
    fn from(classes: Vec<EquivalenceClass<A, B>>) -> EquivalenceRelation<A, B> {
        let mut map = HashMap::new();
        let mut default = None;

        for EquivalenceClass { label, set } in classes {
            if let Some(elements) = set {
                for element in elements {
                    map.insert(element, label.clone());
                }
            } else {
                default = Some(label);
            }
        }

        EquivalenceRelation { map, default: default.unwrap() }
    }
}

impl<A, B> FromStr for EquivalenceRelation<A, B>
    where A: Clone + Eq + Hash + FromStr,
          A::Err: Debug,
          B: Clone + Eq + Hash + FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut map = HashMap::new();
        let mut default = None;

        for l in s.lines() {
            if !l.is_empty() {
                match l.trim().parse()? {
                    EquivalenceClass { label, set: Some(elements) } => {
                        map.insert(label, elements);
                    },
                    EquivalenceClass { label, set: None } => {
                        default = Some(label);
                    },
                }
            }
        }

        if let Some(label) = default {
            if let Ok(relation) = EquivalenceRelation::new_safe(map, label) {
                return Ok(relation);
            }
        }

        Err(format!("Could not parse {}", s))
    }
}

impl <A, B> FromStr for EquivalenceClass<A, B>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match parse_class(s.as_bytes()) {
            IResult::Done(_, result) => Ok(result),
            _                        => Err(format!("Could not parse {}", s))
        }
    }
}

fn parse_class<A, B>(input: &[u8]) -> IResult<&[u8], EquivalenceClass<A, B>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
          B: FromStr,
          B::Err: Debug,
{
    do_parse!(
        input,
        label: parse_token >>
            take_while!(is_space) >>
            set: alt!(
                do_parse!(
                    tag!("*") >> (None)
                )
                    |
                do_parse!(
                    the_set: parse_set >> (Some(the_set))
                )
            ) >>
            (EquivalenceClass {
                label,
                set,
            })
    )
}

fn parse_set<A>(input: &[u8]) -> IResult<&[u8], HashSet<A>>
    where A: Eq + FromStr + Hash,
          A::Err: Debug,
{
    match parse_vec(input, parse_token, "[", "]", ",") {
        IResult::Done(rest, parsed) => IResult::Done(rest, HashSet::from_iter(parsed)),
        IResult::Incomplete(needed) => IResult::Incomplete(needed),
        IResult::Error(error) => IResult::Error(error),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_class_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]xyz", "xyz", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [1, 0]1 [2, 3]", "1 [2, 3]", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0  [0, 1, 1]\nxyz", "\nxyz", EquivalenceClass::from((0, Some(vec![0, 1])))),
            ("0 *xyz", "xyz", EquivalenceClass::from((0, None))),
        ];

        for (legal_input, control_rest, control_class) in legal_inputs {
            assert_eq!(
                (control_rest.as_bytes(), control_class),
                parse_class(legal_input.as_bytes()).unwrap()
            );
        }
    }

    #[test]
    fn test_parse_class_incomplete_input() {
        let incomplete_inputs = vec![
            "0",
            "0 [0,",
            "0 [0, 1",
        ];

        for incomplete_input in incomplete_inputs {
            match parse_class::<u8, u8>(incomplete_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Error(_) =>
                    panic!("The input was not handled as incomplete: \'{}\'", incomplete_input),
                IResult::Incomplete(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_class_illegal_input() {
        let illegal_inputs = vec![
            " 0 [0, 1]",
            "[0, 1]",
            "*",
        ];

        for illegal_input in illegal_inputs {
            match parse_class::<u8, u8>(illegal_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }
    }

    #[test]
    fn test_equivalence_relation_project() {
        let rel: EquivalenceRelation<u8, u8> = String::from("0 [0, 1]\n1 [2, 4]\n2 *").parse().unwrap();
        assert_eq!(0, rel.project(&1));
        assert_eq!(1, rel.project(&2));
        assert_eq!(2, rel.project(&3));
        assert_eq!(1, rel.project(&4));
    }

    #[test]
    #[should_panic(
        expected="There can only be one default class in the equivalence relation!"
    )]
    fn test_equivalence_relation_new_colliding_default() {
        let mut map = HashMap::new();
        let classes = vec![
            (0, HashSet::from_iter(vec![0, 1])),
            (1, HashSet::from_iter(vec![2, 3])),
        ];

        for (label, elements) in classes {
            map.insert(label, elements);
        }

        EquivalenceRelation::new(map, 1);
    }

    #[test]
    #[should_panic(
        expected="All classes of the equivalence relation must be disjoint!"
    )]
    fn test_equivalence_relation_new_intersecting_classes() {
        let mut map = HashMap::new();
        let classes = vec![
            (0, HashSet::from_iter(vec![0, 1])),
            (1, HashSet::from_iter(vec![1, 2])),
        ];

        for (label, elements) in classes {
            map.insert(label, elements);
        }

        EquivalenceRelation::new(map, 2);
    }

    #[test]
    fn test_equivalence_relation_from_str_legal_input() {
        let legal_inputs = vec![
            ("0 [0, 1]\n1 [2, 3]\n2 *", EquivalenceRelation::from(vec![
                EquivalenceClass::from((0, Some(vec![0, 1]))),
                EquivalenceClass::from((1, Some(vec![2, 3]))),
                EquivalenceClass::from((2, None)),
            ])),
            (" 0 [0, 1]\n 1 [2, 3]  \n2 *  ", EquivalenceRelation::from(vec![
                EquivalenceClass::from((0, Some(vec![0, 1]))),
                EquivalenceClass::from((1, Some(vec![2, 3]))),
                EquivalenceClass::from((2, None))
            ])),
        ];

        for (legal_input, control_relation) in legal_inputs {
            assert_eq!(
                control_relation,
                EquivalenceRelation::from_str(legal_input).unwrap()
            );
        }
    }

    #[test]
    fn test_equivalence_relation_from_str_illegal_input() {
        let illegal_inputs = vec![
            "0 [0, 1]1 [2, 3]2 *",
            "0 [0, 1]\n1 [1, 2]\n2 *",
            "0 [0, 1]\n1 [0, 1]\n2 *",
            "0 [0, 1]\n1 [2, 3]\n2 [*]",
            "0 [0, 1]\n1 [a, 3]\n2 *",
            "0 [0, 1]\na [2, 3]\n2 *",
        ];

        for illegal_input in illegal_inputs {
            match EquivalenceRelation::<u8, u8>::from_str(illegal_input) {
                Ok(parsed) =>
                    panic!("Was able to parse the illegal input \'{}\' as \'{:?}\'",
                           illegal_input, parsed),
                Err(_) => (),
            }
        }
    }
}
<|endoftext|>