<commit_before>//! A simple library for parsing an XML file into an in-memory tree structure
//!
//! Not recommended for large XML files, as it will load the entire file into memory.
//!
//! # Example
//!
//! ```no_run
//! use xmltree::Element;
//! use std::fs::File;
//!
//! let data: &'static str = r##"
//! <?xml version="1.0" encoding="utf-8" standalone="yes"?>
//! <names>
//!     <name first="bob" last="jones" />
//!     <name first="elizabeth" last="smith" />
//! </names>
//! "##;
//!
//! let mut names_element = Element::parse(data.as_bytes()).unwrap();
//!
//! println!("{:#?}", names_element);
//! {
//!     // get first `name` element
//!     let name = names_element.get_mut_child("name").expect("Can't find name element");
//!     name.attributes.insert("suffix".to_owned(), "mr".to_owned());
//! }
//! names_element.write(File::create("result.xml").unwrap());
//!
//!
//! ```
extern crate xml;

use std::collections::HashMap;
use std::io::{Read, Write};
use std::borrow::Cow;
use std::fmt;

use xml::reader::{EventReader, XmlEvent};
pub use xml::writer::{EmitterConfig, Error};
pub use xml::namespace::Namespace;

/// Represents an XML element.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Element {
    /// This elements prefix, if any
    pub prefix: Option<String>,

    /// This elements namespace, if any
    pub namespace: Option<String>,

    /// The full list of namespaces, if any
    ///
    /// The `Namespace` type is exported from the `xml-rs` crate.
    pub namespaces: Option<Namespace>,

    /// The name of the Element.  Does not include any namespace info
    pub name: String,

    /// The Element attributes
    pub attributes: HashMap<String, String>,

    /// Children
    pub children: Vec<Element>,

    /// The text data for this element
    pub text: Option<String>,
}

/// Errors that can occur parsing XML
#[derive(Debug)]
pub enum ParseError {
    /// The XML is invalid
    MalformedXml(xml::reader::Error),
    /// This library is unable to process this XML. This can occur if, for
    /// example, the XML contains processing instructions.
    CannotParse,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::MalformedXml(ref e) => write!(f, "Malformed XML. {}", e),
            ParseError::CannotParse => write!(f, "Cannot parse"),
        }
    }
}

impl std::error::Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::MalformedXml(..) => "Malformed XML",
            ParseError::CannotParse => "Cannot parse",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            ParseError::MalformedXml(ref e) => Some(e),
            ParseError::CannotParse => None,
        }
    }
}

fn build<B: Read>(reader: &mut EventReader<B>, mut elem: Element) -> Result<Element, ParseError> {
    loop {
        match reader.next() {
            Ok(XmlEvent::EndElement { ref name }) => {
                if name.local_name == elem.name {
                    return Ok(elem);
                } else {
                    return Err(ParseError::CannotParse);
                }
            }
            Ok(XmlEvent::StartElement {
                name,
                attributes,
                namespace,
            }) => {
                let mut attr_map = HashMap::new();
                for attr in attributes {
                    attr_map.insert(attr.name.local_name, attr.value);
                }

                let new_elem = Element {
                    prefix: name.prefix,
                    namespace: name.namespace,
                    namespaces: if namespace.is_essentially_empty() {
                        None
                    } else {
                        Some(namespace)
                    },
                    name: name.local_name,
                    attributes: attr_map,
                    children: Vec::new(),
                    text: None,
                };
                elem.children.push(try!(build(reader, new_elem)));
            }
            Ok(XmlEvent::Characters(s)) => {
                elem.text = Some(s);
            }
            Ok(XmlEvent::Whitespace(..)) | Ok(XmlEvent::Comment(..)) => (),
            Ok(XmlEvent::CData(s)) => elem.text = Some(s),
            Ok(XmlEvent::StartDocument { .. })
            | Ok(XmlEvent::EndDocument)
            | Ok(XmlEvent::ProcessingInstruction { .. }) => return Err(ParseError::CannotParse),
            Err(e) => return Err(ParseError::MalformedXml(e)),
        }
    }
}

impl Element {
    /// Create a new empty element with given name
    ///
    /// All other fields are empty
    pub fn new(name: &str) -> Element {
        Element {
            name: String::from(name),
            prefix: None,
            namespace: None,
            namespaces: None,
            attributes: HashMap::new(),
            children: Vec::new(),
            text: None,
        }
    }

    /// Parses some data into an Element
    pub fn parse<R: Read>(r: R) -> Result<Element, ParseError> {
        let mut reader = EventReader::new(r);
        loop {
            match reader.next() {
                Ok(XmlEvent::StartElement {
                    name,
                    attributes,
                    namespace,
                }) => {
                    let mut attr_map = HashMap::new();
                    for attr in attributes {
                        attr_map.insert(attr.name.local_name, attr.value);
                    }

                    let root = Element {
                        prefix: name.prefix,
                        namespace: name.namespace,
                        namespaces: if namespace.is_essentially_empty() {
                            None
                        } else {
                            Some(namespace)
                        },
                        name: name.local_name,
                        attributes: attr_map,
                        children: Vec::new(),
                        text: None,
                    };
                    return build(&mut reader, root);
                }
                Ok(XmlEvent::Comment(..))
                | Ok(XmlEvent::Whitespace(..))
                | Ok(XmlEvent::StartDocument { .. }) => continue,
                Ok(XmlEvent::EndDocument)
                | Ok(XmlEvent::EndElement { .. })
                | Ok(XmlEvent::Characters(..))
                | Ok(XmlEvent::CData(..))
                | Ok(XmlEvent::ProcessingInstruction { .. }) => return Err(ParseError::CannotParse),
                Err(e) => return Err(ParseError::MalformedXml(e)),
            }
        }
    }

    fn _write<B: Write>(&self, emitter: &mut xml::writer::EventWriter<B>) -> Result<(), Error> {
        use xml::writer::events::XmlEvent;
        use xml::name::Name;
        use xml::namespace::Namespace;
        use xml::attribute::Attribute;

        let mut name = Name::local(&self.name);
        if let Some(ref ns) = self.namespace {
            name.namespace = Some(ns);
        }
        if let Some(ref p) = self.prefix {
            name.prefix = Some(p);
        }

        let mut attributes = Vec::with_capacity(self.attributes.len());
        for (k, v) in &self.attributes {
            attributes.push(Attribute {
                name: Name::local(k),
                value: v,
            });
        }

        let empty_ns = Namespace::empty();
        let namespace = if let Some(ref ns) = self.namespaces {
            Cow::Borrowed(ns)
        } else {
            Cow::Borrowed(&empty_ns)
        };

        emitter.write(XmlEvent::StartElement {
            name: name,
            attributes: Cow::Owned(attributes),
            namespace: namespace,
        })?;
        if let Some(ref t) = self.text {
            emitter.write(XmlEvent::Characters(t))?;
        }
        for elem in &self.children {
            elem._write(emitter)?;
        }
        emitter.write(XmlEvent::EndElement { name: Some(name) })?;

        Ok(())
    }

    /// Writes out this element as the root element in an new XML document
    pub fn write<W: Write>(&self, w: W) -> Result<(), Error> {
        self.write_with_config(w, EmitterConfig::new())
    }

    /// Writes out this element as the root element in a new XML document using the provided configuration
    pub fn write_with_config<W: Write>(&self, w: W, config: EmitterConfig) -> Result<(), Error> {
        use xml::writer::EventWriter;
        use xml::writer::events::XmlEvent;
        use xml::common::XmlVersion;

        let mut emitter = EventWriter::new_with_config(w, config);
        emitter.write(XmlEvent::StartDocument {
            version: XmlVersion::Version10,
            encoding: None,
            standalone: None,
        })?;
        self._write(&mut emitter)
    }

    /// Find a child element with the given name and return a reference to it.
    pub fn get_child<K>(&self, k: K) -> Option<&Element>
    where
        String: PartialEq<K>,
    {
        self.children.iter().find(|e| e.name == k)
    }

    /// Find a child element with the given name and return a mutable reference to it.
    pub fn get_mut_child<K>(&mut self, k: K) -> Option<&mut Element>
    where
        String: PartialEq<K>,
    {
        self.children.iter_mut().find(|e| e.name == k)
    }

    /// Find a child element with the given name, remove and return it.
    pub fn take_child<K>(&mut self, k: K) -> Option<Element>
    where
        String: PartialEq<K>,
    {
        self.children
            .iter()
            .position(|e| e.name == k)
            .map(|i| self.children.remove(i))
    }
}
<commit_msg>fix warning: the item  is imported redundantly<commit_after>//! A simple library for parsing an XML file into an in-memory tree structure
//!
//! Not recommended for large XML files, as it will load the entire file into memory.
//!
//! # Example
//!
//! ```no_run
//! use xmltree::Element;
//! use std::fs::File;
//!
//! let data: &'static str = r##"
//! <?xml version="1.0" encoding="utf-8" standalone="yes"?>
//! <names>
//!     <name first="bob" last="jones" />
//!     <name first="elizabeth" last="smith" />
//! </names>
//! "##;
//!
//! let mut names_element = Element::parse(data.as_bytes()).unwrap();
//!
//! println!("{:#?}", names_element);
//! {
//!     // get first `name` element
//!     let name = names_element.get_mut_child("name").expect("Can't find name element");
//!     name.attributes.insert("suffix".to_owned(), "mr".to_owned());
//! }
//! names_element.write(File::create("result.xml").unwrap());
//!
//!
//! ```
extern crate xml;

use std::collections::HashMap;
use std::io::{Read, Write};
use std::borrow::Cow;
use std::fmt;

use xml::reader::{EventReader, XmlEvent};
pub use xml::writer::{EmitterConfig, Error};
pub use xml::namespace::Namespace;

/// Represents an XML element.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Element {
    /// This elements prefix, if any
    pub prefix: Option<String>,

    /// This elements namespace, if any
    pub namespace: Option<String>,

    /// The full list of namespaces, if any
    ///
    /// The `Namespace` type is exported from the `xml-rs` crate.
    pub namespaces: Option<Namespace>,

    /// The name of the Element.  Does not include any namespace info
    pub name: String,

    /// The Element attributes
    pub attributes: HashMap<String, String>,

    /// Children
    pub children: Vec<Element>,

    /// The text data for this element
    pub text: Option<String>,
}

/// Errors that can occur parsing XML
#[derive(Debug)]
pub enum ParseError {
    /// The XML is invalid
    MalformedXml(xml::reader::Error),
    /// This library is unable to process this XML. This can occur if, for
    /// example, the XML contains processing instructions.
    CannotParse,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParseError::MalformedXml(ref e) => write!(f, "Malformed XML. {}", e),
            ParseError::CannotParse => write!(f, "Cannot parse"),
        }
    }
}

impl std::error::Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::MalformedXml(..) => "Malformed XML",
            ParseError::CannotParse => "Cannot parse",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        match *self {
            ParseError::MalformedXml(ref e) => Some(e),
            ParseError::CannotParse => None,
        }
    }
}

fn build<B: Read>(reader: &mut EventReader<B>, mut elem: Element) -> Result<Element, ParseError> {
    loop {
        match reader.next() {
            Ok(XmlEvent::EndElement { ref name }) => {
                if name.local_name == elem.name {
                    return Ok(elem);
                } else {
                    return Err(ParseError::CannotParse);
                }
            }
            Ok(XmlEvent::StartElement {
                name,
                attributes,
                namespace,
            }) => {
                let mut attr_map = HashMap::new();
                for attr in attributes {
                    attr_map.insert(attr.name.local_name, attr.value);
                }

                let new_elem = Element {
                    prefix: name.prefix,
                    namespace: name.namespace,
                    namespaces: if namespace.is_essentially_empty() {
                        None
                    } else {
                        Some(namespace)
                    },
                    name: name.local_name,
                    attributes: attr_map,
                    children: Vec::new(),
                    text: None,
                };
                elem.children.push(try!(build(reader, new_elem)));
            }
            Ok(XmlEvent::Characters(s)) => {
                elem.text = Some(s);
            }
            Ok(XmlEvent::Whitespace(..)) | Ok(XmlEvent::Comment(..)) => (),
            Ok(XmlEvent::CData(s)) => elem.text = Some(s),
            Ok(XmlEvent::StartDocument { .. })
            | Ok(XmlEvent::EndDocument)
            | Ok(XmlEvent::ProcessingInstruction { .. }) => return Err(ParseError::CannotParse),
            Err(e) => return Err(ParseError::MalformedXml(e)),
        }
    }
}

impl Element {
    /// Create a new empty element with given name
    ///
    /// All other fields are empty
    pub fn new(name: &str) -> Element {
        Element {
            name: String::from(name),
            prefix: None,
            namespace: None,
            namespaces: None,
            attributes: HashMap::new(),
            children: Vec::new(),
            text: None,
        }
    }

    /// Parses some data into an Element
    pub fn parse<R: Read>(r: R) -> Result<Element, ParseError> {
        let mut reader = EventReader::new(r);
        loop {
            match reader.next() {
                Ok(XmlEvent::StartElement {
                    name,
                    attributes,
                    namespace,
                }) => {
                    let mut attr_map = HashMap::new();
                    for attr in attributes {
                        attr_map.insert(attr.name.local_name, attr.value);
                    }

                    let root = Element {
                        prefix: name.prefix,
                        namespace: name.namespace,
                        namespaces: if namespace.is_essentially_empty() {
                            None
                        } else {
                            Some(namespace)
                        },
                        name: name.local_name,
                        attributes: attr_map,
                        children: Vec::new(),
                        text: None,
                    };
                    return build(&mut reader, root);
                }
                Ok(XmlEvent::Comment(..))
                | Ok(XmlEvent::Whitespace(..))
                | Ok(XmlEvent::StartDocument { .. }) => continue,
                Ok(XmlEvent::EndDocument)
                | Ok(XmlEvent::EndElement { .. })
                | Ok(XmlEvent::Characters(..))
                | Ok(XmlEvent::CData(..))
                | Ok(XmlEvent::ProcessingInstruction { .. }) => return Err(ParseError::CannotParse),
                Err(e) => return Err(ParseError::MalformedXml(e)),
            }
        }
    }

    fn _write<B: Write>(&self, emitter: &mut xml::writer::EventWriter<B>) -> Result<(), Error> {
        use xml::writer::events::XmlEvent;
        use xml::name::Name;
        use xml::attribute::Attribute;

        let mut name = Name::local(&self.name);
        if let Some(ref ns) = self.namespace {
            name.namespace = Some(ns);
        }
        if let Some(ref p) = self.prefix {
            name.prefix = Some(p);
        }

        let mut attributes = Vec::with_capacity(self.attributes.len());
        for (k, v) in &self.attributes {
            attributes.push(Attribute {
                name: Name::local(k),
                value: v,
            });
        }

        let empty_ns = Namespace::empty();
        let namespace = if let Some(ref ns) = self.namespaces {
            Cow::Borrowed(ns)
        } else {
            Cow::Borrowed(&empty_ns)
        };

        emitter.write(XmlEvent::StartElement {
            name: name,
            attributes: Cow::Owned(attributes),
            namespace: namespace,
        })?;
        if let Some(ref t) = self.text {
            emitter.write(XmlEvent::Characters(t))?;
        }
        for elem in &self.children {
            elem._write(emitter)?;
        }
        emitter.write(XmlEvent::EndElement { name: Some(name) })?;

        Ok(())
    }

    /// Writes out this element as the root element in an new XML document
    pub fn write<W: Write>(&self, w: W) -> Result<(), Error> {
        self.write_with_config(w, EmitterConfig::new())
    }

    /// Writes out this element as the root element in a new XML document using the provided configuration
    pub fn write_with_config<W: Write>(&self, w: W, config: EmitterConfig) -> Result<(), Error> {
        use xml::writer::EventWriter;
        use xml::writer::events::XmlEvent;
        use xml::common::XmlVersion;

        let mut emitter = EventWriter::new_with_config(w, config);
        emitter.write(XmlEvent::StartDocument {
            version: XmlVersion::Version10,
            encoding: None,
            standalone: None,
        })?;
        self._write(&mut emitter)
    }

    /// Find a child element with the given name and return a reference to it.
    pub fn get_child<K>(&self, k: K) -> Option<&Element>
    where
        String: PartialEq<K>,
    {
        self.children.iter().find(|e| e.name == k)
    }

    /// Find a child element with the given name and return a mutable reference to it.
    pub fn get_mut_child<K>(&mut self, k: K) -> Option<&mut Element>
    where
        String: PartialEq<K>,
    {
        self.children.iter_mut().find(|e| e.name == k)
    }

    /// Find a child element with the given name, remove and return it.
    pub fn take_child<K>(&mut self, k: K) -> Option<Element>
    where
        String: PartialEq<K>,
    {
        self.children
            .iter()
            .position(|e| e.name == k)
            .map(|i| self.children.remove(i))
    }
}
<|endoftext|>


<commit_before>pub mod prelude;

#[macro_export] macro_rules! lisp {
    // special forms
    ((lambda (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {
        // regular lambda
        |$($argn:$argt),*| -> $ret { $(lisp!($body));* }
    };
    ((lambda $s:ident (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {{
        // recursive lambda
        // $s MUST be "self"
        // recurse by calling (self ...)
        struct F;
        impl FnOnce<($($argt,)*)> for F {
            type Output = $ret;
            extern "rust-call" fn call_once($s, ($($argn,)*): ($($argt,)*)) -> $ret {
                $(lisp!($body));*
            }
        }
        impl FnMut<($($argt,)*)> for F {
            extern "rust-call" fn call_mut(&mut $s, ($($argn,)*): ($($argt,)*)) -> $ret {
                $(lisp!($body));*
            }
        }
        impl Fn<($($argt,)*)> for F {
            extern "rust-call" fn call(&$s, ($($argn,)*): ($($argt,)*)) -> $ret {
                $(lisp!($body));*
            }
        }
        F
    }};
    ((defn $name:ident (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {
        fn $name($($argn:$argt),*) -> $ret { $(lisp!($body));* }
    };
    ((_if $cond:tt $yes:tt $no:tt)) => {
        if lisp!($cond) { lisp!($yes) } else { lisp!($no) }
    };
    ((_while $cond:tt $($body:tt)*)) => {
        while lisp!($cond) { $(lisp!($body));* }
    };
    ((_match $var:tt $(($cond:tt $arm:tt))*)) => {
        match lisp!($var) {
            $(lisp!(__PAT__ $cond) => lisp!($arm)),*
        }
    };
    ((_prn $($arg:tt)*)) => {
        println!($(lisp!($arg)),*)
    };

    // variables
    ((_let mut $var:ident $val:tt)) => {
        let mut $var = lisp!($val);
    };
    ((_let $var:ident $val:tt)) => {
        let $var = lisp!($val);
    };
    ((_set $var:ident $val:tt)) => {
        $var = lisp!($val);
    };

    // escape hatch
    ((_rust $body:block)) => {
        { $body }
    };

    // list parsing
    (($($elem:tt)*)) => {
        lisp!(__LIST__ $($elem),*)
    };

    // operators and function calls
    (__LIST__ -,    $arg:tt   ) => { lisp!(__UNARY_OP__  _neg,   $arg   ) };
    (__LIST__ !,    $arg:tt   ) => { lisp!(__UNARY_OP__  _not,   $arg   ) };
    (__LIST__ +,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _add, $($arg),*) };
    (__LIST__ &,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _and, $($arg),*) };
    (__LIST__ |,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _or,  $($arg),*) };
    (__LIST__ ^,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _xor, $($arg),*) };
    (__LIST__ /,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _div, $($arg),*) };
    (__LIST__ *,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _mul, $($arg),*) };
    (__LIST__ %,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _rem, $($arg),*) };
    (__LIST__ <<, $($arg:tt),*) => { lisp!(__BINARY_OP__ _shl, $($arg),*) };
    (__LIST__ >>, $($arg:tt),*) => { lisp!(__BINARY_OP__ _shr, $($arg),*) };
    (__LIST__ -,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _sub, $($arg),*) };
    (__LIST__ ==, $($arg:tt),*) => { lisp!(__BINARY_OP__ _eq,  $($arg),*) };
    (__LIST__ !=, $($arg:tt),*) => { lisp!(__BINARY_OP__ _ne,  $($arg),*) };
    (__LIST__ >,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _gt,  $($arg),*) };
    (__LIST__ <,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _lt,  $($arg),*) };
    (__LIST__ >=, $($arg:tt),*) => { lisp!(__BINARY_OP__ _ge,  $($arg),*) };
    (__LIST__ <=, $($arg:tt),*) => { lisp!(__BINARY_OP__ _le,  $($arg),*) };

    (__UNARY_OP__  $op:ident, $a:tt)        => { lisp!(__LIST__ $op, $a) };
    (__BINARY_OP__ $op:ident, $a:tt, $b:tt) => { lisp!(__LIST__ $op, $a, $b) };
    (__BINARY_OP__ $op:ident, $a:tt, $b:tt, $($rest:tt),+) =>
                                               { $op(lisp!($a),
                                                     lisp!(__BINARY_OP__ $op, $b, $($rest),+)) };
    (__LIST__ $name:expr) => {
        lisp!($name)()
    };
    (__LIST__ $name:expr, $($arg:tt),*) => {
        lisp!($name)($(lisp!($arg)),*)
    };

    // one expression
    ($e:expr) => ($e);
    (__PAT__ $p:pat) => ($p);

    // empty
    () => (());
}

<commit_msg>operators now right-associative<commit_after>pub mod prelude;

#[macro_export] macro_rules! lisp {
    // special forms
    ((lambda (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {
        // regular lambda
        |$($argn:$argt),*| -> $ret { $(lisp!($body));* }
    };
    ((lambda $s:ident (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {{
        // recursive lambda
        // $s MUST be "self"
        // recurse by calling (self ...)
        struct F;
        impl FnOnce<($($argt,)*)> for F {
            type Output = $ret;
            extern "rust-call" fn call_once($s, ($($argn,)*): ($($argt,)*)) -> $ret {
                $(lisp!($body));*
            }
        }
        impl FnMut<($($argt,)*)> for F {
            extern "rust-call" fn call_mut(&mut $s, ($($argn,)*): ($($argt,)*)) -> $ret {
                $(lisp!($body));*
            }
        }
        impl Fn<($($argt,)*)> for F {
            extern "rust-call" fn call(&$s, ($($argn,)*): ($($argt,)*)) -> $ret {
                $(lisp!($body));*
            }
        }
        F
    }};
    ((defn $name:ident (($(($argn:ident $argt:ty))*) $ret:ty) $($body:tt)*)) => {
        fn $name($($argn:$argt),*) -> $ret { $(lisp!($body));* }
    };
    ((_if $cond:tt $yes:tt $no:tt)) => {
        if lisp!($cond) { lisp!($yes) } else { lisp!($no) }
    };
    ((_while $cond:tt $($body:tt)*)) => {
        while lisp!($cond) { $(lisp!($body));* }
    };
    ((_match $var:tt $(($cond:tt $arm:tt))*)) => {
        match lisp!($var) {
            $(lisp!(__PAT__ $cond) => lisp!($arm)),*
        }
    };
    ((_prn $($arg:tt)*)) => {
        println!($(lisp!($arg)),*)
    };

    // variables
    ((_let mut $var:ident $val:tt)) => {
        let mut $var = lisp!($val);
    };
    ((_let $var:ident $val:tt)) => {
        let $var = lisp!($val);
    };
    ((_set $var:ident $val:tt)) => {
        $var = lisp!($val);
    };

    // escape hatch
    ((_rust $body:block)) => {
        { $body }
    };

    // list parsing
    (($($elem:tt)*)) => {
        lisp!(__LIST__ $($elem),*)
    };

    // parsers for unary and binary operators
    (__LIST__ -,    $arg:tt   ) => { lisp!(__UNARY_OP__  _neg,   $arg   ) };
    (__LIST__ !,    $arg:tt   ) => { lisp!(__UNARY_OP__  _not,   $arg   ) };
    (__LIST__ +,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _add, $($arg),*) };
    (__LIST__ &,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _and, $($arg),*) };
    (__LIST__ |,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _or,  $($arg),*) };
    (__LIST__ ^,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _xor, $($arg),*) };
    (__LIST__ /,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _div, $($arg),*) };
    (__LIST__ *,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _mul, $($arg),*) };
    (__LIST__ %,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _rem, $($arg),*) };
    (__LIST__ <<, $($arg:tt),*) => { lisp!(__BINARY_OP__ _shl, $($arg),*) };
    (__LIST__ >>, $($arg:tt),*) => { lisp!(__BINARY_OP__ _shr, $($arg),*) };
    (__LIST__ -,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _sub, $($arg),*) };
    (__LIST__ ==, $($arg:tt),*) => { lisp!(__BINARY_OP__ _eq,  $($arg),*) };
    (__LIST__ !=, $($arg:tt),*) => { lisp!(__BINARY_OP__ _ne,  $($arg),*) };
    (__LIST__ >,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _gt,  $($arg),*) };
    (__LIST__ <,  $($arg:tt),*) => { lisp!(__BINARY_OP__ _lt,  $($arg),*) };
    (__LIST__ >=, $($arg:tt),*) => { lisp!(__BINARY_OP__ _ge,  $($arg),*) };
    (__LIST__ <=, $($arg:tt),*) => { lisp!(__BINARY_OP__ _le,  $($arg),*) };

    // generically turn unary/binary operators into function calls
    // binary operators can be used as n-ary operators through __REDUCE__
    (__UNARY_OP__  $op:ident, $a:tt)        => { lisp!(__LIST__ $op, $a)      };
    (__BINARY_OP__ $op:ident, $a:tt, $b:tt) => { lisp!(__LIST__ $op, $a, $b)  };
    (__BINARY_OP__ $op:ident, $a:tt, $b:tt, $($rest:tt),+) =>
                                               { lisp!(__REDUCE__ $op,
                                                                  ($op $a $b),
                                                                  $($rest),+) };

    // reduce implementation
    (__REDUCE__ $op:ident, $acc:tt)                       => { lisp!($acc)                     };
    (__REDUCE__ $op:ident, $acc:tt, $a:tt)                => { lisp!(__REDUCE__ $op,
                                                                                ($op $acc $a)) };
    (__REDUCE__ $op:ident, $acc:tt, $a:tt, $($rest:tt),+) => { lisp!(__REDUCE__ $op,
                                                                                ($op $acc $a),
                                                                                $($rest),+)    };

    // function calls
    (__LIST__ $name:expr) => {
        lisp!($name)()
    };
    (__LIST__ $name:expr, $($arg:tt),*) => {
        lisp!($name)($(lisp!($arg)),*)
    };

    // one expression
    ($e:expr) => ($e);
    (__PAT__ $p:pat) => ($p);

    // empty
    () => (());
}

<|endoftext|>


<commit_before>//! # urlshortener
//!
//! An easy library for retrieving short urls.
//!
//! ## Installation
//!
//! Add the following dependency to your project's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! urlshortener = "0.4"
//! ```
//!
//! And add this to your root file:
//!
//! ```no_run
//! extern crate urlshortener;
//! ```
//!
//! ## Usage
//!
//! Creating a short URL via a specified provider is very simple:
//!
//! ```no_run
//! use urlshortener::{Provider, UrlShortener};
//!
//! let us = UrlShortener::new();
//! let short_url = us.generate("https://my-long-url.com", Provider::IsGd);
//! ```
//!
//! Or attempting all URL shorteners until one is successfully generated:
//!
//! ```no_run
//! use urlshortener::{Provider, UrlShortener};
//!
//! let us = UrlShortener::new();
//! let short_url = us.try_generate("https://my-long-url.com");
//! ```

#[macro_use]
extern crate log;
extern crate hyper;

pub mod providers;

pub use providers::{Provider, providers};

use providers::{parse, request};
use hyper::Client;
use std::io::{ErrorKind, Error, Read};
use std::time::Duration;

/// Url shortener - the way to retrieve a short url.
pub struct UrlShortener {
    client: Client,
}

impl UrlShortener {
    /// Creates new `UrlShortener` with default (3 seconds) timeout.
    pub fn new() -> UrlShortener {
        UrlShortener::with_timeout(3)
    }

    /// Creates new `UrlShortener` and with read timeout.
    pub fn with_timeout(seconds: u64) -> UrlShortener {
        let mut client = hyper::Client::new();
        client.set_read_timeout(Some(Duration::from_secs(seconds)));

        UrlShortener {
            client: client,
        }
    }

    /// Try to generate a short URL from each provider, iterating over each
    /// provider until a short URL is successfully generated.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use urlshortener::UrlShortener;
    ///
    /// let us = UrlShortener::new();
    /// let long_url = "https://rust-lang.org";
    /// let short_url = us.try_generate(long_url);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an `Error<ErrorKind::Other>` if there is an error generating a
    /// short URL from all providers.
    pub fn try_generate(&self, url: &str) -> Result<String, Error> {
        let mut providers = providers();

        let x = 0usize;

        loop {
            if providers.len() == 0 {
                break
            }

            // This would normally have the potential to panic, except that a
            // check to ensure there is an element at this index is performed.
            let provider = providers.remove(x);
            let res = self.generate(url, provider);

            if let Ok(s) = res {
                return Ok(s)
            } else {
                warn!("Failed to get short link from service: {}",
                      res.unwrap_err());
            }
        }
        error!("Failed to get short link from any service");
        Err(Error::new(ErrorKind::Other, "Failed to get short link from any service"))
    }

    /// Attempts to get a short URL using the specified provider.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use urlshortener::{Provider, UrlShortener};
    ///
    /// let us = UrlShortener::new();
    /// let long_url = "http://rust-lang.org";
    /// let short_url = us.generate(long_url, Provider::IsGd);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an `Error<<ErrorKind::Other>` if there is an error generating a
    /// short URL from the given provider due to either:
    ///
    /// a. a decode error;
    /// b. the service being unavailable
    pub fn generate(&self, url: &str, provider: Provider) -> Result<String, Error> {
        let response_opt = request(url, &self.client, provider);

        if let Some(mut response) = response_opt {
            if response.status.is_success() {
                let mut short_url = String::new();
                if try!(response.read_to_string(&mut short_url)) > 0 {
                    if let Some(s) = parse(&short_url, provider) {
                        return Ok(s)
                    } else {
                        return Err(Error::new(ErrorKind::Other, "Decode error"))
                    }
                }
            }
        }
        Err(Error::new(ErrorKind::Other, "Service is unavailable"))
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn providers() {
        let us = ::UrlShortener::with_timeout(5);
        let url = "http://stackoverflow.com";

        for provider in ::providers() {
            assert!(us.generate(url, provider).is_ok());
        }
    }
}
<commit_msg>Fixes #3<commit_after>//! # urlshortener
//!
//! An easy library for retrieving short urls.
//!
//! ## Installation
//!
//! Add the following dependency to your project's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! urlshortener = "0.4"
//! ```
//!
//! And add this to your root file:
//!
//! ```no_run
//! extern crate urlshortener;
//! ```
//!
//! ## Usage
//!
//! Creating a short URL via a specified provider is very simple:
//!
//! ```no_run
//! use urlshortener::{Provider, UrlShortener};
//!
//! let us = UrlShortener::new();
//! let short_url = us.generate("https://my-long-url.com", Provider::IsGd);
//! assert!(short_url.is_ok());
//! ```
//!
//! Or attempting all URL shorteners until one is successfully generated:
//!
//! ```no_run
//! use urlshortener::UrlShortener;
//!
//! let us = UrlShortener::new();
//! let short_url = us.try_generate("https://my-long-url.com");
//! assert!(short_url.is_ok());
//! ```

#[macro_use]
extern crate log;
extern crate hyper;

pub mod providers;

pub use providers::{Provider, providers};

use providers::{parse, request};
use hyper::Client;
use std::io::{ErrorKind, Error, Read};
use std::time::Duration;

/// Url shortener - the way to retrieve a short url.
pub struct UrlShortener {
    client: Client,
}

impl UrlShortener {
    /// Creates new `UrlShortener` with default (3 seconds) timeout.
    pub fn new() -> UrlShortener {
        UrlShortener::with_timeout(3)
    }

    /// Creates new `UrlShortener` and with read timeout.
    pub fn with_timeout(seconds: u64) -> UrlShortener {
        let mut client = hyper::Client::new();
        client.set_read_timeout(Some(Duration::from_secs(seconds)));

        UrlShortener {
            client: client,
        }
    }

    /// Try to generate a short URL from each provider, iterating over each
    /// provider until a short URL is successfully generated.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use urlshortener::UrlShortener;
    ///
    /// let us = UrlShortener::new();
    /// let long_url = "https://rust-lang.org";
    /// let short_url = us.try_generate(long_url);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an `Error<ErrorKind::Other>` if there is an error generating a
    /// short URL from all providers.
    pub fn try_generate(&self, url: &str) -> Result<String, Error> {
        let mut providers = providers();

        let x = 0usize;

        loop {
            if providers.len() == 0 {
                break
            }

            // This would normally have the potential to panic, except that a
            // check to ensure there is an element at this index is performed.
            let provider = providers.remove(x);
            let res = self.generate(url, provider);

            if let Ok(s) = res {
                return Ok(s)
            } else {
                warn!("Failed to get short link from service: {}",
                      res.unwrap_err());
            }
        }
        error!("Failed to get short link from any service");
        Err(Error::new(ErrorKind::Other, "Failed to get short link from any service"))
    }

    /// Attempts to get a short URL using the specified provider.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use urlshortener::{Provider, UrlShortener};
    ///
    /// let us = UrlShortener::new();
    /// let long_url = "http://rust-lang.org";
    /// let short_url = us.generate(long_url, Provider::IsGd);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns an `Error<<ErrorKind::Other>` if there is an error generating a
    /// short URL from the given provider due to either:
    ///
    /// a. a decode error;
    /// b. the service being unavailable
    pub fn generate(&self, url: &str, provider: Provider) -> Result<String, Error> {
        let response_opt = request(url, &self.client, provider);

        if let Some(mut response) = response_opt {
            if response.status.is_success() {
                let mut short_url = String::new();
                if try!(response.read_to_string(&mut short_url)) > 0 {
                    if let Some(s) = parse(&short_url, provider) {
                        return Ok(s)
                    } else {
                        return Err(Error::new(ErrorKind::Other, "Decode error"))
                    }
                }
            }
        }
        Err(Error::new(ErrorKind::ConnectionAborted, "Service is unavailable"))
    }
}

#[cfg(test)]
mod tests {
    use std::io::ErrorKind;

    #[test]
    fn providers() {
        let us = ::UrlShortener::with_timeout(5);
        let url = "http://stackoverflow.com";

        for provider in ::providers() {
            if let Some(err) = us.generate(url, provider).err() {
                assert!(err.kind() == ErrorKind::ConnectionAborted);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Read from multiple input streams.
//!
//! A `FileInput` implements the `std::io::Read` trait and reads the contents of each file
//! specified (`-` means standard input), or standard input if none are given.
//!
//! An example that prints out all the lines in each of the two files specified:
//!
//! ```
//! use std::io::{BufRead,BufReader};
//! use fileinput::FileInput;
//!
//! let filenames = vec!["testdata/1", "testdata/2"];
//! let fileinput = FileInput::new(&filenames);
//! let mut reader = BufReader::new(fileinput);
//!
//! for line in reader.lines() {
//!     println!("{}", line.unwrap());
//! }
//! ```
use std::fs::File;
use std::io;
use std::io::{Read,stdin};
use std::borrow::Borrow;

#[derive(Debug, Eq, PartialEq)]
enum Source {
    Stdin,
    File(String),
}

fn make_source_vec<T>(filenames: &[T]) -> Vec<Source> where T: Borrow<str> {
    if filenames.is_empty() {
        return vec![Source::Stdin];
    }

    let mut sources = Vec::with_capacity(filenames.len());
    for filename in filenames {
        sources.push(match filename.borrow() {
            "-" => Source::Stdin,
            filename => Source::File(filename.to_string()),
        });
    }
    sources
}

struct State {
    source: Source,
    reader: Box<Read>,
}

/// A wrapper which reads from multiple streams.
pub struct FileInput {
    sources: Vec<Source>,
    state: Option<State>,
}

impl FileInput {
    /// Constructs a new `FileInput` that will read from the files specified.
    pub fn new<T>(paths: &[T]) -> FileInput where T: Borrow<str> {
        FileInput {
            sources: make_source_vec(paths),
            state: None,
        }
    }

    /// Returns the filename of the current file being read.
    ///
    /// This function will return `None` if no reading has been done yet or all the inputs have
    /// been drained. If the current input is standard in, this will return `"<stdin>"`.
    pub fn filename(&self) -> Option<String> {
        if self.state.is_none() {
            return None;
        }

        Some(match self.state.as_ref().unwrap().source {
            Source::Stdin => "<stdin>".to_string(),
            Source::File(ref file) => file.clone(),
        })
    }

    fn open_next_file(&mut self) -> io::Result<()> {
        let next_source = self.sources.remove(0);
        let reader: Box<Read> = match &next_source {
            &Source::Stdin => Box::new(stdin()),
            &Source::File(ref path) => Box::new(try!(File::open(path))),
        };

        self.state = Some(State {
            source: next_source,
            reader: reader,
        });

        Ok(())
    }
}

impl Read for FileInput {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            if self.state.is_none() {
                if self.sources.is_empty() {
                    return Ok(0);
                }

                try!(self.open_next_file());
            }

            let bytes_read = try!(self.state.as_mut().unwrap().reader.read(buf));

            if bytes_read == 0 {
                self.state = None;
                continue;
            }

            return Ok(bytes_read);
        }
    }
}

#[cfg(test)]
mod test {
    mod source_vec {
        use super::super::{make_source_vec,Source};

        #[test]
        fn empty_list_makes_stdin() {
            let names: Vec<String> = vec![];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn dash_makes_stdin() {
            let names = vec!["-"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn filename_makes_path() {
            let names = vec!["example-file"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("example-file".to_string())]);
        }

        #[test]
        fn mixed() {
            let names = vec!["one", "two", "-", "three"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("one".to_string()), Source::File("two".to_string()), Source::Stdin, Source::File("three".to_string())]);
        }
    }

    mod fileinput {
        use super::super::*;
        use std::io::{Read,ErrorKind,BufRead,BufReader};

        #[test]
        fn read_files() {
            let paths = vec!["testdata/1", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn skip_empty_file() {
            let paths = vec!["testdata/1", "testdata/empty", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn get_filename() {
            let paths = vec!["testdata/1", "testdata/2"];
            let fileinput = FileInput::new(&paths);
            let mut reader = BufReader::new(fileinput);
            let mut buffer = String::new();

            assert_eq!(reader.get_ref().filename(), None);
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().filename(), Some("testdata/1".to_string()));
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().filename(), Some("testdata/2".to_string()));
            reader.read_line(&mut buffer).unwrap();
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().filename(), None);
        }

        #[test]
        fn error_on_nonexistent_file() {
            let paths = vec!["testdata/NOPE"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();
            let result = fileinput.read_to_string(&mut buffer);

            assert_eq!(result.unwrap_err().kind(), ErrorKind::NotFound);
        }
    }
}
<commit_msg>Return an enum instead of string to indicate current source.<commit_after>//! Read from multiple input streams.
//!
//! A `FileInput` implements the `std::io::Read` trait and reads the contents of each file
//! specified (`-` means standard input), or standard input if none are given.
//!
//! An example that prints out all the lines in each of the two files specified:
//!
//! ```
//! use std::io::{BufRead,BufReader};
//! use fileinput::FileInput;
//!
//! let filenames = vec!["testdata/1", "testdata/2"];
//! let fileinput = FileInput::new(&filenames);
//! let mut reader = BufReader::new(fileinput);
//!
//! for line in reader.lines() {
//!     println!("{}", line.unwrap());
//! }
//! ```
use std::fs::File;
use std::io;
use std::io::{Read,stdin};
use std::borrow::Borrow;

/// A file source.
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Source {
    /// Read from the process's standard in.
    Stdin,
    /// Read from the specified file.
    File(String),
}

fn make_source_vec<T>(filenames: &[T]) -> Vec<Source> where T: Borrow<str> {
    if filenames.is_empty() {
        return vec![Source::Stdin];
    }

    let mut sources = Vec::with_capacity(filenames.len());
    for filename in filenames {
        sources.push(match filename.borrow() {
            "-" => Source::Stdin,
            filename => Source::File(filename.to_string()),
        });
    }
    sources
}

struct State {
    source: Source,
    reader: Box<Read>,
}

/// A wrapper which reads from multiple streams.
pub struct FileInput {
    sources: Vec<Source>,
    state: Option<State>,
}

impl FileInput {
    /// Constructs a new `FileInput` that will read from the files specified.
    pub fn new<T>(paths: &[T]) -> FileInput where T: Borrow<str> {
        FileInput {
            sources: make_source_vec(paths),
            state: None,
        }
    }

    /// Returns the current source being read from.
    ///
    /// This function will return `None` if no reading has been done yet or all the inputs have
    /// been drained.
    pub fn source(&self) -> Option<Source> {
        self.state.as_ref().map(|s| s.source.clone())
    }

    fn open_next_file(&mut self) -> io::Result<()> {
        let next_source = self.sources.remove(0);
        let reader: Box<Read> = match &next_source {
            &Source::Stdin => Box::new(stdin()),
            &Source::File(ref path) => Box::new(try!(File::open(path))),
        };

        self.state = Some(State {
            source: next_source,
            reader: reader,
        });

        Ok(())
    }
}

impl Read for FileInput {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            if self.state.is_none() {
                if self.sources.is_empty() {
                    return Ok(0);
                }

                try!(self.open_next_file());
            }

            let bytes_read = try!(self.state.as_mut().unwrap().reader.read(buf));

            if bytes_read == 0 {
                self.state = None;
                continue;
            }

            return Ok(bytes_read);
        }
    }
}

#[cfg(test)]
mod test {
    mod source_vec {
        use super::super::{make_source_vec,Source};

        #[test]
        fn empty_list_makes_stdin() {
            let names: Vec<String> = vec![];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn dash_makes_stdin() {
            let names = vec!["-"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::Stdin]);
        }

        #[test]
        fn filename_makes_path() {
            let names = vec!["example-file"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("example-file".to_string())]);
        }

        #[test]
        fn mixed() {
            let names = vec!["one", "two", "-", "three"];
            let paths = make_source_vec(&names);
            assert_eq!(paths, [Source::File("one".to_string()), Source::File("two".to_string()), Source::Stdin, Source::File("three".to_string())]);
        }
    }

    mod fileinput {
        use super::super::*;
        use std::io::{Read,ErrorKind,BufRead,BufReader};

        #[test]
        fn read_files() {
            let paths = vec!["testdata/1", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn skip_empty_file() {
            let paths = vec!["testdata/1", "testdata/empty", "testdata/2"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();

            fileinput.read_to_string(&mut buffer).unwrap();

            assert_eq!(buffer, "One.\nTwo.\nTwo.\n");
        }

        #[test]
        fn get_source() {
            let paths = vec!["testdata/1", "testdata/2"];
            let fileinput = FileInput::new(&paths);
            let mut reader = BufReader::new(fileinput);
            let mut buffer = String::new();

            assert_eq!(reader.get_ref().source(), None);
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/1".to_string())));
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), Some(Source::File("testdata/2".to_string())));
            reader.read_line(&mut buffer).unwrap();
            reader.read_line(&mut buffer).unwrap();
            assert_eq!(reader.get_ref().source(), None);
        }

        #[test]
        fn error_on_nonexistent_file() {
            let paths = vec!["testdata/NOPE"];
            let mut fileinput = FileInput::new(&paths);
            let mut buffer = String::new();
            let result = fileinput.read_to_string(&mut buffer);

            assert_eq!(result.unwrap_err().kind(), ErrorKind::NotFound);
        }
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "chrono_time")]
extern crate chrono;
extern crate dtoa;
extern crate encoding;
extern crate flate2;
#[cfg(feature = "embed_image")]
extern crate image;
extern crate itoa;
extern crate linked_hash_map;
extern crate pom;
extern crate time;

#[macro_use]
mod object;
mod datetime;
pub use crate::object::{Dictionary, Object, ObjectId, Stream, StringFormat};

mod document;
mod object_stream;
mod xref;
pub use crate::document::Document;

pub mod content;
mod creator;
mod encodings;
mod filters;
mod parser;
mod processor;
mod reader;
mod writer;
pub mod xobject;
<commit_msg>extern crate is not required anymore with 2018 edition.<commit_after>#[macro_use]
mod object;
mod datetime;
pub use crate::object::{Dictionary, Object, ObjectId, Stream, StringFormat};

mod document;
mod object_stream;
mod xref;
pub use crate::document::Document;

pub mod content;
mod creator;
mod encodings;
mod filters;
mod parser;
mod processor;
mod reader;
mod writer;
pub mod xobject;
<|endoftext|>


<commit_before>#[deny(missing_docs)]

extern crate input;
extern crate piston_window;
extern crate rustc_serialize;
extern crate viewport;

mod builder;

use input::{Input, Button, Motion};
use piston_window::Size;
use std::collections::HashMap;
use std::default::Default;
use viewport::Viewport;

pub use builder::InputMapBuilder;

/// Represents a logical action to be bound to a particular button press, e.g.
/// jump, attack, or move forward
pub trait Action: Copy + PartialEq + Eq { }

/// A translated action.
#[derive(Debug, Copy, Clone)]
pub enum Translated<A: Action> {
    /// A keypress event which was bound to an action
    Press(A),

    /// A key release event which was bound to an action
    Release(A),

    /// A translated mouse motion. The logical origin of a translated MouseCursor event
    /// is in the top left corner of the window, and the logical scroll is non-natural.
    /// Relative events are unchanged for now.
    Move(Motion)
}

/// An object which translates piston::input::Input events into input_map::Translated<A> events
#[derive(Clone)]
pub struct InputMap<A: Action> {
    keymap: KeyMap<A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputMap<A> {

    /// Creates an empty InputMap.
    pub fn new(size: Size) -> Self {
        InputMap {
            keymap: KeyMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    /// Translate an Input into a Translated<A> event
    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(($but_state:ident, $but_var:ident) => (
            match self.keymap.translate($but_var) {
                Some(act) => Some(Translated::$but_state(act)),
                None => None
            });
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    pub fn rebind_button(&mut self, _but: Button, _act: A) {
        // TODO implement
    }

    pub fn add_binding(&mut self, but: Button, act: A) {
        self.keymap.add_mapping(but, act);
    }

    /// Get all the bindings for an action
    pub fn get_bindings_for_action(&self, _act: A) -> ButtonTuple {
        ButtonTuple(None, None, None) // TODO implement
    }

    /// Re-set the mouse bounds size used for calculating mouse events
    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.viewport_size = size
    }

    /// Re-set the mouse bounds size from a viewport
    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslator {
    pub x_axis_motion_inverted: bool,
    pub y_axis_motion_inverted: bool,
    pub x_axis_scroll_inverted: bool,
    pub y_axis_scroll_inverted: bool,
    pub viewport_size: Size
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            x_axis_motion_inverted: false,
            y_axis_motion_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.viewport_size;
                    (width as f64, height as f64)
                };

                let cx = if self.x_axis_motion_inverted { sw - x } else { x };
                let cy = if self.y_axis_motion_inverted { sh - y } else { y };

                Motion::MouseCursor(cx, cy)
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[derive(Clone, Debug)]
struct KeyMap<A: Action> {
    btn_map: HashMap<ButtonTuple, A>
}

impl<A: Action> KeyMap<A> {
    fn new() -> Self {
        KeyMap {
            btn_map: HashMap::new()
        }
    }

    fn add_mapping(&mut self, button: Button, action: A) {
        let mut bt = self.get_bindings_for_action(action).unwrap_or(ButtonTuple::new());
        let bt = if bt.insert_inplace(button) {bt} else {ButtonTuple::new()};
        self.btn_map.insert(bt, action);
    }

    fn get_bindings_for_action(&self, action: A) -> Option<ButtonTuple> {
        self.btn_map.iter().find(|&(_, &a)| a == action).map(|(&bt, _)| bt)
    }

    #[allow(dead_code)]
    fn get_ref_bindings_for_action(&self, action: A) -> Option<&ButtonTuple> {
        self.btn_map.iter().find(|&(_, &a)| a == action).map(|(bt, _)| bt)
    }

    #[cfg(unimplemented)] // This gives a compile error
    fn get_ref_bindings_for_action_mut(&mut self, action: A) -> Option<&mut ButtonTuple> {
        self.btn_map.iter_mut().find(|&(_, &mut a)| a == action).as_mut().map(|&mut (bt, _)| bt)
    }

    fn translate(&self, button: Button) -> Option<A> {
        self.btn_map.iter().find(|&(&bt, _)| bt.contains(button)).map(|(_, &a)| a)
    }
}

/// A three-element tuple of Option<Button>. Used as the key of an InputMap
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(pub Option<Button>, pub Option<Button>, pub Option<Button>);

impl ButtonTuple {
    fn new() -> Self { Default::default() }

    fn contains(&self, btn: Button) -> bool {
        let sbtn = Some(btn);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    #[allow(dead_code)]
    fn remove_inplace(&mut self, btn: Button) {
        let sbtn = Some(btn);
        if self.0 == sbtn {self.0 = None}
        if self.1 == sbtn {self.1 = None}
        if self.2 == sbtn {self.2 = None}
    }

    #[allow(dead_code)]
    fn replace_inplace(&mut self, btn_idx: u32, btn: Button) -> bool {
        match btn_idx {
            0 => {self.0 = Some(btn); true},
            1 => {self.1 = Some(btn); true},
            2 => {self.2 = Some(btn); true},
            _ => false
        }
    }

    fn insert_inplace(&mut self, btn: Button) -> bool {
        match self {
            &mut ButtonTuple(a, b, c) if a.is_none() => {*self = ButtonTuple(Some(btn), b, c); true},
            &mut ButtonTuple(a, b, c) if b.is_none() => {*self = ButtonTuple(a, Some(btn), c); true},
            &mut ButtonTuple(a, b, c) if c.is_none() => {*self = ButtonTuple(a, b, Some(btn)); true}
            _ => false
        }
    }
}

impl Default for ButtonTuple {
    fn default() -> Self { ButtonTuple(None, None, None) }
}
<commit_msg>Rename input_map to input_translator<commit_after>#[deny(missing_docs)]

extern crate input;
extern crate piston_window;
extern crate rustc_serialize;
extern crate viewport;

mod builder;

use input::{Input, Button, Motion};
use piston_window::Size;
use std::collections::HashMap;
use std::default::Default;
use viewport::Viewport;

pub use builder::InputTranslatorBuilder;

/// Represents a logical action to be bound to a particular button press, e.g.
/// jump, attack, or move forward
pub trait Action: Copy + PartialEq + Eq { }

/// A translated action.
#[derive(Debug, Copy, Clone)]
pub enum Translated<A: Action> {
    /// A keypress event which was bound to an action
    Press(A),

    /// A key release event which was bound to an action
    Release(A),

    /// A translated mouse motion. The logical origin of a translated MouseCursor event
    /// is in the top left corner of the window, and the logical scroll is non-natural.
    /// Relative events are unchanged for now.
    Move(Motion)
}

/// An object which translates piston::input::Input events into input_map::Translated<A> events
#[derive(Clone)]
pub struct InputTranslator<A: Action> {
    keymap: KeyMap<A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputTranslator<A> {

    /// Creates an empty InputTranslator.
    pub fn new(size: Size) -> Self {
        InputTranslator {
            keymap: KeyMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    /// Translate an Input into a Translated<A> event
    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(($but_state:ident, $but_var:ident) => (
            match self.keymap.translate($but_var) {
                Some(act) => Some(Translated::$but_state(act)),
                None => None
            });
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    pub fn rebind_button(&mut self, _but: Button, _act: A) {
        // TODO implement
    }

    pub fn add_binding(&mut self, but: Button, act: A) {
        self.keymap.add_mapping(but, act);
    }

    /// Get all the bindings for an action
    pub fn get_bindings_for_action(&self, _act: A) -> ButtonTuple {
        ButtonTuple(None, None, None) // TODO implement
    }

    /// Re-set the mouse bounds size used for calculating mouse events
    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.viewport_size = size
    }

    /// Re-set the mouse bounds size from a viewport
    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslator {
    pub x_axis_motion_inverted: bool,
    pub y_axis_motion_inverted: bool,
    pub x_axis_scroll_inverted: bool,
    pub y_axis_scroll_inverted: bool,
    pub viewport_size: Size
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            x_axis_motion_inverted: false,
            y_axis_motion_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.viewport_size;
                    (width as f64, height as f64)
                };

                let cx = if self.x_axis_motion_inverted { sw - x } else { x };
                let cy = if self.y_axis_motion_inverted { sh - y } else { y };

                Motion::MouseCursor(cx, cy)
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[derive(Clone, Debug)]
struct KeyMap<A: Action> {
    btn_map: HashMap<ButtonTuple, A>
}

impl<A: Action> KeyMap<A> {
    fn new() -> Self {
        KeyMap {
            btn_map: HashMap::new()
        }
    }

    fn add_mapping(&mut self, button: Button, action: A) {
        let mut bt = self.get_bindings_for_action(action).unwrap_or(ButtonTuple::new());
        let bt = if bt.insert_inplace(button) {bt} else {ButtonTuple::new()};
        self.btn_map.insert(bt, action);
    }

    fn get_bindings_for_action(&self, action: A) -> Option<ButtonTuple> {
        self.btn_map.iter().find(|&(_, &a)| a == action).map(|(&bt, _)| bt)
    }

    #[allow(dead_code)]
    fn get_ref_bindings_for_action(&self, action: A) -> Option<&ButtonTuple> {
        self.btn_map.iter().find(|&(_, &a)| a == action).map(|(bt, _)| bt)
    }

    #[cfg(unimplemented)] // This gives a compile error
    fn get_ref_bindings_for_action_mut(&mut self, action: A) -> Option<&mut ButtonTuple> {
        self.btn_map.iter_mut().find(|&(_, &mut a)| a == action).as_mut().map(|&mut (bt, _)| bt)
    }

    fn translate(&self, button: Button) -> Option<A> {
        self.btn_map.iter().find(|&(&bt, _)| bt.contains(button)).map(|(_, &a)| a)
    }
}

/// A three-element tuple of Option<Button>. Used as the key of an InputTranslator
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(pub Option<Button>, pub Option<Button>, pub Option<Button>);

impl ButtonTuple {
    fn new() -> Self { Default::default() }

    fn contains(&self, btn: Button) -> bool {
        let sbtn = Some(btn);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    #[allow(dead_code)]
    fn remove_inplace(&mut self, btn: Button) {
        let sbtn = Some(btn);
        if self.0 == sbtn {self.0 = None}
        if self.1 == sbtn {self.1 = None}
        if self.2 == sbtn {self.2 = None}
    }

    #[allow(dead_code)]
    fn replace_inplace(&mut self, btn_idx: u32, btn: Button) -> bool {
        match btn_idx {
            0 => {self.0 = Some(btn); true},
            1 => {self.1 = Some(btn); true},
            2 => {self.2 = Some(btn); true},
            _ => false
        }
    }

    fn insert_inplace(&mut self, btn: Button) -> bool {
        match self {
            &mut ButtonTuple(a, b, c) if a.is_none() => {*self = ButtonTuple(Some(btn), b, c); true},
            &mut ButtonTuple(a, b, c) if b.is_none() => {*self = ButtonTuple(a, Some(btn), c); true},
            &mut ButtonTuple(a, b, c) if c.is_none() => {*self = ButtonTuple(a, b, Some(btn)); true}
            _ => false
        }
    }
}

impl Default for ButtonTuple {
    fn default() -> Self { ButtonTuple(None, None, None) }
}
<|endoftext|>


<commit_before>//! create blog from markdown files.
//!
//! # features
//!
//! * markdown format
//! * TeX style math support
//! * post tags index
//! * hidden post
//! * post title is the title of markdown file
//! * post url is some to path of markdown file

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://docs.rs/mdblog")]

extern crate chrono;
extern crate config;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate log;
extern crate pulldown_cmark;
extern crate serde_json;
extern crate tera;
extern crate walkdir;

mod errors;
mod post;
mod theme;
mod utils;

use std::collections::BTreeMap;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::rc::Rc;

use tera::{Context, Tera};
use walkdir::{DirEntry, WalkDir};
use serde_json::{Map, Value};

use config::Config;
pub use errors::{Error, Result};
pub use theme::Theme;
pub use post::Post;
pub use utils::create_file;

/// blog object
pub struct Mdblog {
    /// blog root path
    root: PathBuf,
    /// blog settings
    settings: Config,
    /// blog theme
    theme: Theme,
    /// blog render
    renderer: Tera,
    /// collection of blog posts
    posts: Vec<Rc<Post>>,
    /// tagged posts
    tags: BTreeMap<String, Vec<Rc<Post>>>,
}

impl Mdblog {
    /// create Mdblog from the `root` path
    pub fn new<P: AsRef<Path>>(root: P) -> Result<Mdblog> {
        let root = root.as_ref();
        let settings = Mdblog::get_default_settings()?;
        let theme_name: String = settings.get("theme")?;
        let theme = Mdblog::get_theme(root, &theme_name)?;
        let renderer = Mdblog::get_renderer(root, &theme_name)?;
        Ok(Mdblog {
            root: root.to_owned(),
            settings: settings,
            theme: theme,
            renderer: renderer,
            posts: Vec::new(),
            tags: BTreeMap::new(),
        })
    }

    /// get default settings
    pub fn get_default_settings() -> Result<Config> {
        let mut settings = Config::default();
        settings.set_default("theme", "simple")?;
        settings.set_default("site_logo", "/static/logo.png")?;
        settings.set_default("site_name", "Mdblog")?;
        settings.set_default("site_motto", "Simple is Beautiful!")?;
        settings.set_default("footer_note", "Keep It Simple, Stupid!")?;
        Ok(settings)
    }

    pub fn load_customize_settings(&mut self) -> Result<()> {
        self.settings.merge(config::File::with_name("Config.toml"))?;
        self.settings.merge(config::Environment::with_prefix("BLOG"))?;
        Ok(())
    }

    /// get theme
    pub fn get_theme<P: AsRef<Path>>(root: P, name: &str) -> Result<Theme> {
        let mut theme = Theme::new(root.as_ref());
        theme.load(name)?;
        Ok(theme)
    }

    pub fn get_renderer<P: AsRef<Path>>(root: P, theme_name: &str) -> Result<Tera> {
        let template_dir = root.as_ref()
                               .join("_themes")
                               .join(theme_name)
                               .join("templates");
        debug!("template dir: {}", template_dir.display());
        let renderer = Tera::new(&format!("{}/*", template_dir.display()))?;
        Ok(renderer)
    }

    pub fn load(&mut self) -> Result<()> {
        self.load_customize_settings()?;
        let posts_dir = self.root.join("posts");
        let walker = WalkDir::new(&posts_dir).into_iter();
        for entry in walker.filter_entry(|e| !is_hidden(e)) {
            let entry = entry.expect("get walker entry error");
            if !is_markdown_file(&entry) {
                continue;
            }
            let mut post = Post::new(&self.root,
                                     &entry.path()
                                           .strip_prefix(&self.root)
                                           .expect("create post path error")
                                           .to_owned());
            post.load()?;
            let post = Rc::new(post);
            self.posts.push(post.clone());
            if !post.is_hidden() {
                for tag in post.tags() {
                    let mut ps = self.tags.entry(tag.to_string()).or_insert(Vec::new());
                    ps.push(post.clone());
                }
            }
        }
        self.posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        for (_, tag_posts) in self.tags.iter_mut() {
            tag_posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        }
        Ok(())
    }

    /// init Mdblog with `theme`.
    ///
    /// theme directory is created at `root/_theme` directory.
    /// if `theme` is `None`, use the default theme(`simple`).
    pub fn init(&mut self) -> Result<()> {
        if self.root.exists() {
            return Err(Error::RootDirExisted(self.root.clone()));
        }

        let mut hello_post = create_file(&self.root.join("posts").join("hello.md"))?;
        hello_post.write_all(b"date: 1970-01-01 00:00:00\n")?;
        hello_post.write_all(b"tags: hello, world\n")?;
        hello_post.write_all(b"\n")?;
        hello_post.write_all(b"# hello\n\nhello world!\n")?;

        let settings = Mdblog::get_default_settings()?;
        let mut config_file = create_file(&self.root.join("Config.toml"))?;
        config_file.write_all(b"theme = \"simple\"\n\
                                site_logo = \"/static/logo.png\"\n\
                                site_name = \"Mdblog\"\n\
                                site_motto = \"Simple is Beautiful!\"\n\
                                footer_note = \"Keep It Simple, Stupid!\"\n\
                                ")?;
        self.theme.load(&self.settings.get_str("theme").unwrap())?;
        self.theme.init_dir()?;
        std::fs::create_dir_all(self.root.join("media"))?;
        Ok(())
    }

    /// create the blog html files to `root/_build/` directory.
    ///
    /// if `theme` is `None`, use the default theme(`simple`).
    pub fn build(&mut self) -> Result<()> {
        self.export()?;
        Ok(())
    }

    /// unimplemented.
    pub fn server(&self, port: u16) {
        println!("server blog at localhost:{}", port);
    }

    pub fn export(&self) -> Result<()> {
        self.export_media()?;
        self.export_static()?;
        self.export_posts()?;
        self.export_index()?;
        self.export_tags()?;
        Ok(())
    }

    pub fn media_dest<P: AsRef<Path>>(&self, media: P) -> PathBuf {
        let relpath = media.as_ref()
                           .strip_prefix(&self.root.join("media"))
                           .expect("create post path error")
                           .to_owned();
        self.root.join("_builds/media").join(relpath)
    }

    pub fn export_media(&self) -> Result<()> {
        debug!("exporting media ...");
        let walker = WalkDir::new(&self.root.join("media")).into_iter();
        for entry in walker.filter_entry(|e| !is_hidden(e)) {
            let entry = entry.expect("get walker entry error");
            let src_path = entry.path();
            if src_path.is_dir() {
                std::fs::create_dir_all(self.media_dest(src_path))?;
                continue;
            }
            std::fs::copy(src_path, self.media_dest(src_path))?;
        }
        Ok(())
    }

    pub fn export_static(&self) -> Result<()> {
        self.theme.export_static()
    }

    pub fn export_posts(&self) -> Result<()> {
        for post in &self.posts {
            let dest = post.dest();
            let mut f = create_file(&dest)?;
            let html = self.render_post(post)?;
            f.write(html.as_bytes())?;
        }
        Ok(())
    }

    pub fn export_index(&self) -> Result<()> {
        let dest = self.root.join("_builds/index.html");
        let mut f = create_file(&dest)?;
        let html = self.render_index()?;
        f.write(html.as_bytes())?;
        Ok(())
    }

    pub fn export_tags(&self) -> Result<()> {
        for tag in self.tags.keys() {
            let dest = self.root.join(format!("_builds/blog/tags/{}.html", tag));
            let mut f = create_file(&dest)?;
            let html = self.render_tag(tag)?;
            f.write(html.as_bytes())?;
        }
        Ok(())
    }

    fn tag_url(&self, name: &str) -> String {
        format!("/blog/tags/{}.html", &name)
    }

    fn tag_map<T>(&self, name: &str, posts: &Vec<T>) -> Map<String, Value> {
        let mut map = Map::new();
        map.insert("name".to_string(), Value::String(name.to_string()));
        let tag_len = format!("{:?}", &posts.len());
        map.insert("num".to_string(), Value::String(tag_len));
        map.insert("url".to_string(), Value::String(self.tag_url(&name)));
        map
    }

    pub fn get_base_context(&self, title: &str) -> Result<Context> {
        let mut context = Context::new();
        context.add("title", &title);
        context.add("site_logo", &self.settings.get_str("site_logo")?);
        context.add("site_name", &self.settings.get_str("site_name")?);
        context.add("site_motto", &self.settings.get_str("site_motto")?);
        context.add("footer_note", &self.settings.get_str("footer_note")?);
        let mut all_tags = Vec::new();
        for (tag_key, tag_posts) in &self.tags {
            all_tags.push(self.tag_map(&tag_key, &tag_posts));
        }
        all_tags.sort_by(|a, b| {
                             a.get("name").unwrap()
                              .as_str()
                              .expect("get name error")
                              .to_lowercase()
                              .cmp(&b.get("name")
                                     .expect("get name error")
                                     .as_str()
                                     .expect("get name error")
                                     .to_lowercase())
                         });
        context.add("all_tags", &all_tags);
        Ok(context)
    }

    pub fn render_post(&self, post: &Post) -> Result<String> {
        debug!("rendering post({}) ...", post.path.display());
        let mut context = self.get_base_context(&post.title())?;
        context.add("content", &post.content());
        let mut post_tags = Vec::new();
        if !post.is_hidden() {
            context.add("datetime",
                        &post.datetime().format("%Y-%m-%d %H:%M:%S").to_string());
            for tag_key in post.tags() {
                let tag_posts = self.tags.get(tag_key)
                                    .expect(&format!("post tag({}) does not add to blog tags",
                                                     tag_key));
                post_tags.push(self.tag_map(&tag_key, &tag_posts));
            }
        } else {
            context.add("datetime", &"".to_string());
        }

        context.add("post_tags", &post_tags);
        Ok(self.renderer.render("post.tpl", &context)?)
    }

    pub fn render_index(&self) -> Result<String> {
        debug!("rendering index ...");
        let mut context = self.get_base_context(&self.settings.get_str("site_name")?)?;
        context.add("posts", &self.get_posts_maps(&self.posts)?);
        Ok(self.renderer.render("index.tpl", &context)?)
    }

    fn get_posts_maps(&self, posts: &Vec<Rc<Post>>) -> Result<Vec<Map<String, Value>>> {
        let mut maps = Vec::new();
        for post in posts.iter().filter(|p| !p.is_hidden()) {
            maps.push(post.map());
        }
        Ok(maps)
    }

    pub fn render_tag(&self, tag: &str) -> Result<String> {
        debug!("rendering tag({}) ...", tag);
        let mut context = self.get_base_context(&tag)?;
        let posts = self.tags
                        .get(tag)
                        .expect(&format!("get tag({}) error", &tag));
        context.add("posts", &self.get_posts_maps(&posts)?);
        Ok(self.renderer.render("tag.tpl", &context)?)
    }
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("."))
         .unwrap_or(false)
}

fn is_markdown_file(entry: &DirEntry) -> bool {
    if !entry.path().is_file() {
        return false;
    }
    let fname = entry.file_name().to_str();
    match fname {
        None => {
            return false;
        },
        Some(s) => {
            if s.starts_with(|c| (c == '.') | (c == '~')) {
                return false;
            } else if s.ends_with(".md") {
                return true;
            } else {
                return false;
            }
        },
    }
}
<commit_msg>use default settings when init config file<commit_after>//! create blog from markdown files.
//!
//! # features
//!
//! * markdown format
//! * TeX style math support
//! * post tags index
//! * hidden post
//! * post title is the title of markdown file
//! * post url is some to path of markdown file

#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "https://www.rust-lang.org/favicon.ico",
       html_root_url = "https://docs.rs/mdblog")]

extern crate chrono;
extern crate config;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate log;
extern crate pulldown_cmark;
extern crate serde_json;
extern crate tera;
extern crate walkdir;

mod errors;
mod post;
mod theme;
mod utils;

use std::collections::BTreeMap;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::rc::Rc;

use tera::{Context, Tera};
use walkdir::{DirEntry, WalkDir};
use serde_json::{Map, Value};

use config::{Config, Source};
pub use errors::{Error, Result};
pub use theme::Theme;
pub use post::Post;
pub use utils::create_file;

/// blog object
pub struct Mdblog {
    /// blog root path
    root: PathBuf,
    /// blog settings
    settings: Config,
    /// blog theme
    theme: Theme,
    /// blog render
    renderer: Tera,
    /// collection of blog posts
    posts: Vec<Rc<Post>>,
    /// tagged posts
    tags: BTreeMap<String, Vec<Rc<Post>>>,
}

impl Mdblog {
    /// create Mdblog from the `root` path
    pub fn new<P: AsRef<Path>>(root: P) -> Result<Mdblog> {
        let root = root.as_ref();
        let settings = Mdblog::get_default_settings()?;
        let theme_name: String = settings.get("theme")?;
        let theme = Mdblog::get_theme(root, &theme_name)?;
        let renderer = Mdblog::get_renderer(root, &theme_name)?;
        Ok(Mdblog {
            root: root.to_owned(),
            settings: settings,
            theme: theme,
            renderer: renderer,
            posts: Vec::new(),
            tags: BTreeMap::new(),
        })
    }

    /// get default settings
    pub fn get_default_settings() -> Result<Config> {
        let mut settings = Config::default();
        settings.set_default("theme", "simple")?;
        settings.set_default("site_logo", "/static/logo.png")?;
        settings.set_default("site_name", "Mdblog")?;
        settings.set_default("site_motto", "Simple is Beautiful!")?;
        settings.set_default("footer_note", "Keep It Simple, Stupid!")?;
        Ok(settings)
    }

    pub fn load_customize_settings(&mut self) -> Result<()> {
        self.settings.merge(config::File::with_name("Config.toml"))?;
        self.settings.merge(config::Environment::with_prefix("BLOG"))?;
        Ok(())
    }

    /// get theme
    pub fn get_theme<P: AsRef<Path>>(root: P, name: &str) -> Result<Theme> {
        let mut theme = Theme::new(root.as_ref());
        theme.load(name)?;
        Ok(theme)
    }

    pub fn get_renderer<P: AsRef<Path>>(root: P, theme_name: &str) -> Result<Tera> {
        let template_dir = root.as_ref()
                               .join("_themes")
                               .join(theme_name)
                               .join("templates");
        debug!("template dir: {}", template_dir.display());
        let renderer = Tera::new(&format!("{}/*", template_dir.display()))?;
        Ok(renderer)
    }

    pub fn load(&mut self) -> Result<()> {
        self.load_customize_settings()?;
        let posts_dir = self.root.join("posts");
        let walker = WalkDir::new(&posts_dir).into_iter();
        for entry in walker.filter_entry(|e| !is_hidden(e)) {
            let entry = entry.expect("get walker entry error");
            if !is_markdown_file(&entry) {
                continue;
            }
            let mut post = Post::new(&self.root,
                                     &entry.path()
                                           .strip_prefix(&self.root)
                                           .expect("create post path error")
                                           .to_owned());
            post.load()?;
            let post = Rc::new(post);
            self.posts.push(post.clone());
            if !post.is_hidden() {
                for tag in post.tags() {
                    let mut ps = self.tags.entry(tag.to_string()).or_insert(Vec::new());
                    ps.push(post.clone());
                }
            }
        }
        self.posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        for (_, tag_posts) in self.tags.iter_mut() {
            tag_posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        }
        Ok(())
    }

    /// init Mdblog with `theme`.
    ///
    /// theme directory is created at `root/_theme` directory.
    /// if `theme` is `None`, use the default theme(`simple`).
    pub fn init(&mut self) -> Result<()> {
        if self.root.exists() {
            return Err(Error::RootDirExisted(self.root.clone()));
        }

        let mut hello_post = create_file(&self.root.join("posts").join("hello.md"))?;
        hello_post.write_all(b"date: 1970-01-01 00:00:00\n")?;
        hello_post.write_all(b"tags: hello, world\n")?;
        hello_post.write_all(b"\n")?;
        hello_post.write_all(b"# hello\n\nhello world!\n")?;

        let settings = Mdblog::get_default_settings()?;
        let mut config_file = create_file(&self.root.join("Config.toml"))?;
        let mut pairs = settings.collect()?
                                .into_iter()
                                .collect::<Vec<_>>();
        pairs.sort_by(|a, b| a.0.cmp(&b.0));
        for (key, value) in pairs {
            config_file.write_fmt(format_args!("{} = \"{}\"\n", key, value))?;
        }
        self.theme.load(&self.settings.get_str("theme").unwrap())?;
        self.theme.init_dir()?;
        std::fs::create_dir_all(self.root.join("media"))?;
        Ok(())
    }

    /// create the blog html files to `root/_build/` directory.
    ///
    /// if `theme` is `None`, use the default theme(`simple`).
    pub fn build(&mut self) -> Result<()> {
        self.export()?;
        Ok(())
    }

    /// unimplemented.
    pub fn server(&self, port: u16) {
        println!("server blog at localhost:{}", port);
    }

    pub fn export(&self) -> Result<()> {
        self.export_media()?;
        self.export_static()?;
        self.export_posts()?;
        self.export_index()?;
        self.export_tags()?;
        Ok(())
    }

    pub fn media_dest<P: AsRef<Path>>(&self, media: P) -> PathBuf {
        let relpath = media.as_ref()
                           .strip_prefix(&self.root.join("media"))
                           .expect("create post path error")
                           .to_owned();
        self.root.join("_builds/media").join(relpath)
    }

    pub fn export_media(&self) -> Result<()> {
        debug!("exporting media ...");
        let walker = WalkDir::new(&self.root.join("media")).into_iter();
        for entry in walker.filter_entry(|e| !is_hidden(e)) {
            let entry = entry.expect("get walker entry error");
            let src_path = entry.path();
            if src_path.is_dir() {
                std::fs::create_dir_all(self.media_dest(src_path))?;
                continue;
            }
            std::fs::copy(src_path, self.media_dest(src_path))?;
        }
        Ok(())
    }

    pub fn export_static(&self) -> Result<()> {
        self.theme.export_static()
    }

    pub fn export_posts(&self) -> Result<()> {
        for post in &self.posts {
            let dest = post.dest();
            let mut f = create_file(&dest)?;
            let html = self.render_post(post)?;
            f.write(html.as_bytes())?;
        }
        Ok(())
    }

    pub fn export_index(&self) -> Result<()> {
        let dest = self.root.join("_builds/index.html");
        let mut f = create_file(&dest)?;
        let html = self.render_index()?;
        f.write(html.as_bytes())?;
        Ok(())
    }

    pub fn export_tags(&self) -> Result<()> {
        for tag in self.tags.keys() {
            let dest = self.root.join(format!("_builds/blog/tags/{}.html", tag));
            let mut f = create_file(&dest)?;
            let html = self.render_tag(tag)?;
            f.write(html.as_bytes())?;
        }
        Ok(())
    }

    fn tag_url(&self, name: &str) -> String {
        format!("/blog/tags/{}.html", &name)
    }

    fn tag_map<T>(&self, name: &str, posts: &Vec<T>) -> Map<String, Value> {
        let mut map = Map::new();
        map.insert("name".to_string(), Value::String(name.to_string()));
        let tag_len = format!("{:?}", &posts.len());
        map.insert("num".to_string(), Value::String(tag_len));
        map.insert("url".to_string(), Value::String(self.tag_url(&name)));
        map
    }

    pub fn get_base_context(&self, title: &str) -> Result<Context> {
        let mut context = Context::new();
        context.add("title", &title);
        context.add("site_logo", &self.settings.get_str("site_logo")?);
        context.add("site_name", &self.settings.get_str("site_name")?);
        context.add("site_motto", &self.settings.get_str("site_motto")?);
        context.add("footer_note", &self.settings.get_str("footer_note")?);
        let mut all_tags = Vec::new();
        for (tag_key, tag_posts) in &self.tags {
            all_tags.push(self.tag_map(&tag_key, &tag_posts));
        }
        all_tags.sort_by(|a, b| {
                             a.get("name").unwrap()
                              .as_str()
                              .expect("get name error")
                              .to_lowercase()
                              .cmp(&b.get("name")
                                     .expect("get name error")
                                     .as_str()
                                     .expect("get name error")
                                     .to_lowercase())
                         });
        context.add("all_tags", &all_tags);
        Ok(context)
    }

    pub fn render_post(&self, post: &Post) -> Result<String> {
        debug!("rendering post({}) ...", post.path.display());
        let mut context = self.get_base_context(&post.title())?;
        context.add("content", &post.content());
        let mut post_tags = Vec::new();
        if !post.is_hidden() {
            context.add("datetime",
                        &post.datetime().format("%Y-%m-%d %H:%M:%S").to_string());
            for tag_key in post.tags() {
                let tag_posts = self.tags.get(tag_key)
                                    .expect(&format!("post tag({}) does not add to blog tags",
                                                     tag_key));
                post_tags.push(self.tag_map(&tag_key, &tag_posts));
            }
        } else {
            context.add("datetime", &"".to_string());
        }

        context.add("post_tags", &post_tags);
        Ok(self.renderer.render("post.tpl", &context)?)
    }

    pub fn render_index(&self) -> Result<String> {
        debug!("rendering index ...");
        let mut context = self.get_base_context(&self.settings.get_str("site_name")?)?;
        context.add("posts", &self.get_posts_maps(&self.posts)?);
        Ok(self.renderer.render("index.tpl", &context)?)
    }

    fn get_posts_maps(&self, posts: &Vec<Rc<Post>>) -> Result<Vec<Map<String, Value>>> {
        let mut maps = Vec::new();
        for post in posts.iter().filter(|p| !p.is_hidden()) {
            maps.push(post.map());
        }
        Ok(maps)
    }

    pub fn render_tag(&self, tag: &str) -> Result<String> {
        debug!("rendering tag({}) ...", tag);
        let mut context = self.get_base_context(&tag)?;
        let posts = self.tags
                        .get(tag)
                        .expect(&format!("get tag({}) error", &tag));
        context.add("posts", &self.get_posts_maps(&posts)?);
        Ok(self.renderer.render("tag.tpl", &context)?)
    }
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("."))
         .unwrap_or(false)
}

fn is_markdown_file(entry: &DirEntry) -> bool {
    if !entry.path().is_file() {
        return false;
    }
    let fname = entry.file_name().to_str();
    match fname {
        None => {
            return false;
        },
        Some(s) => {
            if s.starts_with(|c| (c == '.') | (c == '~')) {
                return false;
            } else if s.ends_with(".md") {
                return true;
            } else {
                return false;
            }
        },
    }
}
<|endoftext|>