<commit_before>//! The Liquid templating language for Rust
//!
//! __http://liquidmarkup.org/__
//!
//! ```toml
//! [dependencies]
//! liquid = "0.9"
//! ```
//!
//! ## Example
//! ```rust
//! use liquid::{Renderable, Context, Value};
//!
//! let template = liquid::parse("Liquid! {{num | minus: 2}}", Default::default()).unwrap();
//!
//! let mut context = Context::new();
//! context.set_val("num", Value::Num(4f32));
//!
//! let output = template.render(&mut context);
//! assert_eq!(output.unwrap(), Some("Liquid! 2".to_string()));
//! ```
#![crate_name = "liquid"]
#![doc(html_root_url = "https://cobalt-org.github.io/liquid-rust/")]

// Deny warnings, except in dev mode
#![deny(warnings)]
// #![deny(missing_docs)]
#![cfg_attr(feature="dev", warn(warnings))]

// Allow zero pointers for lazy_static. Otherwise clippy will complain.
#![allow(unknown_lints)]
#![allow(zero_ptr)]

#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate chrono;
extern crate unicode_segmentation;
extern crate itertools;

use std::collections::HashMap;
use lexer::Element;
use tags::{assign_tag, cycle_tag, include_tag, break_tag, continue_tag, comment_block, raw_block,
           for_block, if_block, unless_block, capture_block, case_block};
use std::default::Default;
use std::fs::File;
use std::io::prelude::Read;
use std::path::{PathBuf, Path};
use error::Result;

pub use value::Value;
pub use context::Context;
pub use template::Template;
pub use error::Error;
pub use filters::FilterError;
pub use token::Token;

pub mod lexer;
pub mod parser;

mod token;
mod error;
mod template;
mod output;
mod text;
mod tags;
mod filters;
mod value;
mod variable;
mod context;

/// A trait for creating custom tags. This is a simple type alias for a function.
///
/// This function will be called whenever the parser encounters a tag and returns
/// a new [Renderable](trait.Renderable.html) based on its parameters. The received parameters
/// specify the name of the tag, the argument [Tokens](lexer/enum.Token.html) passed to
/// the tag and the global [`LiquidOptions`](struct.LiquidOptions.html).
///
/// ## Minimal Example
/// ```
/// # use liquid::{Renderable, LiquidOptions, Context, Error};
///
/// struct HelloWorld;
///
/// impl Renderable for HelloWorld {
///     fn render(&self, _context: &mut Context) -> Result<Option<String>, Error>{
///         Ok(Some("Hello World!".to_owned()))
///     }
/// }
///
/// let mut options : LiquidOptions = Default::default();
/// options.tags.insert("hello_world".to_owned(), Box::new(|_tag_name, _arguments, _options| {
///      Ok(Box::new(HelloWorld))
/// }));
///
/// let template = liquid::parse("{{hello_world}}", options).unwrap();
/// let mut data = Context::new();
/// let output = template.render(&mut data);
/// assert_eq!(output.unwrap(), Some("Hello World!".to_owned()));
/// ```
pub type Tag = Fn(&str, &[Token], &LiquidOptions) -> Result<Box<Renderable>>;

/// A trait for creating custom custom block-size tags (`{% if something %}{% endif %}`).
/// This is a simple type alias for a function.
///
/// This function will be called whenever the parser encounters a block and returns
/// a new `Renderable` based on its parameters. The received parameters specify the name
/// of the block, the argument [Tokens](lexer/enum.Token.html) passed to
/// the block, a Vec of all [Elements](lexer/enum.Element.html) inside the block and
/// the global [`LiquidOptions`](struct.LiquidOptions.html).
pub type Block = Fn(&str, &[Token], &[Element], &LiquidOptions) -> Result<Box<Renderable>>;

/// Any object (tag/block) that can be rendered by liquid must implement this trait.
pub trait Renderable: Send + Sync {
    /// Renders the Renderable instance given a Liquid context.
    /// The Result that is returned signals if there was an error rendering,
    /// the Option<String> that is wrapped by the Result will be None if
    /// the render has run successfully but there is no content to render.
    fn render(&self, context: &mut Context) -> Result<Option<String>>;
}

/// Options that `liquid::parse` takes
#[derive(Default)]
pub struct LiquidOptions {
    /// Holds all custom block-size tags
    pub blocks: HashMap<String, Box<Block>>,
    /// Holds all custom tags
    pub tags: HashMap<String, Box<Tag>>,
    /// The path to which paths in include tags should be relative to
    pub file_system: Option<PathBuf>,
}

impl LiquidOptions {
    /// Creates a LiquidOptions instance, pre-seeded with all known
    /// tags and blocks.
    pub fn with_known_blocks() -> LiquidOptions {
        let mut options = LiquidOptions::default();
        options.register_known_blocks();
        options
    }

    /// Registers all known tags and blocks in an existing options
    /// struct
    pub fn register_known_blocks(&mut self) {
        self.register_tag("assign", Box::new(assign_tag));
        self.register_tag("break", Box::new(break_tag));
        self.register_tag("continue", Box::new(continue_tag));
        self.register_tag("cycle", Box::new(cycle_tag));
        self.register_tag("include", Box::new(include_tag));

        self.register_block("raw", Box::new(raw_block));
        self.register_block("if", Box::new(if_block));
        self.register_block("unless", Box::new(unless_block));
        self.register_block("for", Box::new(for_block));
        self.register_block("comment", Box::new(comment_block));
        self.register_block("capture", Box::new(capture_block));
        self.register_block("case", Box::new(case_block));
    }

    /// Inserts a new custom block into the options object
    pub fn register_block(&mut self, name: &str, block: Box<Block>) {
        self.blocks.insert(name.to_owned(), block);
    }

    /// Inserts a new custom tag into the options object
    pub fn register_tag(&mut self, name: &str, tag: Box<Tag>) {
        self.tags.insert(name.to_owned(), tag);
    }
}

/// Parses a liquid template, returning a Template object.
/// # Examples
///
/// ## Minimal Template
///
/// ```
/// use liquid::{Renderable, LiquidOptions, Context};
///
/// let template = liquid::parse("Liquid!", LiquidOptions::default()).unwrap();
/// let mut data = Context::new();
/// let output = template.render(&mut data);
/// assert_eq!(output.unwrap(), Some("Liquid!".to_owned()));
/// ```
///
pub fn parse(text: &str, options: LiquidOptions) -> Result<Template> {
    let mut options = options;
    options.register_known_blocks();

    let tokens = try!(lexer::tokenize(text));
    parser::parse(&tokens, &options).map(Template::new)
}

/// Parse a liquid template from a file, returning a `Result<Template, Error>`.
/// # Examples
///
/// ## Minimal Template
///
/// `template.txt`:
///
/// ```text
/// "Liquid {{data}}"
/// ```
///
/// Your rust code:
///
/// ```rust,no_run
/// use liquid::{Renderable, LiquidOptions, Context, Value};
///
/// let template = liquid::parse_file("path/to/template.txt",
///                                   LiquidOptions::default()).unwrap();
/// let mut data = Context::new();
/// data.set_val("data", Value::Num(4f32));
/// let output = template.render(&mut data);
/// assert_eq!(output.unwrap(), Some("Liquid 4\n".to_string()));
/// ```
///
pub fn parse_file<P: AsRef<Path>>(fp: P, options: LiquidOptions) -> Result<Template> {
    let mut options = options;
    options.register_known_blocks();

    let mut f = try!(File::open(fp));
    let mut buf = String::new();
    try!(f.read_to_string(&mut buf));

    let tokens = try!(lexer::tokenize(&buf));
    parser::parse(&tokens, &options).map(Template::new)
}
<commit_msg>fix(Value): Publicly expose Object and Array<commit_after>//! The Liquid templating language for Rust
//!
//! __http://liquidmarkup.org/__
//!
//! ```toml
//! [dependencies]
//! liquid = "0.9"
//! ```
//!
//! ## Example
//! ```rust
//! use liquid::{Renderable, Context, Value};
//!
//! let template = liquid::parse("Liquid! {{num | minus: 2}}", Default::default()).unwrap();
//!
//! let mut context = Context::new();
//! context.set_val("num", Value::Num(4f32));
//!
//! let output = template.render(&mut context);
//! assert_eq!(output.unwrap(), Some("Liquid! 2".to_string()));
//! ```
#![crate_name = "liquid"]
#![doc(html_root_url = "https://cobalt-org.github.io/liquid-rust/")]

// Deny warnings, except in dev mode
#![deny(warnings)]
// #![deny(missing_docs)]
#![cfg_attr(feature="dev", warn(warnings))]

// Allow zero pointers for lazy_static. Otherwise clippy will complain.
#![allow(unknown_lints)]
#![allow(zero_ptr)]

#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate chrono;
extern crate unicode_segmentation;
extern crate itertools;

use std::collections::HashMap;
use lexer::Element;
use tags::{assign_tag, cycle_tag, include_tag, break_tag, continue_tag, comment_block, raw_block,
           for_block, if_block, unless_block, capture_block, case_block};
use std::default::Default;
use std::fs::File;
use std::io::prelude::Read;
use std::path::{PathBuf, Path};
use error::Result;

pub use value::Value;
pub use value::Object;
pub use value::Array;
pub use context::Context;
pub use template::Template;
pub use error::Error;
pub use filters::FilterError;
pub use token::Token;

pub mod lexer;
pub mod parser;

mod token;
mod error;
mod template;
mod output;
mod text;
mod tags;
mod filters;
mod value;
mod variable;
mod context;

/// A trait for creating custom tags. This is a simple type alias for a function.
///
/// This function will be called whenever the parser encounters a tag and returns
/// a new [Renderable](trait.Renderable.html) based on its parameters. The received parameters
/// specify the name of the tag, the argument [Tokens](lexer/enum.Token.html) passed to
/// the tag and the global [`LiquidOptions`](struct.LiquidOptions.html).
///
/// ## Minimal Example
/// ```
/// # use liquid::{Renderable, LiquidOptions, Context, Error};
///
/// struct HelloWorld;
///
/// impl Renderable for HelloWorld {
///     fn render(&self, _context: &mut Context) -> Result<Option<String>, Error>{
///         Ok(Some("Hello World!".to_owned()))
///     }
/// }
///
/// let mut options : LiquidOptions = Default::default();
/// options.tags.insert("hello_world".to_owned(), Box::new(|_tag_name, _arguments, _options| {
///      Ok(Box::new(HelloWorld))
/// }));
///
/// let template = liquid::parse("{{hello_world}}", options).unwrap();
/// let mut data = Context::new();
/// let output = template.render(&mut data);
/// assert_eq!(output.unwrap(), Some("Hello World!".to_owned()));
/// ```
pub type Tag = Fn(&str, &[Token], &LiquidOptions) -> Result<Box<Renderable>>;

/// A trait for creating custom custom block-size tags (`{% if something %}{% endif %}`).
/// This is a simple type alias for a function.
///
/// This function will be called whenever the parser encounters a block and returns
/// a new `Renderable` based on its parameters. The received parameters specify the name
/// of the block, the argument [Tokens](lexer/enum.Token.html) passed to
/// the block, a Vec of all [Elements](lexer/enum.Element.html) inside the block and
/// the global [`LiquidOptions`](struct.LiquidOptions.html).
pub type Block = Fn(&str, &[Token], &[Element], &LiquidOptions) -> Result<Box<Renderable>>;

/// Any object (tag/block) that can be rendered by liquid must implement this trait.
pub trait Renderable: Send + Sync {
    /// Renders the Renderable instance given a Liquid context.
    /// The Result that is returned signals if there was an error rendering,
    /// the Option<String> that is wrapped by the Result will be None if
    /// the render has run successfully but there is no content to render.
    fn render(&self, context: &mut Context) -> Result<Option<String>>;
}

/// Options that `liquid::parse` takes
#[derive(Default)]
pub struct LiquidOptions {
    /// Holds all custom block-size tags
    pub blocks: HashMap<String, Box<Block>>,
    /// Holds all custom tags
    pub tags: HashMap<String, Box<Tag>>,
    /// The path to which paths in include tags should be relative to
    pub file_system: Option<PathBuf>,
}

impl LiquidOptions {
    /// Creates a LiquidOptions instance, pre-seeded with all known
    /// tags and blocks.
    pub fn with_known_blocks() -> LiquidOptions {
        let mut options = LiquidOptions::default();
        options.register_known_blocks();
        options
    }

    /// Registers all known tags and blocks in an existing options
    /// struct
    pub fn register_known_blocks(&mut self) {
        self.register_tag("assign", Box::new(assign_tag));
        self.register_tag("break", Box::new(break_tag));
        self.register_tag("continue", Box::new(continue_tag));
        self.register_tag("cycle", Box::new(cycle_tag));
        self.register_tag("include", Box::new(include_tag));

        self.register_block("raw", Box::new(raw_block));
        self.register_block("if", Box::new(if_block));
        self.register_block("unless", Box::new(unless_block));
        self.register_block("for", Box::new(for_block));
        self.register_block("comment", Box::new(comment_block));
        self.register_block("capture", Box::new(capture_block));
        self.register_block("case", Box::new(case_block));
    }

    /// Inserts a new custom block into the options object
    pub fn register_block(&mut self, name: &str, block: Box<Block>) {
        self.blocks.insert(name.to_owned(), block);
    }

    /// Inserts a new custom tag into the options object
    pub fn register_tag(&mut self, name: &str, tag: Box<Tag>) {
        self.tags.insert(name.to_owned(), tag);
    }
}

/// Parses a liquid template, returning a Template object.
/// # Examples
///
/// ## Minimal Template
///
/// ```
/// use liquid::{Renderable, LiquidOptions, Context};
///
/// let template = liquid::parse("Liquid!", LiquidOptions::default()).unwrap();
/// let mut data = Context::new();
/// let output = template.render(&mut data);
/// assert_eq!(output.unwrap(), Some("Liquid!".to_owned()));
/// ```
///
pub fn parse(text: &str, options: LiquidOptions) -> Result<Template> {
    let mut options = options;
    options.register_known_blocks();

    let tokens = try!(lexer::tokenize(text));
    parser::parse(&tokens, &options).map(Template::new)
}

/// Parse a liquid template from a file, returning a `Result<Template, Error>`.
/// # Examples
///
/// ## Minimal Template
///
/// `template.txt`:
///
/// ```text
/// "Liquid {{data}}"
/// ```
///
/// Your rust code:
///
/// ```rust,no_run
/// use liquid::{Renderable, LiquidOptions, Context, Value};
///
/// let template = liquid::parse_file("path/to/template.txt",
///                                   LiquidOptions::default()).unwrap();
/// let mut data = Context::new();
/// data.set_val("data", Value::Num(4f32));
/// let output = template.render(&mut data);
/// assert_eq!(output.unwrap(), Some("Liquid 4\n".to_string()));
/// ```
///
pub fn parse_file<P: AsRef<Path>>(fp: P, options: LiquidOptions) -> Result<Template> {
    let mut options = options;
    options.register_known_blocks();

    let mut f = try!(File::open(fp));
    let mut buf = String::new();
    try!(f.read_to_string(&mut buf));

    let tokens = try!(lexer::tokenize(&buf));
    parser::parse(&tokens, &options).map(Template::new)
}
<|endoftext|>


<commit_before>#![feature(core, io)]
#![cfg_attr(test, deny(warnings))]

extern crate "rustc-serialize" as rustc_serialize;

extern crate conduit;
extern crate "conduit-middleware" as middleware;
extern crate "conduit-utils" as utils;

use std::error::Error;
use std::io::prelude::*;
use std::marker;
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Json};

use conduit::Request;
use utils::RequestDelegator;
use middleware::Middleware;

pub struct BodyReader<T> {
    _marker: marker::PhantomData<fn() -> T>,
}

impl<T: Decodable + 'static> BodyReader<T> {
    pub fn new() -> BodyReader<T> {
        BodyReader { _marker: marker::PhantomData }
    }
}

impl<T: Decodable + 'static> Middleware for BodyReader<T> {
    fn before(&self, req: &mut Request) -> Result<(), Box<Error+Send>> {
        let json: T = try!(decode::<T>(req.body()));

        req.mut_extensions().insert(json);
        Ok(())
    }
}

fn decode<T: Decodable>(reader: &mut Read) -> Result<T, Box<Error+Send>> {
    let j = try!(Json::from_reader(reader).map_err(|e| Box::new(e) as Box<Error+Send>));
    let mut decoder = json::Decoder::new(j);
    Decodable::decode(&mut decoder).map_err(|e| Box::new(e) as Box<Error+Send>)
}

pub fn json_params<'a, T: Decodable + 'static>(req: &'a Request) -> Option<&'a T> {
    req.extensions().find::<T>()
}

#[cfg(test)]
mod tests {
    extern crate "conduit-test" as conduit_test;

    use {json_params, BodyReader};

    use std::collections::HashMap;
    use std::io::{self, Cursor};
    use rustc_serialize::json;

    use conduit::{Request, Response, Handler, Method};
    use middleware::MiddlewareBuilder;

    #[derive(PartialEq, RustcDecodable, RustcEncodable, Debug)]
    struct Person {
        name: String,
        location: String
    }

    fn handler(req: &mut Request) -> io::Result<Response> {
        let person = json_params::<Person>(req);
        let out = person.map(|p| json::encode(p).unwrap()).expect("No JSON");

        Ok(Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: Box::new(Cursor::new(out.into_bytes()))
        })
    }

    #[test]
    fn test_body_params() {
        let mut req = conduit_test::MockRequest::new(Method::Get, "/");
        req.with_body(br#"{ "name": "Alex Crichton", "location": "San Francisco" }"#);

        let mut middleware = MiddlewareBuilder::new(handler);
        middleware.add(BodyReader::<Person>::new());

        let mut res = middleware.call(&mut req).ok().expect("No response");
        let person = super::decode::<Person>(&mut *res.body).ok().expect("No JSON response");
        assert_eq!(person, Person {
            name: "Alex Crichton".to_string(),
            location: "San Francisco".to_string()
        });
    }
}
<commit_msg>Update to rust master<commit_after>#![feature(core)]
#![cfg_attr(test, deny(warnings))]

extern crate "rustc-serialize" as rustc_serialize;

extern crate conduit;
extern crate "conduit-middleware" as middleware;
extern crate "conduit-utils" as utils;

use std::error::Error;
use std::io::prelude::*;
use std::marker;
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Json};

use conduit::Request;
use utils::RequestDelegator;
use middleware::Middleware;

pub struct BodyReader<T> {
    _marker: marker::PhantomData<fn() -> T>,
}

impl<T: Decodable + 'static> BodyReader<T> {
    pub fn new() -> BodyReader<T> {
        BodyReader { _marker: marker::PhantomData }
    }
}

impl<T: Decodable + 'static> Middleware for BodyReader<T> {
    fn before(&self, req: &mut Request) -> Result<(), Box<Error+Send>> {
        let json: T = try!(decode::<T>(req.body()));

        req.mut_extensions().insert(json);
        Ok(())
    }
}

fn decode<T: Decodable>(reader: &mut Read) -> Result<T, Box<Error+Send>> {
    let j = try!(Json::from_reader(reader).map_err(|e| Box::new(e) as Box<Error+Send>));
    let mut decoder = json::Decoder::new(j);
    Decodable::decode(&mut decoder).map_err(|e| Box::new(e) as Box<Error+Send>)
}

pub fn json_params<'a, T: Decodable + 'static>(req: &'a Request) -> Option<&'a T> {
    req.extensions().find::<T>()
}

#[cfg(test)]
mod tests {
    extern crate "conduit-test" as conduit_test;

    use {json_params, BodyReader};

    use std::collections::HashMap;
    use std::io::{self, Cursor};
    use rustc_serialize::json;

    use conduit::{Request, Response, Handler, Method};
    use middleware::MiddlewareBuilder;

    #[derive(PartialEq, RustcDecodable, RustcEncodable, Debug)]
    struct Person {
        name: String,
        location: String
    }

    fn handler(req: &mut Request) -> io::Result<Response> {
        let person = json_params::<Person>(req);
        let out = person.map(|p| json::encode(p).unwrap()).expect("No JSON");

        Ok(Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: Box::new(Cursor::new(out.into_bytes()))
        })
    }

    #[test]
    fn test_body_params() {
        let mut req = conduit_test::MockRequest::new(Method::Get, "/");
        req.with_body(br#"{ "name": "Alex Crichton", "location": "San Francisco" }"#);

        let mut middleware = MiddlewareBuilder::new(handler);
        middleware.add(BodyReader::<Person>::new());

        let mut res = middleware.call(&mut req).ok().expect("No response");
        let person = super::decode::<Person>(&mut *res.body).ok().expect("No JSON response");
        assert_eq!(person, Person {
            name: "Alex Crichton".to_string(),
            location: "San Francisco".to_string()
        });
    }
}
<|endoftext|>


<commit_before>#![feature(phase, plugin_registrar)]

extern crate rustc;
extern crate syntax;

#[phase(plugin, link)] extern crate log;

use rustc::plugin::Registry;
use syntax::ast::{Attribute, Item, MetaItem};
use syntax::ext::base::{ExtCtxt, ItemModifier, SyntaxExtension};
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ptr::P;

use std::error::FromError;
use std::io::{mod, IoResult};

enum AttrError {
    IoError(io::IoError),
    Path,
    Syntax
}

impl FromError<io::IoError> for AttrError {
    fn from_error(e: io::IoError) -> AttrError {
        AttrError::IoError(e)
    }
}

fn extract_doc_path(meta: &MetaItem) -> Result<Path, AttrError> {
    use syntax::ast::MetaItem_::MetaNameValue as NameValue;
    use syntax::ast::Lit_::LitStr;

    match meta.node {
        NameValue(_, ref lit) => {
            match lit.node {
                LitStr(ref path, _) => {
                    Path::new_opt(path).ok_or(AttrError::Path)
                },
                _ => Err(AttrError::Syntax)
            }
        },
        _ => Err(AttrError::Syntax)
    }
}

fn slurp_doc_file(cx: &mut ExtCtxt, path: Path, sp: Span) -> IoResult<String> {
    use std::io::File;

    let source_path = Path::new(cx.codemap().span_to_filename(sp));
    let path = source_path.dir_path().join(path);
    let mut file = try!(File::open(&path));
    file.read_to_string()
}

fn mk_doc_attr(doc_string: String) -> Attribute {
    use syntax::attr;
    let meta_item = attr::mk_name_value_item_str(
        token::intern_and_get_ident("doc"),
        token::intern_and_get_ident(doc_string.as_slice())
    );
    attr::mk_attr_inner(attr::mk_attr_id(), meta_item)
}

fn expand_attr_(cx: &mut ExtCtxt, sp: Span, meta: &MetaItem,
                attrs: &mut Vec<Attribute>) -> Result<(), AttrError> {

    let path = try!(extract_doc_path(meta));
    let doc_string = try!(slurp_doc_file(cx, path, sp));

    let attr = mk_doc_attr(doc_string);
    attrs.push(attr);

    Ok(())
}

fn expand_attr(cx: &mut ExtCtxt, sp: Span, meta: &MetaItem,
               item: P<Item>) -> P<Item> {
    use syntax::ptr::P;

    let mut item = item.and_then(|x| x); // unwrap the item
    if let Err(e) = expand_attr_(cx, sp, meta, &mut item.attrs) {
        match e {
            AttrError::Path => {
                cx.span_err(sp, "Invalid path in doc_file attribute");
            }
            AttrError::Syntax => {
                cx.span_err(sp, "Invalid use of doc_file attribute, use like: \
                            `#[doc_file = \"foo.markdown\"]`");
            }
            AttrError::IoError(e) => {
                let msg = format!("IO error reading doc_file attribute: {}", e);
                cx.span_err(sp, msg.as_slice());
            }
        };
    };
    P(item)
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    let modifier = box expand_attr as Box<ItemModifier>;
    reg.register_syntax_extension(token::intern("doc_file"),
                                  SyntaxExtension::Modifier(modifier));
}
<commit_msg>use P::map instead of P::and_then<commit_after>#![feature(phase, plugin_registrar)]

extern crate rustc;
extern crate syntax;

#[phase(plugin, link)] extern crate log;

use rustc::plugin::Registry;
use syntax::ast::{Attribute, Item, MetaItem};
use syntax::ext::base::{ExtCtxt, ItemModifier, SyntaxExtension};
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::ptr::P;

use std::error::FromError;
use std::io::{mod, IoResult};

enum AttrError {
    IoError(io::IoError),
    Path,
    Syntax
}

impl FromError<io::IoError> for AttrError {
    fn from_error(e: io::IoError) -> AttrError {
        AttrError::IoError(e)
    }
}

fn extract_doc_path(meta: &MetaItem) -> Result<Path, AttrError> {
    use syntax::ast::MetaItem_::MetaNameValue as NameValue;
    use syntax::ast::Lit_::LitStr;

    match meta.node {
        NameValue(_, ref lit) => {
            match lit.node {
                LitStr(ref path, _) => {
                    Path::new_opt(path).ok_or(AttrError::Path)
                },
                _ => Err(AttrError::Syntax)
            }
        },
        _ => Err(AttrError::Syntax)
    }
}

fn slurp_doc_file(cx: &mut ExtCtxt, path: Path, sp: Span) -> IoResult<String> {
    use std::io::File;

    let source_path = Path::new(cx.codemap().span_to_filename(sp));
    let path = source_path.dir_path().join(path);
    let mut file = try!(File::open(&path));
    file.read_to_string()
}

fn mk_doc_attr(doc_string: String) -> Attribute {
    use syntax::attr;
    let meta_item = attr::mk_name_value_item_str(
        token::intern_and_get_ident("doc"),
        token::intern_and_get_ident(doc_string.as_slice())
    );
    attr::mk_attr_inner(attr::mk_attr_id(), meta_item)
}

fn expand_attr_(cx: &mut ExtCtxt, sp: Span, meta: &MetaItem,
                attrs: &mut Vec<Attribute>) -> Result<(), AttrError> {

    let path = try!(extract_doc_path(meta));
    let doc_string = try!(slurp_doc_file(cx, path, sp));

    let attr = mk_doc_attr(doc_string);
    attrs.push(attr);

    Ok(())
}

fn expand_attr(cx: &mut ExtCtxt, sp: Span, meta: &MetaItem,
               item: P<Item>) -> P<Item> {
    item.map(|mut item| {
        if let Err(e) = expand_attr_(cx, sp, meta, &mut item.attrs) {
            match e {
                AttrError::Path => {
                    cx.span_err(sp, "Invalid path in doc_file attribute");
                }
                AttrError::Syntax => {
                    cx.span_err(sp, "Invalid use of doc_file attribute, use like: \
                                `#[doc_file = \"foo.markdown\"]`");
                }
                AttrError::IoError(e) => {
                    let msg = format!("IO error reading doc_file attribute: {}", e);
                    cx.span_err(sp, msg.as_slice());
                }
            };
        };
        item
    })
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    let modifier = box expand_attr as Box<ItemModifier>;
    reg.register_syntax_extension(token::intern("doc_file"),
                                  SyntaxExtension::Modifier(modifier));
}
<|endoftext|>


<commit_before>#![feature(associated_consts)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(question_mark)]
//#![feature(plugin)]

//#![plugin(clippy)]

extern crate jsrs_common;

#[macro_use] extern crate matches;

pub mod alloc;
mod scope;
mod test_utils;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::rc::Rc;

use jsrs_common::ast::Exp;
use jsrs_common::backend::Backend;
use jsrs_common::types::js_var::{JsPtrEnum, JsVar};
use jsrs_common::types::binding::{Binding, UniqueBinding};

use alloc::AllocBox;
use jsrs_common::gc_error::{GcError, Result};
use scope::{LookupError, Scope, ScopeTag};

pub struct ScopeManager {
    scopes: Vec<Scope>,
    closures: HashMap<UniqueBinding, Scope>,
    alloc_box: Rc<RefCell<AllocBox>>,
}

impl ScopeManager {
    fn new(alloc_box: Rc<RefCell<AllocBox>>) -> ScopeManager {
        ScopeManager {
            scopes: vec![Scope::new(ScopeTag::Call, &alloc_box)],
            closures: HashMap::new(),
            alloc_box: alloc_box,
        }
    }

    #[allow(dead_code)]
    #[inline]
    fn curr_scope(&self) -> &Scope {
        self.scopes.last().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn curr_scope_mut(&mut self) -> &mut Scope {
        self.scopes.last_mut().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn global_scope(&self) -> &Scope {
        self.scopes.get(0).expect("Tried to access global scope, but none existed")
    }

    #[inline]
    fn global_scope_mut(&mut self) -> &mut Scope {
        self.scopes.get_mut(0).expect("Tried to access global scope, but none existed")
    }

    pub fn push_closure_scope(&mut self, closure: &UniqueBinding) -> Result<()> {
        let closure_scope = self.closures.remove(closure).ok_or(GcError::Scope)?;
        self.scopes.push(closure_scope);
        Ok(())
    }

    pub fn push_scope(&mut self, exp: &Exp) {
        let tag = match *exp {
            Exp::Call(..) => ScopeTag::Call,
            _ => ScopeTag::Block,
        };
        self.scopes.push(Scope::new(tag, &self.alloc_box));
    }

    pub fn pop_scope(&mut self, returning_closure: Option<UniqueBinding>, gc_yield: bool) -> Result<()> {
        if let Some(mut scope) = self.scopes.pop() {
            // Clean up the dying scope's stack and take ownership of its heap-allocated data for
            // later collection
            if self.scopes.is_empty() {
                // The global scope was popped and the program is ending.
                scope.trigger_gc();
                return Err(GcError::Scope);
            }
            if let Some(unique) = returning_closure {
                let mut closure_scope = Scope::new(ScopeTag::Closure(unique.clone()), &self.alloc_box);
                scope.transfer_stack(&mut closure_scope, true)?;
                self.closures.insert(unique, closure_scope);
            } else {
                if !matches!(scope.tag, ScopeTag::Closure(_)) {
                    scope.transfer_stack(self.curr_scope_mut(), false)?
                }
            }
            // Potentially trigger the garbage collector
            if gc_yield {
                self.curr_scope_mut().trigger_gc();
            }
            if let ScopeTag::Closure(unique) = scope.tag.clone() {
                self.closures.insert(unique.clone(), scope);
            }
            Ok(())
        } else {
            Err(GcError::Scope)
        }
    }

    pub fn rename_closure(&mut self, old: &UniqueBinding, new: &UniqueBinding) -> bool {
        if self.closures.contains_key(old) {
            let mut scope = self.closures.remove(old).unwrap();
            scope.tag = ScopeTag::Closure(new.clone());
            self.closures.insert(new.clone(), scope);
            true
        } else {
            false
        }
    }
}

impl Backend for ScopeManager {
    fn alloc(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<Binding> {
        let binding = var.binding.clone();
        self.curr_scope_mut().push_var(var, ptr)?;
        Ok(binding)
    }

    /// Try to load the variable behind a binding
    fn load(&self, bnd: &Binding) -> Result<(JsVar, Option<JsPtrEnum>)> {
        let lookup = || {
            for scope in self.scopes.iter().rev() {
                match scope.get_var_copy(bnd) {
                    Ok(v) => { return Ok(v); },
                    Err(LookupError::FnBoundary) => {
                        return Err(GcError::Load(bnd.clone()));
                    },
                    Err(LookupError::CheckParent) => {},
                    Err(LookupError::Unreachable) => unreachable!(),
                }
            }
            Err(GcError::Load(bnd.clone()))
        };
        match lookup() {
            Ok(v) => Ok(v),
            Err(GcError::Load(bnd)) =>
                self.global_scope().get_var_copy(&bnd)
                .map_err(|_| GcError::Load(bnd.clone())),
            _ => unreachable!(),
        }
    }

    fn store(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = self.curr_scope_mut().update_var(var, ptr);
        if let Err(GcError::Store(var, ptr)) = res {
            self.global_scope_mut().update_var(var, ptr)
        } else {
            res
        }
    }
}

pub fn init_gc() -> ScopeManager {
    let alloc_box = Rc::new(RefCell::new(AllocBox::new()));
    ScopeManager::new(alloc_box)
}

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

    use jsrs_common::ast::Exp;
    use jsrs_common::backend::Backend;
    use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};
    use jsrs_common::types::binding::Binding;

    use jsrs_common::gc_error::GcError;
    use test_utils;

    #[test]
    fn test_push_closure_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        let (fn_var, fn_ptr) = test_utils::make_fn(&None, &Vec::new());
        let unique = fn_var.unique.clone();
        mgr.alloc(fn_var, Some(fn_ptr)).unwrap();
        mgr.pop_scope(Some(unique.clone()), false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
        mgr.push_closure_scope(&unique).unwrap();
        assert_eq!(mgr.closures.len(), 0);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
    }

    #[test]
    fn test_pop_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        assert_eq!(mgr.scopes.len(), 2);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.scopes.len(), 1);
    }

    #[test]
    fn test_pop_scope_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let res = mgr.pop_scope(None, false);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Scope)));
    }

    #[test]
    fn test_alloc() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.alloc(test_utils::make_num(1.), None).unwrap();
        mgr.push_scope(&Exp::Undefined);
        mgr.alloc(test_utils::make_num(2.), None).unwrap();
        assert!(mgr.alloc_box.borrow().is_empty());
    }

    #[test]
    fn test_load() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();
        let load = mgr.load(&x_bnd);
        assert!(load.is_ok());
        let load = load.unwrap();
        match load.0.t {
            JsType::JsNum(n) => assert!(f64::abs(n - 1.) < 0.0001),
            _ => unreachable!(),
        }
        assert!(load.1.is_none());
    }

    #[test]
    fn test_load_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mgr = ScopeManager::new(alloc_box);
        let bnd = Binding::new("".to_owned());
        let res = mgr.load(&bnd);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Load(_))));
        if let Err(GcError::Load(res_bnd)) = res {
            assert_eq!(bnd, res_bnd);
        }
    }

    #[test]
    fn test_store() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box,);
        mgr.push_scope(&Exp::Undefined);

        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();

        let (mut var, _) = mgr.load(&x_bnd).unwrap();
        var.t = JsType::JsNum(2.);

        assert!(mgr.store(var, None).is_ok());
    }

    #[test]
    fn test_store_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        assert!(mgr.store(x, None).is_err());
    }

    #[test]
    fn test_load_from_parent_scope_across_fn_boundary() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_err());
        assert!(matches!(copy, Err(GcError::Load(_))));
    }

    #[test]
    fn test_load_from_parent_scope_no_fn_call() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Undefined);
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);
        // Make some scopes
        mgr.push_scope(&Exp::Undefined);
        {
            // Push a child scope
            mgr.push_scope(&Exp::Undefined);
            // Allocate some non-root variables (numbers)
            mgr.alloc(test_utils::make_num(0.), None).unwrap();
            mgr.alloc(test_utils::make_num(1.), None).unwrap();
            mgr.alloc(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None),
                           (JsKey::JsSym("false".to_string()),
                            var, Some(ptr))];
            let (var, ptr) = test_utils::make_obj(kvs, mgr.alloc_box.clone());

            // Push the obj into the current scope
            let bnd = mgr.alloc(var, Some(ptr)).unwrap();
            // The heap should now have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Replace the string in the object with something else so it's no longer live
            let copy = mgr.load(&bnd);
            let (var_cp, mut ptr_cp) = copy.unwrap();
            let key = JsKey::JsSym("false".to_string());
            match *&mut ptr_cp {
                Some(JsPtrEnum::JsObj(ref mut obj)) => {
                    obj.add_key(key, test_utils::make_num(-1.), None, &mut *(mgr.alloc_box.borrow_mut()));
                },
                _ => unreachable!()
            }
            mgr.store(var_cp, ptr_cp).unwrap();
            // The heap should still have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            mgr.pop_scope(None, true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(mgr.curr_scope().len(), 1);
        // But the string it had allocated shouldn't, since we leaked it into the void
        assert_eq!(mgr.alloc_box.borrow().len(), 1);
    }
}
<commit_msg>Make alloc_box pub in ScopeManager (needed for objects to work)<commit_after>#![feature(associated_consts)]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(question_mark)]
//#![feature(plugin)]

//#![plugin(clippy)]

extern crate jsrs_common;

#[macro_use] extern crate matches;

pub mod alloc;
mod scope;
mod test_utils;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::rc::Rc;

use jsrs_common::ast::Exp;
use jsrs_common::backend::Backend;
use jsrs_common::types::js_var::{JsPtrEnum, JsVar};
use jsrs_common::types::binding::{Binding, UniqueBinding};

use alloc::AllocBox;
use jsrs_common::gc_error::{GcError, Result};
use scope::{LookupError, Scope, ScopeTag};

pub struct ScopeManager {
    scopes: Vec<Scope>,
    closures: HashMap<UniqueBinding, Scope>,
    pub alloc_box: Rc<RefCell<AllocBox>>,
}

impl ScopeManager {
    fn new(alloc_box: Rc<RefCell<AllocBox>>) -> ScopeManager {
        ScopeManager {
            scopes: vec![Scope::new(ScopeTag::Call, &alloc_box)],
            closures: HashMap::new(),
            alloc_box: alloc_box,
        }
    }

    #[allow(dead_code)]
    #[inline]
    fn curr_scope(&self) -> &Scope {
        self.scopes.last().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn curr_scope_mut(&mut self) -> &mut Scope {
        self.scopes.last_mut().expect("Tried to access current scope, but none existed")
    }

    #[inline]
    fn global_scope(&self) -> &Scope {
        self.scopes.get(0).expect("Tried to access global scope, but none existed")
    }

    #[inline]
    fn global_scope_mut(&mut self) -> &mut Scope {
        self.scopes.get_mut(0).expect("Tried to access global scope, but none existed")
    }

    pub fn push_closure_scope(&mut self, closure: &UniqueBinding) -> Result<()> {
        let closure_scope = self.closures.remove(closure).ok_or(GcError::Scope)?;
        self.scopes.push(closure_scope);
        Ok(())
    }

    pub fn push_scope(&mut self, exp: &Exp) {
        let tag = match *exp {
            Exp::Call(..) => ScopeTag::Call,
            _ => ScopeTag::Block,
        };
        self.scopes.push(Scope::new(tag, &self.alloc_box));
    }

    pub fn pop_scope(&mut self, returning_closure: Option<UniqueBinding>, gc_yield: bool) -> Result<()> {
        if let Some(mut scope) = self.scopes.pop() {
            // Clean up the dying scope's stack and take ownership of its heap-allocated data for
            // later collection
            if self.scopes.is_empty() {
                // The global scope was popped and the program is ending.
                scope.trigger_gc();
                return Err(GcError::Scope);
            }
            if let Some(unique) = returning_closure {
                let mut closure_scope = Scope::new(ScopeTag::Closure(unique.clone()), &self.alloc_box);
                scope.transfer_stack(&mut closure_scope, true)?;
                self.closures.insert(unique, closure_scope);
            } else {
                if !matches!(scope.tag, ScopeTag::Closure(_)) {
                    scope.transfer_stack(self.curr_scope_mut(), false)?
                }
            }
            // Potentially trigger the garbage collector
            if gc_yield {
                self.curr_scope_mut().trigger_gc();
            }
            if let ScopeTag::Closure(unique) = scope.tag.clone() {
                self.closures.insert(unique.clone(), scope);
            }
            Ok(())
        } else {
            Err(GcError::Scope)
        }
    }

    pub fn rename_closure(&mut self, old: &UniqueBinding, new: &UniqueBinding) -> bool {
        if self.closures.contains_key(old) {
            let mut scope = self.closures.remove(old).unwrap();
            scope.tag = ScopeTag::Closure(new.clone());
            self.closures.insert(new.clone(), scope);
            true
        } else {
            false
        }
    }
}

impl Backend for ScopeManager {
    fn alloc(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<Binding> {
        let binding = var.binding.clone();
        self.curr_scope_mut().push_var(var, ptr)?;
        Ok(binding)
    }

    /// Try to load the variable behind a binding
    fn load(&self, bnd: &Binding) -> Result<(JsVar, Option<JsPtrEnum>)> {
        let lookup = || {
            for scope in self.scopes.iter().rev() {
                match scope.get_var_copy(bnd) {
                    Ok(v) => { return Ok(v); },
                    Err(LookupError::FnBoundary) => {
                        return Err(GcError::Load(bnd.clone()));
                    },
                    Err(LookupError::CheckParent) => {},
                    Err(LookupError::Unreachable) => unreachable!(),
                }
            }
            Err(GcError::Load(bnd.clone()))
        };
        match lookup() {
            Ok(v) => Ok(v),
            Err(GcError::Load(bnd)) =>
                self.global_scope().get_var_copy(&bnd)
                .map_err(|_| GcError::Load(bnd.clone())),
            _ => unreachable!(),
        }
    }

    fn store(&mut self, var: JsVar, ptr: Option<JsPtrEnum>) -> Result<()> {
        let res = self.curr_scope_mut().update_var(var, ptr);
        if let Err(GcError::Store(var, ptr)) = res {
            self.global_scope_mut().update_var(var, ptr)
        } else {
            res
        }
    }
}

pub fn init_gc() -> ScopeManager {
    let alloc_box = Rc::new(RefCell::new(AllocBox::new()));
    ScopeManager::new(alloc_box)
}

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

    use jsrs_common::ast::Exp;
    use jsrs_common::backend::Backend;
    use jsrs_common::types::js_var::{JsKey, JsPtrEnum, JsType, JsVar};
    use jsrs_common::types::binding::Binding;

    use jsrs_common::gc_error::GcError;
    use test_utils;

    #[test]
    fn test_push_closure_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        let (fn_var, fn_ptr) = test_utils::make_fn(&None, &Vec::new());
        let unique = fn_var.unique.clone();
        mgr.alloc(fn_var, Some(fn_ptr)).unwrap();
        mgr.pop_scope(Some(unique.clone()), false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
        mgr.push_closure_scope(&unique).unwrap();
        assert_eq!(mgr.closures.len(), 0);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.closures.len(), 1);
    }

    #[test]
    fn test_pop_scope() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.push_scope(&Exp::Undefined);
        assert_eq!(mgr.scopes.len(), 2);
        mgr.pop_scope(None, false).unwrap();
        assert_eq!(mgr.scopes.len(), 1);
    }

    #[test]
    fn test_pop_scope_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let res = mgr.pop_scope(None, false);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Scope)));
    }

    #[test]
    fn test_alloc() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        mgr.alloc(test_utils::make_num(1.), None).unwrap();
        mgr.push_scope(&Exp::Undefined);
        mgr.alloc(test_utils::make_num(2.), None).unwrap();
        assert!(mgr.alloc_box.borrow().is_empty());
    }

    #[test]
    fn test_load() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();
        let load = mgr.load(&x_bnd);
        assert!(load.is_ok());
        let load = load.unwrap();
        match load.0.t {
            JsType::JsNum(n) => assert!(f64::abs(n - 1.) < 0.0001),
            _ => unreachable!(),
        }
        assert!(load.1.is_none());
    }

    #[test]
    fn test_load_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mgr = ScopeManager::new(alloc_box);
        let bnd = Binding::new("".to_owned());
        let res = mgr.load(&bnd);
        assert!(res.is_err());
        assert!(matches!(res, Err(GcError::Load(_))));
        if let Err(GcError::Load(res_bnd)) = res {
            assert_eq!(bnd, res_bnd);
        }
    }

    #[test]
    fn test_store() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box,);
        mgr.push_scope(&Exp::Undefined);

        let x = test_utils::make_num(1.);
        let x_bnd = mgr.alloc(x, None).unwrap();

        let (mut var, _) = mgr.load(&x_bnd).unwrap();
        var.t = JsType::JsNum(2.);

        assert!(mgr.store(var, None).is_ok());
    }

    #[test]
    fn test_store_fail() {
        let alloc_box = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(alloc_box);
        let x = test_utils::make_num(1.);
        assert!(mgr.store(x, None).is_err());
    }

    #[test]
    fn test_load_from_parent_scope_across_fn_boundary() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_err());
        assert!(matches!(copy, Err(GcError::Load(_))));
    }

    #[test]
    fn test_load_from_parent_scope_no_fn_call() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);

        // Avoids having just the global scope available
        mgr.push_scope(&Exp::Call(box Exp::Undefined, vec![]));
        let (x, x_ptr) = test_utils::make_str("x");
        let x_bnd = mgr.alloc(x, Some(x_ptr)).unwrap();

        mgr.push_scope(&Exp::Undefined);
        let copy = mgr.load(&x_bnd);

        assert!(copy.is_ok());
        let (var_copy, ptr_copy) = copy.unwrap();
        assert!(matches!(var_copy, JsVar { t: JsType::JsPtr(_), .. }));
        assert!(ptr_copy.is_some());
    }

    #[test]
    fn test_transfer_stack_with_yield() {
        let heap = test_utils::make_alloc_box();
        let mut mgr = ScopeManager::new(heap);
        // Make some scopes
        mgr.push_scope(&Exp::Undefined);
        {
            // Push a child scope
            mgr.push_scope(&Exp::Undefined);
            // Allocate some non-root variables (numbers)
            mgr.alloc(test_utils::make_num(0.), None).unwrap();
            mgr.alloc(test_utils::make_num(1.), None).unwrap();
            mgr.alloc(test_utils::make_num(2.), None).unwrap();

            // Make a string to put into an object
            // (so it's heap-allocated and we can lose its ref from the object)
            let (var, ptr) = test_utils::make_str("test");

            // Create an obj of { true: 1.0, false: heap("test") }
            let kvs = vec![(JsKey::JsSym("true".to_string()),
                            test_utils::make_num(1.), None),
                           (JsKey::JsSym("false".to_string()),
                            var, Some(ptr))];
            let (var, ptr) = test_utils::make_obj(kvs, mgr.alloc_box.clone());

            // Push the obj into the current scope
            let bnd = mgr.alloc(var, Some(ptr)).unwrap();
            // The heap should now have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Replace the string in the object with something else so it's no longer live
            let copy = mgr.load(&bnd);
            let (var_cp, mut ptr_cp) = copy.unwrap();
            let key = JsKey::JsSym("false".to_string());
            match *&mut ptr_cp {
                Some(JsPtrEnum::JsObj(ref mut obj)) => {
                    obj.add_key(key, test_utils::make_num(-1.), None, &mut *(mgr.alloc_box.borrow_mut()));
                },
                _ => unreachable!()
            }
            mgr.store(var_cp, ptr_cp).unwrap();
            // The heap should still have 2 things in it: an object and a string
            assert_eq!(mgr.alloc_box.borrow().len(), 2);

            // Kill the current scope & give its refs to the parent,
            // allowing the GC to kick in beforehand.
            mgr.pop_scope(None, true).unwrap();
        }
        // The object we created above should still exist
        assert_eq!(mgr.curr_scope().len(), 1);
        // But the string it had allocated shouldn't, since we leaked it into the void
        assert_eq!(mgr.alloc_box.borrow().len(), 1);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;
extern crate byteorder;

use std::fs::File;
use std::io::Read;

use byteorder::{ByteOrder, LittleEndian};
use nom::IResult::Done;

const MAGIC_NUMBER: &'static str = "VOX ";

#[derive(Debug, PartialEq)]
pub struct DotVoxData {
    version: u32,
    models: Vec<Model>
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Voxel {
  x: u8,
  y: u8,
  z: u8,
  i: u8
}

#[derive(Debug, PartialEq)]
pub struct Size {
  x: u32,
  y: u32,
  z: u32
}

#[derive(Debug, PartialEq)]
pub struct Model {
  size: Size,
  voxels: Vec<Voxel>
}

named!(take_u32 <&[u8], u32>, map!(take!(4), LittleEndian::read_u32));
named!(take_u8 <&[u8], u8>, map!(take!(1), |u: &[u8]| *u.first().unwrap()));

named!(parse_voxel <&[u8], Voxel>, chain!(
  x: take_u8 ~
  y: take_u8 ~
  z: take_u8 ~
  i: take_u8,
  || Voxel { x: x, y: y, z: z, i: i }
));

named!(parse_voxels <&[u8], Vec<Voxel> >, chain!(
  take!(12) ~
  num_voxels: take_u32 ~
  voxels: many_m_n!(num_voxels as usize, num_voxels as usize, parse_voxel),
  || voxels
));

named!(parse_size <&[u8], Size>, chain!(
  take!(12) ~
  x: take_u32 ~
  y: take_u32 ~
  z: take_u32,
  || Size { x: x, y: y, z: z }
));

named!(parse_model <&[u8], Model>, chain!(
  size: parse_size ~
  voxels: parse_voxels,
  || Model { size: size, voxels: voxels }
));

named!(parse_models <&[u8], Vec<Model> >, chain!(
  take!(12) ~
  model_count: take_u32 ~
  models: many_m_n!(model_count as usize, model_count as usize, parse_model),
  || models
));

named!(parse_vox_file <&[u8], DotVoxData>, chain!(
  tag!(MAGIC_NUMBER) ~
  version: take_u32 ~
  take!(12) ~
  models: switch!(peek!(take!(4)),
    b"PACK" => call!(parse_models) |
    b"SIZE" => map!(call!(parse_model), |m| vec!(m))
  ),
  || DotVoxData {
    version: version,
    models: models
  }
));

pub fn load(filename: &str) -> Result<DotVoxData, &'static str> {
  match File::open(filename) {
    Ok(mut f) => {
      let mut buffer = Vec::new();
      f.read_to_end(&mut buffer).expect("Unable to read file");
      match parse_vox_file(&buffer) {
        Done(_, parsed) => Ok(parsed),
        _ => Err("Not a valid MagicaVoxel .vox file")
      }
    },
    Err(_) => Err("Unable to load file")
  }
}

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

    fn placeholder() -> DotVoxData {
      DotVoxData {
        version: 150,
        models: vec!(
          Model {
            size: Size { x: 2, y: 2, z: 2 },
            voxels: vec!(
              Voxel { x: 0, y: 0, z: 0, i: 226 },
              Voxel { x: 0, y: 1, z: 1, i: 216 },
              Voxel { x: 1, y: 0, z: 1, i: 236 },
              Voxel { x: 1, y: 1, z: 0, i: 6 }
            )
          }
        )
      }
    }

    #[test]
    fn valid_file_is_read_successfully() {
        let result = load("src/resources/placeholder.vox");
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), placeholder());
    }

    #[test]
    fn not_present_file_causes_error() {
        let result = load("src/resources/not_here.vox");
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), "Unable to load file");
    }

    #[test]
    fn non_vox_file_causes_error() {
        let result = load("src/resources/not_a.vox");
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), "Not a valid MagicaVoxel .vox file");
    }

    #[test]
    fn can_parse_vox_file() {
      let bytes = include_bytes!("resources/placeholder.vox").to_vec();
      let result = super::parse_vox_file(&bytes);
      assert!(result.is_done());
      let (_, models) = result.unwrap();
      assert_eq!(models, placeholder());
    }

    #[test]
    fn can_parse_size_chunk() {
      let bytes = include_bytes!("resources/valid_size.bytes").to_vec();
      let result = super::parse_size(&bytes);
      assert!(result.is_done());
      let (_, size) = result.unwrap();
      assert_eq!(size, Size { x: 24, y: 24, z: 24 } );
    }

    #[test]
    fn can_parse_voxels_chunk() {
      let bytes = include_bytes!("resources/valid_voxels.bytes").to_vec();
      let result = super::parse_voxels(&bytes);
      assert!(result.is_done());
      let (_, voxels) = result.unwrap();
      assert_eq!(voxels, vec!(Voxel { x: 0, y: 12, z: 22, i: 226 }, Voxel { x: 12, y: 23, z: 13, i: 226 }));
    }
}
<commit_msg>Add rustdoc comments.<commit_after>//! Load MagicaVoxel .vox files into Rust
#![deny(missing_docs)]

#[macro_use]
extern crate nom;
extern crate byteorder;

use std::fs::File;
use std::io::Read;

use byteorder::{ByteOrder, LittleEndian};
use nom::IResult::Done;

const MAGIC_NUMBER: &'static str = "VOX ";

/// Container for .vox file data
#[derive(Debug, PartialEq)]
pub struct DotVoxData {
    /// The version number of the .vox file.
    pub version: u32,
    /// A Vec of all the models contained within this file.
    pub models: Vec<Model>
}

/// A Voxel
///
/// A Voxel is a point in 3D space, with an indexed colour attached.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Voxel {
  /// The X coordinate for the Voxel
  pub x: u8,
  /// The Y coordinate for the Voxel
  pub y: u8,
  /// The Z coordinate for the Voxel
  pub z: u8,
  /// Index in the Color Pallete
  pub i: u8
}

/// The size of a model in voxels
///
/// Indicates the size of the model in Voxels.
#[derive(Debug, PartialEq)]
pub struct Size {
  /// The width of the model in voxels.
  pub x: u32,
  /// The height of the model in voxels.
  pub y: u32,
  /// The depth of the model in voxels.
  pub z: u32
}

/// A renderable voxel Model
#[derive(Debug, PartialEq)]
pub struct Model {
  /// The size of the model in voxels
  pub size: Size,
  /// The voxels to be displayed.
  pub voxels: Vec<Voxel>
}

named!(take_u32 <&[u8], u32>, map!(take!(4), LittleEndian::read_u32));
named!(take_u8 <&[u8], u8>, map!(take!(1), |u: &[u8]| *u.first().unwrap()));

named!(parse_voxel <&[u8], Voxel>, chain!(
  x: take_u8 ~
  y: take_u8 ~
  z: take_u8 ~
  i: take_u8,
  || Voxel { x: x, y: y, z: z, i: i }
));

named!(parse_voxels <&[u8], Vec<Voxel> >, chain!(
  take!(12) ~
  num_voxels: take_u32 ~
  voxels: many_m_n!(num_voxels as usize, num_voxels as usize, parse_voxel),
  || voxels
));

named!(parse_size <&[u8], Size>, chain!(
  take!(12) ~
  x: take_u32 ~
  y: take_u32 ~
  z: take_u32,
  || Size { x: x, y: y, z: z }
));

named!(parse_model <&[u8], Model>, chain!(
  size: parse_size ~
  voxels: parse_voxels,
  || Model { size: size, voxels: voxels }
));

named!(parse_models <&[u8], Vec<Model> >, chain!(
  take!(12) ~
  model_count: take_u32 ~
  models: many_m_n!(model_count as usize, model_count as usize, parse_model),
  || models
));

named!(parse_vox_file <&[u8], DotVoxData>, chain!(
  tag!(MAGIC_NUMBER) ~
  version: take_u32 ~
  take!(12) ~
  models: switch!(peek!(take!(4)),
    b"PACK" => call!(parse_models) |
    b"SIZE" => map!(call!(parse_model), |m| vec!(m))
  ),
  || DotVoxData {
    version: version,
    models: models
  }
));

/// Loads the supplied MagicaVoxel .vox file
///
/// Loads the supplied file, parses it, and returns a `DotVoxData` containing the version of the
/// MagicaVoxel file, and `Vec<Model>` containing all `Model`s contained within the file.
///
/// # Panics
/// No panics should occur with this library - if you find one, please raise a GitHub issue for it.
///
/// # Errors
/// All errors are strings, and should describe the issue that caused them to occur.
///
/// # Examples
///
/// Loading a file:
///
/// ```
/// use dot_vox::*;
///
/// let result = load("src/resources/placeholder.vox");
/// assert_eq!(result.unwrap(), DotVoxData {
///   version: 150,
///   models: vec!(
///     Model {
///       size: Size { x: 2, y: 2, z: 2 },
///       voxels: vec!(
///         Voxel { x: 0, y: 0, z: 0, i: 226 },
///         Voxel { x: 0, y: 1, z: 1, i: 216 },
///         Voxel { x: 1, y: 0, z: 1, i: 236 },
///         Voxel { x: 1, y: 1, z: 0, i: 6 }
///       )
///     }
///   )
/// });
/// ```
pub fn load(filename: &str) -> Result<DotVoxData, &'static str> {
  match File::open(filename) {
    Ok(mut f) => {
      let mut buffer = Vec::new();
      f.read_to_end(&mut buffer).expect("Unable to read file");
      match parse_vox_file(&buffer) {
        Done(_, parsed) => Ok(parsed),
        _ => Err("Not a valid MagicaVoxel .vox file")
      }
    },
    Err(_) => Err("Unable to load file")
  }
}

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

    fn placeholder() -> DotVoxData {
      DotVoxData {
        version: 150,
        models: vec!(
          Model {
            size: Size { x: 2, y: 2, z: 2 },
            voxels: vec!(
              Voxel { x: 0, y: 0, z: 0, i: 226 },
              Voxel { x: 0, y: 1, z: 1, i: 216 },
              Voxel { x: 1, y: 0, z: 1, i: 236 },
              Voxel { x: 1, y: 1, z: 0, i: 6 }
            )
          }
        )
      }
    }

    #[test]
    fn valid_file_is_read_successfully() {
        let result = load("src/resources/placeholder.vox");
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), placeholder());
    }

    #[test]
    fn not_present_file_causes_error() {
        let result = load("src/resources/not_here.vox");
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), "Unable to load file");
    }

    #[test]
    fn non_vox_file_causes_error() {
        let result = load("src/resources/not_a.vox");
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), "Not a valid MagicaVoxel .vox file");
    }

    #[test]
    fn can_parse_vox_file() {
      let bytes = include_bytes!("resources/placeholder.vox").to_vec();
      let result = super::parse_vox_file(&bytes);
      assert!(result.is_done());
      let (_, models) = result.unwrap();
      assert_eq!(models, placeholder());
    }

    #[test]
    fn can_parse_size_chunk() {
      let bytes = include_bytes!("resources/valid_size.bytes").to_vec();
      let result = super::parse_size(&bytes);
      assert!(result.is_done());
      let (_, size) = result.unwrap();
      assert_eq!(size, Size { x: 24, y: 24, z: 24 } );
    }

    #[test]
    fn can_parse_voxels_chunk() {
      let bytes = include_bytes!("resources/valid_voxels.bytes").to_vec();
      let result = super::parse_voxels(&bytes);
      assert!(result.is_done());
      let (_, voxels) = result.unwrap();
      assert_eq!(voxels, vec!(Voxel { x: 0, y: 12, z: 22, i: 226 }, Voxel { x: 12, y: 23, z: 13, i: 226 }));
    }
}
<|endoftext|>


<commit_before>//! The `reversi` library (ver. 0.4.0) provides the main structures and mechanics required to run a Reversi game.
//! In view of possible AIs developement, the library keeps an eye on performances.
//! Released under MIT license.

// TODO:
// ReversiError::NoUndo needs a turn::Turn value

#![crate_type = "dylib"]
#![crate_name = "reversi"]
#![feature(test)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate rand;
extern crate test;

pub mod board;
pub mod turn;
pub mod game;

#[cfg(test)]
mod reversi_test;

use std::fmt;
use board::{Coord, Direction};
use std::result;

/// The errors that may be generated by running a Reversi game.
#[derive(Debug, Clone, Copy)]
pub enum ReversiError {
    /// It has been attempted to create or use a coordinate with out-of-bound indexes.
    OutOfBoundCoord(Coord),
    /// It has been attempted to step out of the board's bounds.
    OutOfBoundStep(Coord, Direction),
    /// It has been attempted to create or use an out-of-bound indexe.
    OutOfBoundIndex(usize),
    /// It has been attempted to move on a cell which was already taken.
    CellAlreadyTaken(Coord),
    /// It was looked for a disk in a cell which is empty.
    EmptyCell(Coord),
    /// It has been attempted to move on a illegal cell.
    IllegalMove(Coord),
    /// It has been tried to move when the game was already ended.
    EndedGame(turn::Turn),
    /// Undoing a turn is not possible
    NoUndo,
}

/// Aliasing given by taking `ReversiError` as standard error value.
pub type Result<T> = result::Result<T, ReversiError>;

impl fmt::Display for ReversiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ReversiError::OutOfBoundCoord(coord) => write!(f, "Out of bound coordinates: {:?}", coord),
            ReversiError::OutOfBoundStep(coord, dir) => write!(f, "Out of bound step: {:?} going {:?}", coord, dir),
            ReversiError::OutOfBoundIndex(index) => write!(f, "Out of bound index: {:?}", index),
            ReversiError::CellAlreadyTaken(coord) => write!(f, "The cell you want to move to is already taken: {:?}", coord),
            ReversiError::IllegalMove(coord) => write!(f, "Illegal move: {:?}", coord),
            ReversiError::EndedGame(game) => write!(f, "The game is already ended:\n {:?}", game),
            ReversiError::EmptyCell(coord) => write!(f, "The cell you want is empty: {:?}", coord),
            ReversiError::NoUndo => write!(f, "Undoing is not possible!"),
        }
    }
}

/// There are two sides in Reversi: `Dark` and `Light`
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Side {
    Dark,
    Light,
}

impl Side {
    /// Get self's opposite side.
    pub fn opposite(&self) -> Side {
        match *self {
            Side::Dark  => Side::Light,
            Side::Light => Side::Dark,
        }
    }
}
<commit_msg>Revert "#![crate_type = "dylib"]"<commit_after>//! The `reversi` library (ver. 0.4.0) provides the main structures and mechanics required to run a Reversi game.
//! In view of possible AIs developement, the library keeps an eye on performances.
//! Released under MIT license.

// TODO:
// ReversiError::NoUndo needs a turn::Turn value

#![crate_type = "rlib"]
#![crate_name = "reversi"]
#![feature(test)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate rand;
extern crate test;

pub mod board;
pub mod turn;
pub mod game;

#[cfg(test)]
mod reversi_test;

use std::fmt;
use board::{Coord, Direction};
use std::result;

/// The errors that may be generated by running a Reversi game.
#[derive(Debug, Clone, Copy)]
pub enum ReversiError {
    /// It has been attempted to create or use a coordinate with out-of-bound indexes.
    OutOfBoundCoord(Coord),
    /// It has been attempted to step out of the board's bounds.
    OutOfBoundStep(Coord, Direction),
    /// It has been attempted to create or use an out-of-bound indexe.
    OutOfBoundIndex(usize),
    /// It has been attempted to move on a cell which was already taken.
    CellAlreadyTaken(Coord),
    /// It was looked for a disk in a cell which is empty.
    EmptyCell(Coord),
    /// It has been attempted to move on a illegal cell.
    IllegalMove(Coord),
    /// It has been tried to move when the game was already ended.
    EndedGame(turn::Turn),
    /// Undoing a turn is not possible
    NoUndo,
}

/// Aliasing given by taking `ReversiError` as standard error value.
pub type Result<T> = result::Result<T, ReversiError>;

impl fmt::Display for ReversiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ReversiError::OutOfBoundCoord(coord) => write!(f, "Out of bound coordinates: {:?}", coord),
            ReversiError::OutOfBoundStep(coord, dir) => write!(f, "Out of bound step: {:?} going {:?}", coord, dir),
            ReversiError::OutOfBoundIndex(index) => write!(f, "Out of bound index: {:?}", index),
            ReversiError::CellAlreadyTaken(coord) => write!(f, "The cell you want to move to is already taken: {:?}", coord),
            ReversiError::IllegalMove(coord) => write!(f, "Illegal move: {:?}", coord),
            ReversiError::EndedGame(game) => write!(f, "The game is already ended:\n {:?}", game),
            ReversiError::EmptyCell(coord) => write!(f, "The cell you want is empty: {:?}", coord),
            ReversiError::NoUndo => write!(f, "Undoing is not possible!"),
        }
    }
}

/// There are two sides in Reversi: `Dark` and `Light`
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Side {
    Dark,
    Light,
}

impl Side {
    /// Get self's opposite side.
    pub fn opposite(&self) -> Side {
        match *self {
            Side::Dark  => Side::Light,
            Side::Light => Side::Dark,
        }
    }
}
<|endoftext|>


<commit_before>//! This crate allows one-liners to read from a terminal
//! A minimal example to get an i32 from the command line is
//!
//! ```rust,no_run
//! use text_io::read;
//!
//! fn main() {
//!     let i: i32 = read!();
//! }
//! ```
//!
//! The `read!()` macro will always read until the next ascii whitespace character
//! (`\n`, `\r`, `\t` or space).
//!
//! Any type that implements the `FromStr` trait can be read with the `read!` macro.
//!
//! # Advanced
//! Text parsing can be done similar to `println!` by adding a format string
//! to the macro:
//!
//! ```rust,no_run
//! use text_io::read;
//!
//! let i: i32 = read!("The answer: {}!");
//! ```
//!
//! This will read `"The answer: "`, then an integer, then an exclamation mark. Any deviation from
//! the format string will result in a panic.
//!
//! Note: only a single value can be read per `read!` invocation.

use std::error;
use std::fmt;
use std::str::FromStr;

#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum Error {
    MissingMatch,
    MissingClosingBrace,
    UnexpectedValue(u8, Option<u8>),
    InvalidUtf8(Vec<u8>),
    PartialUtf8(usize, Vec<u8>),
    Parse(String, &'static str),
}

impl error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use crate::Error::*;
        use std::str::from_utf8;

        match *self {
            InvalidUtf8(ref raw) => write!(f, "input was not valid utf8: {:?}", raw),
            Parse(ref s, arg) => write!(f, "could not parse {} as target type of {}", s, arg),
            UnexpectedValue(exp, act) => write!(
                f,
                "found value {:?} not matching the pattern value {}",
                act.map(|b| b as char),
                exp as char
            ),
            PartialUtf8(n, ref raw) => write!(
                f,
                "input was only partially valid utf8: \"{}\" followed by {:?}",
                from_utf8(&raw[..n]).unwrap(),
                &raw[n..]
            ),
            MissingMatch => write!(f, "Bad read! format string: did not contain {{}}"),
            MissingClosingBrace => write!(
                f,
                "found single open curly brace at the end of the format string"
            ),
        }
    }
}

pub fn match_next(expected: u8, iter: &mut dyn Iterator<Item = u8>) -> Result<(), Error> {
    let next = iter.next();
    if next != Some(expected) {
        return Err(Error::UnexpectedValue(expected, next));
    }
    Ok(())
}

pub fn parse_capture<T>(
    name: &'static str,
    next: Option<u8>,
    iter: &mut dyn Iterator<Item = u8>,
) -> Result<T, Error>
where
    T: FromStr,
    <T as FromStr>::Err: ::std::fmt::Debug,
{
    static WHITESPACES: &[u8] = b"\t\r\n ";
    let raw: Vec<u8> = match next {
        Some(c) => iter.take_while(|&ch| ch != c).collect(),
        None => iter
            .skip_while(|ch| WHITESPACES.contains(ch))
            .take_while(|ch| !WHITESPACES.contains(ch))
            .collect(),
    };
    match String::from_utf8(raw) {
        Ok(s) => FromStr::from_str(&s).map_err(|_| Error::Parse(s, name)),
        Err(e) => {
            let n = e.utf8_error().valid_up_to();
            let raw = e.into_bytes();
            if n == 0 {
                Err(Error::InvalidUtf8(raw))
            } else {
                Err(Error::PartialUtf8(n, raw))
            }
        }
    }
}

/// ```rust,no_run
/// use text_io::try_read;
///
/// let i: i32 = try_read!("The answer: {}!").unwrap();
/// let i: Result<i32, _> = try_read!("The {}{{}}!", "The answer is 42!".bytes());
/// assert!(i.is_err());
/// ```
///
/// ```rust
/// use text_io::try_read;
///
/// let i: Result<i32, _> = try_read!("The answer is {}!", "The answer is 42!".bytes());
/// assert!(i.is_ok());
///
/// let i: Result<i32, _> = try_read!("The {}{{}}!", "The answer is 42!".bytes());
/// assert!(i.is_err());
/// ```
#[macro_export]
macro_rules! try_read(
    () => { $crate::try_read!("{}") };
    ($text:expr) => {{
        (|| -> std::result::Result<_, $crate::Error> {
            let __try_read_var__;
            $crate::try_scan!($text, __try_read_var__);
            Ok(__try_read_var__)
        })()
    }};
    ($text:expr, $input:expr) => {{
        (|| -> std::result::Result<_, $crate::Error> {
            let __try_read_var__;
            $crate::try_scan!($input => $text, __try_read_var__);
            Ok(__try_read_var__)
        })()
    }};
);

/// ```rust,no_run
/// use text_io::try_scan;
///
/// fn parser() -> Result<i32, Box<std::error::Error>> {
///     let i: i32;
///     let text = "The answer is 42!";
///
///     try_scan!(text.bytes() => "The answer is {}!", i);
///
///     assert_eq!(i, 1);
///     Ok(i)
/// }
/// ```
#[macro_export]
macro_rules! try_scan(
    ($pattern:expr, $($arg:expr),*) => {
        use ::std::io::Read;
        $crate::try_scan!(::std::io::stdin().bytes().map(std::result::Result::unwrap) => $pattern, $($arg),*);
        format_args!($pattern, $($arg),*);
    };
    ($input:expr => $pattern:expr, $($arg:expr),*) => {{
        $crate::try_scan!(@impl question_mark; $input => $pattern, $($arg),*)
    }};
    (@question_mark: $($e:tt)+) => {{
        ($($e)+)?
    }};
    (@unwrap: $($e:tt)+) => {{
        ($($e)+).unwrap()
    }};
    (@impl $action:tt; $input:expr => $pattern:expr, $($arg:expr),*) => {{
        #![allow(clippy::try_err)]
        use $crate::{Error, match_next, parse_capture};

        // typesafe macros :)
        let pattern: &'static str = $pattern;
        let stdin: &mut Iterator<Item = u8> = &mut ($input);

        let mut pattern = pattern.bytes();

        $(
            $arg = loop {
                match $crate::try_scan!(@$action: pattern.next().ok_or(Error::MissingMatch)) {
                    b'{' => match $crate::try_scan!(@$action: pattern.next().ok_or(Error::MissingClosingBrace)) {
                        b'{' => $crate::try_scan!(@$action: match_next(b'{', stdin)),
                        b'}' => break $crate::try_scan!(@$action: parse_capture(stringify!($arg), pattern.next(), stdin)),
                        _ => return $crate::try_scan!(@$action: Err(Error::MissingClosingBrace)),
                    },
                    c => $crate::try_scan!(@$action: match_next(c, stdin)),
                }
            };
        )*

        for c in pattern {
            $crate::try_scan!(@$action: match_next(c, stdin))
        }

        format_args!($pattern, $($arg),*);
    }};
);

/// All text input is handled through this macro
#[macro_export]
macro_rules! read(
    ($($arg:tt)*) => {
        $crate::try_read!($($arg)*).unwrap()
    };
);

/// This macro allows to pass several variables so multiple values can be read
#[macro_export]
macro_rules! scan(
    ($text:expr, $($arg:expr),*) => {
        use ::std::io::Read;
        $crate::scan!(::std::io::stdin().bytes().map(std::result::Result::unwrap) => $text, $($arg),*);
        format_args!($text, $($arg),*);
    };
    ($input:expr => $pattern:expr, $($arg:expr),*) => {{
        $crate::try_scan!(@impl unwrap; $input => $pattern, $($arg),*)
    }};
);
<commit_msg>Revert "allow compilation time checks"<commit_after>//! This crate allows one-liners to read from a terminal
//! A minimal example to get an i32 from the command line is
//!
//! ```rust,no_run
//! use text_io::read;
//!
//! fn main() {
//!     let i: i32 = read!();
//! }
//! ```
//!
//! The `read!()` macro will always read until the next ascii whitespace character
//! (`\n`, `\r`, `\t` or space).
//!
//! Any type that implements the `FromStr` trait can be read with the `read!` macro.
//!
//! # Advanced
//! Text parsing can be done similar to `println!` by adding a format string
//! to the macro:
//!
//! ```rust,no_run
//! use text_io::read;
//!
//! let i: i32 = read!("The answer: {}!");
//! ```
//!
//! This will read `"The answer: "`, then an integer, then an exclamation mark. Any deviation from
//! the format string will result in a panic.
//!
//! Note: only a single value can be read per `read!` invocation.

use std::error;
use std::fmt;
use std::str::FromStr;

#[non_exhaustive]
#[derive(Debug, PartialEq)]
pub enum Error {
    MissingMatch,
    MissingClosingBrace,
    UnexpectedValue(u8, Option<u8>),
    InvalidUtf8(Vec<u8>),
    PartialUtf8(usize, Vec<u8>),
    Parse(String, &'static str),
}

impl error::Error for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use crate::Error::*;
        use std::str::from_utf8;

        match *self {
            InvalidUtf8(ref raw) => write!(f, "input was not valid utf8: {:?}", raw),
            Parse(ref s, arg) => write!(f, "could not parse {} as target type of {}", s, arg),
            UnexpectedValue(exp, act) => write!(
                f,
                "found value {:?} not matching the pattern value {}",
                act.map(|b| b as char),
                exp as char
            ),
            PartialUtf8(n, ref raw) => write!(
                f,
                "input was only partially valid utf8: \"{}\" followed by {:?}",
                from_utf8(&raw[..n]).unwrap(),
                &raw[n..]
            ),
            MissingMatch => write!(f, "Bad read! format string: did not contain {{}}"),
            MissingClosingBrace => write!(
                f,
                "found single open curly brace at the end of the format string"
            ),
        }
    }
}

pub fn match_next(expected: u8, iter: &mut dyn Iterator<Item = u8>) -> Result<(), Error> {
    let next = iter.next();
    if next != Some(expected) {
        return Err(Error::UnexpectedValue(expected, next));
    }
    Ok(())
}

pub fn parse_capture<T>(
    name: &'static str,
    next: Option<u8>,
    iter: &mut dyn Iterator<Item = u8>,
) -> Result<T, Error>
where
    T: FromStr,
    <T as FromStr>::Err: ::std::fmt::Debug,
{
    static WHITESPACES: &[u8] = b"\t\r\n ";
    let raw: Vec<u8> = match next {
        Some(c) => iter.take_while(|&ch| ch != c).collect(),
        None => iter
            .skip_while(|ch| WHITESPACES.contains(ch))
            .take_while(|ch| !WHITESPACES.contains(ch))
            .collect(),
    };
    match String::from_utf8(raw) {
        Ok(s) => FromStr::from_str(&s).map_err(|_| Error::Parse(s, name)),
        Err(e) => {
            let n = e.utf8_error().valid_up_to();
            let raw = e.into_bytes();
            if n == 0 {
                Err(Error::InvalidUtf8(raw))
            } else {
                Err(Error::PartialUtf8(n, raw))
            }
        }
    }
}

/// ```rust,no_run
/// use text_io::try_read;
///
/// let i: i32 = try_read!("The answer: {}!").unwrap();
/// let i: Result<i32, _> = try_read!("The {}{{}}!", "The answer is 42!".bytes());
/// assert!(i.is_err());
/// ```
///
/// ```rust
/// use text_io::try_read;
///
/// let i: Result<i32, _> = try_read!("The answer is {}!", "The answer is 42!".bytes());
/// assert!(i.is_ok());
///
/// let i: Result<i32, _> = try_read!("The {}{{}}!", "The answer is 42!".bytes());
/// assert!(i.is_err());
/// ```
#[macro_export]
macro_rules! try_read(
    () => { $crate::try_read!("{}") };
    ($text:expr) => {{
        (|| -> std::result::Result<_, $crate::Error> {
            let __try_read_var__;
            $crate::try_scan!($text, __try_read_var__);
            Ok(__try_read_var__)
        })()
    }};
    ($text:expr, $input:expr) => {{
        (|| -> std::result::Result<_, $crate::Error> {
            let __try_read_var__;
            $crate::try_scan!($input => $text, __try_read_var__);
            Ok(__try_read_var__)
        })()
    }};
);

/// ```rust,no_run
/// use text_io::try_scan;
///
/// fn parser() -> Result<i32, Box<std::error::Error>> {
///     let i: i32;
///     let text = "The answer is 42!";
///
///     try_scan!(text.bytes() => "The answer is {}!", i);
///
///     assert_eq!(i, 1);
///     Ok(i)
/// }
/// ```
#[macro_export]
macro_rules! try_scan(
    ($pattern:expr, $($arg:expr),*) => {
        use ::std::io::Read;
        $crate::try_scan!(::std::io::stdin().bytes().map(std::result::Result::unwrap) => $pattern, $($arg),*);
    };
    ($input:expr => $pattern:expr, $($arg:expr),*) => {{
        $crate::try_scan!(@impl question_mark; $input => $pattern, $($arg),*)
    }};
    (@question_mark: $($e:tt)+) => {{
        ($($e)+)?
    }};
    (@unwrap: $($e:tt)+) => {{
        ($($e)+).unwrap()
    }};
    (@impl $action:tt; $input:expr => $pattern:expr, $($arg:expr),*) => {{
        #![allow(clippy::try_err)]
        use $crate::{Error, match_next, parse_capture};

        // typesafe macros :)
        let pattern: &'static str = $pattern;
        let stdin: &mut Iterator<Item = u8> = &mut ($input);

        let mut pattern = pattern.bytes();

        $(
            $arg = loop {
                match $crate::try_scan!(@$action: pattern.next().ok_or(Error::MissingMatch)) {
                    b'{' => match $crate::try_scan!(@$action: pattern.next().ok_or(Error::MissingClosingBrace)) {
                        b'{' => $crate::try_scan!(@$action: match_next(b'{', stdin)),
                        b'}' => break $crate::try_scan!(@$action: parse_capture(stringify!($arg), pattern.next(), stdin)),
                        _ => return $crate::try_scan!(@$action: Err(Error::MissingClosingBrace)),
                    },
                    c => $crate::try_scan!(@$action: match_next(c, stdin)),
                }
            };
        )*

        for c in pattern {
            $crate::try_scan!(@$action: match_next(c, stdin))
        }

        format_args!($pattern, $($arg),*);
    }};
);

/// All text input is handled through this macro
#[macro_export]
macro_rules! read(
    ($($arg:tt)*) => {
        $crate::try_read!($($arg)*).unwrap()
    };
);

/// This macro allows to pass several variables so multiple values can be read
#[macro_export]
macro_rules! scan(
    ($text:expr, $($arg:expr),*) => {
        use ::std::io::Read;
        $crate::scan!(::std::io::stdin().bytes().map(std::result::Result::unwrap) => $text, $($arg),*);
    };
    ($input:expr => $pattern:expr, $($arg:expr),*) => {{
        $crate::try_scan!(@impl unwrap; $input => $pattern, $($arg),*)
    }};
);
<|endoftext|>


<commit_before>//! A wrapper around the uchardet library. Detects character encodings.
//!
//! Note that the underlying implemention is written in C and C++, and I'm
//! not aware of any security audits which have been performed against it.
//!
//! ```
//! use uchardet::detect_encoding_name;
//!
//! assert_eq!("WINDOWS-1252",
//!            detect_encoding_name(&[0x46, 0x93, 0x72, 0x61, 0x6e, 0xe7, 0x6f,
//!                0x69, 0x73, 0xe9, 0x94]).unwrap());
//! ```
//!
//! For more information, see [this project on
//! GitHub](https://github.com/emk/rust-uchardet).

// Increase the compiler's recursion limit for the `error_chain` crate.
#![recursion_limit = "1024"]
#![deny(missing_docs)]

#[macro_use]
extern crate error_chain;
extern crate libc;
extern crate uchardet_sys as ffi;

use libc::size_t;
use std::ffi::CStr;
use std::str::from_utf8;

pub use errors::*;

#[allow(missing_docs)]
mod errors {
    error_chain! {
        errors {
            UnrecognizableCharset {
                description("unrecognizable charset")
                display("uchardet was unable to recognize a charset")
            }
            OutOfMemory {
                description("out of memory error")
                display("uchardet ran out of memory")
            }
            Other(int: i32) {
                description("unknown error")
                display("uchardet returned unknown error {}", int)
            }
        }
    }

    impl ErrorKind {
        pub fn from_nsresult(nsresult: ::ffi::nsresult) -> ErrorKind {
            assert!(nsresult != 0);
            match nsresult {
                1 => ErrorKind::OutOfMemory,
                int => ErrorKind::Other(int),
            }
        }

    }
}

/// Detects the encoding of text using the uchardet library.
///
/// EXPERIMENTAL: This may be replaced by a better API soon.
struct EncodingDetector {
    ptr: ffi::uchardet_t
}

/// Return the name of the charset used in `data` or an error if uchardet
/// was unable to detect a charset.
///
/// ```
/// use uchardet::detect_encoding_name;
///
/// assert_eq!("ASCII",
///            detect_encoding_name("ascii".as_bytes()).unwrap());
/// assert_eq!("UTF-8",
///            detect_encoding_name("©français".as_bytes()).unwrap());
/// assert_eq!("WINDOWS-1252",
///            detect_encoding_name(&[0x46, 0x93, 0x72, 0x61, 0x6e, 0xe7, 0x6f,
///                0x69, 0x73, 0xe9, 0x94]).unwrap());
/// ```
pub fn detect_encoding_name(data: &[u8]) -> Result<String> {
    let mut detector = EncodingDetector::new();
    try!(detector.handle_data(data));
    detector.data_end();
    detector.charset()
}

impl EncodingDetector {
    /// Create a new EncodingDetector.
    fn new() -> EncodingDetector {
        let ptr = unsafe { ffi::uchardet_new() };
        assert!(!ptr.is_null());
        EncodingDetector{ptr: ptr}
    }

    /// Pass a chunk of raw bytes to the detector. This is a no-op if a
    /// charset has been detected.
    fn handle_data(&mut self, data: &[u8]) -> Result<()> {
        let nsresult = unsafe {
            ffi::uchardet_handle_data(self.ptr, data.as_ptr() as *const i8,
                                      data.len() as size_t)
        };
        match nsresult {
            0 => Ok(()),
            int => {
                Err(ErrorKind::from_nsresult(int).into())
            }
        }
    }

    /// Notify the detector that we're done calling `handle_data`, and that
    /// we want it to make a guess as to our encoding. This is a no-op if
    /// no data has been passed yet, or if an encoding has been detected
    /// for certain. From reading the code, it appears that you can safely
    /// call `handle_data` after calling this, but I'm not certain.
    fn data_end(&mut self) {
        unsafe { ffi::uchardet_data_end(self.ptr); }
    }

    /// Reset the detector's internal state.
    // fn reset(&mut self) {
    //    unsafe { ffi::uchardet_reset(self.ptr); }
    // }

    /// Get the decoder's current best guess as to the encoding. May return
    /// an error if uchardet was unable to detect an encoding.
    fn charset(&self) -> Result<String> {
        unsafe {
            let internal_str = ffi::uchardet_get_charset(self.ptr);
            assert!(!internal_str.is_null());
            let bytes = CStr::from_ptr(internal_str).to_bytes();
            let charset = from_utf8(bytes);
            match charset {
                Err(_) =>
                    panic!("uchardet_get_charset returned a charset name \
                            containing invalid characters"),
                Ok("") => Err(ErrorKind::UnrecognizableCharset.into()),
                Ok(encoding) => Ok(encoding.to_string())
            }
        }
    }
}

impl Drop for EncodingDetector {
    fn drop(&mut self) {
        unsafe { ffi::uchardet_delete(self.ptr) };
    }
}
<commit_msg>Remove commented-out code<commit_after>//! A wrapper around the uchardet library. Detects character encodings.
//!
//! Note that the underlying implemention is written in C and C++, and I'm
//! not aware of any security audits which have been performed against it.
//!
//! ```
//! use uchardet::detect_encoding_name;
//!
//! assert_eq!("WINDOWS-1252",
//!            detect_encoding_name(&[0x46, 0x93, 0x72, 0x61, 0x6e, 0xe7, 0x6f,
//!                0x69, 0x73, 0xe9, 0x94]).unwrap());
//! ```
//!
//! For more information, see [this project on
//! GitHub](https://github.com/emk/rust-uchardet).

// Increase the compiler's recursion limit for the `error_chain` crate.
#![recursion_limit = "1024"]
#![deny(missing_docs)]

#[macro_use]
extern crate error_chain;
extern crate libc;
extern crate uchardet_sys as ffi;

use libc::size_t;
use std::ffi::CStr;
use std::str::from_utf8;

pub use errors::*;

#[allow(missing_docs)]
mod errors {
    error_chain! {
        errors {
            UnrecognizableCharset {
                description("unrecognizable charset")
                display("uchardet was unable to recognize a charset")
            }
            OutOfMemory {
                description("out of memory error")
                display("uchardet ran out of memory")
            }
            Other(int: i32) {
                description("unknown error")
                display("uchardet returned unknown error {}", int)
            }
        }
    }

    impl ErrorKind {
        pub fn from_nsresult(nsresult: ::ffi::nsresult) -> ErrorKind {
            assert!(nsresult != 0);
            match nsresult {
                1 => ErrorKind::OutOfMemory,
                int => ErrorKind::Other(int),
            }
        }

    }
}

/// Detects the encoding of text using the uchardet library.
///
/// EXPERIMENTAL: This may be replaced by a better API soon.
struct EncodingDetector {
    ptr: ffi::uchardet_t
}

/// Return the name of the charset used in `data` or an error if uchardet
/// was unable to detect a charset.
///
/// ```
/// use uchardet::detect_encoding_name;
///
/// assert_eq!("ASCII",
///            detect_encoding_name("ascii".as_bytes()).unwrap());
/// assert_eq!("UTF-8",
///            detect_encoding_name("©français".as_bytes()).unwrap());
/// assert_eq!("WINDOWS-1252",
///            detect_encoding_name(&[0x46, 0x93, 0x72, 0x61, 0x6e, 0xe7, 0x6f,
///                0x69, 0x73, 0xe9, 0x94]).unwrap());
/// ```
pub fn detect_encoding_name(data: &[u8]) -> Result<String> {
    let mut detector = EncodingDetector::new();
    try!(detector.handle_data(data));
    detector.data_end();
    detector.charset()
}

impl EncodingDetector {
    /// Create a new EncodingDetector.
    fn new() -> EncodingDetector {
        let ptr = unsafe { ffi::uchardet_new() };
        assert!(!ptr.is_null());
        EncodingDetector{ptr: ptr}
    }

    /// Pass a chunk of raw bytes to the detector. This is a no-op if a
    /// charset has been detected.
    fn handle_data(&mut self, data: &[u8]) -> Result<()> {
        let nsresult = unsafe {
            ffi::uchardet_handle_data(self.ptr, data.as_ptr() as *const i8,
                                      data.len() as size_t)
        };
        match nsresult {
            0 => Ok(()),
            int => {
                Err(ErrorKind::from_nsresult(int).into())
            }
        }
    }

    /// Notify the detector that we're done calling `handle_data`, and that
    /// we want it to make a guess as to our encoding. This is a no-op if
    /// no data has been passed yet, or if an encoding has been detected
    /// for certain. From reading the code, it appears that you can safely
    /// call `handle_data` after calling this, but I'm not certain.
    fn data_end(&mut self) {
        unsafe { ffi::uchardet_data_end(self.ptr); }
    }

    /// Get the decoder's current best guess as to the encoding. May return
    /// an error if uchardet was unable to detect an encoding.
    fn charset(&self) -> Result<String> {
        unsafe {
            let internal_str = ffi::uchardet_get_charset(self.ptr);
            assert!(!internal_str.is_null());
            let bytes = CStr::from_ptr(internal_str).to_bytes();
            let charset = from_utf8(bytes);
            match charset {
                Err(_) =>
                    panic!("uchardet_get_charset returned a charset name \
                            containing invalid characters"),
                Ok("") => Err(ErrorKind::UnrecognizableCharset.into()),
                Ok(encoding) => Ok(encoding.to_string())
            }
        }
    }
}

impl Drop for EncodingDetector {
    fn drop(&mut self) {
        unsafe { ffi::uchardet_delete(self.ptr) };
    }
}
<|endoftext|>