<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/.

//! This module is the main module for the Ligature project.
//! It represents to common types and traits used by Ligature.

#![deny(missing_docs)]
//#![deny(missing_doc_example)] <-- for later, when I'm swole

#[macro_use]
extern crate lazy_static;

use regex::Regex;
use serde::{Deserialize, Serialize};

/// A string that represents a Dataset by name.
/// Currently can only be ASCII text separated by /
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
pub struct Dataset(String);

impl Dataset {
    /// Creates a new Dataset and returns a Result based on if it is valid or not.
    pub fn new(name: &str) -> Result<Dataset, LigatureError> {
        lazy_static! {
            static ref RE: Regex =
                Regex::new(r"^([a-zA-Z_]{1}[a-zA-Z0-9_]*)(/[a-zA-Z_]{1}[a-zA-Z0-9_]*)*$").unwrap();
        }

        if RE.is_match(name) {
            Ok(Dataset(name.to_string()))
        } else {
            Err(LigatureError(format!("Invalid Dataset name {}", name)))
        }
    }

    /// Returns the name of the given Dataset.
    pub fn name(&self) -> &str {
        &self.0
    }
}

/// An Entity that is identified by a unique u64 id.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Entity(pub u64);

/// A named connection between an Entity and a Value.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Attribute(String);

impl Attribute {
    /// Creates a new Arrow and returns a Result based on if it is valid or not.
    pub fn new(name: &str) -> Result<Self, LigatureError> {
        lazy_static! {
            static ref RE: Regex = Regex::new(r"^[a-zA-Z_]{1}[a-zA-Z0-9_]*$").unwrap();
        }

        if RE.is_match(name) {
            Ok(Self(name.to_string()))
        } else {
            Err(LigatureError(format!("Invalid Attribute name {}", name)))
        }
    }

    /// Returns the name of the given Arrow.
    pub fn name(&self) -> &str {
        &self.0
    }
}

/// An enum that represents all the currently supported Value types.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum Value {
    /// An Entity.
    Entity(Entity),
    /// A String used for a Ligature literal
    StringLiteral(String),
    /// An i64 used for a Ligature literal
    IntegerLiteral(i64),
    /// An f64 used for a Ligature literal
    FloatLiteral(f64),
}

/// A set of enums used to express range queries when it makes sense for that type (ie no support for BooleanLiteralRange or UnknownLiteralRange since they don't make sense).
#[derive(Debug)]
pub enum Range {
    /// Represents a String range using basic String comparisons.
    StringLiteralRange {
        /// The starting String (inclusive)
        start: String,
        /// The end String (exclusive)
        end: String,
    },
    /// Represents a String range using basic i64 comparisons.
    IntegerLiteralRange {
        /// The starting i64 (inclusive)
        start: i64,
        /// The end i64 (exclusive)
        end: i64,
    },
    /// Represents a String range using basic f64 comparisons.
    FloatLiteralRange {
        /// The starting f64 (inclusive)
        start: f64,
        /// The end f64 (exclusive)
        end: f64,
    },
}

/// A Statement is a grouping of an Entity, an Attribute, and a Value.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct Statement {
    /// The Entity of a Statement
    pub entity: Entity,
    /// The Attribute of a Statement
    pub attribute: Attribute,
    /// The Value of a Statement
    pub value: Value,
}

/// A Statement that has been persisted so it has an assoicated Context Entity.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct PersistedStatement {
    /// The target Statment
    pub statement: Statement,
    /// The Context of this Statement
    pub context: Entity,
}

/// A general struct for representing errors involving Ligature.
/// TODO should probably be an enum with a bunch of specific cases
#[derive(Debug, PartialEq, Eq)]
pub struct LigatureError(pub String);

/// A trait that all Ligature implementations implement.
pub trait Ligature {
    /// Returns all Datasets in a Ligature instance.
    fn all_datasets(&self) -> Box<dyn Iterator<Item = Result<Dataset, LigatureError>>>;

    /// Check if a given Dataset exists.
    fn dataset_exists(&self, dataset: &Dataset) -> Result<bool, LigatureError>;

    /// Returns all Datasets in a Ligature instance that start with the given prefix.
    fn match_datasets_prefix(
        &self,
        prefix: &str,
    ) -> Box<dyn Iterator<Item = Result<Dataset, LigatureError>>>;

    /// Returns all Datasets in a Ligature instance that are in a given range (inclusive, exclusive].
    fn match_datasets_range(
        &self,
        start: &str,
        end: &str,
    ) -> Box<dyn Iterator<Item = Result<Dataset, LigatureError>>>;

    /// Creates a dataset with the given name.
    /// TODO should probably return its own error type { InvalidDataset, DatasetExists, CouldNotCreateDataset }
    fn create_dataset(&self, dataset: &Dataset) -> Result<(), LigatureError>;

    /// Deletes a dataset with the given name.
    /// TODO should probably return its own error type { InvalidDataset, CouldNotDeleteDataset }
    fn delete_dataset(&self, dataset: &Dataset) -> Result<(), LigatureError>;

    /// Initiazes a QueryTx
    /// TODO should probably return its own error type CouldNotInitializeQueryTx
    fn query(&self, dataset: &Dataset) -> Result<Box<dyn QueryTx>, LigatureError>;

    /// Initiazes a WriteTx
    /// TODO should probably return its own error type CouldNotInitializeWriteTx
    fn write(&self, dataset: &Dataset) -> Result<Box<dyn WriteTx>, LigatureError>;
}

/// Represents a QueryTx within the context of a Ligature instance and a single Dataset
pub trait QueryTx {
    /// Returns all PersistedStatements in this Dataset.
    fn all_statements(&self)
        -> Box<dyn Iterator<Item = Result<PersistedStatement, LigatureError>>>;

    /// Returns all PersistedStatements that match the given criteria.
    /// If a parameter is None then it matches all, so passing all Nones is the same as calling all_statements.
    fn match_statements(
        &self,
        source: Option<Entity>,
        arrow: Option<Attribute>,
        target: Option<Value>,
    ) -> Box<dyn Iterator<Item = Result<PersistedStatement, LigatureError>>>;

    /// Retuns all PersistedStatements that match the given criteria.
    /// If a parameter is None then it matches all.
    fn match_statements_range(
        &self,
        source: Option<Entity>,
        arrow: Option<Attribute>,
        target: Range,
    ) -> Box<dyn Iterator<Item = Result<PersistedStatement, LigatureError>>>;

    /// Returns the PersistedStatement for the given context.
    fn statement_for_context(
        &self,
        context: &Entity,
    ) -> Result<Option<PersistedStatement>, LigatureError>;
}

/// Represents a WriteTx within the context of a Ligature instance and a single Dataset
pub trait WriteTx {
    /// Creates a new, unique Entity within this Dataset.
    /// Note: Entitys are shared across named graphs in a given Dataset.
    fn new_entity(&self) -> Result<Entity, LigatureError>;

    /// Adds a given Statement to this Dataset.
    /// If the Statement already exists nothing happens (TODO maybe add it with a new context?).
    /// Note: Potentally could trigger a ValidationError
    fn add_statement(&self, statement: &Statement) -> Result<PersistedStatement, LigatureError>;

    /// Removes a given PersistedStatement from this Dataset.
    /// If the PersistedStatement doesn't exist nothing happens.
    /// Note: Potentally could trigger a ValidationError.
    fn remove_statement(
        &self,
        persisted_statement: &PersistedStatement,
    ) -> Result<(), LigatureError>;

    /// Cancels this transaction so that none of the changes made so far will be stored.
    /// This also closes this transaction so no other methods can be called.
    fn cancel(&self) -> Result<(), LigatureError>;

    /// Commits this transaction.
    /// This also closes this transaction so no other methods can be called.
    fn commit(&self) -> Result<(), LigatureError>;
}
<commit_msg>making write and query accept an fn<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/.

//! This module is the main module for the Ligature project.
//! It represents to common types and traits used by Ligature.

#![deny(missing_docs)]
//#![deny(missing_doc_example)] <-- for later, when I'm swole

#[macro_use]
extern crate lazy_static;

use regex::Regex;
use serde::{Deserialize, Serialize};

/// A string that represents a Dataset by name.
/// Currently can only be ASCII text separated by /
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Serialize, Deserialize)]
pub struct Dataset(String);

impl Dataset {
    /// Creates a new Dataset and returns a Result based on if it is valid or not.
    pub fn new(name: &str) -> Result<Dataset, LigatureError> {
        lazy_static! {
            static ref RE: Regex =
                Regex::new(r"^([a-zA-Z_]{1}[a-zA-Z0-9_]*)(/[a-zA-Z_]{1}[a-zA-Z0-9_]*)*$").unwrap();
        }

        if RE.is_match(name) {
            Ok(Dataset(name.to_string()))
        } else {
            Err(LigatureError(format!("Invalid Dataset name {}", name)))
        }
    }

    /// Returns the name of the given Dataset.
    pub fn name(&self) -> &str {
        &self.0
    }
}

/// An Entity that is identified by a unique u64 id.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Entity(pub u64);

/// A named connection between an Entity and a Value.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Attribute(String);

impl Attribute {
    /// Creates a new Arrow and returns a Result based on if it is valid or not.
    pub fn new(name: &str) -> Result<Self, LigatureError> {
        lazy_static! {
            static ref RE: Regex = Regex::new(r"^[a-zA-Z_]{1}[a-zA-Z0-9_]*$").unwrap();
        }

        if RE.is_match(name) {
            Ok(Self(name.to_string()))
        } else {
            Err(LigatureError(format!("Invalid Attribute name {}", name)))
        }
    }

    /// Returns the name of the given Arrow.
    pub fn name(&self) -> &str {
        &self.0
    }
}

/// An enum that represents all the currently supported Value types.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum Value {
    /// An Entity.
    Entity(Entity),
    /// A String used for a Ligature literal
    StringLiteral(String),
    /// An i64 used for a Ligature literal
    IntegerLiteral(i64),
    /// An f64 used for a Ligature literal
    FloatLiteral(f64),
}

/// A set of enums used to express range queries when it makes sense for that type (ie no support for BooleanLiteralRange or UnknownLiteralRange since they don't make sense).
#[derive(Debug)]
pub enum Range {
    /// Represents a String range using basic String comparisons.
    StringLiteralRange {
        /// The starting String (inclusive)
        start: String,
        /// The end String (exclusive)
        end: String,
    },
    /// Represents a String range using basic i64 comparisons.
    IntegerLiteralRange {
        /// The starting i64 (inclusive)
        start: i64,
        /// The end i64 (exclusive)
        end: i64,
    },
    /// Represents a String range using basic f64 comparisons.
    FloatLiteralRange {
        /// The starting f64 (inclusive)
        start: f64,
        /// The end f64 (exclusive)
        end: f64,
    },
}

/// A Statement is a grouping of an Entity, an Attribute, and a Value.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct Statement {
    /// The Entity of a Statement
    pub entity: Entity,
    /// The Attribute of a Statement
    pub attribute: Attribute,
    /// The Value of a Statement
    pub value: Value,
}

/// A Statement that has been persisted so it has an assoicated Context Entity.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct PersistedStatement {
    /// The target Statment
    pub statement: Statement,
    /// The Context of this Statement
    pub context: Entity,
}

/// A general struct for representing errors involving Ligature.
/// TODO should probably be an enum with a bunch of specific cases
#[derive(Debug, PartialEq, Eq)]
pub struct LigatureError(pub String);

/// A trait that all Ligature implementations implement.
pub trait Ligature {
    /// Returns all Datasets in a Ligature instance.
    fn all_datasets(&self) -> Box<dyn Iterator<Item = Result<Dataset, LigatureError>>>;

    /// Check if a given Dataset exists.
    fn dataset_exists(&self, dataset: &Dataset) -> Result<bool, LigatureError>;

    /// Returns all Datasets in a Ligature instance that start with the given prefix.
    fn match_datasets_prefix(
        &self,
        prefix: &str,
    ) -> Box<dyn Iterator<Item = Result<Dataset, LigatureError>>>;

    /// Returns all Datasets in a Ligature instance that are in a given range (inclusive, exclusive].
    fn match_datasets_range(
        &self,
        start: &str,
        end: &str,
    ) -> Box<dyn Iterator<Item = Result<Dataset, LigatureError>>>;

    /// Creates a dataset with the given name.
    /// TODO should probably return its own error type { InvalidDataset, DatasetExists, CouldNotCreateDataset }
    fn create_dataset(&self, dataset: &Dataset) -> Result<(), LigatureError>;

    /// Deletes a dataset with the given name.
    /// TODO should probably return its own error type { InvalidDataset, CouldNotDeleteDataset }
    fn delete_dataset(&self, dataset: &Dataset) -> Result<(), LigatureError>;

    /// Initiazes a QueryTx
    /// TODO should probably return its own error type CouldNotInitializeQueryTx
    fn query<T>(
        &self,
        dataset: &Dataset,
        f: dyn Fn(&dyn QueryTx) -> Result<T, LigatureError>,
    ) -> Result<T, LigatureError>;

    /// Initiazes a WriteTx
    /// TODO should probably return its own error type CouldNotInitializeWriteTx
    fn write<T>(
        &self,
        dataset: &Dataset,
        f: dyn Fn(&dyn WriteTx) -> Result<T, LigatureError>,
    ) -> Result<T, LigatureError>;
}

/// Represents a QueryTx within the context of a Ligature instance and a single Dataset
pub trait QueryTx {
    /// Returns all PersistedStatements in this Dataset.
    fn all_statements(&self)
        -> Box<dyn Iterator<Item = Result<PersistedStatement, LigatureError>>>;

    /// Returns all PersistedStatements that match the given criteria.
    /// If a parameter is None then it matches all, so passing all Nones is the same as calling all_statements.
    fn match_statements(
        &self,
        source: Option<Entity>,
        arrow: Option<Attribute>,
        target: Option<Value>,
    ) -> Box<dyn Iterator<Item = Result<PersistedStatement, LigatureError>>>;

    /// Retuns all PersistedStatements that match the given criteria.
    /// If a parameter is None then it matches all.
    fn match_statements_range(
        &self,
        source: Option<Entity>,
        arrow: Option<Attribute>,
        target: Range,
    ) -> Box<dyn Iterator<Item = Result<PersistedStatement, LigatureError>>>;

    /// Returns the PersistedStatement for the given context.
    fn statement_for_context(
        &self,
        context: &Entity,
    ) -> Result<Option<PersistedStatement>, LigatureError>;
}

/// Represents a WriteTx within the context of a Ligature instance and a single Dataset
pub trait WriteTx {
    /// Creates a new, unique Entity within this Dataset.
    /// Note: Entitys are shared across named graphs in a given Dataset.
    fn new_entity(&self) -> Result<Entity, LigatureError>;

    /// Adds a given Statement to this Dataset.
    /// If the Statement already exists nothing happens (TODO maybe add it with a new context?).
    /// Note: Potentally could trigger a ValidationError
    fn add_statement(&self, statement: &Statement) -> Result<PersistedStatement, LigatureError>;

    /// Removes a given PersistedStatement from this Dataset.
    /// If the PersistedStatement doesn't exist nothing happens.
    /// Note: Potentally could trigger a ValidationError.
    fn remove_statement(
        &self,
        persisted_statement: &PersistedStatement,
    ) -> Result<(), LigatureError>;

    /// Cancels this transaction so that none of the changes made so far will be stored.
    /// This also closes this transaction so no other methods can be called without returning a LigatureError.
    fn cancel(&self) -> Result<(), LigatureError>;
}
<|endoftext|>


<commit_before>//! Helpers for writing Alfred XML output
//!
//! # Example
//!
//! ```
//! extern crate alfred;
//!
//! use std::io;
//!
//! fn write_items() -> io::IoResult<()> {
//!     let mut xmlw = try!(alfred::XMLWriter::new(io::stdout()));
//!
//!     let item1 = alfred::Item::new("Item 1");
//!     let item2 = alfred::Item {
//!         subtitle: Some("Subtitle".into_maybe_owned()),
//!         ..alfred::Item::new("Item 2")
//!     };
//!     let item3 = alfred::Item {
//!         arg: Some("Argument".into_maybe_owned()),
//!         subtitle: Some("Subtitle".into_maybe_owned()),
//!         icon: Some(alfred::FileType("public.folder".into_maybe_owned())),
//!         ..alfred::Item::new("Item 3")
//!     };
//!
//!     try!(xmlw.write_item(&item1));
//!     try!(xmlw.write_item(&item2));
//!     try!(xmlw.write_item(&item3));
//!
//!     let mut stdout = try!(xmlw.close());
//!     stdout.flush()
//! }
//!
//! fn main() {
//!     match write_items() {
//!         Ok(()) => {},
//!         Err(err) => {
//!             let _ = writeln!(io::stderr(), "Error writing items: {}", err);
//!         }
//!     }
//! }
//! ```

#![feature(if_let, unsafe_destructor)]
#![warn(missing_doc)]

use std::io;
use std::io::BufferedWriter;
use std::mem;
use std::str;

/// Representation of an `<item>`
#[deriving(PartialEq,Eq,Clone)]
pub struct Item<'a> {
    /// Identifier for the results. If given, must be unique among items, and is used for
    /// prioritizing feedback results based on usage. If blank, Alfred uses a UUID and does
    /// not learn from the results.
    pub uid: Option<str::MaybeOwned<'a>>,
    /// The value that is passed to the next portion of the workflow when this item
    /// is selected.
    pub arg: Option<str::MaybeOwned<'a>>,
    /// What type of result this is, if any.
    pub type_: Option<ItemType>,
    /// Whether or not the result item is 'valid'. If `false`, `autocomplete` may be used.
    pub valid: bool,
    /// Autocomplete data for valid=false items. When this item is selected, the autocomplete
    /// value is inserted into the Alfred window.
    pub autocomplete: Option<str::MaybeOwned<'a>>,

    /// Title for the item
    pub title: str::MaybeOwned<'a>,
    /// Subtitle for the item
    pub subtitle: Option<str::MaybeOwned<'a>>,
    /// Icon for the item
    pub icon: Option<Icon<'a>>
}

impl<'a> Item<'a> {
    /// Returns a new Item with the given title
    pub fn new<S: str::IntoMaybeOwned<'a>>(title: S) -> Item<'a> {
        Item {
            uid: None,
            arg: None,
            type_: None,
            valid: true,
            autocomplete: None,
            title: title.into_maybe_owned(),
            subtitle: None,
            icon: None
        }
    }
}

/// Item icons
#[deriving(PartialEq,Eq,Clone)]
pub enum Icon<'a> {
    /// Path to an image file on disk relative to the workflow directory
    PathIcon(str::MaybeOwned<'a>),
    /// Path to a file whose icon will be used
    FileIcon(str::MaybeOwned<'a>),
    /// UTI for a file type to use (e.g. public.folder)
    FileType(str::MaybeOwned<'a>)
}

/// Item types
#[deriving(PartialEq,Eq,Clone)]
pub enum ItemType {
    /// Type representing a file
    FileItemType
}

/// Helper struct used to manage the XML serialization of `Item`s
///
/// When the `XMLWriter` is first created, the XML header is immediately
/// written. When the `XMLWriter` is dropped, the XML footer is written
/// and the `Writer` is flushed.
///
/// Any errors produced by writing the footer are silently ignored. The
/// `close()` method can be used to return any such error.
pub struct XMLWriter<'a, W: Writer + 'a> {
    // Option so close() can remove it
    // Otherwise this must alwyas be Some()
    w: Option<W>,
    last_err: Option<io::IoError>
}

impl<'a, W: Writer + 'a> XMLWriter<'a, W> {
    /// Returns a new `XMLWriter` that writes to the given `Writer`
    ///
    /// The XML header is written immediately.
    pub fn new(mut w: W) -> io::IoResult<XMLWriter<'a, W>> {
        match w.write_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<items>\n") {
            Ok(()) => {
                Ok(XMLWriter {
                    w: Some(w),
                    last_err: None
                })
            }
            Err(err) => Err(err)
        }
    }

    /// Writes an `Item` to the underlying `Writer`
    ///
    /// If a previous write produced an error, any subsequent write will do
    /// nothing and return the same error. This is because the previous write
    /// may have partially completed, and attempting to write any more data
    /// will be unlikely to work properly.
    pub fn write_item(&mut self, item: &Item) -> io::IoResult<()> {
        if let Some(ref err) = self.last_err {
            return Err(err.clone());
        }
        let result = item.write_xml(self.w.as_mut().unwrap(), 1);
        if let Err(ref err) = result {
            self.last_err = Some(err.clone());
        }
        result
    }

    /// Consumes the `XMLWriter` and writes the XML footer
    ///
    /// This method can be used to get any error resulting from writing the
    /// footer. If this method is not used, the footer will be written when the
    /// `XMLWriter` is dropped and any error will be silently ignored.
    ///
    /// As with `write_item()`, if a previous invocation of `write_item()`
    /// returned an error, `close()` will return the same error without
    /// attempting to write the XML footer.
    ///
    /// When this method is used, the XML footer is written, but the `Writer`
    /// is not flushed. When the `XMLWriter` is dropped without calling
    /// `close()`, the `Writer` is flushed after the footer is written.
    pub fn close(mut self) -> io::IoResult<W> {
        let last_err = self.last_err.take();
        let mut w = self.w.take().unwrap();
        unsafe { mem::forget(self); }
        if let Some(err) = last_err {
            return Err(err);
        }
        try!(write_footer(&mut w));
        Ok(w)
    }
}

fn write_footer<'a, W: Writer + 'a>(w: &'a mut W) -> io::IoResult<()> {
    w.write_str("</items>\n")
}

#[unsafe_destructor]
impl<'a, W: Writer + 'a> Drop for XMLWriter<'a, W> {
    fn drop(&mut self) {
        if self.last_err.is_some() {
            return;
        }
        let mut w = self.w.take().unwrap();
        if write_footer(&mut w).is_ok() {
            let _ = w.flush();
        }
    }
}

/// Writes a complete XML document representing the `Item`s to the `Writer`
///
/// The `Writer` is flushed after the XML document is written.
pub fn write_items<W: Writer>(w: W, items: &[Item]) -> io::IoResult<()> {
    let mut xmlw = try!(XMLWriter::new(w));
    for item in items.iter() {
        try!(xmlw.write_item(item));
    }
    let mut w = try!(xmlw.close());
    w.flush()
}

impl<'a> Item<'a> {
    /// Writes the XML fragment representing the `Item` to the `Writer`
    ///
    /// `XMLWriter` should be used instead if at all possible, in order to
    /// write the XML header/footer and maintain proper error discipline.
    pub fn write_xml(&self, w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
        fn write_indent(w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
            for _ in range(0, indent) {
                try!(w.write_str("    "));
            }
            Ok(())
        }

        let mut w = BufferedWriter::with_capacity(512, w);

        try!(write_indent(&mut w, indent));
        try!(w.write_str("<item"));
        match self.uid {
            None => (),
            Some(ref uid) => {
                try!(write!(&mut w, r#" uid="{}""#, encode_entities(uid.as_slice())));
            }
        }
        match self.arg {
            None => (),
            Some(ref arg) => {
                try!(write!(&mut w, r#" arg="{}""#, encode_entities(arg.as_slice())));
            }
        }
        match self.type_ {
            None => (),
            Some(FileItemType) => {
                try!(w.write_str(r#" type="file""#));
            }
        }
        try!(write!(&mut w, r#" valid="{}""#, if self.valid { "yes" } else { "no" }));
        match self.autocomplete {
            None => (),
            Some(ref auto) => {
                try!(write!(&mut w, r#" autocomplete="{}""#, encode_entities(auto.as_slice())));
            }
        }
        try!(w.write_str(">\n"));

        try!(write_indent(&mut w, indent+1));
        try!(write!(&mut w, "<title>{}</title>\n", encode_entities(self.title.as_slice())));

        match self.subtitle {
            None => (),
            Some(ref s) => {
                try!(write_indent(&mut w, indent+1));
                try!(write!(&mut w, "<subtitle>{}</subtitle>\n", encode_entities(s.as_slice())));
            }
        }

        match self.icon {
            None => (),
            Some(ref icon) => {
                try!(write_indent(&mut w, indent+1));
                match *icon {
                    PathIcon(ref s) => {
                        try!(write!(&mut w, "<icon>{}</icon>\n", encode_entities(s.as_slice())));
                    }
                    FileIcon(ref s) => {
                        try!(write!(&mut w, "<icon type=\"fileicon\">{}</icon>\n",
                                      encode_entities(s.as_slice())));
                    }
                    FileType(ref s) => {
                        try!(write!(&mut w, "<icon type=\"filetype\">{}</icon>\n",
                                      encode_entities(s.as_slice())));
                    }
                }
            }
        }

        try!(write_indent(&mut w, indent));
        try!(w.write_str("</item>\n"));

        try!(w.flush());
        Ok(())
    }
}

fn encode_entities<'a>(s: &'a str) -> str::MaybeOwned<'a> {
    fn encode_entity(c: char) -> Option<&'static str> {
        Some(match c {
            '<' => "<",
            '>' => ">",
            '"' => """,
            '&' => "&",
            '\0'...'\x08' |
            '\x0B'...'\x0C' |
            '\x0E'...'\x1F' |
            '\uFFFE' | '\uFFFF' => {
                // these are all invalid characters in XML
                "\uFFFD"
            }
            _ => return None
        })
    }

    if s.chars().any(|c| encode_entity(c).is_some()) {
        let mut res = String::with_capacity(s.len());
        for c in s.chars() {
            match encode_entity(c) {
                Some(ent) => res.push_str(ent),
                None => res.push(c)
            }
        }
        str::Owned(res)
    } else {
        str::Slice(s)
    }
}
<commit_msg>Upgrade <item> support, add ItemBuilder<commit_after>//! Helpers for writing Alfred XML output
//!
//! # Example
//!
//! ```
//! extern crate alfred;
//!
//! use std::io;
//!
//! fn write_items() -> io::IoResult<()> {
//!     let mut xmlw = try!(alfred::XMLWriter::new(io::stdout()));
//!
//!     let item1 = alfred::Item::new("Item 1");
//!     let item2 = alfred::ItemBuilder::new("Item 2")
//!                                     .subtitle("Subtitle")
//!                                     .into_item();
//!     let item3 = alfred::ItemBuilder::new("Item 3")
//!                                     .arg("Argument")
//!                                     .subtitle("Subtitle")
//!                                     .icon_filetype("public.folder")
//!                                     .into_item();
//!
//!     try!(xmlw.write_item(&item1));
//!     try!(xmlw.write_item(&item2));
//!     try!(xmlw.write_item(&item3));
//!
//!     let mut stdout = try!(xmlw.close());
//!     stdout.flush()
//! }
//!
//! fn main() {
//!     match write_items() {
//!         Ok(()) => {},
//!         Err(err) => {
//!             let _ = writeln!(io::stderr(), "Error writing items: {}", err);
//!         }
//!     }
//! }
//! ```

// example environment for a script filter:
//
// alfred_preferences = /Users/kevin/Dropbox (Personal)/Alfred.alfredpreferences
// alfred_preferences_localhash = 24e980586e9906f9f08aa9febc3ef05f603e58ef
// alfred_theme = alfred.theme.yosemite
// alfred_theme_background = rgba(255,255,255,0.98)
// alfred_theme_subtext = 0
// alfred_version = 2.5
// alfred_version_build = 299
// alfred_workflow_bundleid = com.tildesoft.alfred.workflow.github-quick-open
// alfred_workflow_cache = /Users/kevin/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/com.tildesoft.alfred.workflow.github-quick-open
// alfred_workflow_data = /Users/kevin/Library/Application Support/Alfred 2/Workflow Data/com.tildesoft.alfred.workflow.github-quick-open
// alfred_workflow_name = GitHub Quick Open
// alfred_workflow_uid = user.workflow.9D443143-3DF7-4596-993E-DA198039EFAB

#![feature(if_let, unsafe_destructor)]
#![warn(missing_doc)]

use std::collections::HashMap;
use std::io;
use std::io::BufferedWriter;
use std::mem;
use std::str;
use std::str::{MaybeOwned, IntoMaybeOwned};

/// Representation of an `<item>`
#[deriving(PartialEq,Eq,Clone)]
pub struct Item<'a> {
    /// Title for the item
    pub title: MaybeOwned<'a>,
    /// Subtitle for the item
    ///
    /// The subtitle may differ based on the active modifier.
    pub subtitle: HashMap<Option<Modifier>,MaybeOwned<'a>>,
    /// Icon for the item
    pub icon: Option<Icon<'a>>,

    /// Identifier for the results
    ///
    /// If given, must be unique among items, and is used for prioritizing
    /// feedback results based on usage. If blank, Alfred presents results in
    /// the order given and does not learn from them.
    pub uid: Option<MaybeOwned<'a>>,
    /// The value that is passed to the next portion of the workflow when this
    /// item is selected
    pub arg: Option<MaybeOwned<'a>>,
    /// What type of result this is
    pub type_: ItemType,

    /// Whether or not the result is valid
    ///
    /// When `false`, actioning the result will populate the search field with
    /// the `autocomplete` value instead.
    pub valid: bool,
    /// Autocomplete data for the item
    ///
    /// This value is populated into the search field if the tab key is
    /// pressed. If `valid = false`, this value is populated if the item is
    /// actioned.
    pub autocomplete: Option<MaybeOwned<'a>>,

}

impl<'a> Item<'a> {
    /// Returns a new `Item` with the given title
    pub fn new<S: IntoMaybeOwned<'a>>(title: S) -> Item<'a> {
        Item {
            title: title.into_maybe_owned(),
            subtitle: HashMap::new(),
            icon: None,
            uid: None,
            arg: None,
            type_: DefaultItemType,
            valid: true,
            autocomplete: None,
        }
    }
}

/// Helper for building `Item` values
#[deriving(Clone)]
pub struct ItemBuilder<'a> {
    item: Item<'a>
}

impl<'a> ItemBuilder<'a> {
    /// Returns a new `ItemBuilder` with the given title
    pub fn new<S: IntoMaybeOwned<'a>>(title: S) -> ItemBuilder<'a> {
        ItemBuilder {
            item: Item::new(title)
        }
    }

    /// Returns the built `Item`
    pub fn into_item(self) -> Item<'a> {
        self.item
    }
}

impl<'a> ItemBuilder<'a> {
    /// Sets the `title` to the given value
    pub fn title<S: IntoMaybeOwned<'a>>(mut self, title: S) -> ItemBuilder<'a> {
        self.item.title = title.into_maybe_owned();
        self
    }

    /// Sets the default `subtitle` to the given value
    ///
    /// This sets the default subtitle, used when no modifier is pressed,
    /// or when no subtitle is provided for the pressed modifier.
    pub fn subtitle<S: IntoMaybeOwned<'a>>(mut self, subtitle: S) -> ItemBuilder<'a> {
        self.item.subtitle.insert(None, subtitle.into_maybe_owned());
        self
    }

    /// Sets the `subtitle` to the given value with the given modifier
    ///
    /// This sets the subtitle to use when the given modifier is pressed.
    pub fn subtitle_mod<S: IntoMaybeOwned<'a>>(mut self, subtitle: S, modifier: Modifier)
                                              -> ItemBuilder<'a> {
        self.item.subtitle.insert(Some(modifier), subtitle.into_maybe_owned());
        self
    }

    /// Sets the `icon` to an image file on disk
    ///
    /// The path is interpreted relative to the workflow directory
    pub fn icon_path<S: IntoMaybeOwned<'a>>(mut self, path: S) -> ItemBuilder<'a> {
        self.item.icon = Some(PathIcon(path.into_maybe_owned()));
        self
    }

    /// Sets the `icon` to the icon for a given file on disk
    ///
    /// The path is interpreted relative to the workflow directory
    pub fn icon_file<S: IntoMaybeOwned<'a>>(mut self, path: S) -> ItemBuilder<'a> {
        self.item.icon = Some(FileIcon(path.into_maybe_owned()));
        self
    }

    /// Sets the `icon` to the icon for a given file type
    ///
    /// The type is a UTI, such as "public.jpeg".
    pub fn icon_filetype<S: IntoMaybeOwned<'a>>(mut self, filetype: S) -> ItemBuilder<'a> {
        self.item.icon = Some(FileTypeIcon(filetype.into_maybe_owned()));
        self
    }

    /// Sets the `uid` to the given value
    pub fn uid<S: IntoMaybeOwned<'a>>(mut self, uid: S) -> ItemBuilder<'a> {
        self.item.uid = Some(uid.into_maybe_owned());
        self
    }

    /// Sets the `arg` to the given value
    pub fn arg<S: IntoMaybeOwned<'a>>(mut self, arg: S) -> ItemBuilder<'a> {
        self.item.arg = Some(arg.into_maybe_owned());
        self
    }

    /// Sets the `type` to the given value
    pub fn type_(mut self, type_: ItemType) -> ItemBuilder<'a> {
        self.item.type_ = type_;
        self
    }

    /// Sets `valid` to `true`
    pub fn valid(mut self) -> ItemBuilder<'a> {
        self.item.valid = true;
        self
    }

    /// Sets `valid` to `false`
    pub fn invalid(mut self) -> ItemBuilder<'a> {
        self.item.valid = false;
        self
    }

    /// Sets `autocomplete` to the given value
    pub fn autocomplete<S: IntoMaybeOwned<'a>>(mut self, autocomplete: S) -> ItemBuilder<'a> {
        self.item.autocomplete = Some(autocomplete.into_maybe_owned());
        self
    }
}

impl<'a> ItemBuilder<'a> {
    /// Unsets the default `subtitle`
    ///
    /// This unsets the default subtitle.
    pub fn unset_subtitle(mut self) -> ItemBuilder<'a> {
        self.item.subtitle.remove(&None);
        self
    }

    /// Unsets the `subtitle` for the given modifier
    ///
    /// This unsets the subtitle that's used when the given modifier is pressed.
    pub fn unset_subtitle_mod(mut self, modifier: Modifier) -> ItemBuilder<'a> {
        self.item.subtitle.remove(&Some(modifier));
        self
    }

    /// Clears the `subtitle` for all modifiers
    ///
    /// This unsets both the default subtitle and the per-modifier subtitles.
    pub fn clear_subtitle(mut self) -> ItemBuilder<'a> {
        self.item.subtitle.clear();
        self
    }

    /// Unsets the `icon`
    pub fn unset_icon(mut self) -> ItemBuilder<'a> {
        self.item.icon = None;
        self
    }

    /// Unsets the `uid`
    pub fn unset_uid(mut self) -> ItemBuilder<'a> {
        self.item.uid = None;
        self
    }

    /// Unsets the `arg`
    pub fn unset_arg(mut self) -> ItemBuilder<'a> {
        self.item.arg = None;
        self
    }

    // `type` doesn't need unsetting, it uses a default of DefaultItemType instead

    /// Unsets `autocomplete`
    pub fn unset_autocomplete(mut self) -> ItemBuilder<'a> {
        self.item.autocomplete = None;
        self
    }
}

/// Keyboard modifiers
// As far as I can tell, Alfred doesn't support modifier combinations.
#[deriving(Clone,Show,Hash,PartialEq,Eq)]
pub enum Modifier {
    /// Command key
    CmdModifier,
    /// Option/Alt key
    AltModifier,
    /// Control key
    CtrlModifier,
    /// Shift key
    ShiftModifier,
    /// Fn key
    FnModifier
}

/// Item icons
#[deriving(PartialEq,Eq,Clone)]
pub enum Icon<'a> {
    /// Path to an image file on disk relative to the workflow directory
    PathIcon(str::MaybeOwned<'a>),
    /// Path to a file whose icon will be used
    FileIcon(str::MaybeOwned<'a>),
    /// UTI for a file type to use (e.g. public.folder)
    FileTypeIcon(str::MaybeOwned<'a>)
}

/// Item types
#[deriving(PartialEq,Eq,Clone)]
pub enum ItemType {
    /// Default type for an item
    DefaultItemType,
    /// Type representing a file
    ///
    /// Alredy checks that the file exists on disk, and hides the result if it
    /// does not.
    FileItemType,
    /// Type representing a file, with filesystem checks skipped
    ///
    /// Similar to `FileItemType` but skips the check to ensure the file
    /// exists.
    FileSkipCheckItemType
}

/// Helper struct used to manage the XML serialization of `Item`s
///
/// When the `XMLWriter` is first created, the XML header is immediately
/// written. When the `XMLWriter` is dropped, the XML footer is written
/// and the `Writer` is flushed.
///
/// Any errors produced by writing the footer are silently ignored. The
/// `close()` method can be used to return any such error.
pub struct XMLWriter<'a, W: Writer + 'a> {
    // Option so close() can remove it
    // Otherwise this must alwyas be Some()
    w: Option<W>,
    last_err: Option<io::IoError>
}

impl<'a, W: Writer + 'a> XMLWriter<'a, W> {
    /// Returns a new `XMLWriter` that writes to the given `Writer`
    ///
    /// The XML header is written immediately.
    pub fn new(mut w: W) -> io::IoResult<XMLWriter<'a, W>> {
        match w.write_str("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<items>\n") {
            Ok(()) => {
                Ok(XMLWriter {
                    w: Some(w),
                    last_err: None
                })
            }
            Err(err) => Err(err)
        }
    }

    /// Writes an `Item` to the underlying `Writer`
    ///
    /// If a previous write produced an error, any subsequent write will do
    /// nothing and return the same error. This is because the previous write
    /// may have partially completed, and attempting to write any more data
    /// will be unlikely to work properly.
    pub fn write_item(&mut self, item: &Item) -> io::IoResult<()> {
        if let Some(ref err) = self.last_err {
            return Err(err.clone());
        }
        let result = item.write_xml(self.w.as_mut().unwrap(), 1);
        if let Err(ref err) = result {
            self.last_err = Some(err.clone());
        }
        result
    }

    /// Consumes the `XMLWriter` and writes the XML footer
    ///
    /// This method can be used to get any error resulting from writing the
    /// footer. If this method is not used, the footer will be written when the
    /// `XMLWriter` is dropped and any error will be silently ignored.
    ///
    /// As with `write_item()`, if a previous invocation of `write_item()`
    /// returned an error, `close()` will return the same error without
    /// attempting to write the XML footer.
    ///
    /// When this method is used, the XML footer is written, but the `Writer`
    /// is not flushed. When the `XMLWriter` is dropped without calling
    /// `close()`, the `Writer` is flushed after the footer is written.
    pub fn close(mut self) -> io::IoResult<W> {
        let last_err = self.last_err.take();
        let mut w = self.w.take().unwrap();
        unsafe { mem::forget(self); }
        if let Some(err) = last_err {
            return Err(err);
        }
        try!(write_footer(&mut w));
        Ok(w)
    }
}

fn write_footer<'a, W: Writer + 'a>(w: &'a mut W) -> io::IoResult<()> {
    w.write_str("</items>\n")
}

#[unsafe_destructor]
impl<'a, W: Writer + 'a> Drop for XMLWriter<'a, W> {
    fn drop(&mut self) {
        if self.last_err.is_some() {
            return;
        }
        let mut w = self.w.take().unwrap();
        if write_footer(&mut w).is_ok() {
            let _ = w.flush();
        }
    }
}

/// Writes a complete XML document representing the `Item`s to the `Writer`
///
/// The `Writer` is flushed after the XML document is written.
pub fn write_items<W: Writer>(w: W, items: &[Item]) -> io::IoResult<()> {
    let mut xmlw = try!(XMLWriter::new(w));
    for item in items.iter() {
        try!(xmlw.write_item(item));
    }
    let mut w = try!(xmlw.close());
    w.flush()
}

impl<'a> Item<'a> {
    /// Writes the XML fragment representing the `Item` to the `Writer`
    ///
    /// `XMLWriter` should be used instead if at all possible, in order to
    /// write the XML header/footer and maintain proper error discipline.
    pub fn write_xml(&self, w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
        fn write_indent(w: &mut io::Writer, indent: uint) -> io::IoResult<()> {
            for _ in range(0, indent) {
                try!(w.write_str("    "));
            }
            Ok(())
        }

        let mut w = BufferedWriter::with_capacity(512, w);

        try!(write_indent(&mut w, indent));
        try!(w.write_str("<item"));
        if let Some(ref uid) = self.uid {
            try!(write!(&mut w, r#" uid="{}""#, encode_entities(uid.as_slice())));
        }
        if let Some(ref arg) = self.arg {
            try!(write!(&mut w, r#" arg="{}""#, encode_entities(arg.as_slice())));
        }
        match self.type_ {
            DefaultItemType => {}
            FileItemType => {
                try!(w.write_str(r#" type="file""#));
            }
            FileSkipCheckItemType => {
                try!(w.write_str(r#" type="file:skipcheck""#));
            }
        }
        if !self.valid {
            try!(w.write_str(r#" valid="no""#));
        }
        if let Some(ref auto) = self.autocomplete {
            try!(write!(&mut w, r#" autocomplete="{}""#, encode_entities(auto.as_slice())));
        }
        try!(w.write_str(">\n"));

        try!(write_indent(&mut w, indent+1));
        try!(write!(&mut w, "<title>{}</title>\n", encode_entities(self.title.as_slice())));

        for (modifier, subtitle) in self.subtitle.iter() {
            try!(write_indent(&mut w, indent+1));
            if let Some(modifier) = *modifier {
                try!(write!(w, r#"<subtitle mod="{}">"#, match modifier {
                    CmdModifier => "cmd",
                    AltModifier => "alt",
                    CtrlModifier => "ctrl",
                    ShiftModifier => "shift",
                    FnModifier => "fn"
                }));
            } else {
                try!(w.write_str("<subtitle>"));
            }
            try!(write!(w, "{}</subtitle>\n", encode_entities(subtitle.as_slice())));
        }

        if let Some(ref icon) = self.icon {
            try!(write_indent(&mut w, indent+1));
            match *icon {
                PathIcon(ref s) => {
                    try!(write!(&mut w, "<icon>{}</icon>\n", encode_entities(s.as_slice())));
                }
                FileIcon(ref s) => {
                    try!(write!(&mut w, "<icon type=\"fileicon\">{}</icon>\n",
                                    encode_entities(s.as_slice())));
                }
                FileTypeIcon(ref s) => {
                    try!(write!(&mut w, "<icon type=\"filetype\">{}</icon>\n",
                                    encode_entities(s.as_slice())));
                }
            }
        }

        try!(write_indent(&mut w, indent));
        try!(w.write_str("</item>\n"));

        w.flush()
    }
}

fn encode_entities<'a>(s: &'a str) -> str::MaybeOwned<'a> {
    fn encode_entity(c: char) -> Option<&'static str> {
        Some(match c {
            '<' => "<",
            '>' => ">",
            '"' => """,
            '&' => "&",
            '\0'...'\x08' |
            '\x0B'...'\x0C' |
            '\x0E'...'\x1F' |
            '\uFFFE' | '\uFFFF' => {
                // these are all invalid characters in XML
                "\uFFFD"
            }
            _ => return None
        })
    }

    if s.chars().any(|c| encode_entity(c).is_some()) {
        let mut res = String::with_capacity(s.len());
        for c in s.chars() {
            match encode_entity(c) {
                Some(ent) => res.push_str(ent),
                None => res.push(c)
            }
        }
        str::Owned(res)
    } else {
        str::Slice(s)
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) the nom-lua contributors. All rights reserved.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation. This file is also subject
 * to the Linking exception provided in the LICENSE file that
 * accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#![feature(unicode)]

//#![deny(missing_docs)]
//#![deny(warnings)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]

#[macro_use]
extern crate nom;

#[macro_use]
extern crate hexf_parse;

extern crate std_unicode;

// this is going to be usefull when looking at this crate
// https://www.lua.org/manual/5.3/manual.html#9

macro_rules! ast_panic_test {
    ($name: ident, $func: ident, $input: expr) => {
        #[test]
        #[should_panic]
        fn $name () {
            use super::*;
            $func($input.as_bytes()).unwrap().1;
        }
    }
}

macro_rules! ast_test {
    ($name: ident, $func: ident, $input: expr, $output: expr) => {
        #[test]
        fn $name () {
            use super::*;
            assert_eq!($func($input.as_bytes()).unwrap().1, $output);
        }
    }
}

macro_rules! ast_valid {
    ($name: ident, $func: ident, $input: expr) => {
        #[test]
        fn $name () {
            use super::*;
            assert!(match $func($input.as_bytes()).unwrap().1 {
                _ => true,
            });
        }
    }
}

macro_rules! ast_invalid {
    ($name: ident, $func: ident, $input: expr) => {
        #[test]
        #[should_panic]
        fn $name () {
            use super::*;
            $func($input.as_bytes()).unwrap().1;
        }
    }
}

macro_rules! astb {
    ($name: ident, $($a: expr),*) => {
        $name($(Box::new($a)),*)
    };
}

macro_rules! ast {
    ($name: ident) => {
        $name
    };
    ($name: ident, $($a: expr),*) => {
        $name($($a),*)
    };
}

use function::parse_block;
use ast::ASTNode;
use std::io::Read;

pub mod ast;
pub mod op;
pub mod number;
pub mod exp;
pub mod string;
pub mod name;
pub mod var;
pub mod field;
pub mod statement;
pub mod function;

pub use nom::IResult;

//named!(pub parse_chunk<ASTNode>, ws!(parse_block));
use exp::parse_exp;
named!(pub parse_chunk<ASTNode>, dbg_dmp!(ws!(parse_exp)));

// TODO: Implement our own Error type
pub fn parse_string<'a, T: Into<&'a [u8]>>(s: T) -> Option<ASTNode> {
    match parse_chunk(s.into()) {
        IResult::Done(_, a) => Some(a),
        _ => None
    }
}

pub fn parse<T: Read>(mut s: T) -> Option<ASTNode> {
    let mut buf = vec![];
    s.read_to_end(&mut buf);
    buf.pop(); //Remove EOF
    match parse_chunk(&buf) {
        IResult::Done(_, a) => Some(a),
        _ => None
    }
}
<commit_msg>Export ASTNode<commit_after>/*
 * Copyright (c) the nom-lua contributors. All rights reserved.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation. This file is also subject
 * to the Linking exception provided in the LICENSE file that
 * accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#![feature(unicode)]

//#![deny(missing_docs)]
//#![deny(warnings)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]

#[macro_use]
extern crate nom;

#[macro_use]
extern crate hexf_parse;

extern crate std_unicode;

// this is going to be usefull when looking at this crate
// https://www.lua.org/manual/5.3/manual.html#9

macro_rules! ast_panic_test {
    ($name: ident, $func: ident, $input: expr) => {
        #[test]
        #[should_panic]
        fn $name () {
            use super::*;
            $func($input.as_bytes()).unwrap().1;
        }
    }
}

macro_rules! ast_test {
    ($name: ident, $func: ident, $input: expr, $output: expr) => {
        #[test]
        fn $name () {
            use super::*;
            assert_eq!($func($input.as_bytes()).unwrap().1, $output);
        }
    }
}

macro_rules! ast_valid {
    ($name: ident, $func: ident, $input: expr) => {
        #[test]
        fn $name () {
            use super::*;
            assert!(match $func($input.as_bytes()).unwrap().1 {
                _ => true,
            });
        }
    }
}

macro_rules! ast_invalid {
    ($name: ident, $func: ident, $input: expr) => {
        #[test]
        #[should_panic]
        fn $name () {
            use super::*;
            $func($input.as_bytes()).unwrap().1;
        }
    }
}

macro_rules! astb {
    ($name: ident, $($a: expr),*) => {
        $name($(Box::new($a)),*)
    };
}

macro_rules! ast {
    ($name: ident) => {
        $name
    };
    ($name: ident, $($a: expr),*) => {
        $name($($a),*)
    };
}

use function::parse_block;
pub use ast::ASTNode;
use std::io::Read;

pub mod ast;
pub mod op;
pub mod number;
pub mod exp;
pub mod string;
pub mod name;
pub mod var;
pub mod field;
pub mod statement;
pub mod function;

pub use nom::IResult;

//named!(pub parse_chunk<ASTNode>, ws!(parse_block));
use exp::parse_exp;
named!(pub parse_chunk<ASTNode>, dbg_dmp!(ws!(parse_exp)));

// TODO: Implement our own Error type
pub fn parse_string<'a, T: Into<&'a [u8]>>(s: T) -> Option<ASTNode> {
    match parse_chunk(s.into()) {
        IResult::Done(_, a) => Some(a),
        _ => None
    }
}

pub fn parse<T: Read>(mut s: T) -> Option<ASTNode> {
    let mut buf = vec![];
    s.read_to_end(&mut buf);
    buf.pop(); //Remove EOF
    match parse_chunk(&buf) {
        IResult::Done(_, a) => Some(a),
        _ => None
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
extern crate core;

#[cfg(feature = "alloc")]
extern crate alloc;

use core::cell::{Cell, RefCell};
#[cfg(feature = "std")]
use std::ffi::{CStr, CString};
#[cfg(feature = "std")]
use std::os::raw::c_char;
#[cfg(feature = "alloc")]
use core::ops::Deref;
use core::ptr;

#[cfg(feature = "alloc")]
use alloc::boxed::Box;

/// Trait for types that implement `as_ptr`.
///
/// This is implemented by types which can be converted
/// to a pointer from a borrowed reference.
///
/// # Example
/// ```
/// use ptrplus::AsPtr;
///
/// let x: &u32 = &5;
/// let y: *const u32 = x.as_ptr();
/// unsafe {
///     assert_eq!(*y, 5);
/// }
/// ```
///
/// ```
/// use ptrplus::AsPtr;
///
/// let x = 5;
/// let o1: Option<&u32> = None;
/// let o2: Option<&u32> = Some(&x);
///
/// assert!(o1.as_ptr().is_null());
/// assert!(!o2.as_ptr().is_null());
/// unsafe {
///     assert_eq!(*o2.as_ptr(), 5);
/// }
/// ```
pub trait AsPtr {
    /// The type pointed to
    ///
    /// `as_ptr` will return a pointer to this type
    type Raw;

    /// Returns a raw pointer to the contained content
    ///
    /// The caller must ensure `self` outlives the pointer
    /// that is returned, or else it will end up pointing
    /// to garbage.
    ///
    /// Mutating `self` may also invalidate this pointer,
    /// depending on the implementation.
    fn as_ptr(&self) -> *const Self::Raw;
}

impl<T> AsPtr for [T] {
    type Raw = T;
    fn as_ptr(&self) -> *const T {
        <[T]>::as_ptr(self)
    }
}

impl<'a, T> AsPtr for &'a T
where
    T: Sized,
{
    type Raw = T;
    fn as_ptr(&self) -> *const T {
        *self as *const T
    }
}

impl<T> AsPtr for *const T {
    type Raw = T;
    fn as_ptr(&self) -> *const T {
        *self
    }
}

impl<T> AsPtr for Cell<T>
where
    T: Copy,
{
    type Raw = T;
    fn as_ptr(&self) -> *const T {
        Cell::as_ptr(self)
    }
}

impl<T> AsPtr for RefCell<T> {
    type Raw = T;
    fn as_ptr(&self) -> *const T {
        RefCell::as_ptr(self)
    }
}

#[cfg(feature = "std")]
impl AsPtr for CStr {
    type Raw = c_char;
    fn as_ptr(&self) -> *const c_char {
        CStr::as_ptr(self)
    }
}

#[cfg(feature = "std")]
impl AsPtr for CString {
    type Raw = c_char;

    fn as_ptr(&self) -> *const c_char {
        CStr::as_ptr(self)
    }
}

#[cfg(feature = "alloc")]
impl<T> AsPtr for Box<T> {
    type Raw = T;
    fn as_ptr(&self) -> *const T {
        self.deref().as_ptr()
    }
}

impl<T> AsPtr for Option<T>
where
    T: AsPtr,
{
    type Raw = T::Raw;
    fn as_ptr(&self) -> *const T::Raw {
        match self {
            Some(ref v) => v.as_ptr(),
            None => ptr::null(),
        }
    }
}

/// Trait for types that implement `into_raw`
///
/// This is implemented by types that can be converted
/// into a pointer by consuming ownership of the object
///
/// # Example
/// ```
/// use ptrplus::IntoRaw;
///
/// let x: Box<u32> = Box::new(5);
/// let y: *mut u32 = IntoRaw::into_raw(x);
/// unsafe {
///   assert_eq!(*y, 5);
///   *y = 6;
///   Box::from_raw(y);
/// }
///
/// ```
///
/// ```
/// use ptrplus::{FromRaw, IntoRaw};
///
/// let o1: Option<Box<u32>> = None;
/// let o2: Option<Box<u32>> = Some(Box::new(5));
///
/// let p1: *mut u32 = o1.into_raw();
/// let p2: *mut u32 = o2.into_raw();
///
/// assert!(p1.is_null());
/// assert!(!p2.is_null());
/// unsafe {
///     assert_eq!(*p2, 5);
///     let o1: Option<Box<u32>> = Option::from_raw(p1);
///     let o2: Option<Box<u32>> = Option::from_raw(p2);
///     assert!(o1.is_none());
///     assert!(!o2.is_none());
/// }
/// ```
pub trait IntoRaw {
    /// The type pointed to
    ///
    /// `into_raw` returns a mutable pointer to this type
    type Raw;

    /// Consumes `self` returning the wrapped raw pointer.
    ///
    /// After calling this method, the caller is responsable
    /// for making sure any resources attached to this pointer
    /// (such as memory) are cleaned up. The proper way to do this
    /// is to convert the pointer back to `Self`.
    ///
    /// See `FromRaw`
    fn into_raw(self) -> *mut Self::Raw;
}

#[cfg(feature = "alloc")]
impl<T> IntoRaw for Box<T> {
    type Raw = T;
    fn into_raw(self) -> *mut T {
        Box::into_raw(self)
    }
}

#[cfg(feature = "std")]
impl IntoRaw for CString {
    type Raw = c_char;
    fn into_raw(self) -> *mut c_char {
        CString::into_raw(self)
    }
}

impl<T> IntoRaw for *mut T {
    type Raw = T;
    fn into_raw(self) -> *mut T {
        self
    }
}

impl<T> IntoRaw for Option<T>
where
    T: IntoRaw,
{
    type Raw = T::Raw;
    fn into_raw(self) -> *mut T::Raw {
        match self {
            Some(v) => v.into_raw(),
            None => ptr::null_mut(),
        }
    }
}

/// Trait for types that can be created from a raw pointer
///
/// # Examples
/// ```
/// use ptrplus::{FromRaw, IntoRaw};
///
/// let x: Box<u32> = Box::new(5);
/// let y = x.into_raw();
/// let z: Box<u32> = unsafe { FromRaw::from_raw(y) };
/// assert_eq!(*z, 5);
///
/// ```
///
pub trait FromRaw<T> {
    /// Create `Self` from a raw pointer
    ///
    /// After calling this method the raw pointer
    /// is owned by the resulting object. This
    /// means that the resulting object should
    /// clean up any resources associated with
    /// the pointer (such as memory).
    ///
    /// # Safety
    ///
    /// `raw` must be a pointer that is compatible with
    /// the resulting type. For example, if `Self` is
    /// `Box<T>`, then `raw` must be a pointer to memory allocated
    /// as a Box. The exact requirements depend on the implementation.
    ///
    /// Additionally, this function takes ownership of the pointer. If
    /// `raw` or an alias thereof is used after calling this function
    /// it can potentially result in double-free, data races, or other
    /// undefined behavior.
    unsafe fn from_raw(raw: *mut T) -> Self;
}

#[cfg(feature = "alloc")]
impl<T> FromRaw<T> for Box<T> {
    unsafe fn from_raw(raw: *mut T) -> Self {
        Box::from_raw(raw)
    }
}

#[cfg(feature = "std")]
impl FromRaw<c_char> for CString {
    unsafe fn from_raw(raw: *mut c_char) -> CString {
        CString::from_raw(raw)
    }
}

impl<T> FromRaw<T> for *mut T {
    unsafe fn from_raw(raw: *mut T) -> *mut T {
        raw
    }
}

impl<T, U> FromRaw<U> for Option<T>
where
    T: FromRaw<U>,
{
    unsafe fn from_raw(raw: *mut U) -> Option<T> {
        if raw.is_null() {
            None
        } else {
            Some(T::from_raw(raw))
        }
    }
}
<commit_msg>Add support for Rc and Arc<commit_after>#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
extern crate core;

#[cfg(feature = "alloc")]
extern crate alloc;

use core::cell::{Cell, RefCell};
#[cfg(feature = "alloc")]
use core::ops::Deref;
use core::ptr::{self, NonNull};
#[cfg(feature = "std")]
use std::ffi::{CStr, CString};
#[cfg(feature = "std")]
use std::os::raw::c_char;

#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "alloc")]
use alloc::rc::Rc;
#[cfg(feature = "alloc")]
use alloc::sync::Arc;

macro_rules! asptr_wrapper {
    ($name:ident) => {
        impl<T> AsPtr for $name<T> {
            type Raw = T;
            #[inline]
            fn as_ptr(&self) -> *const T {
                $name::as_ptr(self)
            }
        }
    };
}

#[cfg(feature = "alloc")]
macro_rules! owned_ptr_wrapper {
    ($name:ident) => {
        impl<T> IntoRaw for $name<T> {
            type Raw = T;
            fn into_raw(self) -> *mut T {
                $name::into_raw(self) as *mut T
            }
        }
        impl<T> FromRaw<T> for $name<T> {
            unsafe fn from_raw(raw: *mut T) -> $name<T> {
                $name::from_raw(raw)
            }
        }
    };
}

/// Trait for types that implement `as_ptr`.
///
/// This is implemented by types which can be converted
/// to a pointer from a borrowed reference.
///
/// # Example
/// ```
/// use ptrplus::AsPtr;
///
/// let x: &u32 = &5;
/// let y: *const u32 = x.as_ptr();
/// unsafe {
///     assert_eq!(*y, 5);
/// }
/// ```
///
/// ```
/// use ptrplus::AsPtr;
///
/// let x = 5;
/// let o1: Option<&u32> = None;
/// let o2: Option<&u32> = Some(&x);
///
/// assert!(o1.as_ptr().is_null());
/// assert!(!o2.as_ptr().is_null());
/// unsafe {
///     assert_eq!(*o2.as_ptr(), 5);
/// }
/// ```
pub trait AsPtr {
    /// The type pointed to
    ///
    /// `as_ptr` will return a pointer to this type
    type Raw;

    /// Returns a raw pointer to the contained content
    ///
    /// The caller must ensure `self` outlives the pointer
    /// that is returned, or else it will end up pointing
    /// to garbage.
    ///
    /// Mutating `self` may also invalidate this pointer,
    /// depending on the implementation.
    fn as_ptr(&self) -> *const Self::Raw;
}

impl<T> AsPtr for [T] {
    type Raw = T;
    #[inline]
    fn as_ptr(&self) -> *const T {
        <[T]>::as_ptr(self)
    }
}

impl<'a, T> AsPtr for &'a T
where
    T: Sized,
{
    type Raw = T;
    #[inline]
    fn as_ptr(&self) -> *const T {
        *self as *const T
    }
}

impl<T> AsPtr for NonNull<T> {
    type Raw = T;
    #[inline]
    fn as_ptr(&self) -> *const T {
        NonNull::as_ptr(*self)
    }
}

impl<T> AsPtr for *const T {
    type Raw = T;
    #[inline]
    fn as_ptr(&self) -> *const T {
        *self
    }
}

#[cfg(feature = "std")]
impl AsPtr for CStr {
    type Raw = c_char;
    #[inline]
    fn as_ptr(&self) -> *const c_char {
        CStr::as_ptr(self)
    }
}

#[cfg(feature = "std")]
impl AsPtr for CString {
    type Raw = c_char;
    #[inline]
    fn as_ptr(&self) -> *const c_char {
        CStr::as_ptr(self)
    }
}

#[cfg(feature = "alloc")]
impl<T> AsPtr for Box<T> {
    type Raw = T;
    #[inline]
    fn as_ptr(&self) -> *const T {
        self.deref().as_ptr()
    }
}

impl<T> AsPtr for Option<T>
where
    T: AsPtr,
{
    type Raw = T::Raw;
    #[inline]
    fn as_ptr(&self) -> *const T::Raw {
        match self {
            Some(ref v) => v.as_ptr(),
            None => ptr::null(),
        }
    }
}

asptr_wrapper!(Cell);
asptr_wrapper!(RefCell);
#[cfg(feature = "alloc")]
asptr_wrapper!(Rc);
#[cfg(feature = "alloc")]
asptr_wrapper!(Arc);

/// Trait for types that implement `into_raw`
///
/// This is implemented by types that can be converted
/// into a pointer by consuming ownership of the object
///
/// # Example
/// ```
/// use ptrplus::IntoRaw;
///
/// let x: Box<u32> = Box::new(5);
/// let y: *mut u32 = IntoRaw::into_raw(x);
/// unsafe {
///   assert_eq!(*y, 5);
///   *y = 6;
///   Box::from_raw(y);
/// }
///
/// ```
///
/// ```
/// use ptrplus::{FromRaw, IntoRaw};
///
/// let o1: Option<Box<u32>> = None;
/// let o2: Option<Box<u32>> = Some(Box::new(5));
///
/// let p1: *mut u32 = o1.into_raw();
/// let p2: *mut u32 = o2.into_raw();
///
/// assert!(p1.is_null());
/// assert!(!p2.is_null());
/// unsafe {
///     assert_eq!(*p2, 5);
///     let o1: Option<Box<u32>> = Option::from_raw(p1);
///     let o2: Option<Box<u32>> = Option::from_raw(p2);
///     assert!(o1.is_none());
///     assert!(!o2.is_none());
/// }
/// ```
pub trait IntoRaw {
    /// The type pointed to
    ///
    /// `into_raw` returns a mutable pointer to this type
    type Raw;

    /// Consumes `self` returning the wrapped raw pointer.
    ///
    /// After calling this method, the caller is responsable
    /// for making sure any resources attached to this pointer
    /// (such as memory) are cleaned up. The proper way to do this
    /// is to convert the pointer back to `Self`.
    ///
    /// See `FromRaw`
    fn into_raw(self) -> *mut Self::Raw;
}

#[cfg(feature = "std")]
impl IntoRaw for CString {
    type Raw = c_char;
    #[inline]
    fn into_raw(self) -> *mut c_char {
        CString::into_raw(self)
    }
}

impl<T> IntoRaw for *mut T {
    type Raw = T;
    #[inline]
    fn into_raw(self) -> *mut T {
        self
    }
}

impl<T> IntoRaw for NonNull<T> {
    type Raw = T;
    #[inline]
    fn into_raw(self) -> *mut T {
        self.as_ptr()
    }
}

impl<T> IntoRaw for Option<T>
where
    T: IntoRaw,
{
    type Raw = T::Raw;
    #[inline]
    fn into_raw(self) -> *mut T::Raw {
        match self {
            Some(v) => v.into_raw(),
            None => ptr::null_mut(),
        }
    }
}

/// Trait for types that can be created from a raw pointer
///
/// # Examples
/// ```
/// use ptrplus::{FromRaw, IntoRaw};
///
/// let x: Box<u32> = Box::new(5);
/// let y = x.into_raw();
/// let z: Box<u32> = unsafe { FromRaw::from_raw(y) };
/// assert_eq!(*z, 5);
///
/// ```
///
pub trait FromRaw<T> {
    /// Create `Self` from a raw pointer
    ///
    /// After calling this method the raw pointer
    /// is owned by the resulting object. This
    /// means that the resulting object should
    /// clean up any resources associated with
    /// the pointer (such as memory).
    ///
    /// # Safety
    ///
    /// `raw` must be a pointer that is compatible with
    /// the resulting type. For example, if `Self` is
    /// `Box<T>`, then `raw` must be a pointer to memory allocated
    /// as a Box. The exact requirements depend on the implementation.
    ///
    /// Generally, the `raw` pointer must be the result of a previous
    /// call to `into_raw` on the corresponding type. This the case for
    /// types such as `Box`, `Rc`, and `Arc`.
    ///
    /// Additionally, this function takes ownership of the pointer. If
    /// `raw` or an alias thereof is used after calling this function
    /// it can potentially result in double-free, data races, or other
    /// undefined behavior.
    unsafe fn from_raw(raw: *mut T) -> Self;
}

#[cfg(feature = "std")]
impl FromRaw<c_char> for CString {
    #[inline]
    unsafe fn from_raw(raw: *mut c_char) -> CString {
        CString::from_raw(raw)
    }
}

/// This implementation is always safe
impl<T> FromRaw<T> for *mut T {
    #[inline]
    unsafe fn from_raw(raw: *mut T) -> *mut T {
        raw
    }
}

/// This implementation is always safe
impl<T> FromRaw<T> for *const T {
    #[inline]
    unsafe fn from_raw(raw: *mut T) -> *const T {
        raw
    }
}

/// ## Safety
/// The input pointer must be non-null.
///
/// `Option<NonNull<T>>::from_raw` can be used if the pointer may be null.
impl<T> FromRaw<T> for NonNull<T> {
    #[inline]
    unsafe fn from_raw(raw: *mut T) -> NonNull<T> {
        NonNull::new_unchecked(raw)
    }
}

/// ## Safety
/// The input pointer must either be null (resulting in `None`), or be safe
/// to convert into the inner pointer type.
impl<T, U> FromRaw<U> for Option<T>
where
    T: FromRaw<U>,
{
    unsafe fn from_raw(raw: *mut U) -> Option<T> {
        if raw.is_null() {
            None
        } else {
            Some(T::from_raw(raw))
        }
    }
}

#[cfg(feature = "alloc")]
owned_ptr_wrapper!(Box);
#[cfg(feature = "alloc")]
owned_ptr_wrapper!(Rc);
#[cfg(feature = "alloc")]
owned_ptr_wrapper!(Arc);
<|endoftext|>


<commit_before>use std::iter::repeat;

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

    #[test]
    fn zeros_creates_zero_filled_vector_of_correct_size_and_length() {
        assert_eq!(zeros(0), Vec::new());
        assert_eq!(zeros(1), vec![0]);
        assert_eq!(zeros(10), vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    }

    #[test]
    fn index_test_1() {
        let array1 = QArray {
            dimensions: vec![1, 1, 1],
            data: vec![1]
        };

        assert_eq!(index(&array1, vec![0, 0, 0]), 0);
    }

    #[test]
    fn index_test_2() {
        let array2 = QArray {
            dimensions: vec![2, 2, 1],
            data: vec![1, 2, 3, 4]
        };

        assert_eq!(index(&array2, vec![1, 0, 0]), 1);
        assert_eq!(index(&array2, vec![0, 1, 0]), 2);
    }

    #[test]
    fn index_test_3() {
        let array3 = QArray {
            dimensions: vec![2, 4, 3],
            data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
                       11, 12, 13, 14, 15, 16, 17,
                       18, 19, 20, 21, 22, 23, 24]
        };

        assert_eq!(index(&array3, vec![1, 2, 1]), 13);
        assert_eq!(index(&array3, vec![0, 2, 2]), 20);
    }
}

/// Return a new vector of the given length, filled with zeroes.
/// TODO: make this work for N dimensional vectors
///
/// ```
/// use quota::zeros;
/// assert_eq!(zeros(4), vec![0, 0, 0, 0])
/// ```
pub fn zeros(n: usize) -> Vec<isize> {
    repeat(0).take(n).collect::<Vec<isize>>()
}

pub struct QArray {
    dimensions: Vec<usize>,
    data: Vec<isize>
}

pub fn index(array: &QArray, coordinates: Vec<usize>) -> usize {
    coordinates.into_iter().enumerate().map(|(idx, coord)| {
        array.dimensions.iter().take(idx).fold(coord, |acc, x| acc * x )
    }).fold(0, |acc, x| acc + x)
}
<commit_msg>Add the ability to construct QArrays from nested Vecs<commit_after>use std::iter::repeat;

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

    #[test]
    fn zeros_creates_zero_filled_vector_of_correct_size_and_length() {
        let a1 = zeros(vec![0]);
        assert_eq!(a1.dimensions, vec![0]);
        assert_eq!(a1.data, vec![]);

        let a2 = zeros(vec![1]);
        assert_eq!(a2.dimensions, vec![1]);
        assert_eq!(a2.data, vec![0]);

        let a3 = zeros(vec![10]);
        assert_eq!(a3.dimensions, vec![10]);
        assert_eq!(a3.data, vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

        let a4 = zeros(vec![1, 2, 3]);
        assert_eq!(a4.dimensions, vec![1, 2, 3]);
        assert_eq!(a4.data, vec![0, 0, 0, 0, 0, 0]);
    }

    #[test]
    fn index_test_1() {
        let array1 = QArray {
            dimensions: vec![1, 1, 1],
            data: vec![1],
        };

        assert_eq!(index(&array1, vec![0, 0, 0]), 0);
    }

    #[test]
    fn index_test_2() {
        let array2 = QArray {
            dimensions: vec![2, 2, 1],
            data: vec![1, 2, 3, 4],
        };

        assert_eq!(index(&array2, vec![1, 0, 0]), 1);
        assert_eq!(index(&array2, vec![0, 1, 0]), 2);
    }

    #[test]
    fn index_test_3() {
        let array3 = QArray {
            dimensions: vec![2, 4, 3],
            data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
                       22, 23, 24],
        };

        assert_eq!(index(&array3, vec![1, 2, 1]), 13);
        assert_eq!(index(&array3, vec![0, 2, 2]), 20);
    }

    #[test]
    fn test_1d_vec_to_qarray() {
        let v = vec![1, 2, 3];
        let array = v.to_qarray();

        assert_eq!(array.dimensions, vec![3]);
        assert_eq!(array.data, vec![1, 2, 3]);
    }

    #[test]
    fn test_2d_vec_to_qarray() {
        let v1 = vec![vec![1, 2], vec![3, 4]];
        let array1 = v1.to_qarray();

        assert_eq!(array1.dimensions, vec![2, 2]);
        assert_eq!(array1.data, vec![1, 2, 3, 4]);

        let v2 = vec![vec![1, 2, 3], vec![4, 5, 6]];
        let array2 = v2.to_qarray();

        assert_eq!(array2.dimensions, vec![2, 3]);
        assert_eq!(array2.data, vec![1, 2, 3, 4, 5, 6]);
    }

    #[test]
    fn test_3d_vec_to_qarray() {
        let v = vec![vec![vec![1, 2], vec![3, 4]], vec![vec![5, 6], vec![7, 8]]];
        let array = v.to_qarray();

        assert_eq!(array.dimensions, vec![2, 2, 2]);
        assert_eq!(array.data, vec![1, 2, 3, 4, 5, 6, 7, 8]);
    }
}

/// Return a new QArray of the given dimensions, filled with zeroes.
///
/// ```
/// use quota::zeros;
/// let qarray = zeros(vec![1, 2, 3]);
/// assert_eq!(qarray.dimensions, vec![1, 2, 3]);
/// assert_eq!(qarray.data, vec![0, 0, 0, 0, 0, 0])
/// ```
pub fn zeros(dimensions: Vec<usize>) -> QArray {
    let data = repeat(0).take(dimensions.iter().sum()).collect::<Vec<isize>>();
    QArray { dimensions, data }
}

#[derive(Debug)]
pub struct QArray {
    pub dimensions: Vec<usize>,
    pub data: Vec<isize>,
}

pub fn index(array: &QArray, coordinates: Vec<usize>) -> usize {
    coordinates
        .into_iter()
        .enumerate()
        .map(|(idx, coord)| {
                 array
                     .dimensions
                     .iter()
                     .take(idx)
                     .fold(coord, |acc, x| acc * x)
             })
        .fold(0, |acc, x| acc + x)
}

trait ToQArray {
    fn to_qarray(&self) -> QArray;
}

impl ToQArray for Vec<isize> {
    fn to_qarray(&self) -> QArray {
        QArray {
            dimensions: vec![self.len()],
            data: self.clone(),
        }
    }
}

impl ToQArray for Vec<Vec<isize>> {
    fn to_qarray(&self) -> QArray {
        let l1 = self.len();
        let l2 = match self.first() {
            Some(fst) => fst.len(),
            None => 0,
        };
        let dimensions = vec![l1, l2];

        let mut data = Vec::new();

        for row in self {
            for e in row {
                data.push(*e);
            }
        }

        QArray { dimensions, data }
    }
}

impl ToQArray for Vec<Vec<Vec<isize>>> {
    fn to_qarray(&self) -> QArray {
        let l1 = self.len();
        let (l2, l3) = match self.first() {
            Some(fst) => match fst.first() {
                Some(ffst) => (fst.len(), ffst.len()),
                None => (fst.len(), 0)
            },
            None => (0, 0)
        };
        let dimensions = vec![l1, l2, l3];

        let mut data = Vec::new();

        for row in self {
            for col in row {
                for e in col {
                    data.push(*e)
                }
            }
        };

        QArray { dimensions, data }
    }
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// 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/.

//! # pest. The Elegant Parser
//!
//! pest is a [PEG](https://en.wikipedia.org/wiki/Parsing_expression_grammar) parser built with
//! *simplicity* and *speed* in mind.
//!
//! This crate works in conjunction with the [`pest` crate](https://docs.rs/pest) by
//! deriving a grammar implementation based on a provided grammar.
//!
//! ## `.pest` files
//!
//! Grammar definitions reside in custom `.pest` files located in the `src` directory. Their path is
//! relative to `src` and is specified between the `derive` attribute and empty `struct` that
//! `Parser` will be derived on.
//!
//! Because of a limitation in procedural macros, there is no way for Cargo to know that a module
//! needs to be recompiled based on the file that the procedural macro is opening. This leads to the
//! case where modifying a `.pest` file without touching the file where the `derive` is does not
//! recompile it if it already has a working binary in the cache. To avoid this issue, the grammar
//! file can be included in a dummy `const` definition while debugging.
//!
//! ```ignore
//! #[cfg(debug_assertions)]
//! const _GRAMMAR: &'static str = include_str!("path/to/my_grammar.pest"); // relative to this file
//!
//! #[derive(Parser)]
//! #[grammar = "path/to/my_grammar.pest"] // relative to src
//! struct MyParser;
//! ```
//!
//! ## Grammar
//!
//! A grammar is a series of rules separated by whitespace, possibly containing comments.
//!
//! ### Comments
//!
//! Comments start with `//` and end at the end of the line.
//!
//! ```ignore
//! // a comment
//! ```
//!
//! ### Rules
//!
//! Rules have the following form:
//!
//! ```ignore
//! name = optional_modifier { expression }
//! ```
//!
//! The name of the rule is formed from alphanumeric characters or `_` with the condition that the
//! first character is not a digit and is used to create token pairs. When the rule starts being
//! parsed, the starting part of the token is being produced, with the ending part being produced
//! when the rule finishes parsing.
//!
//! The following token pair notation `a(b(), c())` denotes the tokens: start `a`, start `b`, end
//! `b`, start `c`, end `c`, end `a`.
//!
//! #### Modifiers
//!
//! Modifiers are optional and can be one of `_`, `@`, `$`, or `!`. These modifiers change the
//! behavior of the rules.
//!
//! 1. Silent (`_`)
//!
//!     Silent rules do not create token pairs during parsing, nor are they error-reported.
//!
//!     ```ignore
//!     a = _{ "a" }
//!     b =  { a ~ "b" }
//!     ```
//!
//!     Parsing `"ab"` produces the token pair `b()`.
//!
//! 2. Atomic (`@`)
//!
//!     Atomic rules do not accept whitespace or comments within their expressions and have a
//!     cascading effect on any rule they call. I.e. rules that are not atomic but are called by atomic
//!     rules behave atomically.
//!
//!     Any rules called by atomic rules do not generate token pairs.
//!
//!     ```ignore
//!     a =  { "a" }
//!     b = @{ a ~ "b" }
//!
//!     whitespace = _{ " " }
//!     ```
//!
//!     Parsing `"ab"` produces the token pair `b()`, while `"a   b"` produces an error.
//!
//! 3. Compound-atomic (`$`)
//!
//!     Compound-atomic are identical to atomic rules with the exception that rules called by them are
//!     not forbidden from generating token pairs.
//!
//!     ```ignore
//!     a =  { "a" }
//!     b = ${ a ~ "b" }
//!
//!     whitespace = _{ " " }
//!     ```
//!
//!     Parsing `"ab"` produces the token pairs `b(a())`, while `"a   b"` produces an error.
//!
//! 4. Non-atomic (`!`)
//!
//!     Non-atomic are identical to normal rules with the exception that they stop the cascading effect
//!     of atomic and compound-atomic rules.
//!
//!     ```ignore
//!     a =  { "a" }
//!     b = !{ a ~ "b" }
//!     c = @{ b }
//!
//!     whitespace = _{ " " }
//!     ```
//!
//!     Parsing both `"ab"` and `"a   b"` produce the token pairs `c(a())`.
//!
//! #### Expressions
//!
//! Expressions can be either terminals or non-terminals.
//!
//! 1. Terminals
//!
//!     | Terminal   | Usage                                                          |
//!     |------------|----------------------------------------------------------------|
//!     | `"a"`      | matches the exact string `"a"`                                 |
//!     | `^"a"`     | matches the exact string `"a"` case insensitively (ASCII only) |
//!     | `'a'..'z'` | matches one character between `'a'` and `'z'`                  |
//!     | `a`        | matches rule `a`                                               |
//!
//! Strings and characters follow
//! [Rust's escape mechanisms](https://doc.rust-lang.org/reference/tokens.html#byte-escapes), while
//! identifiers can contain alpha-numeric characters and underscores (`_`), as long as they do not
//! start with a digit.
//!
//! 2. Non-terminals
//!
//!     | Non-terminal | Usage                                                      |
//!     |--------------|------------------------------------------------------------|
//!     | `(e)`        | matches `e`                                                |
//!     | `e1 ~ e2`    | matches the sequence `e1` `e2`                             |
//!     | `e1 | e2`    | matches either `e1` or `e1`                                |
//!     | `e*`         | matches `e` zero or more times                             |
//!     | `e+`         | matches `e` one or more times                              |
//!     | `e?`         | optionally matches `e`                                     |
//!     | `&e`         | matches `e` without making progress                        |
//!     | `!e`         | matches if `e` doesn't match without making progress       |
//!     | `push(e)`    | matches `e` and pushes it's captured string down the stack |
//!
//!     where `e`, `e1`, and `e2` are expressions.
//!
//! ## Special rules
//!
//! Special rules can be called within the grammar. They are:
//!
//! * `whitespace` - gets run between rules and sub-rules
//! * `comment` - gets run between rules and sub-rules
//! * `any` - matches exactly one `char`
//! * `soi` - (start-of-input) matches only when a `Parser` is still at the starting position
//! * `eoi` - (end-of-input) matches only when a `Parser` has reached its end
//! * `pop` - pops a string from the stack and matches it
//! * `peek` - peeks a string from the stack and matches it
//!
//! `whitespace` and `comment` should be defined manually if needed. All other rules cannot be
//! overridden.
//!
//! ## `whitespace` and `comment`
//!
//! When defined, these rules get matched automatically in sequences (`~`) and repetitions
//! (`*`, `+`) between expressions. Atomic rules and those rules called by atomic rules are exempt
//! from this behavior.
//!
//! These rules should be defined so as to match one whitespace character and one comment only since
//! they are run in repetitions.
//!
//! If both `whitespace` and `comment` are defined, this grammar:
//!
//! ```ignore
//! a = { b ~ c }
//! ```
//!
//! is effectively transformed into this one behind the scenes:
//!
//! ```ignore
//! a = { b ~ whitespace* ~ (comment? ~ whitespace+)* ~ c }
//! ```
//!
//! ## `Rule`
//!
//! All rules defined or used in the grammar populate a generated `enum` called `Rule`. This
//! implements `pest`'s `RuleType` and can be used throughout the API.

#![doc(html_root_url = "https://docs.rs/pest_derive")]
#![recursion_limit="256"]

#[macro_use]
extern crate pest;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

use std::env;
use std::path::Path;
use std::rc::Rc;

use pest::inputs::FileInput;
use pest::Parser;
use proc_macro::TokenStream;
use quote::Ident;
use syn::{Attribute, Lit, MetaItem};

mod ast;
mod generator;
mod optimizer;
mod parser;
mod validator;

use parser::{GrammarParser, GrammarRule};

#[proc_macro_derive(Parser, attributes(grammar))]
pub fn derive_parser(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    let (name, path) = parse_derive(source);

    let root = env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
    let path = Path::new(&root).join("src/").join(&path);
    let file_name = match path.file_name() {
        Some(file_name) => file_name,
        None => panic!("grammar attribute should point to a file")
    };

    let input = match FileInput::new(&path) {
        Ok(input) => Rc::new(input),
        Err(error) => panic!("error opening {:?}: {}", file_name, error)
    };
    let pairs = match GrammarParser::parse(GrammarRule::grammar_rules, input) {
        Ok(pairs) => pairs,
        Err(error) => panic!("error parsing {:?}\n\n{}", file_name, error.renamed_rules(|rule| {
            match *rule {
                GrammarRule::grammar_rule => "rule".to_owned(),
                GrammarRule::eoi => "end-of-input".to_owned(),
                GrammarRule::assignment_operator => "`=`".to_owned(),
                GrammarRule::silent_modifier => "`_`".to_owned(),
                GrammarRule::atomic_modifier => "`@`".to_owned(),
                GrammarRule::compound_atomic_modifier => "`$`".to_owned(),
                GrammarRule::non_atomic_modifier => "`!`".to_owned(),
                GrammarRule::opening_brace => "`{`".to_owned(),
                GrammarRule::closing_brace => "`}`".to_owned(),
                GrammarRule::opening_paren => "`(`".to_owned(),
                GrammarRule::positive_predicate_operator => "`&`".to_owned(),
                GrammarRule::negative_predicate_operator => "`!`".to_owned(),
                GrammarRule::sequence_operator => "`&`".to_owned(),
                GrammarRule::choice_operator => "`|`".to_owned(),
                GrammarRule::optional_operator => "`?`".to_owned(),
                GrammarRule::repeat_operator => "`*`".to_owned(),
                GrammarRule::repeat_once_operator => "`+`".to_owned(),
                GrammarRule::closing_paren => "`)`".to_owned(),
                GrammarRule::quote => "`\"`".to_owned(),
                GrammarRule::insensitive_string => "`^`".to_owned(),
                GrammarRule::range_operator => "`..`".to_owned(),
                GrammarRule::single_quote => "`'`".to_owned(),
                other_rule => format!("{:?}", other_rule)
            }
        }))
    };

    let (ast, defaults) = parser::consume_rules(pairs);
    let optimized = optimizer::optimize(ast);
    let generated = generator::generate(name, optimized, defaults);

    generated.as_ref().parse().unwrap()
}

fn parse_derive(source: String) -> (Ident, String) {
    let ast = syn::parse_macro_input(&source).unwrap();
    let name = Ident::new(ast.ident.as_ref());

    let grammar: Vec<_> = ast.attrs.iter().filter(|attr| {
        match attr.value {
            MetaItem::NameValue(ref ident, _) => format!("{}", ident) == "grammar",
            _ => false
        }
    }).collect();

    let filename = match grammar.len() {
        0 => panic!("a grammar file needs to be provided with the #[grammar(\"...\")] attribute"),
        1 => get_filename(grammar[0]),
        _ => panic!("only 1 grammar file can be provided")
    };

    (name, filename)
}

fn get_filename(attr: &Attribute) -> String {
    if let MetaItem::NameValue(_, ref lit) = attr.value {
        if let &Lit::Str(ref string, _) = lit {
            string.clone()
        } else {
            panic!("grammar attribute must be a string")
        }
    } else {
        unreachable!();
    }
}

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

    #[test]
    fn derive_ok() {
        let (_, filename) = parse_derive("
            #[other_attr]
            #[grammar = \"myfile.pest\"]
            pub struct MyParser<'a, T>;
        ".to_owned());

        assert_eq!(filename, "myfile.pest");
    }

    #[test]
    #[should_panic(expected = "only 1 grammar file can be provided")]
    fn derive_multiple_grammars() {
        parse_derive("
            #[other_attr]
            #[grammar = \"myfile1.pest\"]
            #[grammar = \"myfile2.pest\"]
            pub struct MyParser<'a, T>;
        ".to_owned());
    }

    #[test]
    #[should_panic(expected = "grammar attribute must be a string")]
    fn derive_wrong_arg() {
        parse_derive("
            #[other_attr]
            #[grammar = 1]
            pub struct MyParser<'a, T>;
        ".to_owned());
    }
}
<commit_msg>Added push/pop/peek docs. Fixes #137.<commit_after>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// 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/.

//! # pest. The Elegant Parser
//!
//! pest is a [PEG](https://en.wikipedia.org/wiki/Parsing_expression_grammar) parser built with
//! *simplicity* and *speed* in mind.
//!
//! This crate works in conjunction with the [`pest` crate](https://docs.rs/pest) by
//! deriving a grammar implementation based on a provided grammar.
//!
//! ## `.pest` files
//!
//! Grammar definitions reside in custom `.pest` files located in the `src` directory. Their path is
//! relative to `src` and is specified between the `derive` attribute and empty `struct` that
//! `Parser` will be derived on.
//!
//! Because of a limitation in procedural macros, there is no way for Cargo to know that a module
//! needs to be recompiled based on the file that the procedural macro is opening. This leads to the
//! case where modifying a `.pest` file without touching the file where the `derive` is does not
//! recompile it if it already has a working binary in the cache. To avoid this issue, the grammar
//! file can be included in a dummy `const` definition while debugging.
//!
//! ```ignore
//! #[cfg(debug_assertions)]
//! const _GRAMMAR: &'static str = include_str!("path/to/my_grammar.pest"); // relative to this file
//!
//! #[derive(Parser)]
//! #[grammar = "path/to/my_grammar.pest"] // relative to src
//! struct MyParser;
//! ```
//!
//! ## Grammar
//!
//! A grammar is a series of rules separated by whitespace, possibly containing comments.
//!
//! ### Comments
//!
//! Comments start with `//` and end at the end of the line.
//!
//! ```ignore
//! // a comment
//! ```
//!
//! ### Rules
//!
//! Rules have the following form:
//!
//! ```ignore
//! name = optional_modifier { expression }
//! ```
//!
//! The name of the rule is formed from alphanumeric characters or `_` with the condition that the
//! first character is not a digit and is used to create token pairs. When the rule starts being
//! parsed, the starting part of the token is being produced, with the ending part being produced
//! when the rule finishes parsing.
//!
//! The following token pair notation `a(b(), c())` denotes the tokens: start `a`, start `b`, end
//! `b`, start `c`, end `c`, end `a`.
//!
//! #### Modifiers
//!
//! Modifiers are optional and can be one of `_`, `@`, `$`, or `!`. These modifiers change the
//! behavior of the rules.
//!
//! 1. Silent (`_`)
//!
//!     Silent rules do not create token pairs during parsing, nor are they error-reported.
//!
//!     ```ignore
//!     a = _{ "a" }
//!     b =  { a ~ "b" }
//!     ```
//!
//!     Parsing `"ab"` produces the token pair `b()`.
//!
//! 2. Atomic (`@`)
//!
//!     Atomic rules do not accept whitespace or comments within their expressions and have a
//!     cascading effect on any rule they call. I.e. rules that are not atomic but are called by atomic
//!     rules behave atomically.
//!
//!     Any rules called by atomic rules do not generate token pairs.
//!
//!     ```ignore
//!     a =  { "a" }
//!     b = @{ a ~ "b" }
//!
//!     whitespace = _{ " " }
//!     ```
//!
//!     Parsing `"ab"` produces the token pair `b()`, while `"a   b"` produces an error.
//!
//! 3. Compound-atomic (`$`)
//!
//!     Compound-atomic are identical to atomic rules with the exception that rules called by them are
//!     not forbidden from generating token pairs.
//!
//!     ```ignore
//!     a =  { "a" }
//!     b = ${ a ~ "b" }
//!
//!     whitespace = _{ " " }
//!     ```
//!
//!     Parsing `"ab"` produces the token pairs `b(a())`, while `"a   b"` produces an error.
//!
//! 4. Non-atomic (`!`)
//!
//!     Non-atomic are identical to normal rules with the exception that they stop the cascading effect
//!     of atomic and compound-atomic rules.
//!
//!     ```ignore
//!     a =  { "a" }
//!     b = !{ a ~ "b" }
//!     c = @{ b }
//!
//!     whitespace = _{ " " }
//!     ```
//!
//!     Parsing both `"ab"` and `"a   b"` produce the token pairs `c(a())`.
//!
//! #### Expressions
//!
//! Expressions can be either terminals or non-terminals.
//!
//! 1. Terminals
//!
//!     | Terminal   | Usage                                                          |
//!     |------------|----------------------------------------------------------------|
//!     | `"a"`      | matches the exact string `"a"`                                 |
//!     | `^"a"`     | matches the exact string `"a"` case insensitively (ASCII only) |
//!     | `'a'..'z'` | matches one character between `'a'` and `'z'`                  |
//!     | `a`        | matches rule `a`                                               |
//!
//! Strings and characters follow
//! [Rust's escape mechanisms](https://doc.rust-lang.org/reference/tokens.html#byte-escapes), while
//! identifiers can contain alpha-numeric characters and underscores (`_`), as long as they do not
//! start with a digit.
//!
//! 2. Non-terminals
//!
//!     | Non-terminal | Usage                                                      |
//!     |--------------|------------------------------------------------------------|
//!     | `(e)`        | matches `e`                                                |
//!     | `e1 ~ e2`    | matches the sequence `e1` `e2`                             |
//!     | `e1 | e2`    | matches either `e1` or `e1`                                |
//!     | `e*`         | matches `e` zero or more times                             |
//!     | `e+`         | matches `e` one or more times                              |
//!     | `e?`         | optionally matches `e`                                     |
//!     | `&e`         | matches `e` without making progress                        |
//!     | `!e`         | matches if `e` doesn't match without making progress       |
//!     | `push(e)`    | matches `e` and pushes it's captured string down the stack |
//!
//!     where `e`, `e1`, and `e2` are expressions.
//!
//! ## Special rules
//!
//! Special rules can be called within the grammar. They are:
//!
//! * `whitespace` - gets run between rules and sub-rules
//! * `comment` - gets run between rules and sub-rules
//! * `any` - matches exactly one `char`
//! * `soi` - (start-of-input) matches only when a `Parser` is still at the starting position
//! * `eoi` - (end-of-input) matches only when a `Parser` has reached its end
//! * `pop` - pops a string from the stack and matches it
//! * `peek` - peeks a string from the stack and matches it
//!
//! `whitespace` and `comment` should be defined manually if needed. All other rules cannot be
//! overridden.
//!
//! ## `whitespace` and `comment`
//!
//! When defined, these rules get matched automatically in sequences (`~`) and repetitions
//! (`*`, `+`) between expressions. Atomic rules and those rules called by atomic rules are exempt
//! from this behavior.
//!
//! These rules should be defined so as to match one whitespace character and one comment only since
//! they are run in repetitions.
//!
//! If both `whitespace` and `comment` are defined, this grammar:
//!
//! ```ignore
//! a = { b ~ c }
//! ```
//!
//! is effectively transformed into this one behind the scenes:
//!
//! ```ignore
//! a = { b ~ whitespace* ~ (comment? ~ whitespace+)* ~ c }
//! ```
//!
//! ## `push`, `pop`, and `peek`
//!
//! `push(e)` simply pushes the captured string of the expression `e` down a stack. This stack can
//! then later be used to match grammar based on its content with `pop` and `peek`.
//!
//! `peek` always matches the string at the top of stack. So, if the stack contains `["a", "b"]`,
//! the this grammar:
//!
//! ```ignore
//! a = { peek }
//! ```
//!
//! is effectively transformed into at parse time:
//!
//! ```ignore
//! a = { "a" }
//! ```
//!
//! `pop` works the same way with the exception that it pops the string off of the stack if the
//! the match worked. With the stack from above, if `pop` matches `"a"`, the stack will be mutated
//! to `["b"]`.
//!
//! ## `Rule`
//!
//! All rules defined or used in the grammar populate a generated `enum` called `Rule`. This
//! implements `pest`'s `RuleType` and can be used throughout the API.

#![doc(html_root_url = "https://docs.rs/pest_derive")]
#![recursion_limit="256"]

#[macro_use]
extern crate pest;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

use std::env;
use std::path::Path;
use std::rc::Rc;

use pest::inputs::FileInput;
use pest::Parser;
use proc_macro::TokenStream;
use quote::Ident;
use syn::{Attribute, Lit, MetaItem};

mod ast;
mod generator;
mod optimizer;
mod parser;
mod validator;

use parser::{GrammarParser, GrammarRule};

#[proc_macro_derive(Parser, attributes(grammar))]
pub fn derive_parser(input: TokenStream) -> TokenStream {
    let source = input.to_string();

    let (name, path) = parse_derive(source);

    let root = env::var("CARGO_MANIFEST_DIR").unwrap_or(".".into());
    let path = Path::new(&root).join("src/").join(&path);
    let file_name = match path.file_name() {
        Some(file_name) => file_name,
        None => panic!("grammar attribute should point to a file")
    };

    let input = match FileInput::new(&path) {
        Ok(input) => Rc::new(input),
        Err(error) => panic!("error opening {:?}: {}", file_name, error)
    };
    let pairs = match GrammarParser::parse(GrammarRule::grammar_rules, input) {
        Ok(pairs) => pairs,
        Err(error) => panic!("error parsing {:?}\n\n{}", file_name, error.renamed_rules(|rule| {
            match *rule {
                GrammarRule::grammar_rule => "rule".to_owned(),
                GrammarRule::eoi => "end-of-input".to_owned(),
                GrammarRule::assignment_operator => "`=`".to_owned(),
                GrammarRule::silent_modifier => "`_`".to_owned(),
                GrammarRule::atomic_modifier => "`@`".to_owned(),
                GrammarRule::compound_atomic_modifier => "`$`".to_owned(),
                GrammarRule::non_atomic_modifier => "`!`".to_owned(),
                GrammarRule::opening_brace => "`{`".to_owned(),
                GrammarRule::closing_brace => "`}`".to_owned(),
                GrammarRule::opening_paren => "`(`".to_owned(),
                GrammarRule::positive_predicate_operator => "`&`".to_owned(),
                GrammarRule::negative_predicate_operator => "`!`".to_owned(),
                GrammarRule::sequence_operator => "`&`".to_owned(),
                GrammarRule::choice_operator => "`|`".to_owned(),
                GrammarRule::optional_operator => "`?`".to_owned(),
                GrammarRule::repeat_operator => "`*`".to_owned(),
                GrammarRule::repeat_once_operator => "`+`".to_owned(),
                GrammarRule::closing_paren => "`)`".to_owned(),
                GrammarRule::quote => "`\"`".to_owned(),
                GrammarRule::insensitive_string => "`^`".to_owned(),
                GrammarRule::range_operator => "`..`".to_owned(),
                GrammarRule::single_quote => "`'`".to_owned(),
                other_rule => format!("{:?}", other_rule)
            }
        }))
    };

    let (ast, defaults) = parser::consume_rules(pairs);
    let optimized = optimizer::optimize(ast);
    let generated = generator::generate(name, optimized, defaults);

    generated.as_ref().parse().unwrap()
}

fn parse_derive(source: String) -> (Ident, String) {
    let ast = syn::parse_macro_input(&source).unwrap();
    let name = Ident::new(ast.ident.as_ref());

    let grammar: Vec<_> = ast.attrs.iter().filter(|attr| {
        match attr.value {
            MetaItem::NameValue(ref ident, _) => format!("{}", ident) == "grammar",
            _ => false
        }
    }).collect();

    let filename = match grammar.len() {
        0 => panic!("a grammar file needs to be provided with the #[grammar(\"...\")] attribute"),
        1 => get_filename(grammar[0]),
        _ => panic!("only 1 grammar file can be provided")
    };

    (name, filename)
}

fn get_filename(attr: &Attribute) -> String {
    if let MetaItem::NameValue(_, ref lit) = attr.value {
        if let &Lit::Str(ref string, _) = lit {
            string.clone()
        } else {
            panic!("grammar attribute must be a string")
        }
    } else {
        unreachable!();
    }
}

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

    #[test]
    fn derive_ok() {
        let (_, filename) = parse_derive("
            #[other_attr]
            #[grammar = \"myfile.pest\"]
            pub struct MyParser<'a, T>;
        ".to_owned());

        assert_eq!(filename, "myfile.pest");
    }

    #[test]
    #[should_panic(expected = "only 1 grammar file can be provided")]
    fn derive_multiple_grammars() {
        parse_derive("
            #[other_attr]
            #[grammar = \"myfile1.pest\"]
            #[grammar = \"myfile2.pest\"]
            pub struct MyParser<'a, T>;
        ".to_owned());
    }

    #[test]
    #[should_panic(expected = "grammar attribute must be a string")]
    fn derive_wrong_arg() {
        parse_derive("
            #[other_attr]
            #[grammar = 1]
            pub struct MyParser<'a, T>;
        ".to_owned());
    }
}
<|endoftext|>