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

//! A library to localize strings, translating them according to runtime options.
//!
//! Basically, this library allows your project to generate a `lformat!` macro, that behaves
//! similarly to `format!`, except the message string (the first argument) might get translated
//! (if you can find the appropriate string for the language).
//!
//! # Usage
//!
//! First, you'll need to add the following to your `Cargo.toml` file:
//!
//! ```toml
//! build = "build.rs"
//!
//! [build-dependencies]
//! crowbook-intl = "0.1.0"
//!
//! [dependencies]
//! crowbook-intl-runtime = "0.1.0"
//! ```
//!
//! You'll then need to create the `build.rs` file, which can look like this:
//!
//! ```rust,ignore
//! extern crate crowbook_intl;
//! use crowbook_intl::{Localizer, Extractor};
//!
//! fn main() {
//!     // Generate a `lang/default.pot` containing strings used to call `lformat!`
//!     let mut extractor = Extractor::new();
//!     extractor.add_messages_from_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/src")).unwrap();
//!     extractor.write_pot_file(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/default.pot")).unwrap();
//!
//!     // Generate the `localize_macros.rs` file
//!     let mut localizer = Localizer::new(&extractor);
//!     // Use env::var instead of env! to avoid problems when cross-compiling
//!     let dest_path = Path::new(&env::var("OUT_DIR").unwrap())
//!        .join("localize_macros.rs");
//!     localizer.write_macro_file(dest_path).unwrap();
//! }
//! ```
//!
//! This will create a `localize_macros.rs` at build time somewhere in `OUT_DIR`, containing the `lformat!` macro.
//! To actually use this macro, you have to create a `src/localize_macros.rs` file that includes it:
//!
//! ```rust,ignore
//! include!(concat!(env!("OUT_DIR"), "/localize_macros.rs"));
//! ```
//!
//! //! To use it, the last step is to modify your `src/lib/lib.rs` file:
//!
//! ```rust,ignore
//! extern crate crowbook_intl_runtime;
//! #[macro_use] mod localize_macros;
//! ```
//!
//! Once this is done, you can start replacing your calls to `format!` with calls to `lformat!`.
//!
//! In order to get translation, you'll need to actually translate the strings in separate
//! files, and set your `build.rs` to load them.
//!
//! E.g., if you have the following code:
//!
//! ```rust,ignore
//! println!("{}", lformat!("Hello, world!"));
//! ```
//!
//! and you want it translated in french, you'll have to create a `lang/fr.po` file
//! from the `lang/default.pot` file containing:
//!
//! ```text
//! msgid "Hello, world!";
//! msgstr "Bonjour le monde !";
//! ```
//!
//! And load it in your `build.rs` file:
//!
//! ```rust,ignore
//! let mut localizer = Localizer::new();
//! localizer.add_lang("fr", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/fr.mp"))).unwrap();
//! (...)
//! ```
//!
//! Once *this* is done, you can use the `localize_macros::set_lang` function
//! to switch the language at runtime:
//!
//! ```rust,ignore
//! use crowbook_intl_runtime::set_lang;
//! set_lang("en");
//! println!("{}", lformat!("Hello, world!")); // prints "Hello, world!"
//! set_lang("fr");
//! println!("{}", lformat!("Hello, world!")); // prints "Bonjour le monde !"
//! ```
//!
//! # Updating your translation
//!
//! When you add new strings that need to be translated (by more calls to `lformat!`),
//! or when you change the content of existing strings, you can use [Gettext's `msgmerge` and `msgcmp`](https://www.gnu.org/software/gettext/manual/html_node/msgmerge-Invocation.html)
//! commands to update your translation. While it is not guaranteed that the formats are
//! strictly identical, it should work. (That is, it is a bug if it doesn't; but at this
//! stage, this library is absolutely not guaranteed to be bug-free.)
//!
//! # Warning
//!
//! In case the complexity of the operation didn't discourage you, I should warn you
//! that this library is highly experimental at this time.

extern crate regex;
#[macro_use] extern crate lazy_static;
extern crate walkdir;

mod common;
mod macrogen;
mod lang;
mod error;
mod localizer;
mod message;
mod extractor;

pub use error::{Result, Error};
pub use localizer::Localizer;
pub use extractor::Extractor;
<commit_msg>Fix comment in lib.rs<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! A library to localize strings, translating them according to runtime options.
//!
//! Basically, this library allows your project to generate a `lformat!` macro, that behaves
//! similarly to `format!`, except the message string (the first argument) might get translated
//! (if you can find the appropriate string for the language).
//!
//! # Usage
//!
//! First, you'll need to add the following to your `Cargo.toml` file:
//!
//! ```toml
//! build = "build.rs"
//!
//! [build-dependencies]
//! crowbook-intl = "0.1.0"
//!
//! [dependencies]
//! crowbook-intl-runtime = "0.1.0"
//! ```
//!
//! You'll then need to create the `build.rs` file, which can look like this:
//!
//! ```rust,ignore
//! extern crate crowbook_intl;
//! use crowbook_intl::{Localizer, Extractor};
//!
//! fn main() {
//!     // Generate a `lang/default.pot` containing strings used to call `lformat!`
//!     let mut extractor = Extractor::new();
//!     extractor.add_messages_from_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/src")).unwrap();
//!     extractor.write_pot_file(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/default.pot")).unwrap();
//!
//!     // Generate the `localize_macros.rs` file
//!     let mut localizer = Localizer::new(&extractor);
//!     // Use env::var instead of env! to avoid problems when cross-compiling
//!     let dest_path = Path::new(&env::var("OUT_DIR").unwrap())
//!        .join("localize_macros.rs");
//!     localizer.write_macro_file(dest_path).unwrap();
//! }
//! ```
//!
//! This will create a `localize_macros.rs` at build time somewhere in `OUT_DIR`, containing the `lformat!` macro.
//! To actually use this macro, you have to create a `src/localize_macros.rs` file that includes it:
//!
//! ```rust,ignore
//! include!(concat!(env!("OUT_DIR"), "/localize_macros.rs"));
//! ```
//!
//! To use it, the last step is to modify your `src/lib/lib.rs` file:
//!
//! ```rust,ignore
//! extern crate crowbook_intl_runtime;
//! #[macro_use] mod localize_macros;
//! ```
//!
//! Once this is done, you can start replacing your calls to `format!` with calls to `lformat!`.
//!
//! In order to get translation, you'll need to actually translate the strings in separate
//! files, and set your `build.rs` to load them.
//!
//! E.g., if you have the following code:
//!
//! ```rust,ignore
//! println!("{}", lformat!("Hello, world!"));
//! ```
//!
//! and you want it translated in french, you'll have to create a `lang/fr.po` file
//! from the `lang/default.pot` file containing:
//!
//! ```text
//! msgid "Hello, world!";
//! msgstr "Bonjour le monde !";
//! ```
//!
//! And load it in your `build.rs` file:
//!
//! ```rust,ignore
//! let mut localizer = Localizer::new();
//! localizer.add_lang("fr", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/fr.mp"))).unwrap();
//! (...)
//! ```
//!
//! Once *this* is done, you can use the `localize_macros::set_lang` function
//! to switch the language at runtime:
//!
//! ```rust,ignore
//! use crowbook_intl_runtime::set_lang;
//! set_lang("en");
//! println!("{}", lformat!("Hello, world!")); // prints "Hello, world!"
//! set_lang("fr");
//! println!("{}", lformat!("Hello, world!")); // prints "Bonjour le monde !"
//! ```
//!
//! # Updating your translation
//!
//! When you add new strings that need to be translated (by more calls to `lformat!`),
//! or when you change the content of existing strings, you can use [Gettext's `msgmerge` and `msgcmp`](https://www.gnu.org/software/gettext/manual/html_node/msgmerge-Invocation.html)
//! commands to update your translation. While it is not guaranteed that the formats are
//! strictly identical, it should work. (That is, it is a bug if it doesn't; but at this
//! stage, this library is absolutely not guaranteed to be bug-free.)
//!
//! # Warning
//!
//! In case the complexity of the operation didn't discourage you, I should warn you
//! that this library is highly experimental at this time.

extern crate regex;
#[macro_use] extern crate lazy_static;
extern crate walkdir;

mod common;
mod macrogen;
mod lang;
mod error;
mod localizer;
mod message;
mod extractor;

pub use error::{Result, Error};
pub use localizer::Localizer;
pub use extractor::Extractor;
<|endoftext|>


<commit_before>#![feature(io)]

extern crate xml;

use std::ascii::AsciiExt;
use std::default::Default;
use std::iter::IteratorExt;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

trait ElementUtils {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str);
    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>);
}

impl ElementUtils for Element {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str) {
        self.tag(elem_with_text(child_name, child_body));
    }

    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>) {
        if let Some(ref c) = *child_body {
            self.tag_with_text(child_name, &c);
        }
    }
}

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    pub fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    pub fn from_read(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        match reader.read_to_string(&mut rss_string) {
            Ok(..) => (),
            Err(..) => return Err("Error reading string from reader"),
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            if let Ok(Some(element)) = builder.push_event(event) {
                return ViaXml::from_xml(element);
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
/// use std::default::Default;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
///     ..Default::default()
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    pub language: Option<String>,
    pub copyright: Option<String>,
    pub managing_editor: Option<String>,
    pub web_master: Option<String>,
    pub pub_date: Option<String>,
    pub last_build_date: Option<String>,
    pub categories: Vec<Category>,
    pub generator: Option<String>,
    pub docs: Option<String>,
    // pub cloud:
    // pub ttl:
    pub image: Option<String>,
    pub rating: Option<String>,
    // pub text_input:
    pub skip_hours: Option<String>,
    pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        channel.tag_with_text("title", &self.title);
        channel.tag_with_text("link", &self.link);
        channel.tag_with_text("description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel.tag_with_text_opt("language", &self.language);
        channel.tag_with_text_opt("copyright", &self.copyright);
        channel.tag_with_text_opt("managingEditor", &self.managing_editor);
        channel.tag_with_text_opt("webMaster", &self.web_master);
        channel.tag_with_text_opt("pubDate", &self.pub_date);
        channel.tag_with_text_opt("lastBuildDate", &self.last_build_date);
        channel.tag_with_text_opt("generator", &self.generator);
        channel.tag_with_text_opt("docs", &self.docs);
        channel.tag_with_text_opt("image", &self.image);
        channel.tag_with_text_opt("rating", &self.rating);
        channel.tag_with_text_opt("skipHours", &self.skip_hours);
        channel.tag_with_text_opt("skipDays", &self.skip_days);

        for category in &self.categories {
            channel.tag(category.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut channel: Channel = Default::default();

        match element.get_child("title", None) {
            Some(element) => channel.title = element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        }

        match element.get_child("link", None) {
            Some(element) => channel.link = element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        }

        match element.get_child("description", None) {
            Some(element) => channel.description = element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        }

        channel.items = element.get_children("item", None)
            .into_iter()
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(channel)
    }
}

#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    // pub author
    pub categories: Vec<Category>,
    // pub comments
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        item.tag_with_text_opt("title", &self.title);
        item.tag_with_text_opt("link", &self.link);
        item.tag_with_text_opt("description", &self.description);

        for category in &self.categories {
            item.tag(category.to_xml());
        }

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut item: Item = Default::default();

        if let Some(element) = element.get_child("title", None) {
            item.title = Some(element.content_str());
        }

        if let Some(element) = element.get_child("link", None) {
            item.link = Some(element.content_str());
        }

        if let Some(element) = element.get_child("description", None) {
            item.description = Some(element.content_str());
        }

        Ok(item)
    }
}

/// Category
///
/// [RSS 2.0 Specification § Category]
/// (http://cyber.law.harvard.edu/rss/rss.html#ltcategorygtSubelementOfLtitemgt)
#[derive(Default)]
pub struct Category {
    pub domain: Option<String>,
    pub value: String,
}

impl ViaXml for Category {
    fn to_xml(&self) -> Element {
        let mut category = match self.domain {
            Some(ref d) => Element::new("category", None, &[("domain", None, d)]),
            None => Element::new("category", None, &[]),
        };
        category.text(&self.value);
        category
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        panic!("TODO")
    }
}

#[cfg(test)]
mod test {
    use std::default::Default;
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
            ..Default::default()
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
            ..Default::default()
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let rss = Rss::from_read(&mut file).unwrap();
        assert!(rss.to_string().len() > 0);
    }

    #[test]
    #[should_panic]
    fn test_from_read_no_channels() {
        let mut rss_bytes = "<rss></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_from_read_one_channel_no_properties() {
        let mut rss_bytes = "<rss><channel></channel></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    fn test_from_read_one_channel() {
        let mut rss_bytes = "<rss><channel><title>Hello world!</title><description></description><link></link></channel></rss>".as_bytes();
        let Rss(channel) = Rss::from_read(&mut rss_bytes).unwrap();
        assert_eq!("Hello world!", channel.title);
    }
}
<commit_msg>Implement from_xml for Category<commit_after>#![feature(io)]

extern crate xml;

use std::ascii::AsciiExt;
use std::default::Default;
use std::iter::IteratorExt;
use std::io;
use xml::{Element, ElementBuilder, Parser, Xml};

trait ElementUtils {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str);
    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>);
}

impl ElementUtils for Element {
    fn tag_with_text(&mut self, child_name: &'static str, child_body: &str) {
        self.tag(elem_with_text(child_name, child_body));
    }

    fn tag_with_text_opt(&mut self, child_name: &'static str, child_body: &Option<String>) {
        if let Some(ref c) = *child_body {
            self.tag_with_text(child_name, &c);
        }
    }
}

fn elem_with_text(tag_name: &'static str, chars: &str) -> Element {
    let mut elem = Element::new(tag_name, None, &[]);
    elem.text(chars);
    elem
}

trait ViaXml {
    fn to_xml(&self) -> Element;
    fn from_xml(element: Element) -> Result<Self, &'static str>;
}

/// RSS
///
/// "At the top level, a RSS document is a \<rss\> element, with a mandatory attribute called
/// version, that specifies the version of RSS that the document conforms to. If it conforms to
/// this specification, the version attribute must be 2.0."
///
/// [RSS 2.0 Specification § RSS]
/// (http://cyber.law.harvard.edu/rss/rss.html#whatIsRss)
#[derive(Default)]
pub struct Rss(pub Channel);

impl ViaXml for Rss {
    fn to_xml(&self) -> Element {
        let mut rss = Element::new("rss", None, &[("version", None, "2.0")]);

        let &Rss(ref channel) = self;
        rss.tag(channel.to_xml());

        rss
    }

    fn from_xml(rss_elem: Element) -> Result<Self, &'static str> {
        if rss_elem.name.to_ascii_lowercase() != "rss" {
            panic!("Expected <rss>, found <{}>", rss_elem.name);
        }

        let channel_elem = match rss_elem.get_child("channel", None) {
            Some(elem) => elem,
            None => return Err("No <channel> element found in <rss>"),
        };

        let channel = try!(ViaXml::from_xml(channel_elem.clone()));

        Ok(Rss(channel))
    }
}

impl Rss {
    pub fn to_string(&self) -> String {
        let mut ret = format!("{}", Xml::PINode("xml version='1.0' encoding='UTF-8'".to_string()));
        ret.push_str(&format!("{}", self.to_xml()));
        ret
    }

    pub fn from_read(reader: &mut io::Read) -> Result<Self, &'static str> {
        let mut rss_string = String::new();

        match reader.read_to_string(&mut rss_string) {
            Ok(..) => (),
            Err(..) => return Err("Error reading string from reader"),
        }

        let mut parser = Parser::new();
        parser.feed_str(&rss_string);

        let mut builder = ElementBuilder::new();

        for event in parser {
            if let Ok(Some(element)) = builder.push_event(event) {
                return ViaXml::from_xml(element);
            }
        }

        Err("RSS read error")
    }
}

/// Channel
///
/// "Subordinate to the \<rss\> element is a single \<channel\> element, which contains information
/// about the channel (metadata) and its contents."
///
/// [RSS 2.0 Specification § Channel]
/// (http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements)
///
/// ## Examples
///
/// ```
/// use rss::Channel;
/// use std::default::Default;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
///     ..Default::default()
/// };
/// ```
#[derive(Default)]
pub struct Channel {
    pub title: String,
    pub link: String,
    pub description: String,
    pub items: Vec<Item>,
    pub language: Option<String>,
    pub copyright: Option<String>,
    pub managing_editor: Option<String>,
    pub web_master: Option<String>,
    pub pub_date: Option<String>,
    pub last_build_date: Option<String>,
    pub categories: Vec<Category>,
    pub generator: Option<String>,
    pub docs: Option<String>,
    // pub cloud:
    // pub ttl:
    pub image: Option<String>,
    pub rating: Option<String>,
    // pub text_input:
    pub skip_hours: Option<String>,
    pub skip_days: Option<String>,
}

impl ViaXml for Channel {
    fn to_xml(&self) -> Element {
        let mut channel = Element::new("channel", None, &[]);

        channel.tag_with_text("title", &self.title);
        channel.tag_with_text("link", &self.link);
        channel.tag_with_text("description", &self.description);

        for item in &self.items {
            channel.tag(item.to_xml());
        }

        channel.tag_with_text_opt("language", &self.language);
        channel.tag_with_text_opt("copyright", &self.copyright);
        channel.tag_with_text_opt("managingEditor", &self.managing_editor);
        channel.tag_with_text_opt("webMaster", &self.web_master);
        channel.tag_with_text_opt("pubDate", &self.pub_date);
        channel.tag_with_text_opt("lastBuildDate", &self.last_build_date);
        channel.tag_with_text_opt("generator", &self.generator);
        channel.tag_with_text_opt("docs", &self.docs);
        channel.tag_with_text_opt("image", &self.image);
        channel.tag_with_text_opt("rating", &self.rating);
        channel.tag_with_text_opt("skipHours", &self.skip_hours);
        channel.tag_with_text_opt("skipDays", &self.skip_days);

        for category in &self.categories {
            channel.tag(category.to_xml());
        }

        channel
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut channel: Channel = Default::default();

        match element.get_child("title", None) {
            Some(element) => channel.title = element.content_str(),
            None => return Err("<channel> is missing required <title> element"),
        }

        match element.get_child("link", None) {
            Some(element) => channel.link = element.content_str(),
            None => return Err("<channel> is missing required <link> element"),
        }

        match element.get_child("description", None) {
            Some(element) => channel.description = element.content_str(),
            None => return Err("<channel> is missing required <description> element"),
        }

        channel.items = element.get_children("item", None)
            .into_iter()
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        channel.categories = element.get_children("category", None)
            .into_iter()
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(channel)
    }
}

#[derive(Default)]
pub struct Item {
    pub title: Option<String>,
    pub link: Option<String>,
    pub description: Option<String>,
    // pub author
    pub categories: Vec<Category>,
    // pub comments
    // pub enclosure
    // pub guid
    // pub pubDate
    // pub source
}

impl ViaXml for Item {
    fn to_xml(&self) -> Element {
        let mut item = Element::new("item", None, &[]);

        item.tag_with_text_opt("title", &self.title);
        item.tag_with_text_opt("link", &self.link);
        item.tag_with_text_opt("description", &self.description);

        for category in &self.categories {
            item.tag(category.to_xml());
        }

        item
    }

    fn from_xml(element: Element) -> Result<Self, &'static str> {
        let mut item: Item = Default::default();

        if let Some(element) = element.get_child("title", None) {
            item.title = Some(element.content_str());
        }

        if let Some(element) = element.get_child("link", None) {
            item.link = Some(element.content_str());
        }

        if let Some(element) = element.get_child("description", None) {
            item.description = Some(element.content_str());
        }

        item.categories = element.get_children("category", None)
            .into_iter()
            .map(|e| ViaXml::from_xml(e.clone()).unwrap())
            .collect();

        Ok(item)
    }
}

/// Category
///
/// [RSS 2.0 Specification § Category]
/// (http://cyber.law.harvard.edu/rss/rss.html#ltcategorygtSubelementOfLtitemgt)
#[derive(Default)]
pub struct Category {
    pub domain: Option<String>,
    pub value: String,
}

impl ViaXml for Category {
    fn to_xml(&self) -> Element {
        let mut category = match self.domain {
            Some(ref d) => Element::new("category", None, &[("domain", None, d)]),
            None => Element::new("category", None, &[]),
        };
        category.text(&self.value);
        category
    }

    fn from_xml(elem: Element) -> Result<Self, &'static str> {
        let domain = elem.get_attribute("domain", None).map(|s| s.to_string());
        let value = elem.content_str();

        Ok(Category {
            domain: domain,
            value: value,
        })
    }
}

#[cfg(test)]
mod test {
    use std::default::Default;
    use std::fs::File;
    use super::{Rss, Item, Channel};

    #[test]
    fn test_basic_to_string() {
        let item = Item {
            title: Some("My first post!".to_string()),
            link: Some("http://myblog.com/post1".to_string()),
            description: Some("This is my first post".to_string()),
            ..Default::default()
        };

        let channel = Channel {
            title: "My Blog".to_string(),
            link: "http://myblog.com".to_string(),
            description: "Where I write stuff".to_string(),
            items: vec![item],
            ..Default::default()
        };

        let rss = Rss(channel);
        assert_eq!(rss.to_string(), "<?xml version=\'1.0\' encoding=\'UTF-8\'?><rss version=\'2.0\'><channel><title>My Blog</title><link>http://myblog.com</link><description>Where I write stuff</description><item><title>My first post!</title><link>http://myblog.com/post1</link><description>This is my first post</description></item></channel></rss>");
    }

    #[test]
    fn test_from_file() {
        let mut file = File::open("test-data/pinboard.xml").unwrap();
        let rss = Rss::from_read(&mut file).unwrap();
        assert!(rss.to_string().len() > 0);
    }

    #[test]
    #[should_panic]
    fn test_from_read_no_channels() {
        let mut rss_bytes = "<rss></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_from_read_one_channel_no_properties() {
        let mut rss_bytes = "<rss><channel></channel></rss>".as_bytes();
        let Rss(_) = Rss::from_read(&mut rss_bytes).unwrap();
    }

    #[test]
    fn test_from_read_one_channel() {
        let mut rss_bytes = "<rss><channel><title>Hello world!</title><description></description><link></link></channel></rss>".as_bytes();
        let Rss(channel) = Rss::from_read(&mut rss_bytes).unwrap();
        assert_eq!("Hello world!", channel.title);
    }
}
<|endoftext|>


<commit_before>//! Parsers which cause errors or modifies the returned error on parse failure.

use lib::marker::PhantomData;

use {Parser, Stream, StreamOnce};
use parser::ParseMode;
use error::{ConsumedResult, Info, ParseError, StreamError, Tracked};

use error::FastResult::*;

#[derive(Clone)]
pub struct Unexpected(Info, PhantomData<fn(I) -> I>)
where
    I: Stream;
impl Parser for Unexpected
where
    I: Stream,
{
    type Input = I;
    type Output = ();
    type PartialState = ();
    #[inline]
    fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult<(), I> {
        EmptyErr(<Self::Input as StreamOnce>::Error::empty(input.position()).into())
    }
    fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
        errors.error.add(StreamError::unexpected(self.0.clone()));
    }
}
/// Always fails with `message` as an unexpected error.
/// Never consumes any input.
///
/// ```
/// # extern crate combine;
/// # use combine::*;
/// # use combine::error::StreamError;
/// # fn main() {
/// let result = unexpected("token")
///     .easy_parse("a");
/// assert!(result.is_err());
/// assert!(
///     result.err()
///         .unwrap()
///         .errors
///         .iter()
///         .any(|m| *m == StreamError::unexpected("token".into()))
/// );
/// # }
/// ```
#[inline(always)]
pub fn unexpected(message: S) -> Unexpected
where
    I: Stream,
    S: Into<Info>,
{
    Unexpected(message.into(), PhantomData)
}

#[derive(Clone)]
pub struct Message
(
    P,
    Info<
::Item,
::Range>,
)
where
    P: Parser;
impl Parser for Message

where
    I: Stream,
    P: Parser<Input = I>,
{
    type Input = I;
    type Output = P::Output;
    type PartialState = P::PartialState;

    parse_mode!();
    #[inline]
    fn parse_mode_impl<M>(
        &mut self,
        mode: M,
        input: &mut Self::Input,
        state: &mut Self::PartialState,
    ) -> ConsumedResult<Self::Output, Self::Input>
    where
        M: ParseMode,
    {
        match self.0.parse_mode(mode, input, state) {
            ConsumedOk(x) => ConsumedOk(x),
            EmptyOk(x) => EmptyOk(x),

            // The message should always be added even if some input was consumed before failing
            ConsumedErr(mut err) => {
                err.add_message(self.1.clone());
                ConsumedErr(err)
            }

            // The message will be added in `add_error`
            EmptyErr(err) => EmptyErr(err),
        }
    }

    fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
        self.0.add_error(errors);
        errors.error.add_message(self.1.clone());
    }
}

/// Equivalent to [`p1.message(msg)`].
///
/// [`p1.message(msg)`]: ../error/trait.Parser.html#method.message
#[inline(always)]
pub fn message
(
    p: P,
    msg: Info<
::Item,
::Range>,
) -> Message

where
    P: Parser,
{
    Message(p, msg)
}

#[derive(Clone)]
pub struct Expected
(
    P,
    Info<
::Item,
::Range>,
)
where
    P: Parser;
impl
 Parser for Expected

where
    P: Parser,
{
    type Input = P::Input;
    type Output = P::Output;
    type PartialState = P::PartialState;

    parse_mode!();
    #[inline(always)]
    fn parse_mode_impl<M>(
        &mut self,
        mode: M,
        input: &mut Self::Input,
        state: &mut Self::PartialState,
    ) -> ConsumedResult<Self::Output, Self::Input>
    where
        M: ParseMode,
    {
        self.0.parse_mode(mode, input, state)
    }

    fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
        ParseError::set_expected(errors, StreamError::expected(self.1.clone()), |errors| {
            self.0.add_error(errors);
        })
    }
}

/// Equivalent to [`p.expected(info)`].
///
/// [`p.expected(info)`]: ../error/trait.Parser.html#method.expected
#[inline(always)]
pub fn expected
(
    p: P,
    info: Info<
::Item,
::Range>,
) -> Expected

where
    P: Parser,
{
    Expected(p, info)
}
<commit_msg>feat: Add the unexpected_any parser<commit_after>//! Parsers which cause errors or modifies the returned error on parse failure.

use lib::marker::PhantomData;

use {Parser, Stream, StreamOnce};
use parser::ParseMode;
use error::{ConsumedResult, Info, ParseError, StreamError, Tracked};

use error::FastResult::*;

#[derive(Clone)]
pub struct Unexpected(Info, PhantomData<fn(I) -> (I, T)>)
where
    I: Stream;
impl Parser for Unexpected
where
    I: Stream,
{
    type Input = I;
    type Output = T;
    type PartialState = ();
    #[inline]
    fn parse_lazy(&mut self, input: &mut Self::Input) -> ConsumedResult<T, I> {
        EmptyErr(<Self::Input as StreamOnce>::Error::empty(input.position()).into())
    }
    fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
        errors.error.add(StreamError::unexpected(self.0.clone()));
    }
}
/// Always fails with `message` as an unexpected error.
/// Never consumes any input.
///
/// Has `()` the output type
///
/// ```
/// # extern crate combine;
/// # use combine::*;
/// # use combine::error::StreamError;
/// # fn main() {
/// let result = unexpected("token")
///     .easy_parse("a");
/// assert!(result.is_err());
/// assert!(
///     result.err()
///         .unwrap()
///         .errors
///         .iter()
///         .any(|m| *m == StreamError::unexpected("token".into()))
/// );
/// # }
/// ```
#[inline(always)]
pub fn unexpected(message: S) -> Unexpected
where
    I: Stream,
    S: Into<Info>,
{
    unexpected_any(message)
}

/// Always fails with `message` as an unexpected error.
/// Never consumes any input.
///
/// May have anything as the output type but must be used such that the output type can inferred.
/// The `unexpected` parser can be used if the output type does not matter
///
/// ```
/// # extern crate combine;
/// # use combine::*;
/// # use combine::parser::error::unexpected_any;
/// # use combine::error::StreamError;
/// # fn main() {
/// let result = token('b').or(unexpected_any("token"))
///     .easy_parse("a");
/// assert!(result.is_err());
/// assert!(
///     result.err()
///         .unwrap()
///         .errors
///         .iter()
///         .any(|m| *m == StreamError::unexpected("token".into()))
/// );
/// # }
/// ```
#[inline(always)]
pub fn unexpected_any(message: S) -> Unexpected
where
    I: Stream,
    S: Into<Info>,
{
    Unexpected(message.into(), PhantomData)
}

#[derive(Clone)]
pub struct Message
(
    P,
    Info<
::Item,
::Range>,
)
where
    P: Parser;
impl Parser for Message

where
    I: Stream,
    P: Parser<Input = I>,
{
    type Input = I;
    type Output = P::Output;
    type PartialState = P::PartialState;

    parse_mode!();
    #[inline]
    fn parse_mode_impl<M>(
        &mut self,
        mode: M,
        input: &mut Self::Input,
        state: &mut Self::PartialState,
    ) -> ConsumedResult<Self::Output, Self::Input>
    where
        M: ParseMode,
    {
        match self.0.parse_mode(mode, input, state) {
            ConsumedOk(x) => ConsumedOk(x),
            EmptyOk(x) => EmptyOk(x),

            // The message should always be added even if some input was consumed before failing
            ConsumedErr(mut err) => {
                err.add_message(self.1.clone());
                ConsumedErr(err)
            }

            // The message will be added in `add_error`
            EmptyErr(err) => EmptyErr(err),
        }
    }

    fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
        self.0.add_error(errors);
        errors.error.add_message(self.1.clone());
    }
}

/// Equivalent to [`p1.message(msg)`].
///
/// [`p1.message(msg)`]: ../error/trait.Parser.html#method.message
#[inline(always)]
pub fn message
(
    p: P,
    msg: Info<
::Item,
::Range>,
) -> Message

where
    P: Parser,
{
    Message(p, msg)
}

#[derive(Clone)]
pub struct Expected
(
    P,
    Info<
::Item,
::Range>,
)
where
    P: Parser;
impl
 Parser for Expected

where
    P: Parser,
{
    type Input = P::Input;
    type Output = P::Output;
    type PartialState = P::PartialState;

    parse_mode!();
    #[inline(always)]
    fn parse_mode_impl<M>(
        &mut self,
        mode: M,
        input: &mut Self::Input,
        state: &mut Self::PartialState,
    ) -> ConsumedResult<Self::Output, Self::Input>
    where
        M: ParseMode,
    {
        self.0.parse_mode(mode, input, state)
    }

    fn add_error(&mut self, errors: &mut Tracked<<Self::Input as StreamOnce>::Error>) {
        ParseError::set_expected(errors, StreamError::expected(self.1.clone()), |errors| {
            self.0.add_error(errors);
        })
    }
}

/// Equivalent to [`p.expected(info)`].
///
/// [`p.expected(info)`]: ../error/trait.Parser.html#method.expected
#[inline(always)]
pub fn expected
(
    p: P,
    info: Info<
::Item,
::Range>,
) -> Expected

where
    P: Parser,
{
    Expected(p, info)
}
<|endoftext|>


<commit_before>#![feature(collections, reflect_marker)]
#![no_std]

extern crate collections;

extern crate abs;
extern crate bounded;
extern crate to_degrees;
extern crate to_radians;
extern crate to_primitive;
extern crate from_primitive;
extern crate max;
extern crate min;
extern crate one;
extern crate pi;
extern crate round;
extern crate signum;
extern crate sqrt;
extern crate trig;
extern crate zero;

use collections::string::ToString;
use core::marker::Reflect;
use core::ops::*;

use abs::Abs;
use bounded::Bounded;
use to_degrees::ToDegrees;
use to_radians::ToRadians;
use to_primitive::ToPrimitive;
use from_primitive::FromPrimitive;
use max::Max;
use min::Min;
use one::One;
use pi::Pi;
use round::Round;
use signum::Signum;
use sqrt::Sqrt;
use trig::Trig;
use zero::Zero;

pub trait Num:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + Bounded
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Pi
    + PartialEq
    + PartialOrd
    + Round
    + ToString
    + Reflect

    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>
    + Neg<Output = Self> {}

impl<T> Num for T where T:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + Bounded
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Pi
    + PartialEq
    + PartialOrd
    + Round
    + ToString
    + Reflect

    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>
    + Neg<Output = T> {}
<commit_msg>clean up<commit_after>#![feature(collections)]
#![no_std]

extern crate collections;

extern crate abs;
extern crate bounded;
extern crate to_degrees;
extern crate to_radians;
extern crate to_primitive;
extern crate from_primitive;
extern crate max;
extern crate min;
extern crate one;
extern crate pi;
extern crate round;
extern crate signum;
extern crate sqrt;
extern crate trig;
extern crate zero;

use collections::string::ToString;
use core::ops::*;

use abs::Abs;
use bounded::Bounded;
use to_degrees::ToDegrees;
use to_radians::ToRadians;
use to_primitive::ToPrimitive;
use from_primitive::FromPrimitive;
use max::Max;
use min::Min;
use one::One;
use pi::Pi;
use round::Round;
use signum::Signum;
use sqrt::Sqrt;
use trig::Trig;
use zero::Zero;

pub trait Num:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + Bounded
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Pi
    + PartialEq
    + PartialOrd
    + Round
    + ToString

    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>

    + AddAssign<Self>
    + MulAssign<Self>
    + SubAssign<Self>
    + DivAssign<Self>
    + RemAssign<Self>

    + Neg<Output = Self> {}

impl<T> Num for T where T:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + Bounded
    + ToDegrees
    + ToRadians
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Pi
    + PartialEq
    + PartialOrd
    + Round
    + ToString

    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>

    + AddAssign<T>
    + MulAssign<T>
    + SubAssign<T>
    + DivAssign<T>
    + RemAssign<T>

    + Neg<Output = T> {}
<|endoftext|>


<commit_before>// Copyright 2017 Daniel P. Clark & other digits Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//
#![deny(missing_docs,trivial_casts,trivial_numeric_casts,
        missing_debug_implementations, missing_copy_implementations,
        unsafe_code,unstable_features,unused_import_braces,unused_qualifications)
]
//! # digits
//!
//! The digits crate is a linked list implementation of a score card flipper.  But
//! in this case it's with any characters you want and you can enumerate through
//! possibilities beyond the numeric limits intrinsic in basic numerc types like `u64`.
//!
//! Primary use case would be brute forcing character sequences.
extern crate base_custom;
#[doc(no_inline)]
pub use base_custom::BaseCustom;
use std::fmt;

/// This struct acts similar to a full number with a custom numeric character base.
/// But the underlying implementation is a linked list where all the methods recurse
/// as far as need to to implement the operations.
#[derive(Clone)]
pub struct Digits<'a> {
  mapping: &'a BaseCustom<char>,
  digit: u64,
  left: Option<Box<Digits<'a>>>,
}

impl<'a> Digits<'a> {
  /// Add two Digits instances together.  The one the `add` method is called on
  /// must be mutable and modifies itself.  The other is consumed.
  ///
  /// # Example
  ///
  /// ```
  /// use digits::{BaseCustom,Digits};
  ///
  /// let base10 = BaseCustom::<char>::new("0123456789".chars().collect());
  ///
  /// let mut eleven = Digits::new(&base10, "11".to_string());
  /// let two = Digits::new(&base10, "2".to_string());
  ///
  /// assert_eq!(eleven.add(two).to_s(), "13");
  /// ```
  ///
  /// # Output
  ///
  /// ```text
  /// "13"
  /// ```
  pub fn add(&mut self, other: Self) -> Self {
    assert!(self.mapping == other.mapping);
    if other.is_end() { return self.clone(); };
    let (last, rest) = other.head_tail();

    // sums current single digit
    let added = self.propagate(self.mapping.gen(last + self.digit));
    let (l, r) = added.head_tail();
    self.digit = l;

    // sums for left
    let mut intermediate = Digits::new_zero(self.mapping);
    if let Some(dg) = r { intermediate.add(dg.replicate()); }
    if let Some(dg) = rest { intermediate.add(dg.replicate()); }

    let current_left = match self.left.clone() {
      None => { Box::new(self.zero()) },
      Some(bx) => { bx },
    }.as_ref().clone();

    self.set_left( intermediate.add(current_left).clone() );
    self.clone()
  }

  // the way to recurse and process Digits
  fn head_tail(self) -> (u64, Option<Box<Self>>) {
    match self.left {
      Some(bx) => (self.digit, Some(bx)),
      None => (self.digit, None),
    }
  }

  // A non-consuming quick end check.
  // More efficient than calling `is_zero` when this applies.
  fn is_end(&self) -> bool {
    self.digit == 0 && match self.left { None => true, _ => false }
  }

  /// Returns bool value of if the number is one.
  pub fn is_one(&self) -> bool {
    if self.digit != 1 { return false }
    match &self.left {
      &None => { true },
      &Some(ref bx) => { bx.is_zero() },
    }
  }

  /// Returns bool value of if the number is zero.
  pub fn is_zero(&self) -> bool {
    if self.digit != 0 { return false }
    match &self.left {
      &None => { true },
      &Some(ref bx) => { bx.is_zero() },
    }
  }

  /// Returns a `usize` of the total linked list length.
  pub fn length(&self) -> usize {
    match &self.left {
      &None => 1,
      &Some(ref l) => { l.length() + 1 }
    }
  }

  /// Multiply two Digits instances together.  The one the `mul` method is called on
  /// must be mutable and modifies itself.  The other is consumed.
  ///
  /// # Example
  ///
  /// ```
  /// use digits::{BaseCustom,Digits};
  ///
  /// let base10 = BaseCustom::<char>::new("0123456789".chars().collect());
  ///
  /// let mut eleven = Digits::new(&base10, "11".to_string());
  /// let two = Digits::new(&base10, "2".to_string());
  ///
  /// assert_eq!(eleven.mul(two).to_s(), "22");
  /// ```
  ///
  /// # Output
  ///
  /// ```text
  /// "22"
  /// ```
  pub fn mul(&mut self, other: Self) -> Self {
    let (d, r) = self.multiply(other, 0).head_tail();
    self.digit = d;
    if let Some(rest) = r { self.set_left(rest.replicate()); }
    self.clone()
  }

  // Internal implementation for multiply. Needs the recursive
  // value of powers of ten for addition.
  fn multiply(&mut self, other: Self, power_of_ten: usize) -> Self {
    let mut additives: Vec<Digits> = vec![];
    let mut position: usize = power_of_ten;
    let mut o = Some(Box::new(other));
    loop {
      match o.clone() {
        Some(thing) => {
          let (dgt, tail) = thing.head_tail();
          o = tail;

          let mltply = self.propagate((self.digit * dgt).to_string()).pow_ten(position);

          let mapping = self.mapping.clone();

          if let Some(ref mut bx) = self.left {
            additives.push(
              bx.clone().multiply(
                Digits::new(
                  mapping,
                  self.mapping.gen(dgt).to_string()
                ),
                position + 1
              )
            );
          };

          if !mltply.is_zero() { additives.push( mltply ); }
        },
        None => break,
      }

      position += 1;
    }

    let mut result = self.zero();

    loop {
      match additives.pop() {
        Some(dg) => {
          result.add(dg);
        },
        None => break,
      }
    }

    result
  }

  /// Creates a new Digits instance with the provided character set and value.
  ///
  /// The first parameter must be a BaseCustom object which defines and maps all values.
  /// The second parameter is a string value with all valid characters from the BaseCustom set.
  pub fn new<S>(mapping: &'a BaseCustom<char>, number: S) -> Digits<'a>
  where S: Into<String> {
    let number = number.into();
    if number.is_empty() { return Digits { mapping: mapping, digit: 0, left: None }; };
    let (last, rest) = {
      let mut n = number.chars().rev();
      (n.next().unwrap(), n.rev().collect::<String>())
    };

    let continuation = {
      if rest.is_empty() {
        None
      } else {
        Some(Box::new(Digits::new(&mapping, rest)))
      }
    };
    Digits {
      mapping: mapping,
      digit: mapping.decimal(last.to_string()),
      left: continuation,
    }
  }

  /// Creates a new Digits instance with value of zero and uses the provided character mapping.
  pub fn new_zero(mapping: &'a BaseCustom<char>) -> Self {
    Digits { mapping: mapping, digit: 0, left: None }
  }

  /// Creates a new Digits instance with value of one and the provided character mapping.
  pub fn new_one(mapping: &'a BaseCustom<char>) -> Self {
    Digits { mapping: mapping, digit: 1, left: None }
  }

  /// Creates a new Digits instance with value of one and uses the current character mapping.
  pub fn one(&self) -> Self {
    Digits::new_one(self.mapping)
  }

  /// The “pinky” is the smallest digit
  /// a.k.a. current digit in the linked list
  /// a.k.a. the right most digit.
  /// This will be a `char` value for that digit.
  pub fn pinky(&self) -> char {
    self.mapping.char(self.digit as usize).unwrap()
  }

  /// Multiplies self times the power-of given Digits parameter.
  ///
  /// # Example
  ///
  /// ```
  /// use digits::{BaseCustom,Digits};
  ///
  /// let base10 = BaseCustom::<char>::new("0123456789".chars().collect());
  ///
  /// let mut eleven = Digits::new(&base10, "11".to_string());
  /// let two = Digits::new(&base10, "2".to_string());
  ///
  /// assert_eq!(eleven.pow(two).to_s(), "121");
  /// ```
  ///
  /// # Output
  ///
  /// ```text
  /// "121"
  /// ```
  pub fn pow(&mut self, mut pwr: Self) -> Self {
    loop {
      match pwr.is_one() {
        true => break,
        false => {
          let copy = self.clone();
          self.mul(copy);
        }
      }
      pwr.pred_till_zero();
    }
    self.clone()
  }

  // multiply self by 10^x without using typical multiplication
  fn pow_ten(&self, positions: usize) -> Self {
    let mut result: Digits = self.clone();
    for _ in 0..positions {
      let original = result;
      result = Digits::new_zero(self.mapping);
      result.set_left(original);
    }
    result
  }

  /// Minuses one unless it's zero, then it just returns a Digits instance of zero.
  pub fn pred_till_zero(&mut self) -> Self {
    if self.digit == 0 {
      if self.is_end() { return self.clone(); }
      self.digit = self.mapping.base - 1;
      let mut one = false;
      if let Some(ref mut bx) = self.left {
        if bx.is_one() {
          one = true;
        } else {
          bx.pred_till_zero();
        }
      };
      if one { self.left = None; }
    } else {
      self.digit -= 1;
    }
    self.clone()
  }

  /// Creates a new Digits instance with the internal character set and given value.
  ///
  /// The parameter is a string value with all valid characters from the BaseCustom set.
  pub fn propagate<S>(&self, number: S) -> Self
  where S: Into<String> {
    Digits::new(self.mapping, number)
  }

  /// An alias for `clone`. _Useful for unboxing._
  pub fn replicate(self) -> Self { self.clone() }

  // logic for setting left linked list continuation
  fn set_left(&mut self, d: Digits<'a>) {
    if d.is_end() {
      self.left = None;
    } else {
      self.left = Some(Box::new(d));
    }
  }

  /// Plus one.
  pub fn succ(&mut self) -> Self {
    let one = self.one();
    self.add(one)
  }

  /// Gives the full value of all digits within the linked list as a String.
  pub fn to_s(&self) -> String {
    let num = self.mapping.gen(self.digit);
    match &self.left {
      &None => format!("{}", num),
      &Some(ref bx) => format!("{}{}", bx.to_s(), num),
    }
  }

  /// Gives the full value of all digits within the linked list as a String.
  pub fn to_string(&self) -> String {
    self.to_s()
  }

  /// Creates a new Digits instance with value of zero and the current character mapping.
  pub fn zero(&self) -> Self {
    Digits::new_zero(self.mapping)
  }
}

/// NODOC
pub trait Into<String> {
  /// NODOC
  fn into(self) -> String;
}

impl<'a> From<Digits<'a>> for String {
  fn from(d: Digits) -> String {
    d.to_s()
  }
}

impl<'a> Into<String> for Digits<'a> {
  fn into(self) -> String {
    self.to_s()
  }
}

impl Into<String> for String {
  fn into(self) -> String {
    self.clone()
  }
}

impl<'a> fmt::Display for Digits<'a> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   write!(f, "Digits<'a> — (Character: '{}', Decimal Value: {}{})",
     self.mapping.gen(self.digit), self.digit, {
       match self.left {
         None => "".to_string(),
         Some(ref l) => format!(", With Preceeding: '{}'", l.to_s()),
       }
     }
     )
  }
}

impl<'a> fmt::Debug for Digits<'a> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{} with base: {:?}", self, self.mapping)
  }
}
<commit_msg>alphabetical update [ci skip]<commit_after>// Copyright 2017 Daniel P. Clark & other digits Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//
#![deny(missing_docs,trivial_casts,trivial_numeric_casts,
        missing_debug_implementations, missing_copy_implementations,
        unsafe_code,unstable_features,unused_import_braces,unused_qualifications)
]
//! # digits
//!
//! The digits crate is a linked list implementation of a score card flipper.  But
//! in this case it's with any characters you want and you can enumerate through
//! possibilities beyond the numeric limits intrinsic in basic numerc types like `u64`.
//!
//! Primary use case would be brute forcing character sequences.
extern crate base_custom;
#[doc(no_inline)]
pub use base_custom::BaseCustom;
use std::fmt;

/// This struct acts similar to a full number with a custom numeric character base.
/// But the underlying implementation is a linked list where all the methods recurse
/// as far as need to to implement the operations.
#[derive(Clone)]
pub struct Digits<'a> {
  mapping: &'a BaseCustom<char>,
  digit: u64,
  left: Option<Box<Digits<'a>>>,
}

impl<'a> Digits<'a> {
  /// Add two Digits instances together.  The one the `add` method is called on
  /// must be mutable and modifies itself.  The other is consumed.
  ///
  /// # Example
  ///
  /// ```
  /// use digits::{BaseCustom,Digits};
  ///
  /// let base10 = BaseCustom::<char>::new("0123456789".chars().collect());
  ///
  /// let mut eleven = Digits::new(&base10, "11".to_string());
  /// let two = Digits::new(&base10, "2".to_string());
  ///
  /// assert_eq!(eleven.add(two).to_s(), "13");
  /// ```
  ///
  /// # Output
  ///
  /// ```text
  /// "13"
  /// ```
  pub fn add(&mut self, other: Self) -> Self {
    assert!(self.mapping == other.mapping);
    if other.is_end() { return self.clone(); };
    let (last, rest) = other.head_tail();

    // sums current single digit
    let added = self.propagate(self.mapping.gen(last + self.digit));
    let (l, r) = added.head_tail();
    self.digit = l;

    // sums for left
    let mut intermediate = Digits::new_zero(self.mapping);
    if let Some(dg) = r { intermediate.add(dg.replicate()); }
    if let Some(dg) = rest { intermediate.add(dg.replicate()); }

    let current_left = match self.left.clone() {
      None => { Box::new(self.zero()) },
      Some(bx) => { bx },
    }.as_ref().clone();

    self.set_left( intermediate.add(current_left).clone() );
    self.clone()
  }

  // the way to recurse and process Digits
  fn head_tail(self) -> (u64, Option<Box<Self>>) {
    match self.left {
      Some(bx) => (self.digit, Some(bx)),
      None => (self.digit, None),
    }
  }

  // A non-consuming quick end check.
  // More efficient than calling `is_zero` when this applies.
  fn is_end(&self) -> bool {
    self.digit == 0 && match self.left { None => true, _ => false }
  }

  /// Returns bool value of if the number is one.
  pub fn is_one(&self) -> bool {
    if self.digit != 1 { return false }
    match &self.left {
      &None => { true },
      &Some(ref bx) => { bx.is_zero() },
    }
  }

  /// Returns bool value of if the number is zero.
  pub fn is_zero(&self) -> bool {
    if self.digit != 0 { return false }
    match &self.left {
      &None => { true },
      &Some(ref bx) => { bx.is_zero() },
    }
  }

  /// Returns a `usize` of the total linked list length.
  pub fn length(&self) -> usize {
    match &self.left {
      &None => 1,
      &Some(ref l) => { l.length() + 1 }
    }
  }

  /// Multiply two Digits instances together.  The one the `mul` method is called on
  /// must be mutable and modifies itself.  The other is consumed.
  ///
  /// # Example
  ///
  /// ```
  /// use digits::{BaseCustom,Digits};
  ///
  /// let base10 = BaseCustom::<char>::new("0123456789".chars().collect());
  ///
  /// let mut eleven = Digits::new(&base10, "11".to_string());
  /// let two = Digits::new(&base10, "2".to_string());
  ///
  /// assert_eq!(eleven.mul(two).to_s(), "22");
  /// ```
  ///
  /// # Output
  ///
  /// ```text
  /// "22"
  /// ```
  pub fn mul(&mut self, other: Self) -> Self {
    let (d, r) = self.multiply(other, 0).head_tail();
    self.digit = d;
    if let Some(rest) = r { self.set_left(rest.replicate()); }
    self.clone()
  }

  // Internal implementation for multiply. Needs the recursive
  // value of powers of ten for addition.
  fn multiply(&mut self, other: Self, power_of_ten: usize) -> Self {
    let mut additives: Vec<Digits> = vec![];
    let mut position: usize = power_of_ten;
    let mut o = Some(Box::new(other));
    loop {
      match o.clone() {
        Some(thing) => {
          let (dgt, tail) = thing.head_tail();
          o = tail;

          let mltply = self.propagate((self.digit * dgt).to_string()).pow_ten(position);

          let mapping = self.mapping.clone();

          if let Some(ref mut bx) = self.left {
            additives.push(
              bx.clone().multiply(
                Digits::new(
                  mapping,
                  self.mapping.gen(dgt).to_string()
                ),
                position + 1
              )
            );
          };

          if !mltply.is_zero() { additives.push( mltply ); }
        },
        None => break,
      }

      position += 1;
    }

    let mut result = self.zero();

    loop {
      match additives.pop() {
        Some(dg) => {
          result.add(dg);
        },
        None => break,
      }
    }

    result
  }

  /// Creates a new Digits instance with the provided character set and value.
  ///
  /// The first parameter must be a BaseCustom object which defines and maps all values.
  /// The second parameter is a string value with all valid characters from the BaseCustom set.
  pub fn new<S>(mapping: &'a BaseCustom<char>, number: S) -> Digits<'a>
  where S: Into<String> {
    let number = number.into();
    if number.is_empty() { return Digits { mapping: mapping, digit: 0, left: None }; };
    let (last, rest) = {
      let mut n = number.chars().rev();
      (n.next().unwrap(), n.rev().collect::<String>())
    };

    let continuation = {
      if rest.is_empty() {
        None
      } else {
        Some(Box::new(Digits::new(&mapping, rest)))
      }
    };
    Digits {
      mapping: mapping,
      digit: mapping.decimal(last.to_string()),
      left: continuation,
    }
  }

  /// Creates a new Digits instance with value of one and the provided character mapping.
  pub fn new_one(mapping: &'a BaseCustom<char>) -> Self {
    Digits { mapping: mapping, digit: 1, left: None }
  }

  /// Creates a new Digits instance with value of zero and uses the provided character mapping.
  pub fn new_zero(mapping: &'a BaseCustom<char>) -> Self {
    Digits { mapping: mapping, digit: 0, left: None }
  }

  /// Creates a new Digits instance with value of one and uses the current character mapping.
  pub fn one(&self) -> Self {
    Digits::new_one(self.mapping)
  }

  /// The “pinky” is the smallest digit
  /// a.k.a. current digit in the linked list
  /// a.k.a. the right most digit.
  /// This will be a `char` value for that digit.
  pub fn pinky(&self) -> char {
    self.mapping.char(self.digit as usize).unwrap()
  }

  /// Multiplies self times the power-of given Digits parameter.
  ///
  /// # Example
  ///
  /// ```
  /// use digits::{BaseCustom,Digits};
  ///
  /// let base10 = BaseCustom::<char>::new("0123456789".chars().collect());
  ///
  /// let mut eleven = Digits::new(&base10, "11".to_string());
  /// let two = Digits::new(&base10, "2".to_string());
  ///
  /// assert_eq!(eleven.pow(two).to_s(), "121");
  /// ```
  ///
  /// # Output
  ///
  /// ```text
  /// "121"
  /// ```
  pub fn pow(&mut self, mut pwr: Self) -> Self {
    loop {
      match pwr.is_one() {
        true => break,
        false => {
          let copy = self.clone();
          self.mul(copy);
        }
      }
      pwr.pred_till_zero();
    }
    self.clone()
  }

  // multiply self by 10^x without using typical multiplication
  fn pow_ten(&self, positions: usize) -> Self {
    let mut result: Digits = self.clone();
    for _ in 0..positions {
      let original = result;
      result = Digits::new_zero(self.mapping);
      result.set_left(original);
    }
    result
  }

  /// Minuses one unless it's zero, then it just returns a Digits instance of zero.
  pub fn pred_till_zero(&mut self) -> Self {
    if self.digit == 0 {
      if self.is_end() { return self.clone(); }
      self.digit = self.mapping.base - 1;
      let mut one = false;
      if let Some(ref mut bx) = self.left {
        if bx.is_one() {
          one = true;
        } else {
          bx.pred_till_zero();
        }
      };
      if one { self.left = None; }
    } else {
      self.digit -= 1;
    }
    self.clone()
  }

  /// Creates a new Digits instance with the internal character set and given value.
  ///
  /// The parameter is a string value with all valid characters from the BaseCustom set.
  pub fn propagate<S>(&self, number: S) -> Self
  where S: Into<String> {
    Digits::new(self.mapping, number)
  }

  /// An alias for `clone`. _Useful for unboxing._
  pub fn replicate(self) -> Self { self.clone() }

  // logic for setting left linked list continuation
  fn set_left(&mut self, d: Digits<'a>) {
    if d.is_end() {
      self.left = None;
    } else {
      self.left = Some(Box::new(d));
    }
  }

  /// Plus one.
  pub fn succ(&mut self) -> Self {
    let one = self.one();
    self.add(one)
  }

  /// Gives the full value of all digits within the linked list as a String.
  pub fn to_s(&self) -> String {
    let num = self.mapping.gen(self.digit);
    match &self.left {
      &None => format!("{}", num),
      &Some(ref bx) => format!("{}{}", bx.to_s(), num),
    }
  }

  /// Gives the full value of all digits within the linked list as a String.
  pub fn to_string(&self) -> String {
    self.to_s()
  }

  /// Creates a new Digits instance with value of zero and the current character mapping.
  pub fn zero(&self) -> Self {
    Digits::new_zero(self.mapping)
  }
}

/// NODOC
pub trait Into<String> {
  /// NODOC
  fn into(self) -> String;
}

impl<'a> From<Digits<'a>> for String {
  fn from(d: Digits) -> String {
    d.to_s()
  }
}

impl<'a> Into<String> for Digits<'a> {
  fn into(self) -> String {
    self.to_s()
  }
}

impl Into<String> for String {
  fn into(self) -> String {
    self.clone()
  }
}

impl<'a> fmt::Display for Digits<'a> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
   write!(f, "Digits<'a> — (Character: '{}', Decimal Value: {}{})",
     self.mapping.gen(self.digit), self.digit, {
       match self.left {
         None => "".to_string(),
         Some(ref l) => format!(", With Preceeding: '{}'", l.to_s()),
       }
     }
     )
  }
}

impl<'a> fmt::Debug for Digits<'a> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "{} with base: {:?}", self, self.mapping)
  }
}
<|endoftext|>


<commit_before>#![feature(libc)]

#![allow(dead_code)]
#![allow(non_camel_case_types)]

extern crate libc;
use libc::c_int;

/**
 * Abstraction of libnl core functionality
 */
#[link(name="nl-3")]
extern "C" {
	// Exposed socket functions
	fn nl_socket_alloc() -> *const nl_sock;
	fn nl_socket_free(socket: *const nl_sock);
	fn nl_socket_get_fd(socket: *const nl_sock) -> i32;
	fn nl_socket_set_buffer_size(socket: *const nl_sock, rxbuf: c_int, txbuf: c_int) -> i32;

	fn nl_socket_set_local_port(socket: *const nl_sock, port: u32);
	fn nl_socket_get_local_port(socket: *const nl_sock) -> u32;

	fn nl_connect(socket: *const nl_sock, protocol: u32) -> i32;
	fn nl_close(socket: *const nl_sock);

	fn nl_socket_set_cb(socket: *const nl_sock, cb: *const nl_cb);
	fn nl_socket_get_cb(socket: *const nl_sock) -> nl_cb;

	// Exposed socket transceiver
	fn nl_send_simple(socket: *const nl_sock, msg_type: c_int, flags: c_int, buf: *const u8, size: c_int) -> i32;
	// Exposed msg functions
	fn nlmsg_alloc() -> *const nl_msg;
	fn nlmsg_free(msg: *const nl_msg);

}

// exposed structures - these are wrapped
#[repr(C)]
struct nl_sock;
#[repr(C)]
struct nl_msg;
#[repr(C)]
struct nl_cb;

// RSNL datatypes wrapping the libnl data structures
pub struct socket {
	ptr: *const nl_sock
}

/* library version of nlmsghdr
 * the name is altered to differentiate
 * between the native libnl and rsnl
 */
pub struct rsnl_msghdr {
	nlmsg_len: u32,
	nlmsg_type: u16,
	nlmsg_flags: u16,
	nlmsg_seq: u32,
	nlmsg_pid: u32
}

pub struct msg {
	ptr: *const nl_msg
}

pub enum NetlinkProtocol {
	route,
	unused,
	usersock,
	firewall,
	sock_diag,
	nflog,
	xfrm,
	selinux,
	iscsi,
	audit,
	fib_lookup,
	connector,
	netfilter,
	ip6_fw,
	dnrtmsg,
	kobject_uevent,
	DMEVENTS,
	scsitransport,
	ecryptfs,
	rdma,
	crypto,
	zu= 30
}

impl socket {
	pub fn new() -> socket {
		unsafe {
			let nlptr = nl_socket_alloc();
			socket {
				ptr: nlptr
			}
		}
	}

	pub fn free(&self) {
		unsafe {
			nl_socket_free(self.ptr)
		}
	}

	pub fn set_buffer_size(&self, rxbuf: c_int, txbuf: c_int) -> i32 {
		unsafe {
			nl_socket_set_buffer_size(self.ptr, rxbuf, txbuf)
		}
	}

	pub fn connect(&self, protocol: NetlinkProtocol) -> i32 {
		unsafe { nl_connect(self.ptr, protocol as u32) }
	}

	pub fn close(&self) {
		unsafe{ nl_close(self.ptr) }
	}

	pub fn get_fd(&self) -> i32 {
		unsafe { nl_socket_get_fd(self.ptr) }
	}

	pub fn get_local_port(&self) -> u32 {
		unsafe { nl_socket_get_local_port(self.ptr) }
	}

	pub fn set_local_port(&self, port: u32) {
		unsafe { nl_socket_set_local_port(self.ptr, port) }
	}

	pub fn send_simple(&self, msg_type: c_int, flags: c_int, buf: *const u8, size: c_int) -> c_int {
		unsafe { nl_send_simple(self.ptr, msg_type, flags, buf, size) }
	}
}

impl msg {
	pub fn new() -> msg {
	unsafe {
		let nlmsg = nlmsg_alloc();
		msg {
			ptr: nlmsg
		}
	}
	}

	pub fn free(&self) {
		unsafe{ nlmsg_free(self.ptr); }
	}
}
<commit_msg>Expose nlmsg_append; add to msg impl<commit_after>#![feature(libc)]

#![allow(dead_code)]
#![allow(non_camel_case_types)]

extern crate libc;
use libc::{c_int, size_t, c_void};
use std::mem;

/**
 * Abstraction of libnl core functionality
 */
#[link(name="nl-3")]
extern "C" {
	// Exposed socket functions
	fn nl_socket_alloc() -> *const nl_sock;
	fn nl_socket_free(socket: *const nl_sock);
	fn nl_socket_get_fd(socket: *const nl_sock) -> i32;
	fn nl_socket_set_buffer_size(socket: *const nl_sock, rxbuf: c_int, txbuf: c_int) -> i32;

	fn nl_socket_set_local_port(socket: *const nl_sock, port: u32);
	fn nl_socket_get_local_port(socket: *const nl_sock) -> u32;

	fn nl_connect(socket: *const nl_sock, protocol: u32) -> i32;
	fn nl_close(socket: *const nl_sock);

	fn nl_socket_set_cb(socket: *const nl_sock, cb: *const nl_cb);
	fn nl_socket_get_cb(socket: *const nl_sock) -> nl_cb;

	// Exposed socket transceiver
	fn nl_send_simple(socket: *const nl_sock, msg_type: c_int, flags: c_int, buf: *const u8, size: c_int) -> i32;

	// Exposed msg functions
	fn nlmsg_alloc() -> *const nl_msg;
	fn nlmsg_free(msg: *const nl_msg);
	fn nlmsg_append(msg: *const nl_msg, data: *const c_void, len: size_t, pad: c_int) -> i32;

}

// exposed structures - these are wrapped
#[repr(C)]
struct nl_sock;
#[repr(C)]
struct nl_msg;
#[repr(C)]
struct nl_cb;

// RSNL datatypes wrapping the libnl data structures
pub struct socket {
	ptr: *const nl_sock
}

/* library version of nlmsghdr
 * the name is altered to differentiate
 * between the native libnl and rsnl
 */
pub struct rsnl_msghdr {
	nlmsg_len: u32,
	nlmsg_type: u16,
	nlmsg_flags: u16,
	nlmsg_seq: u32,
	nlmsg_pid: u32
}

pub struct msg {
	ptr: *const nl_msg
}

pub enum NetlinkProtocol {
	route,
	unused,
	usersock,
	firewall,
	sock_diag,
	nflog,
	xfrm,
	selinux,
	iscsi,
	audit,
	fib_lookup,
	connector,
	netfilter,
	ip6_fw,
	dnrtmsg,
	kobject_uevent,
	DMEVENTS,
	scsitransport,
	ecryptfs,
	rdma,
	crypto,
	zu= 30
}

impl socket {
	pub fn new() -> socket {
		unsafe {
			let nlptr = nl_socket_alloc();
			socket {
				ptr: nlptr
			}
		}
	}

	pub fn free(&self) {
		unsafe {
			nl_socket_free(self.ptr)
		}
	}

	pub fn set_buffer_size(&self, rxbuf: c_int, txbuf: c_int) -> i32 {
		unsafe {
			nl_socket_set_buffer_size(self.ptr, rxbuf, txbuf)
		}
	}

	pub fn connect(&self, protocol: NetlinkProtocol) -> i32 {
		unsafe { nl_connect(self.ptr, protocol as u32) }
	}

	pub fn close(&self) {
		unsafe{ nl_close(self.ptr) }
	}

	pub fn get_fd(&self) -> i32 {
		unsafe { nl_socket_get_fd(self.ptr) }
	}

	pub fn get_local_port(&self) -> u32 {
		unsafe { nl_socket_get_local_port(self.ptr) }
	}

	pub fn set_local_port(&self, port: u32) {
		unsafe { nl_socket_set_local_port(self.ptr, port) }
	}

	pub fn send_simple(&self, msg_type: c_int, flags: c_int, buf: *const u8, size: c_int) -> i32 {
		unsafe { nl_send_simple(self.ptr, msg_type, flags, buf, size) as i32 }
	}
}

impl msg {
	pub fn new() -> msg {
	unsafe {
		let nlmsg = nlmsg_alloc();
		msg {
			ptr: nlmsg
		}
	}
	}

	pub fn free(&self) {
		unsafe{ nlmsg_free(self.ptr); }
	}

    pub fn append<T>(&self, data: &T, len: u32, pad: i32) -> i32 {
        unsafe {
            let vptr: *const c_void = mem::transmute(data);
            nlmsg_append(self.ptr, vptr, len as size_t, pad as c_int) as i32
        }
    }
}
<|endoftext|>


<commit_before>#![crate_name = "htmlstream"]
#![doc(html_root_url = "https://leizongmin.github.io/htmlstream-rust/")]

/// The HTML source position
///
/// # Examples
/// ```
/// # use self::htmlstream::*;
/// let pos = Position {
///     start: 0,
///     end: 10,
/// };
/// ```
#[derive(Debug)]
pub struct Position {
    pub start: usize,
    pub end: usize,
}

/// The tag state
///
/// + `Text`: not a HTML tag, e.g. hello
/// + `Opening`: an opening tag, e.g.
/// + `Closing`: a closing tag, e.g.
/// + `SelfClosing`: a selfclosing tag,e.g.

#[derive(Debug)]
pub enum HTMLTagState {
    Text, Opening, Closing, SelfClosing
}

/// The HTML tag
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let tag = HTMLTag {
///     name: "a".to_string(),
///     html: "<a href=\"#\">link</a>".to_string(),
///     attributes: "href=\"#\"".to_string(),
///     state: HTMLTagState::Opening,
/// };
/// ```
#[derive(Debug)]
pub struct HTMLTag {
    pub name: String,
    pub html: String,
    pub attributes: String,
    pub state: HTMLTagState
}

/// The tag attribute
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let attr = HTMLTagAttribute {
///     name: "href".to_string(),
///     value: "#".to_string(),
/// };
/// ```
#[derive(Debug)]
pub struct HTMLTagAttribute {
    pub name: String,
    pub value: String,
}

const CHAR_SINGLE_QUOTE: u8 = b'\'';
const CHAR_DOUBLE_QUOTE: u8 = b'"';
const CHAR_LT: u8 = b'<';
const CHAR_GT: u8 = b'>';
const CHAR_SLASH: u8 = b'/';
const CHAR_SPACE: u8 = b' ';
const CHAR_EQUAL: u8 = b'=';

/// Parse HTML source, find the tags and call `on_tag()` every time
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let html = "this is a test: <a href=\"http://rust-lang.org\">The Rust Programing Language</a>";
/// parse_html(html, |pos: &Position, tag: &HTMLTag| {
///     println!("{:?} {:?}", pos, tag);
/// });
/// ```
pub fn parse_html<F>(html: &str, on_tag: F) where F: Fn(&Position, &HTMLTag) {
    let mut is_tag_start: bool = false;
    let mut is_quote_start: bool = false;
    let mut is_get_tag_name: bool = false;
    let mut is_closing_tag: bool = false;
    let mut current_tag_name: &str = "";

    let mut quote_char: u8 = 0;
    let mut last_char: u8 = 0;
    let mut last_index: usize = 0;
    let mut current_index: usize = 0;
    let mut attributes_start_index: usize = 0;

    let html_bytes = html.as_bytes();
    for b in html_bytes {
        let c = *b;
        if current_index > 0 {
            last_char = html_bytes[current_index - 1];;
        }
        current_index += 1;

        if is_tag_start {

            if !is_get_tag_name {
                if CHAR_SLASH == c && last_index + 2 == current_index {
                    is_closing_tag = true;
                } else {
                    if c <= CHAR_SPACE || CHAR_SLASH == c || CHAR_GT == c || CHAR_LT == c {
                        if is_closing_tag {
                            current_tag_name = &html[(last_index + 2)..(current_index - 1)];
                        } else {
                            current_tag_name = &html[(last_index + 1)..(current_index - 1)];
                        }
                        attributes_start_index = current_index;
                        is_get_tag_name = true;
                    }
                }
            }

            // only when match the same `quote` char
            if is_quote_start {
                if c == quote_char {
                    is_quote_start = false;
                    continue;
                } else {
                    continue;
                }
            }

            // quote start
            if CHAR_SINGLE_QUOTE == c || CHAR_DOUBLE_QUOTE == c {
                // only when the last char is `equal`
                if CHAR_EQUAL == last_char {
                    is_quote_start = true;
                    quote_char = c;
                }
                continue;
            }

            // tag end
            if CHAR_GT == c {
                let tag_html = &html[last_index..current_index];
                let position = Position { start: last_index, end: current_index };

                let tag_state: HTMLTagState;
                if is_closing_tag {
                    tag_state = HTMLTagState::Closing;
                } else if CHAR_SLASH == last_char {
                    tag_state = HTMLTagState::SelfClosing;
                } else {
                    tag_state = HTMLTagState::Opening;
                }

                let attributes_html: &str;
                if is_get_tag_name && attributes_start_index < current_index - 1 {
                    match tag_state {
                        HTMLTagState::SelfClosing => {
                            attributes_html = &html[attributes_start_index..(current_index - 2)];
                        },
                        HTMLTagState::Opening | HTMLTagState::Closing => {
                            attributes_html = &html[attributes_start_index..(current_index - 1)];
                        },
                        _ => {
                            attributes_html = "";
                        }
                    }
                } else {
                    attributes_html = "";
                }

                let tag = HTMLTag {
                    name: current_tag_name.to_string(),
                    html: tag_html.to_string(),
                    attributes: attributes_html.to_string(),
                    state: tag_state,
                };
                on_tag(&position, &tag);

                last_index = current_index;
                is_tag_start = false;
            }

        } else {

            if CHAR_LT == c {
                // text
                if last_index < current_index - 1 {
                    let tag_html = &html[last_index..(current_index - 1)];
                    let position = Position { start: last_index, end: current_index };
                    let tag = HTMLTag {
                        name: "".to_string(),
                        html: tag_html.to_string(),
                        attributes: "".to_string(),
                        state: HTMLTagState::Text,
                    };
                    on_tag(&position, &tag);
                }

                // init
                is_tag_start = true;
                is_get_tag_name = false;
                is_closing_tag = false;
                is_quote_start = false;
                last_index = current_index - 1;
                continue;
            }

        }
    }

    // the rest text
    if last_index < current_index - 1 {
        let tag_html = &html[last_index..];
        let position = Position { start: last_index, end: current_index };
        let tag = HTMLTag {
            name: "".to_string(),
            html: tag_html.to_string(),
            attributes: "".to_string(),
            state: HTMLTagState::Text,
        };
        on_tag(&position, &tag);
    }
}

/// Parse attributes strings, find the attribute and call `on_tag_attribute()` every time
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let attributes = "href=\"http://rust-lang.org\" title=Rust disabled";
/// parse_attributes(attributes, |pos: &Position, attr: &HTMLTagAttribute| {
///     println!("{:?} {:?}", pos, attr);
/// });
/// ```
pub fn parse_attributes<F>(html: &str, on_tag_attribute: F) where F: Fn(&Position, &HTMLTagAttribute) {
    let mut is_quote_start: bool = false;
    let mut is_attribute_start: bool = false;
    let mut is_get_attribute_name: bool = false;

    let mut quote_char: u8 = 0;
    let mut last_char: u8 = 0;
    let mut last_index: usize = 0;
    let mut current_index: usize = 0;
    let mut value_start_index: usize = 0;

    let html_bytes = html.as_bytes();
    for b in html_bytes {
        let c = *b;
        if current_index > 0 {
            last_char = html_bytes[current_index - 1];;
        }
        current_index += 1;

        if is_attribute_start {

            if is_get_attribute_name {

                if is_quote_start {
                    if c == quote_char {
                        // only when match the same `quote` char
                        if c == quote_char {
                            let name = &html[last_index..(value_start_index - 1)];
                            let value = &html[(value_start_index + 1)..(current_index - 1)];
                            let position = Position { start: last_index, end: current_index };
                            let attribute = HTMLTagAttribute {
                                name: name.to_string(),
                                value: value.to_string(),
                            };
                            on_tag_attribute(&position, &attribute);

                            is_attribute_start = false;
                            is_quote_start = false;
                            is_get_attribute_name = false;
                            continue;
                        }
                    } else {
                        continue;
                    }
                }

                // quote start
                if CHAR_SINGLE_QUOTE == c || CHAR_DOUBLE_QUOTE == c {
                    // only when the last char is `equal`
                    if CHAR_EQUAL == last_char {
                        is_quote_start = true;
                        quote_char = c;
                    }
                    continue;
                }

                // only when match a `blank` char
                if c <= CHAR_SPACE {
                    let name = &html[last_index..(value_start_index - 1)];
                    let value = &html[(value_start_index)..(current_index - 1)];
                    let position = Position { start: last_index, end: current_index };
                    let attribute = HTMLTagAttribute {
                        name: name.to_string(),
                        value: value.to_string(),
                    };
                    on_tag_attribute(&position, &attribute);

                    is_attribute_start = false;
                    is_quote_start = false;
                    is_get_attribute_name = false;
                    continue;
                }

            } else {

                // only when match an `equal` char, start the attribute value
                if CHAR_EQUAL == c {
                    value_start_index = current_index;
                    is_get_attribute_name = true;
                    continue;
                }

                // only when match an `blank` char, stop current attribute
                if c <= CHAR_SPACE {
                    let name = &html[last_index..(current_index - 1)];
                    let position = Position { start: last_index, end: current_index };
                    let attribute = HTMLTagAttribute {
                        name: name.to_string(),
                        value: "".to_string(),
                    };
                    on_tag_attribute(&position, &attribute);

                    is_attribute_start = false;
                    is_quote_start = false;
                    is_get_attribute_name = false;
                    continue;
                }

            }

        } else {

            // ignore `blank` char
            if c <= CHAR_SPACE {
                continue;
            }

            is_attribute_start = true;
            is_get_attribute_name = false;
            is_quote_start = false;
            last_index = current_index - 1;

        }
    }

    // the rest text
    let name = &html[last_index..];
    let position = Position { start: last_index, end: current_index };
    let attribute = HTMLTagAttribute {
        name: name.to_string(),
        value: "".to_string()
    };
    on_tag_attribute(&position, &attribute);
}
<commit_msg>增加PartialEq<commit_after>#![crate_name = "htmlstream"]
#![doc(html_root_url = "https://leizongmin.github.io/htmlstream-rust/")]

/// The HTML source position
///
/// # Examples
/// ```
/// # use self::htmlstream::*;
/// let pos = Position {
///     start: 0,
///     end: 10,
/// };
/// ```
#[derive(Debug, PartialEq)]
pub struct Position {
    pub start: usize,
    pub end: usize,
}

/// The tag state
///
/// + `Text`: not a HTML tag, e.g. hello
/// + `Opening`: an opening tag, e.g.
/// + `Closing`: a closing tag, e.g.
/// + `SelfClosing`: a selfclosing tag,e.g.

#[derive(Debug, PartialEq)]
pub enum HTMLTagState {
    Text, Opening, Closing, SelfClosing
}

/// The HTML tag
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let tag = HTMLTag {
///     name: "a".to_string(),
///     html: "<a href=\"#\">link</a>".to_string(),
///     attributes: "href=\"#\"".to_string(),
///     state: HTMLTagState::Opening,
/// };
/// ```
#[derive(Debug, PartialEq)]
pub struct HTMLTag {
    pub name: String,
    pub html: String,
    pub attributes: String,
    pub state: HTMLTagState
}

/// The tag attribute
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let attr = HTMLTagAttribute {
///     name: "href".to_string(),
///     value: "#".to_string(),
/// };
/// ```
#[derive(Debug, PartialEq)]
pub struct HTMLTagAttribute {
    pub name: String,
    pub value: String,
}

const CHAR_SINGLE_QUOTE: u8 = b'\'';
const CHAR_DOUBLE_QUOTE: u8 = b'"';
const CHAR_LT: u8 = b'<';
const CHAR_GT: u8 = b'>';
const CHAR_SLASH: u8 = b'/';
const CHAR_SPACE: u8 = b' ';
const CHAR_EQUAL: u8 = b'=';

/// Parse HTML source, find the tags and call `on_tag()` every time
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let html = "this is a test: <a href=\"http://rust-lang.org\">The Rust Programing Language</a>";
/// parse_html(html, |pos: &Position, tag: &HTMLTag| {
///     println!("{:?} {:?}", pos, tag);
/// });
/// ```
pub fn parse_html<F>(html: &str, on_tag: F) where F: Fn(&Position, &HTMLTag) {
    let mut is_tag_start: bool = false;
    let mut is_quote_start: bool = false;
    let mut is_get_tag_name: bool = false;
    let mut is_closing_tag: bool = false;
    let mut current_tag_name: &str = "";

    let mut quote_char: u8 = 0;
    let mut last_char: u8 = 0;
    let mut last_index: usize = 0;
    let mut current_index: usize = 0;
    let mut attributes_start_index: usize = 0;

    let html_bytes = html.as_bytes();
    for b in html_bytes {
        let c = *b;
        if current_index > 0 {
            last_char = html_bytes[current_index - 1];;
        }
        current_index += 1;

        if is_tag_start {

            if !is_get_tag_name {
                if CHAR_SLASH == c && last_index + 2 == current_index {
                    is_closing_tag = true;
                } else {
                    if c <= CHAR_SPACE || CHAR_SLASH == c || CHAR_GT == c || CHAR_LT == c {
                        if is_closing_tag {
                            current_tag_name = &html[(last_index + 2)..(current_index - 1)];
                        } else {
                            current_tag_name = &html[(last_index + 1)..(current_index - 1)];
                        }
                        attributes_start_index = current_index;
                        is_get_tag_name = true;
                    }
                }
            }

            // only when match the same `quote` char
            if is_quote_start {
                if c == quote_char {
                    is_quote_start = false;
                    continue;
                } else {
                    continue;
                }
            }

            // quote start
            if CHAR_SINGLE_QUOTE == c || CHAR_DOUBLE_QUOTE == c {
                // only when the last char is `equal`
                if CHAR_EQUAL == last_char {
                    is_quote_start = true;
                    quote_char = c;
                }
                continue;
            }

            // tag end
            if CHAR_GT == c {
                let tag_html = &html[last_index..current_index];
                let position = Position { start: last_index, end: current_index };

                let tag_state: HTMLTagState;
                if is_closing_tag {
                    tag_state = HTMLTagState::Closing;
                } else if CHAR_SLASH == last_char {
                    tag_state = HTMLTagState::SelfClosing;
                } else {
                    tag_state = HTMLTagState::Opening;
                }

                let attributes_html: &str;
                if is_get_tag_name && attributes_start_index < current_index - 1 {
                    match tag_state {
                        HTMLTagState::SelfClosing => {
                            attributes_html = &html[attributes_start_index..(current_index - 2)];
                        },
                        HTMLTagState::Opening | HTMLTagState::Closing => {
                            attributes_html = &html[attributes_start_index..(current_index - 1)];
                        },
                        _ => {
                            attributes_html = "";
                        }
                    }
                } else {
                    attributes_html = "";
                }

                let tag = HTMLTag {
                    name: current_tag_name.to_string(),
                    html: tag_html.to_string(),
                    attributes: attributes_html.to_string(),
                    state: tag_state,
                };
                on_tag(&position, &tag);

                last_index = current_index;
                is_tag_start = false;
            }

        } else {

            if CHAR_LT == c {
                // text
                if last_index < current_index - 1 {
                    let tag_html = &html[last_index..(current_index - 1)];
                    let position = Position { start: last_index, end: current_index };
                    let tag = HTMLTag {
                        name: "".to_string(),
                        html: tag_html.to_string(),
                        attributes: "".to_string(),
                        state: HTMLTagState::Text,
                    };
                    on_tag(&position, &tag);
                }

                // init
                is_tag_start = true;
                is_get_tag_name = false;
                is_closing_tag = false;
                is_quote_start = false;
                last_index = current_index - 1;
                continue;
            }

        }
    }

    // the rest text
    if last_index < current_index - 1 {
        let tag_html = &html[last_index..];
        let position = Position { start: last_index, end: current_index };
        let tag = HTMLTag {
            name: "".to_string(),
            html: tag_html.to_string(),
            attributes: "".to_string(),
            state: HTMLTagState::Text,
        };
        on_tag(&position, &tag);
    }
}

/// Parse attributes strings, find the attribute and call `on_tag_attribute()` every time
///
/// # Examples
///
/// ```
/// # use self::htmlstream::*;
/// let attributes = "href=\"http://rust-lang.org\" title=Rust disabled";
/// parse_attributes(attributes, |pos: &Position, attr: &HTMLTagAttribute| {
///     println!("{:?} {:?}", pos, attr);
/// });
/// ```
pub fn parse_attributes<F>(html: &str, on_tag_attribute: F) where F: Fn(&Position, &HTMLTagAttribute) {
    let mut is_quote_start: bool = false;
    let mut is_attribute_start: bool = false;
    let mut is_get_attribute_name: bool = false;

    let mut quote_char: u8 = 0;
    let mut last_char: u8 = 0;
    let mut last_index: usize = 0;
    let mut current_index: usize = 0;
    let mut value_start_index: usize = 0;

    let html_bytes = html.as_bytes();
    for b in html_bytes {
        let c = *b;
        if current_index > 0 {
            last_char = html_bytes[current_index - 1];;
        }
        current_index += 1;

        if is_attribute_start {

            if is_get_attribute_name {

                if is_quote_start {
                    if c == quote_char {
                        // only when match the same `quote` char
                        if c == quote_char {
                            let name = &html[last_index..(value_start_index - 1)];
                            let value = &html[(value_start_index + 1)..(current_index - 1)];
                            let position = Position { start: last_index, end: current_index };
                            let attribute = HTMLTagAttribute {
                                name: name.to_string(),
                                value: value.to_string(),
                            };
                            on_tag_attribute(&position, &attribute);

                            is_attribute_start = false;
                            is_quote_start = false;
                            is_get_attribute_name = false;
                            continue;
                        }
                    } else {
                        continue;
                    }
                }

                // quote start
                if CHAR_SINGLE_QUOTE == c || CHAR_DOUBLE_QUOTE == c {
                    // only when the last char is `equal`
                    if CHAR_EQUAL == last_char {
                        is_quote_start = true;
                        quote_char = c;
                    }
                    continue;
                }

                // only when match a `blank` char
                if c <= CHAR_SPACE {
                    let name = &html[last_index..(value_start_index - 1)];
                    let value = &html[(value_start_index)..(current_index - 1)];
                    let position = Position { start: last_index, end: current_index };
                    let attribute = HTMLTagAttribute {
                        name: name.to_string(),
                        value: value.to_string(),
                    };
                    on_tag_attribute(&position, &attribute);

                    is_attribute_start = false;
                    is_quote_start = false;
                    is_get_attribute_name = false;
                    continue;
                }

            } else {

                // only when match an `equal` char, start the attribute value
                if CHAR_EQUAL == c {
                    value_start_index = current_index;
                    is_get_attribute_name = true;
                    continue;
                }

                // only when match an `blank` char, stop current attribute
                if c <= CHAR_SPACE {
                    let name = &html[last_index..(current_index - 1)];
                    let position = Position { start: last_index, end: current_index };
                    let attribute = HTMLTagAttribute {
                        name: name.to_string(),
                        value: "".to_string(),
                    };
                    on_tag_attribute(&position, &attribute);

                    is_attribute_start = false;
                    is_quote_start = false;
                    is_get_attribute_name = false;
                    continue;
                }

            }

        } else {

            // ignore `blank` char
            if c <= CHAR_SPACE {
                continue;
            }

            is_attribute_start = true;
            is_get_attribute_name = false;
            is_quote_start = false;
            last_index = current_index - 1;

        }
    }

    // the rest text
    let name = &html[last_index..];
    let position = Position { start: last_index, end: current_index };
    let attribute = HTMLTagAttribute {
        name: name.to_string(),
        value: "".to_string()
    };
    on_tag_attribute(&position, &attribute);
}

#[test]
fn test_parse_html() {
    let html = "this is a test: The Rust Programing Language";
    let mut tags: Vec<&HTMLTag> = vec![];
    parse_html(html, |pos: &Position, tag: &HTMLTag| {
        println!("{:?} {:?}", pos, tag);
        tags.push(tag);
    });
}
<|endoftext|>