<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};

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

fn tag_text_elem(parent: &mut Element, child_name: &'static str, child_body: &str) {
    parent.tag(elem_with_text(child_name, child_body));
}

fn tag_text_elem_opt(parent: &mut Element, child_name: &'static str, child_body: &Option<String>) {
    if let Some(ref c) = *child_body {
        tag_text_elem(parent, child_name, &c);
    }
}

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 {
    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
    }

    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 {
            match builder.push_event(event) {
                Ok(Some(element)) => 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;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
/// };
/// ```
#[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 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, &[]);

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

        for item in &self.items {
            channel.tag(item.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 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, &[]);

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

        item
    }

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

        match element.get_child("title", None) {
            Some(element) => item.title = Some(element.content_str()),
            None => (),
        }

        match element.get_child("link", None) {
            Some(element) => item.link = Some(element.content_str()),
            None => (),
        }

        match element.get_child("description", None) {
            Some(element) => item.description = Some(element.content_str()),
            None => (),
        }

        Ok(item)
    }
}

#[cfg(test)]
mod test {
    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()),
        };

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

        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_fail]
    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_fail]
    fn test_from_read_one_channel() {
        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_with_title() {
        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>Convert util functions to private methods<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 {
    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
    }

    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 {
            match builder.push_event(event) {
                Ok(Some(element)) => 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;
///
/// let channel = Channel {
///     title: "My Blog".to_string(),
///     link: "http://myblog.com".to_string(),
///     description: "Where I write stuff".to_string(),
///     items: vec![],
/// };
/// ```
#[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 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
    }

    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 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);

        item
    }

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

        match element.get_child("title", None) {
            Some(element) => item.title = Some(element.content_str()),
            None => (),
        }

        match element.get_child("link", None) {
            Some(element) => item.link = Some(element.content_str()),
            None => (),
        }

        match element.get_child("description", None) {
            Some(element) => item.description = Some(element.content_str()),
            None => (),
        }

        Ok(item)
    }
}

#[cfg(test)]
mod test {
    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()),
        };

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

        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_fail]
    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_fail]
    fn test_from_read_one_channel() {
        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_with_title() {
        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>#![no_std]
//!
//! You can populate [`Petnames`] with your own word lists, but the word lists
//! from upstream [petname](https://github.com/dustinkirkland/petname) are
//! included with the `default_dictionary` feature (enabled by default). See
//! [`Petnames::small`], [`Petnames::medium`], and [`Petnames::large`] to select
//! a particular built-in word list, or use the [`Default`] implementation.
//!
//! The other thing you need is a random number generator from [rand][]:
//!
//! ```rust
//! let mut rng = rand::thread_rng();
//! let pname = petname::Petnames::default().generate(&mut rng, 7, ":");
//! ```
//!
//! It may be more convenient to use the default random number generator:
//!
//! ```rust
//! let pname = petname::Petnames::default().generate_one(7, ":");
//! ```
//!
//! There's a [convenience function][petname] that'll do all of this:
//!
//! ```rust
//! let pname = petname::petname(7, ":");
//! ```
//!
//! But the most flexible approach is to create an [`Iterator`] with
//! [`iter`][`Petnames::iter`]:
//!
//! ```rust
//! let mut rng = rand::thread_rng();
//! let petnames = petname::Petnames::default();
//! let ten_thousand_names: Vec<String> =
//!   petnames.iter(&mut rng, 3, "_").take(10000).collect();
//! ```
//!
//! You can modify the word lists to, for example, only use words beginning with
//! the letter "b":
//!
//! ```rust
//! let mut petnames = petname::Petnames::default();
//! petnames.retain(|s| s.starts_with("b"));
//! petnames.generate_one(3, ".");
//! ```
//!

extern crate alloc;

use alloc::{
    string::{String, ToString},
    vec::Vec,
};

use itertools::Itertools;
use rand::seq::SliceRandom;

/// Convenience function to generate a new petname from default word lists.
#[allow(dead_code)]
#[cfg(feature = "std_rng")]
#[cfg(feature = "default_dictionary")]
pub fn petname(words: u8, separator: &str) -> String {
    Petnames::new().generate_one(words, separator)
}

/// A word list.
pub type Words<'a> = Vec<&'a str>;

/// Word lists and the logic to combine them into _petnames_.
///
/// A _petname_ with `n` words will contain, in order:
///
///   * `n - 2` adverbs when `n >= 2`, otherwise 0 adverbs.
///   * 1 adjective when `n >= 2`, otherwise 0 adjectives.
///   * 1 name / noun when `n >= 1`, otherwise 0 names.
///
#[derive(Clone, Debug, PartialEq)]
pub struct Petnames<'a> {
    pub adjectives: Words<'a>,
    pub adverbs: Words<'a>,
    pub names: Words<'a>,
}

impl<'a> Petnames<'a> {
    /// Constructs a new `Petnames` from the default (small) word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn new() -> Self {
        Self::default()
    }

    /// Constructs a new `Petnames` from the small word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn small() -> Self {
        Self::init(
            include_str!("../words/small/adjectives.txt"),
            include_str!("../words/small/adverbs.txt"),
            include_str!("../words/small/names.txt"),
        )
    }

    /// Constructs a new `Petnames` from the medium word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn medium() -> Self {
        Self::init(
            include_str!("../words/medium/adjectives.txt"),
            include_str!("../words/medium/adverbs.txt"),
            include_str!("../words/medium/names.txt"),
        )
    }

    /// Constructs a new `Petnames` from the large word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn large() -> Self {
        Self::init(
            include_str!("../words/large/adjectives.txt"),
            include_str!("../words/large/adverbs.txt"),
            include_str!("../words/large/names.txt"),
        )
    }

    /// Constructs a new `Petnames` from the given word lists.
    ///
    /// The words are extracted from the given strings by splitting on whitespace.
    pub fn init(adjectives: &'a str, adverbs: &'a str, names: &'a str) -> Self {
        Self {
            adjectives: adjectives.split_whitespace().collect(),
            adverbs: adverbs.split_whitespace().collect(),
            names: names.split_whitespace().collect(),
        }
    }

    /// Keep words matching a predicate.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(feature = "default_dictionary")]
    /// let mut petnames = petname::Petnames::default();
    /// # #[cfg(feature = "default_dictionary")]
    /// petnames.retain(|s| s.starts_with("b"));
    /// # #[cfg(feature = "default_dictionary")]
    /// # #[cfg(feature = "std_rng")]
    /// petnames.generate_one(2, ".");
    /// ```
    ///
    /// This is merely a convenience wrapper that applies the same predicate to
    /// the adjectives, adverbs, and names lists.
    ///
    pub fn retain<F>(&mut self, mut predicate: F)
    where
        F: FnMut(&str) -> bool,
    {
        self.adjectives.retain(|word| predicate(word));
        self.adverbs.retain(|word| predicate(word));
        self.names.retain(|word| predicate(word));
    }

    /// Calculate the cardinality of this `Petnames`.
    ///
    /// If this is low, names may be repeated by the generator with a higher
    /// frequency than your use-case may allow. If it is 0 (zero) the generator
    /// will panic (unless `words` is also zero).
    ///
    /// This can saturate. If the total possible combinations of words exceeds
    /// `u128::MAX` then this will return `u128::MAX`.
    pub fn cardinality(&self, words: u8) -> u128 {
        Lists(self, words)
            .map(|list| list.len() as u128)
            .fold1(u128::saturating_mul)
            .unwrap_or(0u128)
    }

    /// Generate a new petname.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let mut rng = rand::thread_rng();
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// petname::Petnames::default().generate(&mut rng, 7, ":");
    /// ```
    ///
    /// # Notes
    ///
    /// This may return fewer words than you request if one or more of the word
    /// lists are empty. For example, if there are no adverbs, requesting 3 or
    /// more words may still yield only "doubtful-salmon".
    ///
    pub fn generate<RNG>(&self, rng: &mut RNG, words: u8, separator: &str) -> String
    where
        RNG: rand::Rng,
    {
        itertools::Itertools::intersperse(
            Lists(self, words)
                .filter_map(|list| list.choose(rng))
                .cloned(),
            separator,
        )
        .collect::<String>()
    }

    /// Generate a single new petname.
    ///
    /// This is like `generate` but uses `rand::thread_rng` as the random
    /// source. For efficiency use `generate` when creating multiple names, or
    /// when you want to use a custom source of randomness.
    #[cfg(feature = "std_rng")]
    pub fn generate_one(&self, words: u8, separator: &str) -> String {
        self.generate(&mut rand::thread_rng(), words, separator)
    }

    /// Iterator yielding petnames.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let mut rng = rand::thread_rng();
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let petnames = petname::Petnames::default();
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let mut iter = petnames.iter(&mut rng, 4, "_");
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// println!("name: {}", iter.next().unwrap());
    /// ```
    ///
    pub fn iter<RNG>(&self, rng: &'a mut RNG, words: u8, separator: &str) -> Names<RNG>
    where
        RNG: rand::Rng,
    {
        Names {
            petnames: self,
            rng,
            words,
            separator: separator.to_string(),
        }
    }

    /// Iterator yielding unique – i.e. non-repeating – petnames.
    pub fn iter_unique<RNG>(
        &'a self,
        rng: &'a mut RNG,
        words: u8,
        separator: &str,
    ) -> NamesUnique<'a, core::iter::Cycle<alloc::vec::IntoIter<&'a str>>>
    where
        RNG: rand::Rng,
    {
        NamesUnique {
            iters: Lists(self, words)
                .cloned()
                .map(|mut list| {
                    list.shuffle(rng); // Could be expensive.
                    list.push(""); // Cycle marker.
                    (list.into_iter().cycle(), None)
                })
                .collect(),
            separator: separator.to_string(),
        }
    }
}

#[cfg(feature = "default_dictionary")]
impl<'a> Default for Petnames<'a> {
    fn default() -> Self {
        Self::small()
    }
}

/// Iterator over a `Petnames`' word lists.
///
/// This yields the appropriate lists from which to select a word when
/// constructing a petname of `n` words. For example, if you want 3 words in
/// your petname, this will first yield the adverbs word list, then adjectives,
/// then names.
struct Lists<'a>(&'a Petnames<'a>, u8);

impl<'a> Iterator for Lists<'a> {
    type Item = &'a Words<'a>;

    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, Some(self.1 as usize))
    }

    fn next(&mut self) -> Option<Self::Item> {
        let Self(petnames, ref mut word) = self;
        match word {
            0 => None,
            1 => {
                *word -= 1;
                Some(&petnames.names)
            }
            2 => {
                *word -= 1;
                Some(&petnames.adjectives)
            }
            _ => {
                *word -= 1;
                Some(&petnames.adverbs)
            }
        }
    }
}

/// Iterator yielding petnames.
pub struct Names<'a, RNG>
where
    RNG: rand::Rng,
{
    petnames: &'a Petnames<'a>,
    rng: &'a mut RNG,
    words: u8,
    separator: String,
}

impl<'a, RNG> Names<'a, RNG>
where
    RNG: rand::Rng,
{
    /// Calculate the cardinality of this iterator; see `Petnames::cardinality`.
    #[allow(dead_code)]
    pub fn cardinality(&self) -> u128 {
        self.petnames.cardinality(self.words)
    }
}

impl<'a, RNG> Iterator for Names<'a, RNG>
where
    RNG: rand::Rng,
{
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        Some(
            self.petnames
                .generate(self.rng, self.words, &self.separator),
        )
    }
}

/// Iterator yielding unique petnames.
pub struct NamesUnique<'a, ITERATOR>
where
    ITERATOR: Iterator<Item = &'a str>,
{
    iters: Vec<(ITERATOR, Option<ITERATOR::Item>)>,
    separator: String,
}

impl<'a, ITERATOR> Iterator for NamesUnique<'a, ITERATOR>
where
    ITERATOR: Iterator<Item = &'a str>,
{
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        let mut bump = true; // Request advance of next iterator.
        for (iter, word) in self.iters.iter_mut() {
            if bump || word.is_none() {
                match iter.next() {
                    None => {
                        // This shouldn't happen because we expect the iterators
                        // to cycle. However, if it does, we're definitely done.
                        return None;
                    }
                    Some("") => {
                        // This is the cycle end marker. We want to get another
                        // new word from this iterator, and advance the *next*
                        // iterator too.
                        match iter.next() {
                            None => return None,
                            Some("") => return None,
                            Some(s) => *word = Some(s),
                        }
                        bump = true
                    }
                    Some(s) => {
                        // We have a new word from this iterator, so we do not
                        // yet need to advance the next iterator.
                        *word = Some(s);
                        bump = false
                    }
                }
            }
        }
        if bump {
            // We reached the end of the last iterator, hence we're done.
            None
        } else {
            // We can construct a word!
            Some(
                itertools::Itertools::intersperse(
                    self.iters.iter().filter_map(|(_, w)| *w),
                    &self.separator,
                )
                .collect(),
            )
        }
    }
}
<commit_msg> NamesUnique::new<commit_after>#![no_std]
//!
//! You can populate [`Petnames`] with your own word lists, but the word lists
//! from upstream [petname](https://github.com/dustinkirkland/petname) are
//! included with the `default_dictionary` feature (enabled by default). See
//! [`Petnames::small`], [`Petnames::medium`], and [`Petnames::large`] to select
//! a particular built-in word list, or use the [`Default`] implementation.
//!
//! The other thing you need is a random number generator from [rand][]:
//!
//! ```rust
//! let mut rng = rand::thread_rng();
//! let pname = petname::Petnames::default().generate(&mut rng, 7, ":");
//! ```
//!
//! It may be more convenient to use the default random number generator:
//!
//! ```rust
//! let pname = petname::Petnames::default().generate_one(7, ":");
//! ```
//!
//! There's a [convenience function][petname] that'll do all of this:
//!
//! ```rust
//! let pname = petname::petname(7, ":");
//! ```
//!
//! But the most flexible approach is to create an [`Iterator`] with
//! [`iter`][`Petnames::iter`]:
//!
//! ```rust
//! let mut rng = rand::thread_rng();
//! let petnames = petname::Petnames::default();
//! let ten_thousand_names: Vec<String> =
//!   petnames.iter(&mut rng, 3, "_").take(10000).collect();
//! ```
//!
//! You can modify the word lists to, for example, only use words beginning with
//! the letter "b":
//!
//! ```rust
//! let mut petnames = petname::Petnames::default();
//! petnames.retain(|s| s.starts_with("b"));
//! petnames.generate_one(3, ".");
//! ```
//!

extern crate alloc;

use alloc::{
    string::{String, ToString},
    vec::Vec,
};

use itertools::Itertools;
use rand::seq::SliceRandom;

/// Convenience function to generate a new petname from default word lists.
#[allow(dead_code)]
#[cfg(feature = "std_rng")]
#[cfg(feature = "default_dictionary")]
pub fn petname(words: u8, separator: &str) -> String {
    Petnames::new().generate_one(words, separator)
}

/// A word list.
pub type Words<'a> = Vec<&'a str>;

/// Word lists and the logic to combine them into _petnames_.
///
/// A _petname_ with `n` words will contain, in order:
///
///   * `n - 2` adverbs when `n >= 2`, otherwise 0 adverbs.
///   * 1 adjective when `n >= 2`, otherwise 0 adjectives.
///   * 1 name / noun when `n >= 1`, otherwise 0 names.
///
#[derive(Clone, Debug, PartialEq)]
pub struct Petnames<'a> {
    pub adjectives: Words<'a>,
    pub adverbs: Words<'a>,
    pub names: Words<'a>,
}

impl<'a> Petnames<'a> {
    /// Constructs a new `Petnames` from the default (small) word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn new() -> Self {
        Self::default()
    }

    /// Constructs a new `Petnames` from the small word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn small() -> Self {
        Self::init(
            include_str!("../words/small/adjectives.txt"),
            include_str!("../words/small/adverbs.txt"),
            include_str!("../words/small/names.txt"),
        )
    }

    /// Constructs a new `Petnames` from the medium word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn medium() -> Self {
        Self::init(
            include_str!("../words/medium/adjectives.txt"),
            include_str!("../words/medium/adverbs.txt"),
            include_str!("../words/medium/names.txt"),
        )
    }

    /// Constructs a new `Petnames` from the large word lists.
    #[cfg(feature = "default_dictionary")]
    pub fn large() -> Self {
        Self::init(
            include_str!("../words/large/adjectives.txt"),
            include_str!("../words/large/adverbs.txt"),
            include_str!("../words/large/names.txt"),
        )
    }

    /// Constructs a new `Petnames` from the given word lists.
    ///
    /// The words are extracted from the given strings by splitting on whitespace.
    pub fn init(adjectives: &'a str, adverbs: &'a str, names: &'a str) -> Self {
        Self {
            adjectives: adjectives.split_whitespace().collect(),
            adverbs: adverbs.split_whitespace().collect(),
            names: names.split_whitespace().collect(),
        }
    }

    /// Keep words matching a predicate.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(feature = "default_dictionary")]
    /// let mut petnames = petname::Petnames::default();
    /// # #[cfg(feature = "default_dictionary")]
    /// petnames.retain(|s| s.starts_with("b"));
    /// # #[cfg(feature = "default_dictionary")]
    /// # #[cfg(feature = "std_rng")]
    /// petnames.generate_one(2, ".");
    /// ```
    ///
    /// This is merely a convenience wrapper that applies the same predicate to
    /// the adjectives, adverbs, and names lists.
    ///
    pub fn retain<F>(&mut self, mut predicate: F)
    where
        F: FnMut(&str) -> bool,
    {
        self.adjectives.retain(|word| predicate(word));
        self.adverbs.retain(|word| predicate(word));
        self.names.retain(|word| predicate(word));
    }

    /// Calculate the cardinality of this `Petnames`.
    ///
    /// If this is low, names may be repeated by the generator with a higher
    /// frequency than your use-case may allow. If it is 0 (zero) the generator
    /// will panic (unless `words` is also zero).
    ///
    /// This can saturate. If the total possible combinations of words exceeds
    /// `u128::MAX` then this will return `u128::MAX`.
    pub fn cardinality(&self, words: u8) -> u128 {
        Lists(self, words)
            .map(|list| list.len() as u128)
            .fold1(u128::saturating_mul)
            .unwrap_or(0u128)
    }

    /// Generate a new petname.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let mut rng = rand::thread_rng();
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// petname::Petnames::default().generate(&mut rng, 7, ":");
    /// ```
    ///
    /// # Notes
    ///
    /// This may return fewer words than you request if one or more of the word
    /// lists are empty. For example, if there are no adverbs, requesting 3 or
    /// more words may still yield only "doubtful-salmon".
    ///
    pub fn generate<RNG>(&self, rng: &mut RNG, words: u8, separator: &str) -> String
    where
        RNG: rand::Rng,
    {
        itertools::Itertools::intersperse(
            Lists(self, words)
                .filter_map(|list| list.choose(rng))
                .cloned(),
            separator,
        )
        .collect::<String>()
    }

    /// Generate a single new petname.
    ///
    /// This is like `generate` but uses `rand::thread_rng` as the random
    /// source. For efficiency use `generate` when creating multiple names, or
    /// when you want to use a custom source of randomness.
    #[cfg(feature = "std_rng")]
    pub fn generate_one(&self, words: u8, separator: &str) -> String {
        self.generate(&mut rand::thread_rng(), words, separator)
    }

    /// Iterator yielding petnames.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let mut rng = rand::thread_rng();
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let petnames = petname::Petnames::default();
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// let mut iter = petnames.iter(&mut rng, 4, "_");
    /// # #[cfg(all(feature = "std_rng", feature = "default_dictionary"))]
    /// println!("name: {}", iter.next().unwrap());
    /// ```
    ///
    pub fn iter<RNG>(&self, rng: &'a mut RNG, words: u8, separator: &str) -> Names<RNG>
    where
        RNG: rand::Rng,
    {
        Names {
            petnames: self,
            rng,
            words,
            separator: separator.to_string(),
        }
    }

    /// Iterator yielding unique – i.e. non-repeating – petnames.
    pub fn iter_unique<RNG>(
        &'a self,
        rng: &'a mut RNG,
        words: u8,
        separator: &str,
    ) -> NamesUnique<'a, core::iter::Cycle<alloc::vec::IntoIter<&'a str>>>
    where
        RNG: rand::Rng,
    {
        NamesUnique {
            iters: Lists(self, words)
                .cloned()
                .map(|mut list| {
                    list.shuffle(rng); // Could be expensive.
                    list.push(""); // Cycle marker.
                    (list.into_iter().cycle(), None)
                })
                .collect(),
            separator: separator.to_string(),
        }
    }
}

#[cfg(feature = "default_dictionary")]
impl<'a> Default for Petnames<'a> {
    fn default() -> Self {
        Self::small()
    }
}

/// Iterator over a `Petnames`' word lists.
///
/// This yields the appropriate lists from which to select a word when
/// constructing a petname of `n` words. For example, if you want 3 words in
/// your petname, this will first yield the adverbs word list, then adjectives,
/// then names.
struct Lists<'a>(&'a Petnames<'a>, u8);

impl<'a> Iterator for Lists<'a> {
    type Item = &'a Words<'a>;

    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, Some(self.1 as usize))
    }

    fn next(&mut self) -> Option<Self::Item> {
        let Self(petnames, ref mut word) = self;
        match word {
            0 => None,
            1 => {
                *word -= 1;
                Some(&petnames.names)
            }
            2 => {
                *word -= 1;
                Some(&petnames.adjectives)
            }
            _ => {
                *word -= 1;
                Some(&petnames.adverbs)
            }
        }
    }
}

/// Iterator yielding petnames.
pub struct Names<'a, RNG>
where
    RNG: rand::Rng,
{
    petnames: &'a Petnames<'a>,
    rng: &'a mut RNG,
    words: u8,
    separator: String,
}

impl<'a, RNG> Names<'a, RNG>
where
    RNG: rand::Rng,
{
    /// Calculate the cardinality of this iterator; see `Petnames::cardinality`.
    #[allow(dead_code)]
    pub fn cardinality(&self) -> u128 {
        self.petnames.cardinality(self.words)
    }
}

impl<'a, RNG> Iterator for Names<'a, RNG>
where
    RNG: rand::Rng,
{
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        Some(
            self.petnames
                .generate(self.rng, self.words, &self.separator),
        )
    }
}

/// Iterator yielding unique petnames.
pub struct NamesUnique<'a, ITERATOR>
where
    ITERATOR: Iterator<Item = &'a str>,
{
    iters: Vec<(ITERATOR, Option<ITERATOR::Item>)>,
    separator: String,
}

impl<'a> NamesUnique<'a, core::iter::Cycle<alloc::vec::IntoIter<&'a str>>> {
    pub fn new<RNG>(lists: &[&'a Words<'a>], rng: &'a mut RNG, separator: &str) -> Self
    where
        RNG: rand::Rng,
    {
        Self {
            iters: lists
                .iter()
                .cloned()
                .cloned()
                .map(|mut list| {
                    list.shuffle(rng); // Could be expensive.
                    list.push(""); // Cycle marker.
                    (list.into_iter().cycle(), None)
                })
                .collect(),
            separator: separator.to_string(),
        }
    }
}

impl<'a, ITERATOR> Iterator for NamesUnique<'a, ITERATOR>
where
    ITERATOR: Iterator<Item = &'a str>,
{
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        let mut bump = true; // Request advance of next iterator.
        for (iter, word) in self.iters.iter_mut() {
            if bump || word.is_none() {
                match iter.next() {
                    None => {
                        // This shouldn't happen because we expect the iterators
                        // to cycle. However, if it does, we're definitely done.
                        return None;
                    }
                    Some("") => {
                        // This is the cycle end marker. We want to get another
                        // new word from this iterator, and advance the *next*
                        // iterator too.
                        match iter.next() {
                            None => return None,
                            Some("") => return None,
                            Some(s) => *word = Some(s),
                        }
                        bump = true
                    }
                    Some(s) => {
                        // We have a new word from this iterator, so we do not
                        // yet need to advance the next iterator.
                        *word = Some(s);
                        bump = false
                    }
                }
            }
        }
        if bump {
            // We reached the end of the last iterator, hence we're done.
            None
        } else {
            // We can construct a word!
            Some(
                itertools::Itertools::intersperse(
                    self.iters.iter().filter_map(|(_, w)| *w),
                    &self.separator,
                )
                .collect(),
            )
        }
    }
}
<|endoftext|>


<commit_before>//! stal-rs
//! ====
//!
//! Set algebra solver for Redis in Rust, based on
//! [Stal](https://github.com/soveran/stal).
//!
//! Description
//! -----------
//!
//! `stal-rs` provide set operations and resolves them in [Redis][redis].
//!
//! Usage
//! -----
//!
//! `stal-rs` has no dependencies. It produces a vector of Redis operations that
//! have to be run by the user.
//!
//! ```rust
//! extern crate stal;
//!
//! let foobar = stal::Set::Inter(vec![stal::Set::Key(b"foo".to_vec()), stal::Set::Key(b"bar".to_vec())]);
//! let foobar_nobaz = stal::Set::Diff(vec![foobar, stal::Set::Key(b"baz".to_vec())]);
//! let foobar_nobaz_andqux = stal::Set::Union(vec![stal::Set::Key(b"qux".to_vec()), foobar_nobaz]);
//!
//! assert_eq!(
//!    stal::Stal::new("SMEMBERS".to_string(), foobar_nobaz_andqux).solve(),
//!    (
//!     vec![
//!         vec![b"MULTI".to_vec()],
//!         vec![b"SINTERSTORE".to_vec(), b"stal:2".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!         vec![b"SDIFFSTORE".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec(), b"baz".to_vec()],
//!         vec![b"SUNIONSTORE".to_vec(), b"stal:0".to_vec(), b"qux".to_vec(), b"stal:1".to_vec()],
//!         vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//!         vec![b"DEL".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec()],
//!         vec![b"EXEC".to_vec()],
//!     ],
//!     4
//!    ));
//! ```
//!
//! `stal-rs` translates the internal calls to  `SUNION`, `SDIFF` and
//! `SINTER` into `SDIFFSTORE`, `SINTERSTORE` and `SUNIONSTORE` to
//! perform the underlying operations, and it takes care of generating
//! and deleting any temporary keys.
//!
//! The outmost command can be any set operation, for example:
//!
//! ```rust
//! extern crate stal;
//! let myset = stal::Set::Key(b"my set".to_vec());
//! stal::Stal::new("SCARD".to_string(), myset).solve();
//! ```
//!
//! If you want to preview the commands `Stal` will send to generate
//! the results, you can use `Stal.explain`:
//!
//! ```rust
//! extern crate stal;
//!
//! assert_eq!(
//! stal::Stal::new("SMEMBERS".to_string(),
//!         stal::Set::Inter(vec![
//!             stal::Set::Union(vec![
//!                 stal::Set::Key(b"foo".to_vec()),
//!                 stal::Set::Key(b"bar".to_vec()),
//!                 ]),
//!             stal::Set::Key(b"baz".to_vec()),
//!             ])
//!         ).explain(),
//! vec![
//!     vec![b"SUNIONSTORE".to_vec(), b"stal:1".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!     vec![b"SINTERSTORE".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"baz".to_vec()],
//!     vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//! ]
//! )
//! ```
//!
//! All commands are wrapped in a `MULTI/EXEC` transaction.
//!
//! [redis]: http://redis.io

#![crate_name = "stal"]
#![crate_type = "lib"]

/// A set of values. It can be generated from a Redis key or from a set
/// operation based on other sets.
pub enum Set {
    /// A key
    Key(Vec<u8>),
    /// All the elements in any of the provided sets
    Union(Vec<Set>),
    /// All the elements in all the sets
    Inter(Vec<Set>),
    /// All the elements in the first set that are not in the other sets
    Diff(Vec<Set>),
}
use Set::*;

impl Set {
    /// Maps the operation to its Redis command name.
    fn command(&self) -> &'static str {
        match *self {
            Key(_) => unreachable!(),
            Union(_) => "SUNIONSTORE",
            Inter(_) => "SINTERSTORE",
            Diff(_) => "SDIFFSTORE",
        }
    }

    /// Appends the operation to `ops` and any temporary id created to `ids`.
    /// Returns the key representing the set.
    pub fn convert(&self, ids: &mut Vec<String>, ops: &mut Vec<Vec<Vec<u8>>>) -> Vec<u8> {
        let sets = match *self {
            Key(ref k) => return k.clone(),
            Union(ref sets) => sets,
            Inter(ref sets) => sets,
            Diff(ref sets) => sets,
        };

        let mut op = Vec::with_capacity(2 + sets.len());
        let id = format!("stal:{}", ids.len());
        let r = id.as_bytes().to_vec();
        ids.push(id);
        op.push(self.command().as_bytes().to_vec());
        op.push(r.clone());
        op.extend(sets.into_iter().map(|s| s.convert(ids, ops)));
        ops.push(op);
        r
    }
}

/// An operation to be executed on a set
pub struct Stal {
    /// Any Redis set operation
    operation: String,
    /// Set in which execute the operation
    set: Set,
}

impl Stal {
    pub fn new(operation: String, set: Set) -> Self {
        Stal {
            operation: operation,
            set: set,
        }
    }

    /// Returns a list of operations to run. For debug only.
    pub fn explain(&self) -> Vec<Vec<Vec<u8>>> {
        let mut ids = vec![];
        let mut ops = vec![];
        let key = self.set.convert(&mut ids, &mut ops);
        ops.push(vec![self.operation.as_bytes().to_vec(), key]);
        ops
    }

    /// Returns a lit of operations, wrapped in a multi/exec.
    /// The last operation is always exec, and the returned `usize` indicates
    /// the return value of the `operation`.
    pub fn solve(&self) -> (Vec<Vec<Vec<u8>>>, usize) {
        let mut ids = vec![];
        let mut ops = vec![vec![b"MULTI".to_vec()]];
        let key = self.set.convert(&mut ids, &mut ops);
        ops.push(vec![self.operation.as_bytes().to_vec(), key]);
        let pos = ops.len() - 1;
        if ids.len() > 0 {
            let mut del = vec![b"DEL".to_vec()];
            del.extend(ids.into_iter().map(|x| x.as_bytes().to_vec()));
            ops.push(del);
        }
        ops.push(vec![b"EXEC".to_vec()]);
        (ops, pos)
    }
}
<commit_msg>Support custom operations<commit_after>//! stal-rs
//! ====
//!
//! Set algebra solver for Redis in Rust, based on
//! [Stal](https://github.com/soveran/stal).
//!
//! Description
//! -----------
//!
//! `stal-rs` provide set operations and resolves them in [Redis][redis].
//!
//! Usage
//! -----
//!
//! `stal-rs` has no dependencies. It produces a vector of Redis operations that
//! have to be run by the user.
//!
//! ```rust
//! extern crate stal;
//!
//! let foobar = stal::Set::Inter(vec![stal::Set::Key(b"foo".to_vec()), stal::Set::Key(b"bar".to_vec())]);
//! let foobar_nobaz = stal::Set::Diff(vec![foobar, stal::Set::Key(b"baz".to_vec())]);
//! let foobar_nobaz_andqux = stal::Set::Union(vec![stal::Set::Key(b"qux".to_vec()), foobar_nobaz]);
//!
//! assert_eq!(
//!    stal::Stal::new("SMEMBERS".to_string(), foobar_nobaz_andqux).solve(),
//!    (
//!     vec![
//!         vec![b"MULTI".to_vec()],
//!         vec![b"SINTERSTORE".to_vec(), b"stal:2".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!         vec![b"SDIFFSTORE".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec(), b"baz".to_vec()],
//!         vec![b"SUNIONSTORE".to_vec(), b"stal:0".to_vec(), b"qux".to_vec(), b"stal:1".to_vec()],
//!         vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//!         vec![b"DEL".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"stal:2".to_vec()],
//!         vec![b"EXEC".to_vec()],
//!     ],
//!     4
//!    ));
//! ```
//!
//! `stal-rs` translates the internal calls to  `SUNION`, `SDIFF` and
//! `SINTER` into `SDIFFSTORE`, `SINTERSTORE` and `SUNIONSTORE` to
//! perform the underlying operations, and it takes care of generating
//! and deleting any temporary keys.
//!
//! The outmost command can be any set operation, for example:
//!
//! ```rust
//! extern crate stal;
//! let myset = stal::Set::Key(b"my set".to_vec());
//! stal::Stal::new("SCARD".to_string(), myset).solve();
//! ```
//!
//! If you want to preview the commands `Stal` will send to generate
//! the results, you can use `Stal.explain`:
//!
//! ```rust
//! extern crate stal;
//!
//! assert_eq!(
//! stal::Stal::new("SMEMBERS".to_string(),
//!         stal::Set::Inter(vec![
//!             stal::Set::Union(vec![
//!                 stal::Set::Key(b"foo".to_vec()),
//!                 stal::Set::Key(b"bar".to_vec()),
//!                 ]),
//!             stal::Set::Key(b"baz".to_vec()),
//!             ])
//!         ).explain(),
//! vec![
//!     vec![b"SUNIONSTORE".to_vec(), b"stal:1".to_vec(), b"foo".to_vec(), b"bar".to_vec()],
//!     vec![b"SINTERSTORE".to_vec(), b"stal:0".to_vec(), b"stal:1".to_vec(), b"baz".to_vec()],
//!     vec![b"SMEMBERS".to_vec(), b"stal:0".to_vec()],
//! ]
//! )
//! ```
//!
//! All commands are wrapped in a `MULTI/EXEC` transaction.
//!
//! [redis]: http://redis.io

#![crate_name = "stal"]
#![crate_type = "lib"]

/// A set of values. It can be generated from a Redis key or from a set
/// operation based on other sets.
pub enum Set {
    /// A key
    Key(Vec<u8>),
    /// All the elements in any of the provided sets
    Union(Vec<Set>),
    /// All the elements in all the sets
    Inter(Vec<Set>),
    /// All the elements in the first set that are not in the other sets
    Diff(Vec<Set>),
}
use Set::*;

impl Set {
    /// Maps the operation to its Redis command name.
    fn command(&self) -> &'static str {
        match *self {
            Key(_) => unreachable!(),
            Union(_) => "SUNIONSTORE",
            Inter(_) => "SINTERSTORE",
            Diff(_) => "SDIFFSTORE",
        }
    }

    /// Appends the operation to `ops` and any temporary id created to `ids`.
    /// Returns the key representing the set.
    pub fn convert(&self, ids: &mut Vec<String>, ops: &mut Vec<Vec<Vec<u8>>>) -> Vec<u8> {
        let sets = match *self {
            Key(ref k) => return k.clone(),
            Union(ref sets) => sets,
            Inter(ref sets) => sets,
            Diff(ref sets) => sets,
        };

        let mut op = Vec::with_capacity(2 + sets.len());
        let id = format!("stal:{}", ids.len());
        let r = id.as_bytes().to_vec();
        ids.push(id);
        op.push(self.command().as_bytes().to_vec());
        op.push(r.clone());
        op.extend(sets.into_iter().map(|s| s.convert(ids, ops)));
        ops.push(op);
        r
    }
}

/// An operation to be executed on a set
pub struct Stal {
    /// A Redis command
    command: Vec<Vec<u8>>,
    /// Set in which execute the operation
    sets: Vec<(Set, usize)>,
}

impl Stal {
    pub fn new(operation: String, set: Set) -> Self {
        Stal {
            command: vec![operation.as_bytes().to_vec(), vec![]],
            sets: vec![(set, 1)],
        }
    }

    fn add_ops(&self, ids: &mut Vec<String>, ops: &mut Vec<Vec<Vec<u8>>>) {
        let mut command = self.command.clone();
        for args in self.sets.iter() {
            command.push(args.0.convert(ids, ops));
            command.swap_remove(args.1);
        }
        ops.push(command);
    }

    /// Returns a list of operations to run. For debug only.
    pub fn explain(&self) -> Vec<Vec<Vec<u8>>> {
        let mut ids = vec![];
        let mut ops = vec![];
        self.add_ops(&mut ids, &mut ops);
        ops
    }

    /// Returns a lit of operations, wrapped in a multi/exec.
    /// The last operation is always exec, and the returned `usize` indicates
    /// the return value of the `operation`.
    pub fn solve(&self) -> (Vec<Vec<Vec<u8>>>, usize) {
        let mut ids = vec![];
        let mut ops = vec![vec![b"MULTI".to_vec()]];
        self.add_ops(&mut ids, &mut ops);
        let pos = ops.len() - 1;
        if ids.len() > 0 {
            let mut del = vec![b"DEL".to_vec()];
            del.extend(ids.into_iter().map(|x| x.as_bytes().to_vec()));
            ops.push(del);
        }
        ops.push(vec![b"EXEC".to_vec()]);
        (ops, pos)
    }
}
<|endoftext|>


<commit_before>/*!

**useful functions for working with frame-rates, sample-rates,
other rates,
time durations,
frequencies, etc and for keeping constant fps.**

`rate`, `sample rate`, `frame rate`, `fps`, `frequency`, etc
express the same concept, are therefore used interchangeably.

you can use hertz to compute the time resolution
and frequency range of a signal that can be meaningfully analyzed by a
[short-time fourier transform](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation):

```
extern crate hertz;

fn main() {
    let sample_rate = 44100;
    let window_size = 4096;
    let step_size = 512;

    // 11 hertz is the maximum frequency that can be meaningfully analyzed
    assert_eq!(
        11.,
        hertz::rayleigh(sample_rate as f64, window_size as f64).round());

    // 22050 hertz is the maximum frequency that can be meaningfully analyzed
    assert_eq!(
        22050.,
        hertz::nyquist(sample_rate as f64).round());

    // 12 ms is the time resolution we get when analyzing a 44100 hertz
    // signal with a step size of 512
    assert_eq!(
        12.,
        hertz::s_to_ms(hertz::cycles_per_second_to_seconds_per_cycle(
            sample_rate as f64,
            step_size as f64)).round());
}
```

you can use hertz to keep a constant framerate in a game or other
computer graphics application:

```no_run
fn main() {
    let frames_per_second: usize = 60;

    loop {
        let ns_at_frame_start = hertz::current_time_ns();

        // here's where logic and rendering would go.
        // this is never called more than frames_per_second
        // times per second.

        hertz::sleep_for_constant_rate(
            frames_per_second, ns_at_frame_start);
    }
}
```
*/

extern crate time;

use std::ops::{Mul, Div, Range};

/// nanoseconds to seconds
#[inline]
pub fn ns_to_s(ns: u64) -> f64 {
    (ns as f64) / 1_000_000_000.
}

/// seconds to nanoseconds
#[inline]
pub fn s_to_ns(s: f64) -> f64 {
    s * 1_000_000_000.
}

/// nanoseconds to milliseconds
#[inline]
pub fn ns_to_ms(ns: u64) -> f64 {
    (ns as f64) / 1_000_000.
}

/// seconds to milliseconds
#[inline]
pub fn s_to_ms<T>(s: T) -> T
    where T: Mul<T, Output=T> + From<u16>
{
    s * T::from(1000 as u16)
}

#[test]
fn test_s_to_ms() {
    assert_eq!(s_to_ms(1), 1000);
    assert_eq!(s_to_ms(1.), 1000.);
}

/// milliseconds to seconds
#[inline]
pub fn ms_to_s<T>(ms: T) -> T
    where T: Div<T, Output=T> + From<u16>
{
    ms / T::from(1000 as u16)
}

#[test]
fn test_ms_to_s() {
    assert_eq!(ms_to_s(1000), 1);
    assert_eq!(ms_to_s(1000.), 1.);
}

/// milliseconds to nanoseconds
#[inline]
pub fn ms_to_ns(ns: f64) -> f64 {
    ns * 1_000_000.
}

/// when given frames per second (or sample rate)
/// returns the duration of a single frame
#[inline]
pub fn fps_to_ns_per_frame(fps: usize) -> u64 {
    (s_to_ns(1.0) / (fps as f64)).round() as u64
}

/// returns the current timestamp in nanoseconds
#[inline]
pub fn current_time_ns() -> u64 {
    time::precise_time_ns()
}

// /// returns the nanoseconds of sleep which are needed to keep a constant
// /// frame rate
// pub fn ns_sleep_needed_for_constant_rate(fps: usize, ns_at_last_frame_start: u64) -> u64 {

/// useful for keeping a constant framerate
pub fn sleep_for_constant_rate(fps: usize, ns_at_last_frame_start: u64) {
    let ns_per_frame = fps_to_ns_per_frame(fps);
    let ns_after: u64 = time::precise_time_ns();
    let ns_elapsed = ns_after - ns_at_last_frame_start;
    if ns_elapsed < ns_per_frame {
        let ns_left_to_sleep = (ns_per_frame - ns_elapsed) as u32;
        let duration = std::time::Duration::new(0, ns_left_to_sleep);
        std::thread::sleep(duration);
    }
}

/// frequency range that can be modeled when taking the short
/// time fourier transform of a signal with `sample_rate` with
/// a sliding window of `window_sizew`.
/// equivalent to `rayleigh(sample_rate, window_size)..nyquist(sample_rate)`
/// increase the window size to increase the lower frequency
/// increase the sample rate to increase the upper frequency
pub fn hertz_range<T>(sample_rate: T, window_size: T) -> Range<T>
    where T: Div<T, Output=T> + From<u16> + Clone
{
    rayleigh(sample_rate.clone(), window_size)..nyquist(sample_rate)
}

#[test]
fn test_hertz_range() {
    assert_eq!(
        hertz_range(44100., 1024. * 8.),
        (5.38330078125)..22050.);
    assert_eq!(
        hertz_range(44100., 1024. * 4.),
        (10.7666015625)..22050.);
    assert_eq!(
        hertz_range(44100., 1024.),
        (43.06640625)..22050.);
    assert_eq!(
        hertz_range(44100., 512.),
        (86.1328125)..22050.);
}

/// maximum frequency in hertz that can be meaningfully analyzed with a given sample rate
/// [https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation)
pub fn nyquist<T>(sample_rate: T) -> T
    where T: Div<T, Output=T> + From<u16>
{
    sample_rate / T::from(2 as u16)
}

#[test]
fn test_nyquist() {
    assert_eq!(nyquist(44100.), 22050.);
}

/// minimum frequency in hertz that can be meaningfully analyzed with a given sample rate and
/// window size
/// [https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Rayleigh_frequency](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Rayleigh_frequency)
pub fn rayleigh<T>(sample_rate: T, window_size: T) -> T
    where T: Div<T, Output=T> + From<u16>
{
    T::from(1 as u16) / cycles_per_second_to_seconds_per_cycle(sample_rate, window_size)
}

#[test]
fn test_rayleigh() {
    assert_eq!(rayleigh(44100., 1024.), 43.06640625);
}

/// window duration in seconds
/// sample_rate in hertz
pub fn cycles_per_second_to_seconds_per_cycle<T>(cycles_per_second: T, cycles: T) -> T
    where T: Div<T, Output=T>
{
    cycles / cycles_per_second
}

#[test]
fn test_seconds_per_window() {
    // 11ms time resolution
    assert_eq!(cycles_per_second_to_seconds_per_cycle(44100., 512.), 0.011609977324263039);
    assert_eq!(cycles_per_second_to_seconds_per_cycle(44100., 1024.), 0.023219954648526078);
}
<commit_msg>update crate docstring<commit_after>/*!

**useful functions for working with frame-rates, sample-rates,
other rates,
time durations,
frequencies, etc and for keeping constant fps.**

`rate`, `sample rate`, `frame rate`, `fps`, `frequency`, etc
express the same concept and are therefore used interchangeably.

you can use hertz to compute the time resolution
and frequency range of that can be meaningfully analyzed by a
[short-time fourier transform](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation)
of a signal:

```
extern crate hertz;

fn main() {
    let sample_rate = 44100;
    let window_size = 4096;
    let step_size = 512;

    // 11 hertz is the maximum frequency that can be meaningfully analyzed
    assert_eq!(
        11.,
        hertz::rayleigh(sample_rate as f64, window_size as f64).round());

    // 22050 hertz is the maximum frequency that can be meaningfully analyzed
    assert_eq!(
        22050.,
        hertz::nyquist(sample_rate as f64).round());

    // 12 ms is the time resolution we get when analyzing a 44100 hertz
    // signal with a step size of 512
    assert_eq!(
        12.,
        hertz::s_to_ms(hertz::cycles_per_second_to_seconds_per_cycle(
            sample_rate as f64,
            step_size as f64)).round());
}
```

you can use hertz to keep a constant framerate in a game or other
computer graphics application:

```no_run
fn main() {
    let frames_per_second: usize = 60;

    loop {
        let ns_at_frame_start = hertz::current_time_ns();

        // here's where logic and rendering would go.
        // this is never called more than frames_per_second
        // times per second.

        hertz::sleep_for_constant_rate(
            frames_per_second, ns_at_frame_start);
    }
}
```
*/

extern crate time;

use std::ops::{Mul, Div, Range};

/// nanoseconds to seconds
#[inline]
pub fn ns_to_s(ns: u64) -> f64 {
    (ns as f64) / 1_000_000_000.
}

/// seconds to nanoseconds
#[inline]
pub fn s_to_ns(s: f64) -> f64 {
    s * 1_000_000_000.
}

/// nanoseconds to milliseconds
#[inline]
pub fn ns_to_ms(ns: u64) -> f64 {
    (ns as f64) / 1_000_000.
}

/// seconds to milliseconds
#[inline]
pub fn s_to_ms<T>(s: T) -> T
    where T: Mul<T, Output=T> + From<u16>
{
    s * T::from(1000 as u16)
}

#[test]
fn test_s_to_ms() {
    assert_eq!(s_to_ms(1), 1000);
    assert_eq!(s_to_ms(1.), 1000.);
}

/// milliseconds to seconds
#[inline]
pub fn ms_to_s<T>(ms: T) -> T
    where T: Div<T, Output=T> + From<u16>
{
    ms / T::from(1000 as u16)
}

#[test]
fn test_ms_to_s() {
    assert_eq!(ms_to_s(1000), 1);
    assert_eq!(ms_to_s(1000.), 1.);
}

/// milliseconds to nanoseconds
#[inline]
pub fn ms_to_ns(ns: f64) -> f64 {
    ns * 1_000_000.
}

/// when given frames per second (or sample rate)
/// returns the duration of a single frame
#[inline]
pub fn fps_to_ns_per_frame(fps: usize) -> u64 {
    (s_to_ns(1.0) / (fps as f64)).round() as u64
}

/// returns the current timestamp in nanoseconds
#[inline]
pub fn current_time_ns() -> u64 {
    time::precise_time_ns()
}

// /// returns the nanoseconds of sleep which are needed to keep a constant
// /// frame rate
// pub fn ns_sleep_needed_for_constant_rate(fps: usize, ns_at_last_frame_start: u64) -> u64 {

/// useful for keeping a constant framerate
pub fn sleep_for_constant_rate(fps: usize, ns_at_last_frame_start: u64) {
    let ns_per_frame = fps_to_ns_per_frame(fps);
    let ns_after: u64 = time::precise_time_ns();
    let ns_elapsed = ns_after - ns_at_last_frame_start;
    if ns_elapsed < ns_per_frame {
        let ns_left_to_sleep = (ns_per_frame - ns_elapsed) as u32;
        let duration = std::time::Duration::new(0, ns_left_to_sleep);
        std::thread::sleep(duration);
    }
}

/// frequency range that can be modeled when taking the short
/// time fourier transform of a signal with `sample_rate` with
/// a sliding window of `window_sizew`.
/// equivalent to `rayleigh(sample_rate, window_size)..nyquist(sample_rate)`
/// increase the window size to increase the lower frequency
/// increase the sample rate to increase the upper frequency
pub fn hertz_range<T>(sample_rate: T, window_size: T) -> Range<T>
    where T: Div<T, Output=T> + From<u16> + Clone
{
    rayleigh(sample_rate.clone(), window_size)..nyquist(sample_rate)
}

#[test]
fn test_hertz_range() {
    assert_eq!(
        hertz_range(44100., 1024. * 8.),
        (5.38330078125)..22050.);
    assert_eq!(
        hertz_range(44100., 1024. * 4.),
        (10.7666015625)..22050.);
    assert_eq!(
        hertz_range(44100., 1024.),
        (43.06640625)..22050.);
    assert_eq!(
        hertz_range(44100., 512.),
        (86.1328125)..22050.);
}

/// maximum frequency in hertz that can be meaningfully analyzed with a given sample rate
/// [https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Explanation)
pub fn nyquist<T>(sample_rate: T) -> T
    where T: Div<T, Output=T> + From<u16>
{
    sample_rate / T::from(2 as u16)
}

#[test]
fn test_nyquist() {
    assert_eq!(nyquist(44100.), 22050.);
}

/// minimum frequency in hertz that can be meaningfully analyzed with a given sample rate and
/// window size
/// [https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Rayleigh_frequency](https://en.wikipedia.org/wiki/Short-time_Fourier_transform#Rayleigh_frequency)
pub fn rayleigh<T>(sample_rate: T, window_size: T) -> T
    where T: Div<T, Output=T> + From<u16>
{
    T::from(1 as u16) / cycles_per_second_to_seconds_per_cycle(sample_rate, window_size)
}

#[test]
fn test_rayleigh() {
    assert_eq!(rayleigh(44100., 1024.), 43.06640625);
}

/// window duration in seconds
/// sample_rate in hertz
pub fn cycles_per_second_to_seconds_per_cycle<T>(cycles_per_second: T, cycles: T) -> T
    where T: Div<T, Output=T>
{
    cycles / cycles_per_second
}

#[test]
fn test_seconds_per_window() {
    // 11ms time resolution
    assert_eq!(cycles_per_second_to_seconds_per_cycle(44100., 512.), 0.011609977324263039);
    assert_eq!(cycles_per_second_to_seconds_per_cycle(44100., 1024.), 0.023219954648526078);
}
<|endoftext|>


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

//! A library to generate EPUB files.
//!
//! The purpose for this library is to make it easier to generate EPUB files:
//! it should take care of most of the boilerplate for you, leaving you only
//! with the task of filling the actual content.
//!
//! # Usage
//!
//! Add this in your `Cargo.toml` file:
//!
//! ```toml
//! [dependencies]
//! epub-builder: "0.1"
//! ```
//!
//! # Example
//!
//! ```ignore
//! extern crate epub_builder;
//!
//! use epub_builder::EpubBuilder;
//! use epub_builder::Result;
//! use epub_builder::ZipLibrary;
//! use epub_builder::EpubContent;
//! use epub_builder::ReferenceType;
//! use epub_builder::TocElement;
//!
//! use std::io;
//! use std::io::Write;
//!
//! // Try to print Zip file to stdout
//! fn run() -> Result<()> {
//!     // Some dummy content to fill our books
//!     let dummy_content = "Dummy content. This should be valid XHTML if you want a valid EPUB!";
//!     let dummy_image = "Not really a PNG image";
//!     let dummy_css = "body { background-color: pink }";
//!
//!     // Create a new EpubBuilder using the zip library
//!     EpubBuilder::new(ZipLibrary::new())?
//!     // Set some metadata
//!         .metadata("author", "Joan Doe")?
//!         .metadata("title", "Dummy Book")?
//!     // Set the stylesheet (create a "stylesheet.css" file in EPUB that is used by some generated files)
//!         .stylesheet(dummy_css.as_bytes())?
//!     // Add a image cover file
//!         .add_cover_image("cover.png", dummy_image.as_bytes(), "image/png")?
//!     // Add a resource that is not part of the linear document structure
//!         .add_resource("some_image.png", dummy_image.as_bytes(), "image/png")?
//!     // Add a cover page
//!         .add_content(EpubContent::new("cover.xhtml", dummy_content.as_bytes())
//!                      .title("Cover")
//!                      .reftype(ReferenceType::Cover))?
//!     // Add a title page
//!         .add_content(EpubContent::new("title.xhtml", dummy_content.as_bytes())
//!                      .title("Title")
//!                      .reftype(ReferenceType::TitlePage))?
//!     // Add a chapter, mark it as beginning of the "real content"
//!         .add_content(EpubContent::new("chapter_1.xhtml", dummy_content.as_bytes())
//!                      .title("Chapter 1")
//!                      .reftype(ReferenceType::Text))?
//!     // Add a second chapter; this one has more toc information about its internal structure
//!         .add_content(EpubContent::new("chapter_2.xhtml", dummy_content.as_bytes())
//!                      .title("Chapter 2")
//!                      .child(TocElement::new("chapter_2.xhtml#1", "Chapter 2, section 1")))?
//!     // Add a section. Since its level is set to 2, it will be attached to the previous chapter.
//!         .add_content(EpubContent::new("section.xhtml", dummy_content.as_bytes())
//!                      .title("Chapter 2, section 2")
//!                      .level(2))?
//!     // Add a chapter without a title, which will thus not appear in the TOC.
//!         .add_content(EpubContent::new("notes.xhtml", dummy_content.as_bytes()))?
//!     // Generate a toc inside of the document, that will be part of the linear structure.
//!         .inline_toc()
//!     // Finally, write the EPUB file to stdout
//!         .generate(&mut io::stdout())?;
//!     Ok(())
//! }
//!
//! fn main() {
//!     match run() {
//!         Ok(_) => {
//!             writeln!(&mut io::stderr(),
//!                      "Successfully wrote epub document to stdout!")
//!                 .unwrap()
//!         }
//!         Err(err) => writeln!(&mut io::stderr(), "Error: {}", err).unwrap(),
//!     };
//! }
//! ```
//!
//! # Features
//!
//! `epub-builder`'s aim is to make EPUB generation simpler. It takes care of zipping
//! the files and generate the following ones:
//!
//! * `mimetype`
//! * `toc.ncx`
//! * `nav.xhtml`
//! * `manifest.xml`
//! * `content.opf`
//! * `com.apple.ibooks.display-options.xml`.
//!
//! It also tries to make it easier to have a correct table of contents, and optionally
//! generate an inline one in the document.
//!
//! Supported EPUB versions:
//!
//! * 2.0.1 (default)
//! * 3.0.1
//!
//! ## Missing features
//!
//! There are various EPUB features that `epub-builder` doesn't handle. Particularly,
//! there are some metadata that could be better
//! handled (e.g. support multiple authors, multiple languages in the document and so on).
//!
//! There are also various things that aren't in the scope of this library: it doesn't
//! provide a default CSS, templates for your XHTML content and so on. This is left to
//! libraries or applications using it.
//!
//! # Conditional compilation
//!
//! EPUB files are Zip files, so we need to zip. By default, this library provides
//! wrappers around both the [Rust zip library](https://crates.io/crates/zip) and calls
//! to the `zip` command that may (or may not) be installed on your system.
//!
//! It is possible to disable the compilation (and the dependencies) of either of these
//! wrappers, using `no-default-features`. (If you don't enable at least one of them this
//! library will be pretty useless).
//!
//! # License
//!
//! This is free software, published under the [Mozilla Public License,
//! version 2.0](https://www.mozilla.org/en-US/MPL/2.0/).
#![deny(missing_docs)]

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate error_chain;
extern crate mustache;
extern crate chrono;
extern crate uuid;
#[cfg(feature = "zip-command")]
extern crate tempdir;
#[cfg(feature = "zip-library")]
extern crate zip as libzip;

mod errors;
mod epub;
mod zip;
mod templates;
mod toc;
mod epub_content;
#[cfg(feature = "zip-command")]
mod zip_command;
#[cfg(feature = "zip-library")]
mod zip_library;

pub use errors::*;
pub use epub::EpubBuilder;
pub use epub::EpubVersion;
pub use zip::Zip;
pub use toc::Toc;
pub use toc::TocElement;
pub use epub_content::EpubContent;
pub use epub_content::ReferenceType;
#[cfg(feature = "zip-command")]
pub use zip_command::ZipCommand;
#[cfg(feature = "zip-library")]
pub use zip_library::ZipLibrary;
<commit_msg>Also fix TOML syntax in doc<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 generate EPUB files.
//!
//! The purpose for this library is to make it easier to generate EPUB files:
//! it should take care of most of the boilerplate for you, leaving you only
//! with the task of filling the actual content.
//!
//! # Usage
//!
//! Add this in your `Cargo.toml` file:
//!
//! ```toml
//! [dependencies]
//! epub-builder = "0.1"
//! ```
//!
//! # Example
//!
//! ```ignore
//! extern crate epub_builder;
//!
//! use epub_builder::EpubBuilder;
//! use epub_builder::Result;
//! use epub_builder::ZipLibrary;
//! use epub_builder::EpubContent;
//! use epub_builder::ReferenceType;
//! use epub_builder::TocElement;
//!
//! use std::io;
//! use std::io::Write;
//!
//! // Try to print Zip file to stdout
//! fn run() -> Result<()> {
//!     // Some dummy content to fill our books
//!     let dummy_content = "Dummy content. This should be valid XHTML if you want a valid EPUB!";
//!     let dummy_image = "Not really a PNG image";
//!     let dummy_css = "body { background-color: pink }";
//!
//!     // Create a new EpubBuilder using the zip library
//!     EpubBuilder::new(ZipLibrary::new())?
//!     // Set some metadata
//!         .metadata("author", "Joan Doe")?
//!         .metadata("title", "Dummy Book")?
//!     // Set the stylesheet (create a "stylesheet.css" file in EPUB that is used by some generated files)
//!         .stylesheet(dummy_css.as_bytes())?
//!     // Add a image cover file
//!         .add_cover_image("cover.png", dummy_image.as_bytes(), "image/png")?
//!     // Add a resource that is not part of the linear document structure
//!         .add_resource("some_image.png", dummy_image.as_bytes(), "image/png")?
//!     // Add a cover page
//!         .add_content(EpubContent::new("cover.xhtml", dummy_content.as_bytes())
//!                      .title("Cover")
//!                      .reftype(ReferenceType::Cover))?
//!     // Add a title page
//!         .add_content(EpubContent::new("title.xhtml", dummy_content.as_bytes())
//!                      .title("Title")
//!                      .reftype(ReferenceType::TitlePage))?
//!     // Add a chapter, mark it as beginning of the "real content"
//!         .add_content(EpubContent::new("chapter_1.xhtml", dummy_content.as_bytes())
//!                      .title("Chapter 1")
//!                      .reftype(ReferenceType::Text))?
//!     // Add a second chapter; this one has more toc information about its internal structure
//!         .add_content(EpubContent::new("chapter_2.xhtml", dummy_content.as_bytes())
//!                      .title("Chapter 2")
//!                      .child(TocElement::new("chapter_2.xhtml#1", "Chapter 2, section 1")))?
//!     // Add a section. Since its level is set to 2, it will be attached to the previous chapter.
//!         .add_content(EpubContent::new("section.xhtml", dummy_content.as_bytes())
//!                      .title("Chapter 2, section 2")
//!                      .level(2))?
//!     // Add a chapter without a title, which will thus not appear in the TOC.
//!         .add_content(EpubContent::new("notes.xhtml", dummy_content.as_bytes()))?
//!     // Generate a toc inside of the document, that will be part of the linear structure.
//!         .inline_toc()
//!     // Finally, write the EPUB file to stdout
//!         .generate(&mut io::stdout())?;
//!     Ok(())
//! }
//!
//! fn main() {
//!     match run() {
//!         Ok(_) => {
//!             writeln!(&mut io::stderr(),
//!                      "Successfully wrote epub document to stdout!")
//!                 .unwrap()
//!         }
//!         Err(err) => writeln!(&mut io::stderr(), "Error: {}", err).unwrap(),
//!     };
//! }
//! ```
//!
//! # Features
//!
//! `epub-builder`'s aim is to make EPUB generation simpler. It takes care of zipping
//! the files and generate the following ones:
//!
//! * `mimetype`
//! * `toc.ncx`
//! * `nav.xhtml`
//! * `manifest.xml`
//! * `content.opf`
//! * `com.apple.ibooks.display-options.xml`.
//!
//! It also tries to make it easier to have a correct table of contents, and optionally
//! generate an inline one in the document.
//!
//! Supported EPUB versions:
//!
//! * 2.0.1 (default)
//! * 3.0.1
//!
//! ## Missing features
//!
//! There are various EPUB features that `epub-builder` doesn't handle. Particularly,
//! there are some metadata that could be better
//! handled (e.g. support multiple authors, multiple languages in the document and so on).
//!
//! There are also various things that aren't in the scope of this library: it doesn't
//! provide a default CSS, templates for your XHTML content and so on. This is left to
//! libraries or applications using it.
//!
//! # Conditional compilation
//!
//! EPUB files are Zip files, so we need to zip. By default, this library provides
//! wrappers around both the [Rust zip library](https://crates.io/crates/zip) and calls
//! to the `zip` command that may (or may not) be installed on your system.
//!
//! It is possible to disable the compilation (and the dependencies) of either of these
//! wrappers, using `no-default-features`. (If you don't enable at least one of them this
//! library will be pretty useless).
//!
//! # License
//!
//! This is free software, published under the [Mozilla Public License,
//! version 2.0](https://www.mozilla.org/en-US/MPL/2.0/).
#![deny(missing_docs)]

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate error_chain;
extern crate mustache;
extern crate chrono;
extern crate uuid;
#[cfg(feature = "zip-command")]
extern crate tempdir;
#[cfg(feature = "zip-library")]
extern crate zip as libzip;

mod errors;
mod epub;
mod zip;
mod templates;
mod toc;
mod epub_content;
#[cfg(feature = "zip-command")]
mod zip_command;
#[cfg(feature = "zip-library")]
mod zip_library;

pub use errors::*;
pub use epub::EpubBuilder;
pub use epub::EpubVersion;
pub use zip::Zip;
pub use toc::Toc;
pub use toc::TocElement;
pub use epub_content::EpubContent;
pub use epub_content::ReferenceType;
#[cfg(feature = "zip-command")]
pub use zip_command::ZipCommand;
#[cfg(feature = "zip-library")]
pub use zip_library::ZipLibrary;
<|endoftext|>


<commit_before>#![deny(missing_docs)]
#![unstable]

//! A library for setting current values for stack scope,
//! such as application structure.

pub use get::Get;
pub use set::Set;
pub use action::Action;

mod refcell;
mod get;
mod set;
mod action;

/// Something that can be set at an object.
///
/// Must be implemented on a `(Property, Object)`
#[unstable]
pub trait SetAt: Pair {
    /// Modify `F` with self.
    fn set_at(
        val: <Self as Pair>::Data,
        obj: &mut <Self as Pair>::Object
    );
}

/// Something that can be retrieved from another object.
#[unstable]
pub trait GetFrom: Pair {
    /// Gets value from object.
    fn get_from(
        obj: &<Self as Pair>::Object
    ) -> <Self as Pair>::Data;
}

/// Does something to an object.
#[unstable]
pub trait ActOn<T>: Pair {
    /// Does something to an object.
    fn act_on(
        action: <Self as Pair>::Data,
        obj: &mut <Self as Pair>::Object
    ) -> T;
}

/// Used to reduce the need for associated types.
pub trait Pair {
    type Data;
    type Object;
}

impl<T, U> Pair for (T, U) {
    type Data = T;
    type Object = U;
}
<commit_msg>Started on quack macro<commit_after>#![deny(missing_docs)]
#![unstable]

//! A library for setting current values for stack scope,
//! such as application structure.

pub use get::Get;
pub use set::Set;
pub use action::Action;

mod refcell;
mod get;
mod set;
mod action;

/// Something that can be set at an object.
///
/// Must be implemented on a `(Property, Object)`
#[unstable]
pub trait SetAt: Pair {
    /// Modify `F` with self.
    fn set_at(
        val: <Self as Pair>::Data,
        obj: &mut <Self as Pair>::Object
    );
}

/// Something that can be retrieved from another object.
#[unstable]
pub trait GetFrom: Pair {
    /// Gets value from object.
    fn get_from(
        obj: &<Self as Pair>::Object
    ) -> <Self as Pair>::Data;
}

/// Does something to an object.
#[unstable]
pub trait ActOn<T>: Pair {
    /// Does something to an object.
    fn act_on(
        action: <Self as Pair>::Data,
        obj: &mut <Self as Pair>::Object
    ) -> T;
}

/// Used to reduce the need for associated types.
pub trait Pair {
    type Data;
    type Object;
}

impl<T, U> Pair for (T, U) {
    type Data = T;
    type Object = U;
}

#[macro_export]
macro_rules! quack {
    (
        $this:ident : $this_type:ty ,
        get:
        $(fn () -> $get_prop_type:ty { $e:expr })*
        set:
        $(fn ($val:ident : $set_prop_type:ty) { $f:expr })*
        action:
        $(fn ($action:ident : $action_type:ty) -> $ret_action_type:ty { $g:expr })*
    ) => {
        $(impl $crate::GetFrom for ($get_prop_type, $this_type) {
            #[inline(always)]
            fn get_from($this: &$this_type) -> $get_prop_type {
                $e
            }
        })*
        $(impl $crate::SetAt for ($set_prop_type, $this_type) {
            #[inline(always)]
            fn set_at($val : $set_prop_type, $this : &mut $this_type) {
                $f
            }
        })*
        $(impl $crate::ActOn<$ret_action_type> for ($action_type, $this_type) {
            #[inline(always)]
            fn act_on(
                $action : $action_type,
                $this: &mut $this_type
            ) -> $ret_action_type {
                $g
            }
        })*
    };
}

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

    pub struct Foo {
        x: i32,
        y: i32,
    }

    impl Foo {
        pub fn new() -> Foo {
            Foo { x: 0, y: 0 }
        }
    }

    pub struct X(pub i32);
    pub struct Y(pub i32);
    pub struct IncX;

    quack! {
        this: Foo,
        get:
            fn () -> X { X(this.x) }
            fn () -> Y { Y(this.y) }
        set:
            fn (x: X) { this.x = x.0 }
            fn (y: Y) { this.y = y.0 }
        action:
            fn (__: IncX) -> () { this.x += 1 }
    }

    #[test]
    fn test_foo() {
        let mut foo = Foo::new().set(X(1));
        let X(x) = foo.get();
        assert_eq!(x, 1);
        foo.action(IncX);
        let X(x) = foo.get();
        assert_eq!(x, 2);
    }
}
<|endoftext|>


<commit_before>#![feature(
    plugin,
    optin_builtin_traits,
)]
#![plugin(interpolate_idents)]

#![macro_reexport(interpolate_idents)]

#[macro_export]
macro_rules! unsafe_unions {
    ($(
        $(#[$attr:meta])*
        union $Union:ident: $Repr:ty {
            $($fields:tt)+
        }
    )+) => {$(

        $(#[$attr])*
        pub struct $Union {
            _data: $Repr
        }

        #[allow(unused, missing_docs)]
        impl $Union {
            unsafe_unions!(GEN_ACCESSORS; $Union; $($fields)+);
            unsafe_unions!(GEN_STORAGE_CHECK; $Union; $Repr; $($fields)+);
        }

    )+};

    (GEN_ACCESSORS; $Union:ident; $($field_name:ident: $field_ty:ty),*) => {$(

        interpolate_idents!{ // FIXME https://github.com/rust-lang/rust/issues/12249

        // new_*
        pub unsafe fn [new_ $field_name](val: $field_ty) -> Self {
            use std::mem;

            Self::__check_storage_type();

            let mut union = $Union{ _data: mem::uninitialized() };
            union.[write_ $field_name](val);
            union
        }

        pub unsafe fn [write_ $field_name](&mut self, val: $field_ty){
            use std::ptr;

            ptr::write(self as *mut Self as *mut $field_ty, val);
        }

        pub unsafe fn [read_ $field_name](&self) -> $field_ty {
            use std::ptr;

            ptr::read(self as *const Self as *const $field_ty)
        }

        pub unsafe fn [as_ $field_name](&self) -> &$field_ty {
            &*(self as *const Self as *const $field_ty)
        }

        pub unsafe fn [as_ $field_name _mut](&mut self) -> &mut $field_ty {
            &mut*(self as *mut Self as *mut $field_ty)
        }

        } // FIXME https://github.com/rust-lang/rust/issues/12249

    )*};
    (GEN_ACCESSORS; $Union:ident; $($field_name:ident: $field_ty:ty,)*) => {
        unsafe_unions!(GEN_ACCESSORS; $Union; $($field_name: $field_ty),*);
    };

    (GEN_STORAGE_CHECK; $Union:ident; $Repr:ty; $($field_name:ident: $field_ty:ty),*) => {

        fn __check_storage_type(){

            // FIXME currently doesn't work [rustc 1.5.0-nightly (9d3e79ad3 2015-10-10)]
            /*{
                // Repr-type may not implement Drop.
                // We can ensure this at compile-time.

                trait NoDrop {}
                impl NoDrop for .. {}
                impl<T: Drop> !NoDrop for T {}

                fn ct_check_nodrop<T: NoDrop>(){}

                ct_check_nodrop::<$Repr>();
            }*/

            {
                // Check Repr-type size
                // Currently, we can only ensure this at run-time (Panics are preferable
                // to segfault, anyways).

                // TODO: errmsg is a bit too wordy.

                use std::mem;

                $(
                    assert!(
                        mem::size_of::<$field_ty>() <= mem::size_of::<$Repr>(),
                        "union '{}': Repr-type needs to be bigger or equal in \
                        size to the biggest field. \
                        '{}' is '{}' bytes, field '{}' of type '{}' is '{}' bytes",
                        stringify!($Union),
                        stringify!($Repr),
                        mem::size_of::<$Repr>(),
                        stringify!($field_name),
                        stringify!($field_ty),
                        mem::size_of::<$field_ty>(),
                    );

                )+

            }

        }

    };
    (GEN_STORAGE_CHECK; $Union:ident; $Repr:ty; $($field_name:ident: $field_ty:ty,)*) => {
        unsafe_unions!(GEN_STORAGE_CHECK; $Union; $Repr; $($field_name: $field_ty),*);
    };
}

<commit_msg>macro_reexport for plugin-macro doesn't work...<commit_after>#![feature(
    plugin,
    optin_builtin_traits,
)]
#![plugin(interpolate_idents)]

#[macro_export]
macro_rules! unsafe_unions {
    ($(
        $(#[$attr:meta])*
        union $Union:ident: $Repr:ty {
            $($fields:tt)+
        }
    )+) => {$(

        $(#[$attr])*
        pub struct $Union {
            _data: $Repr
        }

        #[allow(unused, missing_docs)]
        impl $Union {
            unsafe_unions!(GEN_ACCESSORS; $Union; $($fields)+);
            unsafe_unions!(GEN_STORAGE_CHECK; $Union; $Repr; $($fields)+);
        }

    )+};

    (GEN_ACCESSORS; $Union:ident; $($field_name:ident: $field_ty:ty),*) => {$(

        interpolate_idents!{ // FIXME https://github.com/rust-lang/rust/issues/12249

        // new_*
        pub unsafe fn [new_ $field_name](val: $field_ty) -> Self {
            use std::mem;

            Self::__check_storage_type();

            let mut union = $Union{ _data: mem::uninitialized() };
            union.[write_ $field_name](val);
            union
        }

        pub unsafe fn [write_ $field_name](&mut self, val: $field_ty){
            use std::ptr;

            ptr::write(self as *mut Self as *mut $field_ty, val);
        }

        pub unsafe fn [read_ $field_name](&self) -> $field_ty {
            use std::ptr;

            ptr::read(self as *const Self as *const $field_ty)
        }

        pub unsafe fn [as_ $field_name](&self) -> &$field_ty {
            &*(self as *const Self as *const $field_ty)
        }

        pub unsafe fn [as_ $field_name _mut](&mut self) -> &mut $field_ty {
            &mut*(self as *mut Self as *mut $field_ty)
        }

        } // FIXME https://github.com/rust-lang/rust/issues/12249

    )*};
    (GEN_ACCESSORS; $Union:ident; $($field_name:ident: $field_ty:ty,)*) => {
        unsafe_unions!(GEN_ACCESSORS; $Union; $($field_name: $field_ty),*);
    };

    (GEN_STORAGE_CHECK; $Union:ident; $Repr:ty; $($field_name:ident: $field_ty:ty),*) => {

        fn __check_storage_type(){

            // FIXME currently doesn't work [rustc 1.5.0-nightly (9d3e79ad3 2015-10-10)]
            /*{
                // Repr-type may not implement Drop.
                // We can ensure this at compile-time.

                trait NoDrop {}
                impl NoDrop for .. {}
                impl<T: Drop> !NoDrop for T {}

                fn ct_check_nodrop<T: NoDrop>(){}

                ct_check_nodrop::<$Repr>();
            }*/

            {
                // Check Repr-type size
                // Currently, we can only ensure this at run-time (Panics are preferable
                // to segfault, anyways).

                // TODO: errmsg is a bit too wordy.

                use std::mem;

                $(
                    assert!(
                        mem::size_of::<$field_ty>() <= mem::size_of::<$Repr>(),
                        "union '{}': Repr-type needs to be bigger or equal in \
                        size to the biggest field. \
                        '{}' is '{}' bytes, field '{}' of type '{}' is '{}' bytes",
                        stringify!($Union),
                        stringify!($Repr),
                        mem::size_of::<$Repr>(),
                        stringify!($field_name),
                        stringify!($field_ty),
                        mem::size_of::<$field_ty>(),
                    );

                )+

            }

        }

    };
    (GEN_STORAGE_CHECK; $Union:ident; $Repr:ty; $($field_name:ident: $field_ty:ty,)*) => {
        unsafe_unions!(GEN_STORAGE_CHECK; $Union; $Repr; $($field_name: $field_ty),*);
    };
}

<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]
#![feature(unboxed_closures, phase)]
#![experimental]

extern crate serialize;
extern crate regex;
#[phase(plugin)]
extern crate regex_macros;

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

pub use self::template::{Template, Helper};
pub use self::registry::{Registry};
pub use self::render::{Renderable, RenderError, RenderContext};
pub use self::helpers::{HelperDef};
pub use self::context::{Context, JsonRender, JsonTruthy};

mod template;
mod registry;
mod render;
mod helpers;
mod context;
<commit_msg>Added docs.<commit_after>#![allow(dead_code, unused_variables)]
#![feature(unboxed_closures, phase)]
#![experimental]

//! # Handlebars
//! Handlebars is a modern and extensible templating solution originally created in the JavaScript world. It's used by many popular frameworks like [Ember.js](http://emberjs.com) and Chaplin. It's also ported to some other platforms such as [Java](https://github.com/jknack/handlebars.java).
//!
//! And this is handlebars Rust implementation, designed for server-side page generation. It's a general-purpose library so you use it for any kind of text generation.
//!
//! ## Handlebars spec
//!
//! ### Base
//!
//! You can go to [Handlebars.js](http://handlebarsjs.com/) website for its syntax. This implementation should be compatible with most parts of the spec, except:
//!
//! * raw helper syntax `{{{raw-helper}}}...{{{/raw-helper}}}` is implemented as block helper raw.
//! * type inference in hashes (on my TODO list)
//! * configurable logging (hard-coded to rust native logging, with fixed level `INFO`)
//!
//! ### Extensions
//!
//! We have template reuse facilities support via built-in helpers `>`, `partial` and `block`.
//!
//! There are two ways to reuse a template:
//!
//! * include (using `<`)
//! * inheritance (using `<` together with `block` and `partial`)
//!
//! Consult [Handlebar.java document about template inheritance](http://jknack.github.io/handlebars.java/reuse.html).
//!
//! ## Usage
//!
//! ### Template Creation
//!
//! Templates are created from String.
//!
//! ```
//! extern crate handlebars;
//!
//! use handlebars::Template;
//!
//! fn main() {
//!   let source = "hello {{world}}";
//!   //compile returns an Option, we use unwrap() to deref it directly here
//!   let tpl = Template::compile(source.to_string).unwrap();
//! }
//! ```
//!
//! ### Registration
//!
//! All the templates and helpers have to be registered into Registry, so they can look up each other by name.
//!
//! ```
//! extern crate handlebars;
//!
//! use handlebars::{Template, Registry};
//!
//! fn main() {
//!   let source = "hello {{world}}";
//!   //compile returns an Option, we use unwrap() to deref it directly here
//!   let tpl = Template::compile(source.to_string).unwrap();
//!
//!   let mut handlebars = Registry::new();
//!   handlebars.register_template("hello_world", &tpl);
//! }
//! ```
//!
//! ### Rendering Something
//!
//! I should say that rendering is a little tricky. Since handlebars is originally a JavaScript templating framework. It supports dynamic features like duck-typing, truthy/falsy values. But for a static language like Rust, this is a little difficult. As a solution, I'm using the `serialize::json::Json` internally for data rendering, which seems good by far.
//!
//! That means, if you want to render something, you have to ensure that it implements the `serialize::json::ToJson` trait. Luckily, most built-in types already have trait. However, if you want to render your custom struct, you need to implement this trait manually. (Rust has a deriving facility, but it's just for selected types. Maybe I will add some syntax extensions or macros to simplify this process.)
//!
//! ```
//! extern crate handlebars;
//!
//! use serialize::json::ToJson;
//! use std.collections::BTreeMap;
//!
//! use handlebars::{Template, Registry};
//!
//! fn main() {
//!   let source = "hello {{world}}";
//!   //compile returns an Option, we use unwrap() to deref it directly here
//!   let tpl = Template::compile(source.to_string).unwrap();
//!
//!   let mut handlebars = Registry::new();
//!   handlebars.register_template("hello_world", &tpl);
//!
//!   let mut data = BTreeMap::new();
//!   data.insert("world", "world".to_json());
//!   let result = handlebars.render("hello_world", &data);
//! }
//! ```
//!
//! ### Custom Helper
//!
//! Handlebars is nothing without helpers. You can also create your own helpers with rust. Helpers in handlebars-rust are custom struct implements the `HelperDef` trait, concretely, the `resolve` function.
//!
//! ```
//! extern crate handlebars;
//!
//! use handlebars::{Registry, HelperDef, RenderError, RenderContext, Helper, Context};
//!
//! #[deriving(Copy)]
//! struct SimpleHelper;
//!
//! impl HelperDef for SimpleHelper {
//!   fn resolve(&self, c: &Context, h: &Helper, _: &Registry, rc: &mut RenderContext) -> Result<String, RenderError> {
//!     let param = h.params().get(0).unwrap();
//!
//!     // get value from context data
//!     // rc.get_path() is current json parent path, you should always use it like this
//!     // param is the key of value you want to display
//!     let value = c.navigate(rc.get_path(), param);
//!     Ok(format!("My helper dumps: {} ", value))
//!   }
//! }
//!
//! // create an instance
//! static MY_HELPER: SimpleHelper = SimpleHelper;
//!
//! fn main() {
//!   let mut handlebars = Registry::new();
//!   handlebars.register_helper("simple-helper", box MY_HELPER);
//!
//!   //...
//! }
//! ```
//!
//! You can learn more about helpers by looking into source code of built-in helpers.
//!
//! ## TODO
//!
//! * More friendly ToJson
//! * Type inference in helper hash
//! * Better error report
//!

extern crate serialize;
extern crate regex;
#[phase(plugin)]
extern crate regex_macros;

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

pub use self::template::{Template, Helper};
pub use self::registry::{Registry};
pub use self::render::{Renderable, RenderError, RenderContext};
pub use self::helpers::{HelperDef};
pub use self::context::{Context, JsonRender, JsonTruthy};

mod template;
mod registry;
mod render;
mod helpers;
mod context;
<|endoftext|>