<commit_before>//! This crate is a reimplementation
//! of GNU gettext translation framework in Rust.
//! It allows your Rust programs to parse out GNU MO files
//! containing translations and use them in your user interface.
//!
//! It contains several differences from the official C implementation.
//! Notably, this crate does not in any way depend on a global locale
//! ([2.2](https://www.gnu.org/software/gettext/manual/gettext.html#Setting-the-GUI-Locale))
//! and does not enforce a directory structure
//! for storing your translation catalogs
//! ([11.2.3](https://www.gnu.org/software/gettext/manual/gettext.html#Locating-Catalogs)).
//! Instead, the choice of translation catalog to use is explicitly made by the user.
//!
//! This crate is still in-progress
//! and may not be on par with the original implementation feature-wise.
//!
//! For the exact feature parity see the roadmap in the
//! [README](https://github.com/justinas/gettext#readme).
//!
//! # Example
//!
//! ```
//! extern crate gettext;
//!
//! use std::fs::File;
//! use gettext::Catalog;
//!
//! fn main() {
//!     let f = File::open("french.mo").expect("could not open the catalog");
//!     let catalog = Catalog::parse(f).expect("could not parse the catalog");
//!
//!     // Will print out the French translation
//!     // if it is found in the parsed file
//!     // or "Name" otherwise.
//!     println!("{}", catalog.gettext("Name"));
//! }
//! ```

// https://pascalhertleif.de/artikel/good-practices-for-writing-rust-libraries/
#![deny(missing_docs, missing_debug_implementations,
        trivial_casts, trivial_numeric_casts, unused_import_braces)]

mod parser;

use std::collections::HashMap;
use std::io::Read;
use std::ops::Deref;

use parser::parse_catalog;

pub use parser::Error;

/// Returns the number of the appropriate plural form
/// for the given count `n` of objects for germanic languages.
fn plural_form(n: usize) -> usize {
    if n != 1 {
        1
    } else {
        0
    }
}

fn key_with_context(context: &str, key: &str) -> String {
    let mut result = context.to_owned();
    result.push('\x04');
    result.push_str(key);
    result
}

/// Catalog represents a set of translation strings
/// parsed out of one MO file.
#[derive(Debug)]
pub struct Catalog {
    strings: HashMap<String, Message>,
}

impl Catalog {
    /// Creates a new, empty gettext catalog.
    fn new() -> Self {
        Catalog { strings: HashMap::new() }
    }

    /// Parses a gettext catalog from the given binary MO file.
    /// Returns the `Err` variant upon encountering an invalid file format
    /// or invalid byte sequence in strings.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use gettext::Catalog;
    /// use std::fs::File;
    ///
    /// let file = File::open("french.mo").unwrap();
    /// let catalog = Catalog::parse(file).unwrap();
    /// ```

    pub fn parse<R: Read>(reader: R) -> Result<Self, parser::Error> {
        parse_catalog(reader)
    }

    fn insert(&mut self, msg: Message) {
        let key = match msg.context {
            Some(ref ctxt) => key_with_context(ctxt, &msg.id),
            None => msg.id.clone(),
        };
        self.strings.insert(key, msg);
    }

    /// Returns the singular translation of `msg_id` from the given catalog
    /// or `msg_id` itself if a translation does not exist.
    pub fn gettext<'a>(&'a self, msg_id: &'a str) -> &'a str {
        self.strings.get(&msg_id.to_owned()).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id` from the given catalog
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    pub fn ngettext<'a>(&'a self, msg_id: &'a str, msg_id_plural: &'a str, n: usize) -> &'a str {
        let form_no = plural_form(n);

        match self.strings.get(&msg_id.to_owned()) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }

    /// Returns the singular translation of `msg_id`
    /// in the context `msg_context`
    /// or `msg_id` itself if a translation does not exist.
    // TODO: DRY gettext/pgettext
    pub fn pgettext<'a>(&'a self, msg_context: &'a str, msg_id: &'a str) -> &'a str {
        let key = key_with_context(msg_context, &msg_id);
        self.strings.get(&key).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id`
    /// in the context `msg_context`
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    // TODO: DRY ngettext/npgettext
    pub fn npgettext<'a>(&'a self,
                         msg_context: &'a str,
                         msg_id: &'a str,
                         msg_id_plural: &'a str,
                         n: usize)
                         -> &'a str {
        let key = key_with_context(msg_context, &msg_id);
        let form_no = plural_form(n);
        match self.strings.get(&key) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Eq, PartialEq)]
struct Message {
    id: String,
    context: Option<String>,
    translated: Vec<String>,
}

impl Message {
    fn new<T: Into<String>>(id: T, context: Option<T>, translated: Vec<T>) -> Self {
        Message {
            id: id.into(),
            context: context.map(Into::into),
            translated: translated.into_iter().map(Into::into).collect(),
        }
    }

    fn get_translated(&self, form_no: usize) -> Option<&str> {
        self.translated.get(form_no).map(|s| s.deref())
    }
}

#[test]
fn catalog_insert() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("thisisid", None, vec![]));
    cat.insert(Message::new("anotherid", Some("context"), vec![]));
    let mut keys = cat.strings.keys().collect::<Vec<_>>();
    keys.sort();
    assert_eq!(keys, &["context\x04anotherid", "thisisid"])
}

#[test]
fn catalog_gettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", None, vec!["Tekstas"]));
    cat.insert(Message::new("Image", Some("context"), vec!["Paveikslelis"]));
    assert_eq!(cat.gettext("Text"), "Tekstas");
    assert_eq!(cat.gettext("Image"), "Image");
}

#[test]
fn catalog_ngettext() {
    let mut cat = Catalog::new();
    {
        // n == 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Text");
        // n != 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Texts");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Texts");
    }
    {
        cat.insert(Message::new("Text", None, vec!["Tekstas", "Tekstai"]));
        // n == 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Tekstas");
        // n != 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Tekstai");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Tekstai");
    }
}

#[test]
fn catalog_pgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas"]));
    assert_eq!(cat.pgettext("unit test", "Text"), "Tekstas");
    assert_eq!(cat.pgettext("integration test", "Text"), "Text");
}

#[test]
fn catalog_npgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas", "Tekstai"]));

    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 1), "Tekstas");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 0), "Tekstai");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 2), "Tekstai");

    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 1),
               "Text");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 0),
               "Texts");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 2),
               "Texts");
}
<commit_msg>Ignore the example<commit_after>//! This crate is a reimplementation
//! of GNU gettext translation framework in Rust.
//! It allows your Rust programs to parse out GNU MO files
//! containing translations and use them in your user interface.
//!
//! It contains several differences from the official C implementation.
//! Notably, this crate does not in any way depend on a global locale
//! ([2.2](https://www.gnu.org/software/gettext/manual/gettext.html#Setting-the-GUI-Locale))
//! and does not enforce a directory structure
//! for storing your translation catalogs
//! ([11.2.3](https://www.gnu.org/software/gettext/manual/gettext.html#Locating-Catalogs)).
//! Instead, the choice of translation catalog to use is explicitly made by the user.
//!
//! This crate is still in-progress
//! and may not be on par with the original implementation feature-wise.
//!
//! For the exact feature parity see the roadmap in the
//! [README](https://github.com/justinas/gettext#readme).
//!
//! # Example
//!
//! ```ignore
//! extern crate gettext;
//!
//! use std::fs::File;
//! use gettext::Catalog;
//!
//! fn main() {
//!     let f = File::open("french.mo").expect("could not open the catalog");
//!     let catalog = Catalog::parse(f).expect("could not parse the catalog");
//!
//!     // Will print out the French translation
//!     // if it is found in the parsed file
//!     // or "Name" otherwise.
//!     println!("{}", catalog.gettext("Name"));
//! }
//! ```

// https://pascalhertleif.de/artikel/good-practices-for-writing-rust-libraries/
#![deny(missing_docs, missing_debug_implementations,
        trivial_casts, trivial_numeric_casts, unused_import_braces)]

mod parser;

use std::collections::HashMap;
use std::io::Read;
use std::ops::Deref;

use parser::parse_catalog;

pub use parser::Error;

/// Returns the number of the appropriate plural form
/// for the given count `n` of objects for germanic languages.
fn plural_form(n: usize) -> usize {
    if n != 1 {
        1
    } else {
        0
    }
}

fn key_with_context(context: &str, key: &str) -> String {
    let mut result = context.to_owned();
    result.push('\x04');
    result.push_str(key);
    result
}

/// Catalog represents a set of translation strings
/// parsed out of one MO file.
#[derive(Debug)]
pub struct Catalog {
    strings: HashMap<String, Message>,
}

impl Catalog {
    /// Creates a new, empty gettext catalog.
    fn new() -> Self {
        Catalog { strings: HashMap::new() }
    }

    /// Parses a gettext catalog from the given binary MO file.
    /// Returns the `Err` variant upon encountering an invalid file format
    /// or invalid byte sequence in strings.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use gettext::Catalog;
    /// use std::fs::File;
    ///
    /// let file = File::open("french.mo").unwrap();
    /// let catalog = Catalog::parse(file).unwrap();
    /// ```

    pub fn parse<R: Read>(reader: R) -> Result<Self, parser::Error> {
        parse_catalog(reader)
    }

    fn insert(&mut self, msg: Message) {
        let key = match msg.context {
            Some(ref ctxt) => key_with_context(ctxt, &msg.id),
            None => msg.id.clone(),
        };
        self.strings.insert(key, msg);
    }

    /// Returns the singular translation of `msg_id` from the given catalog
    /// or `msg_id` itself if a translation does not exist.
    pub fn gettext<'a>(&'a self, msg_id: &'a str) -> &'a str {
        self.strings.get(&msg_id.to_owned()).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id` from the given catalog
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    pub fn ngettext<'a>(&'a self, msg_id: &'a str, msg_id_plural: &'a str, n: usize) -> &'a str {
        let form_no = plural_form(n);

        match self.strings.get(&msg_id.to_owned()) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }

    /// Returns the singular translation of `msg_id`
    /// in the context `msg_context`
    /// or `msg_id` itself if a translation does not exist.
    // TODO: DRY gettext/pgettext
    pub fn pgettext<'a>(&'a self, msg_context: &'a str, msg_id: &'a str) -> &'a str {
        let key = key_with_context(msg_context, &msg_id);
        self.strings.get(&key).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id`
    /// in the context `msg_context`
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    // TODO: DRY ngettext/npgettext
    pub fn npgettext<'a>(&'a self,
                         msg_context: &'a str,
                         msg_id: &'a str,
                         msg_id_plural: &'a str,
                         n: usize)
                         -> &'a str {
        let key = key_with_context(msg_context, &msg_id);
        let form_no = plural_form(n);
        match self.strings.get(&key) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Eq, PartialEq)]
struct Message {
    id: String,
    context: Option<String>,
    translated: Vec<String>,
}

impl Message {
    fn new<T: Into<String>>(id: T, context: Option<T>, translated: Vec<T>) -> Self {
        Message {
            id: id.into(),
            context: context.map(Into::into),
            translated: translated.into_iter().map(Into::into).collect(),
        }
    }

    fn get_translated(&self, form_no: usize) -> Option<&str> {
        self.translated.get(form_no).map(|s| s.deref())
    }
}

#[test]
fn catalog_insert() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("thisisid", None, vec![]));
    cat.insert(Message::new("anotherid", Some("context"), vec![]));
    let mut keys = cat.strings.keys().collect::<Vec<_>>();
    keys.sort();
    assert_eq!(keys, &["context\x04anotherid", "thisisid"])
}

#[test]
fn catalog_gettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", None, vec!["Tekstas"]));
    cat.insert(Message::new("Image", Some("context"), vec!["Paveikslelis"]));
    assert_eq!(cat.gettext("Text"), "Tekstas");
    assert_eq!(cat.gettext("Image"), "Image");
}

#[test]
fn catalog_ngettext() {
    let mut cat = Catalog::new();
    {
        // n == 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Text");
        // n != 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Texts");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Texts");
    }
    {
        cat.insert(Message::new("Text", None, vec!["Tekstas", "Tekstai"]));
        // n == 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Tekstas");
        // n != 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Tekstai");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Tekstai");
    }
}

#[test]
fn catalog_pgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas"]));
    assert_eq!(cat.pgettext("unit test", "Text"), "Tekstas");
    assert_eq!(cat.pgettext("integration test", "Text"), "Text");
}

#[test]
fn catalog_npgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas", "Tekstai"]));

    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 1), "Tekstas");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 0), "Tekstai");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 2), "Tekstai");

    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 1),
               "Text");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 0),
               "Texts");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 2),
               "Texts");
}
<|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 http://mozilla.org/MPL/2.0/.

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

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

#[macro_use]
extern crate lazy_static;

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

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

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

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

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

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

/// A named connection between two nodes.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Arrow(String);

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

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

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

/// An enum that represents all the currently supported literal types.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum Vertex {
    /// A node.
    Node(Node),
    /// A tagged String used for an RDF literal
    StringLiteral(String),
    /// A tagged bool used for an RDF literal
    BooleanLiteral(bool),
    /// A tagged i64 used for an RDF literal
    LongLiteral(i64),
    /// A tagged f64 used for an RDF literal
    FloatLiteral(f64),
}

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

/// A Link is a grouping of a source, an arrow, and a target.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct Link {
    /// The Source of a Link
    pub source: Vertex,
    /// The Arrow of a Link
    pub arrow: Arrow,
    /// The Target of a Link
    pub target: Vertex,
}

/// A Link that has been persisted so it has an assoicated context.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct PersistedLink {
    /// The Target of a Link
    pub link: Link,
    /// The Context of this Link
    pub context: Node,
}

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

/// A struct that is returned from SPAQRL and wander queries.
pub struct QueryResult {
    /// A Vec of headers for the results.
    pub headers: Vec<String>,
    /// A stream of results, the inner Vec has the same lenth as the headers Vec.
    pub results: Box<dyn Iterator<Item = Result<Vec<Vertex>, LigatureError>>>,
}

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

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

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

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

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

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

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

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

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

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

    /// Returns the PersistedLink for the given context.
    fn link_for_context(&self, context: Node) -> Result<PersistedLink, LigatureError>;

    /// Run a wander query.
    fn wander_query(&self, query: String) -> Result<QueryResult, LigatureError>;
}

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

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

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

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

    /// Commits this transaction.
    /// This also closes this transaction so no other methods can be called.
    fn commit(&self) -> Result<(), LigatureError>;
}
<commit_msg>clean up, adding dataset_exists, renaming match_datasets_prefix<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

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

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

#[macro_use]
extern crate lazy_static;

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

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

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

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

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

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

/// A named connection between two nodes.
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct Arrow(String);

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

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

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

/// An enum that represents all the currently supported literal types.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub enum Vertex {
    /// A node.
    Node(Node),
    /// A tagged String used for an RDF literal
    StringLiteral(String),
    /// A tagged bool used for an RDF literal
    BooleanLiteral(bool),
    /// A tagged i64 used for an RDF literal
    LongLiteral(i64),
    /// A tagged f64 used for an RDF literal
    FloatLiteral(f64),
}

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

/// A Link is a grouping of a source, an arrow, and a target.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct Link {
    /// The Source of a Link
    pub source: Vertex,
    /// The Arrow of a Link
    pub arrow: Arrow,
    /// The Target of a Link
    pub target: Vertex,
}

/// A Link that has been persisted so it has an assoicated Context Node.
#[derive(Debug, Clone, PartialOrd, PartialEq)]
pub struct PersistedLink {
    /// The Target of a Link
    pub link: Link,
    /// The Context of this Link
    pub context: Node,
}

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

/// A struct that is returned from SPAQRL and wander queries.
pub struct QueryResult {
    /// A Vec of headers for the results.
    pub headers: Vec<String>,
    /// A stream of results, the inner Vec has the same lenth as the headers Vec.
    pub results: Box<dyn Iterator<Item = Result<Vec<Vertex>, LigatureError>>>,
}

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

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

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

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

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

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

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

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

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

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

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

    /// Returns the PersistedLink for the given context.
    fn link_for_context(&self, context: &Node) -> Result<PersistedLink, LigatureError>;

    /// Run a wander query.
    fn wander_query(&self, query: &str) -> Result<QueryResult, LigatureError>;
}

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

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

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

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

    /// Commits this transaction.
    /// This also closes this transaction so no other methods can be called.
    fn commit(&self) -> Result<(), LigatureError>;
}
<|endoftext|>


<commit_before>class cat<U> {
  priv {
    let mut meows : uint;
  }

  let how_hungry : int;

  new(in_x : uint, in_y : int) { self.meows = in_x; self.how_hungry = in_y; }

  fn speak() {
    self.meows += 1u;
  }
  fn meow_count() -> uint { self.meows }
}

fn main() {
  let _nyan : cat<int> = cat::<int>(52u, 99);
  //  let kitty = cat(1000u, 2);
}
<commit_msg>also xfail class-typarams (doesn't work on Windows)<commit_after>// xfail-test
// needs metadata encoding on Windows
class cat<U> {
  priv {
    let mut meows : uint;
  }

  let how_hungry : int;

  new(in_x : uint, in_y : int) { self.meows = in_x; self.how_hungry = in_y; }

  fn speak() {
    self.meows += 1u;
  }
  fn meow_count() -> uint { self.meows }
}

fn main() {
  let _nyan : cat<int> = cat::<int>(52u, 99);
  //  let kitty = cat(1000u, 2);
}
<|endoftext|>


<commit_before>//! Url Encoded middleware for Iron
//!
//! This middleware focuses on parsing the incoming url parameters from client requests.
#![crate_id = "urlencoded"]
#![license = "MIT"]

extern crate url;
extern crate iron;
extern crate serialize;

use iron::{Request, Response, Middleware, Alloy};
use iron::mixin::GetUrl;
use iron::middleware::{Status, Continue};

use std::collections::HashMap;

/// Stores a `HashMap` of a `String` and a `Vec<Strings>` to address
/// client data that is sent with multiple values for a single key.
#[deriving(Clone)]
pub struct Encoded(pub HashMap<String, Vec<String>>);

/// This middleware is used for parsing url parameters and storing
/// the data as conveniently accessible data `insert`ed into an Alloy.
#[deriving(Clone)]
pub struct UrlEncoded;

/// Creates a `UrlEncoded` instance to `link` to `server.chain`. Calling the
/// function will `insert` a new `HashMap` into the `alloy`.
impl UrlEncoded {
    pub fn new() -> UrlEncoded {
        UrlEncoded
    }
}

impl Middleware for UrlEncoded {
    fn enter(&mut self, req: &mut Request, _ : &mut Response, alloy: &mut Alloy) -> Status {

        let query = match url::path_from_str(req.url().unwrap().as_slice()) {
            Ok(e) => {
                e.query
            },
            _ => {
                return Continue;
            }
        };
        alloy.insert::<Encoded>(Encoded(create_hash(query)));
        Continue
    }
}

fn create_hash(q: Vec<(String, String)>) -> HashMap<String, Vec<String>> {
    let mut hashStrVec: HashMap<String, Vec<String>> = HashMap::new();

    for (k, v) in q.move_iter() {
        hashStrVec.find_with_or_insert_with(
            k, v,
            |_, already, new| {
                already.push(new);
            },
            |_, v| vec![v]
        );
    }
    hashStrVec
}
<commit_msg>(test) Created error handling<commit_after>//! Url Encoded middleware for Iron
//!
//! This middleware focuses on parsing the incoming url parameters from client requests.
#![crate_id = "urlencoded"]
#![license = "MIT"]

extern crate url;
extern crate iron;
extern crate serialize;

use iron::{Request, Response, Middleware, Alloy};
use iron::mixin::GetUrl;
use iron::middleware::{Status, Continue};

use std::collections::HashMap;

/// Stores a `HashMap` of a `String` and a `Vec<Strings>` to address
/// client data that is sent with multiple values for a single key.
#[deriving(Clone)]
pub struct Encoded(pub HashMap<String, Vec<String>>);

/// This middleware is used for parsing url parameters and storing
/// the data as conveniently accessible data `insert`ed into an Alloy.
#[deriving(Clone)]
pub struct UrlEncoded;

/// Creates a `UrlEncoded` instance to `link` to `server.chain`. Calling the
/// function will `insert` a new `HashMap` into the `alloy`.
impl UrlEncoded {
    pub fn new() -> UrlEncoded {
        UrlEncoded
    }
}

impl Middleware for UrlEncoded {

    fn enter(&mut self, req: &mut Request, res: &mut Response, alloy: &mut Alloy) -> Status {

        let raw_url = req.url();

        let path = match raw_url {
            Some(k) => {
                url::path_from_str(k.as_slice())
            },
            None => {
                return Continue;
            }
        };

        let query = match path {
            Ok(e) => {
                e.query
            },
            Err(_) => {
                return Continue;
            }
        };
        alloy.insert::<Encoded>(Encoded(create_hash(query)));
        Continue
    }
}

fn create_hash(q: Vec<(String, String)>) -> HashMap<String, Vec<String>> {

    let mut hashStrVec: HashMap<String, Vec<String>> = HashMap::new();

    for (k, v) in q.move_iter() {
        hashStrVec.find_with_or_insert_with(
            k, v,
            |_, already, new| {
                already.push(new);
            },
            |_, v| vec![v]
        );
    }
    hashStrVec
}

#[test]
fn test_create_hash() {
    let my_vec = vec!(("band".to_string(), "arctic monkeys".to_string()),("band".to_string(), "temper trap".to_string()),("color".to_string(),"green".to_string()));
    let answer = create_hash(my_vec);
    let mut control = HashMap::new();
    control.insert("band".to_string(), vec!("arctic monkeys".to_string(), "temper trap".to_string()));
    control.insert("color".to_string(), vec!("green".to_string()));
    assert!(answer==control);
}<|endoftext|>


<commit_before>extern crate nom_lua53;

use nom_lua53::{parse_all, ParseResult, Statement, Exp};
use nom_lua53::op::BinOp;

#[derive(Debug)]
pub enum LuaValue {
    Nil,
    Integer(isize),
    Float(f64),
    Boolean(bool),
    Str(String),
}

#[derive(Debug)]
pub enum LuaError {
    TypeError(String),
    ArithmeticError(String),
}

use LuaError::*;

type Result<T> = std::result::Result<T, LuaError>;

// What do we say? We say "Merci Basile!"
fn lit_to_string(string: &nom_lua53::string::StringLit) -> String {
    return String::from_utf8_lossy(&(string.0)).to_string();
}

fn eval_arithmetic(left_op: &Box<Exp>, right_op: &Box<Exp>,
                   integer: fn(isize, isize) -> Result<LuaValue>,
                   float: fn(f64, f64) -> Result<LuaValue>) -> Result<LuaValue> {

    let left_op = eval_expr(&left_op)?;
    let right_op = eval_expr(&right_op)?;

    match left_op {
        LuaValue::Integer(i) => match right_op {
            LuaValue::Integer(j) => integer(i, j),
            LuaValue::Float(j) => float(i as f64, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        LuaValue::Float(i) => match right_op {
            LuaValue::Integer(j) => float(i, j as f64),
            LuaValue::Float(j) => float(i, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
    }
}

fn eval_binary_expr(left_op: &Box<Exp>, right_op: &Box<Exp>, operator: &BinOp) -> Result<LuaValue> {
    // We cannot yet evaluate the operands as some binary operators are used to shortcut
    // evaluation.
    match *operator {
        BinOp::Plus => eval_arithmetic(left_op, right_op,
                                       |i, j| Ok(LuaValue::Integer(i+j)),
                                       |i, j| Ok(LuaValue::Float(i+j))),
        BinOp::Minus => eval_arithmetic(left_op, right_op,
                                        |i, j| Ok(LuaValue::Integer(i-j)),
                                        |i, j| Ok(LuaValue::Float(i-j))),
        BinOp::Mul => eval_arithmetic(left_op, right_op,
                                      |i, j| Ok(LuaValue::Integer(i*j)),
                                      |i, j| Ok(LuaValue::Float(i*j))),
        BinOp::Div => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float((i as f64)/(j as f64)))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i/j))
                                      }),
        BinOp::Mod => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Integer(i % j))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i - (i/j).floor()*j))
                                      }),
        BinOp::IntDiv => eval_arithmetic(left_op, right_op,
                                         |i, j| if j == 0 {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer(i/j))
                                         },
                                         |i, j| if j == 0. {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer((i/j).floor() as isize))
                                         }),
        BinOp::Pow => eval_arithmetic(left_op, right_op,
                                         |i, j| Ok(LuaValue::Float((i as f64).powf(j as f64))),
                                         |i, j| Ok(LuaValue::Float(i.powf(j)))),
        _ => Ok(LuaValue::Nil)
    }
}

fn eval_expr(expr: &Exp) -> Result<LuaValue> {
    match *expr {
        Exp::Nil => Ok(LuaValue::Nil),
        Exp::Bool(val) => Ok(LuaValue::Boolean(val)),
        Exp::Num(val) => Ok(match val {
            nom_lua53::num::Numeral::Float(fl) => LuaValue::Float(fl),
            nom_lua53::num::Numeral::Int(i) => LuaValue::Integer(i),
        }),
        Exp::BinExp(ref left, ref op, ref right) => eval_binary_expr(left, right, &op),
        Exp::Str(ref s) => Ok(LuaValue::Str(lit_to_string(s))),
        _ => Ok(LuaValue::Nil),
    }
}

pub fn eval_file(input: &[u8]) {
    match parse_all(input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        let values = ass.vals.expect("There should be some values. Why isn't there any value?!");
                        for (var, val) in ass.vars.iter().zip(values.iter()) {
                            let computed_value = eval_expr(val);
                            println!("Assigning {:?} to {:?}", computed_value, var);
                        }
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}
<commit_msg>Add a bogus test module<commit_after>extern crate nom_lua53;

use nom_lua53::{parse_all, ParseResult, Statement, Exp};
use nom_lua53::op::BinOp;

#[derive(Debug)]
pub enum LuaValue {
    Nil,
    Integer(isize),
    Float(f64),
    Boolean(bool),
    Str(String),
}

#[derive(Debug)]
pub enum LuaError {
    TypeError(String),
    ArithmeticError(String),
}

use LuaError::*;

type Result<T> = std::result::Result<T, LuaError>;

// What do we say? We say "Merci Basile!"
fn lit_to_string(string: &nom_lua53::string::StringLit) -> String {
    return String::from_utf8_lossy(&(string.0)).to_string();
}

fn eval_arithmetic(left_op: &Box<Exp>, right_op: &Box<Exp>,
                   integer: fn(isize, isize) -> Result<LuaValue>,
                   float: fn(f64, f64) -> Result<LuaValue>) -> Result<LuaValue> {

    let left_op = eval_expr(&left_op)?;
    let right_op = eval_expr(&right_op)?;

    match left_op {
        LuaValue::Integer(i) => match right_op {
            LuaValue::Integer(j) => integer(i, j),
            LuaValue::Float(j) => float(i as f64, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        LuaValue::Float(i) => match right_op {
            LuaValue::Integer(j) => float(i, j as f64),
            LuaValue::Float(j) => float(i, j),
            _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
        },
        _ => Err(TypeError("Trying to do arithmetic on non-numerical values.".to_owned()))
    }
}

fn eval_binary_expr(left_op: &Box<Exp>, right_op: &Box<Exp>, operator: &BinOp) -> Result<LuaValue> {
    // We cannot yet evaluate the operands as some binary operators are used to shortcut
    // evaluation.
    match *operator {
        BinOp::Plus => eval_arithmetic(left_op, right_op,
                                       |i, j| Ok(LuaValue::Integer(i+j)),
                                       |i, j| Ok(LuaValue::Float(i+j))),
        BinOp::Minus => eval_arithmetic(left_op, right_op,
                                        |i, j| Ok(LuaValue::Integer(i-j)),
                                        |i, j| Ok(LuaValue::Float(i-j))),
        BinOp::Mul => eval_arithmetic(left_op, right_op,
                                      |i, j| Ok(LuaValue::Integer(i*j)),
                                      |i, j| Ok(LuaValue::Float(i*j))),
        BinOp::Div => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float((i as f64)/(j as f64)))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i/j))
                                      }),
        BinOp::Mod => eval_arithmetic(left_op, right_op,
                                      |i, j| if j == 0 {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Integer(i % j))
                                      },
                                      |i, j| if j == 0. {
                                          Err(ArithmeticError("Dividing by 0".to_owned()))
                                      } else {
                                          Ok(LuaValue::Float(i - (i/j).floor()*j))
                                      }),
        BinOp::IntDiv => eval_arithmetic(left_op, right_op,
                                         |i, j| if j == 0 {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer(i/j))
                                         },
                                         |i, j| if j == 0. {
                                             Err(ArithmeticError("Dividing by 0".to_owned()))
                                         } else {
                                             Ok(LuaValue::Integer((i/j).floor() as isize))
                                         }),
        BinOp::Pow => eval_arithmetic(left_op, right_op,
                                         |i, j| Ok(LuaValue::Float((i as f64).powf(j as f64))),
                                         |i, j| Ok(LuaValue::Float(i.powf(j)))),
        _ => Ok(LuaValue::Nil)
    }
}

fn eval_expr(expr: &Exp) -> Result<LuaValue> {
    match *expr {
        Exp::Nil => Ok(LuaValue::Nil),
        Exp::Bool(val) => Ok(LuaValue::Boolean(val)),
        Exp::Num(val) => Ok(match val {
            nom_lua53::num::Numeral::Float(fl) => LuaValue::Float(fl),
            nom_lua53::num::Numeral::Int(i) => LuaValue::Integer(i),
        }),
        Exp::BinExp(ref left, ref op, ref right) => eval_binary_expr(left, right, &op),
        Exp::Str(ref s) => Ok(LuaValue::Str(lit_to_string(s))),
        _ => Ok(LuaValue::Nil),
    }
}

pub fn eval_file(input: &[u8]) {
    match parse_all(input) {
        ParseResult::Done(blk) => {
            for stmt in blk.stmts {
                println!("{:?}", stmt);
                match stmt {
                    Statement::LVarAssign(ass) => {
                        let values = ass.vals.expect("There should be some values. Why isn't there any value?!");
                        for (var, val) in ass.vars.iter().zip(values.iter()) {
                            let computed_value = eval_expr(val);
                            println!("Assigning {:?} to {:?}", computed_value, var);
                        }
                    }
                    Statement::Assignment(ass) => {
                        println!("Assigning {:?} to {:?}", ass.vals, ass.vars);
                    }
                    _ => {}
                }
            }
        }

        ParseResult::Error(rest, ss) => {
            println!("Error. statements == {:#?}", ss);
            println!("rest == '{}'", String::from_utf8_lossy(rest));
        }
    }
}

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

    #[test]
    fn mytest() {
    }
}
<|endoftext|>


<commit_before>extern crate pulldown_cmark;
extern crate svgbob;

use pulldown_cmark::Parser;
use pulldown_cmark::Event;
use pulldown_cmark::Tag;
use pulldown_cmark::Options;

pub fn parse(arg: &str)->String{
    let parser = Parser::new_ext(arg, Options::all());
    let mut start_bob = false;
    let mut bob_text = String::new();
    let parser = parser.map(|event| match event {
        Event::Text(text) => {
            if start_bob {
                bob_text.push_str(&text);
                Event::Text("".into())
            }else{
                Event::Text(text)
            }
        }
        Event::Start(tag) => {
            match tag{
                Tag::CodeBlock(ref lang) => {
                    if lang == "bob" {
                        start_bob = true;
                        bob_text.clear();
                        Event::Text("".into())
                    }else{
                        Event::Start(tag.clone())
                    }
                }
                _ => Event::Start(tag)
            }
        }
        Event::End(ref tag) => {
            match *tag{
                Tag::CodeBlock(ref lang) => {
                    if lang == "bob" {
                        start_bob = false;
                        let svg = svgbob::to_svg(&bob_text).to_string();
                        bob_text.clear();
                        Event::Html(svg.into())
                    }else{
                        Event::End(tag.clone())
                    }
                }
                _ => Event::End(tag.clone())
            }
        }
        _ => event
    });
    let mut html = String::new();
    pulldown_cmark::html::push_html(&mut html, parser);
    html
}

<commit_msg>cargo fmt<commit_after>extern crate pulldown_cmark;
extern crate svgbob;

use pulldown_cmark::Parser;
use pulldown_cmark::Event;
use pulldown_cmark::Tag;
use pulldown_cmark::Options;

pub fn parse(arg: &str) -> String {
    let parser = Parser::new_ext(arg, Options::all());
    let mut start_bob = false;
    let mut bob_text = String::new();
    let parser = parser.map(|event| match event {
        Event::Text(text) => {
            if start_bob {
                bob_text.push_str(&text);
                Event::Text("".into())
            } else {
                Event::Text(text)
            }
        }
        Event::Start(tag) => {
            match tag {
                Tag::CodeBlock(ref lang) => {
                    if lang == "bob" {
                        start_bob = true;
                        bob_text.clear();
                        Event::Text("".into())
                    } else {
                        Event::Start(tag.clone())
                    }
                }
                _ => Event::Start(tag),
            }
        }
        Event::End(ref tag) => {
            match *tag {
                Tag::CodeBlock(ref lang) => {
                    if lang == "bob" {
                        start_bob = false;
                        let svg = svgbob::to_svg(&bob_text).to_string();
                        bob_text.clear();
                        Event::Html(svg.into())
                    } else {
                        Event::End(tag.clone())
                    }
                }
                _ => Event::End(tag.clone()),
            }
        }
        _ => event,
    });
    let mut html = String::new();
    pulldown_cmark::html::push_html(&mut html, parser);
    html
}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]
#![deny(trivial_casts, unused_qualifications, unused_must_use)]
#[macro_use]
extern crate approx;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate combine;
extern crate crossbeam;
#[macro_use]
extern crate error_chain;
extern crate ieee754 as fp;
extern crate image as img;
extern crate itertools as it;
extern crate nalgebra as na;
extern crate num;
#[macro_use(slog_error, slog_warn, slog_info, slog_debug, slog_trace, slog_log, slog_record,
            slog_record_static, slog_b, slog_kv)]
extern crate slog;
#[macro_use]
extern crate slog_scope;
extern crate uuid;

use std::f32;
use std::ops::{Add, Mul, Sub};

use na::{Point2, Point3, Vector2, Vector3};
use na::core::Scalar;
use num::One;

mod api;
mod blockedarray;
mod block_queue;
pub mod bounds;
mod bsdf;
pub mod bvh;
pub mod camera;
mod cie;
pub mod display;
pub mod efloat;
pub mod filter;
pub mod film;
mod geometry;
pub mod integrator;
mod interaction;
pub mod imageio;
pub mod light;
pub mod lightdistrib;
pub mod material;
pub mod mipmap;
mod noise;
mod paramset;
pub mod parser;
pub mod primitive;
pub mod ray;
pub mod renderer;
mod rng;
pub mod sampling;
pub mod sampler;
pub mod scene;
pub mod shapes;
pub mod spectrum;
mod stats;
pub mod texture;
pub mod transform;

pub mod errors {
    use img;
    #[cfg(openexr)]
    use openexr;

    error_chain!{
        foreign_links {
            Io(::std::io::Error);
            ImgTga(img::ImageError);
            ImgExr(openexr::Error) #[cfg(openexr)];
        }
    }
}

pub type Vector2f = Vector2<f32>;
pub type Vector3f = Vector3<f32>;
pub type Point2f = Point2<f32>;
pub type Point2i = Point2<i32>;
pub type Point3f = Point3<f32>;
pub use transform::Transform;

pub const MACHINE_EPSILON: f32 = f32::EPSILON * 0.5;
pub fn gamma(n: u32) -> f32 {
    (n as f32 * MACHINE_EPSILON) / (1.0 - n as f32 * MACHINE_EPSILON)
}

/// Smallest representable float strictly less than 1
pub const ONE_MINUS_EPSILON: f32 = 0.99999994f32;

/// Linear interpolation between 2 values.
///
/// This version should be generic enough to linearly interpolate between 2 Spectrums using an f32
/// parameter.
pub fn lerp<S, T>(t: S, a: T, b: T) -> T
where
    S: One,
    S: Sub<S, Output = S>,
    S: Copy,
    T: Add<T, Output = T>,
    T: Mul<S, Output = T>,
{
    let one: S = num::one();
    a * (one - t) + b * t
}

/// Return the dimension index (0, 1 or 2) that contains the largest component.
pub fn max_dimension<T>(v: Vector3<T>) -> usize
where
    T: Scalar + PartialOrd,
{
    if v.x > v.y {
        if v.x > v.z {
            0
        } else {
            2
        }
    } else if v.y > v.z {
        1
    } else {
        2
    }
}

/// Permute the components of this vector based on the given indices for x, y and z.
pub fn permute_v<T>(v: &Vector3<T>, x: usize, y: usize, z: usize) -> Vector3<T>
where
    T: Scalar,
{
    Vector3::new(v[x], v[y], v[z])
}

/// Permute the components of this point based on the given indices for x, y and z.
pub fn permute_p<T>(v: &Point3<T>, x: usize, y: usize, z: usize) -> Point3<T>
where
    T: Scalar,
{
    Point3::new(v[x], v[y], v[z])
}

/// Create an orthogonal coordinate system from a single vector.
pub fn coordinate_system(v1: &Vector3f) -> (Vector3f, Vector3f) {
    let v2 = if v1.x.abs() > v1.y.abs() {
        Vector3::new(-v1.z, 0.0, v1.x) / (v1.x * v1.x + v1.z * v1.z).sqrt()
    } else {
        Vector3::new(0.0, v1.z, v1.y) / (v1.y * v1.y + v1.z * v1.z).sqrt()
    };

    let v3 = v1.cross(&v2);

    (v2, v3)
}

// TODO does this exist in std?
pub fn find_interval<P>(size: usize, pred: P) -> usize
where
    P: Fn(usize) -> bool,
{
    let mut first = 0;
    let mut len = size;
    while len > 0 {
        let half = len >> 1;
        let middle = first + half;
        // Bisect range based on value of _pred_ at _middle_
        if pred(middle) {
            first = middle + 1;
            len -= half + 1;
        } else {
            len = half;
        }
    }
    na::clamp(first - 1, 0, size - 2)
}

/// Version of min() that works on PartialOrd, so it works for both u32 and f32.
pub fn min<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.lt(&b) {
        a
    } else {
        b
    }
}

/// Version of max() that works on PartialOrd, so it works for both u32 and f32.
pub fn max<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.gt(&b) {
        a
    } else {
        b
    }
}

#[inline]
pub fn is_power_of_2(v: i32) -> bool {
    (v != 0) && (v & (v - 1)) == 0
}

#[inline]
pub fn round_up_pow_2(v: i32) -> i32 {
    let mut v = v;
    v -= 1;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v + 1
}

#[test]
fn test_gamma() {
    let g5 = gamma(5);
    let p = Point3f::new(-0.4, 0.9, 0.2);
    let v = g5 * &p.coords.abs();
    println!("gamma(5) = {}, p={:?}, v={:?}", gamma(5), p, v);
}

#[test]
fn test_is_power_of_2() {
    assert!(is_power_of_2(4));
    assert!(is_power_of_2(8));
    assert!(is_power_of_2(1024));
    assert!(!is_power_of_2(3));
    assert!(!is_power_of_2(7));
}

#[test]
fn test_round_up_pow_2() {
    assert_eq!(round_up_pow_2(1023), 1024);
    assert_eq!(round_up_pow_2(1024), 1024);
}
<commit_msg>Add Normal3<T> type as newtype over Vector3<T><commit_after>#![recursion_limit = "1024"]
#![deny(trivial_casts, unused_qualifications, unused_must_use)]
#[macro_use]
extern crate approx;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate combine;
extern crate crossbeam;
#[macro_use]
extern crate error_chain;
extern crate ieee754 as fp;
extern crate image as img;
extern crate itertools as it;
extern crate nalgebra as na;
extern crate num;
#[macro_use(slog_error, slog_warn, slog_info, slog_debug, slog_trace, slog_log, slog_record,
            slog_record_static, slog_b, slog_kv)]
extern crate slog;
#[macro_use]
extern crate slog_scope;
extern crate uuid;

use std::f32;
use std::fmt::Debug;
use std::ops::{Add, Deref, Mul, Sub};

use na::{Point2, Point3, Vector2, Vector3};
use na::core::Scalar;
use num::One;

mod api;
mod blockedarray;
mod block_queue;
pub mod bounds;
mod bsdf;
pub mod bvh;
pub mod camera;
mod cie;
pub mod display;
pub mod efloat;
pub mod filter;
pub mod film;
mod geometry;
pub mod integrator;
mod interaction;
pub mod imageio;
pub mod light;
pub mod lightdistrib;
pub mod material;
pub mod mipmap;
mod noise;
mod paramset;
pub mod parser;
pub mod primitive;
pub mod ray;
pub mod renderer;
mod rng;
pub mod sampling;
pub mod sampler;
pub mod scene;
pub mod shapes;
pub mod spectrum;
mod stats;
pub mod texture;
pub mod transform;

pub mod errors {
    use img;
    #[cfg(openexr)]
    use openexr;

    error_chain!{
        foreign_links {
            Io(::std::io::Error);
            ImgTga(img::ImageError);
            ImgExr(openexr::Error) #[cfg(openexr)];
        }
    }
}

pub type Vector2f = Vector2<f32>;
pub type Vector3f = Vector3<f32>;
pub type Point2f = Point2<f32>;
pub type Point2i = Point2<i32>;
pub type Point3f = Point3<f32>;
// `Normal` is just a newtype of a `Vector`
pub struct Normal3<T: Copy + Debug + PartialEq + 'static>(Vector3<T>);
impl<T: Copy + Debug + PartialEq + 'static> Deref for Normal3<T> {
    type Target = Vector3<T>;

    fn deref(&self) -> &Vector3<T> {
        &self.0
    }
}
pub type Normal3f = Normal3<f32>;

pub use transform::Transform;

pub const MACHINE_EPSILON: f32 = f32::EPSILON * 0.5;
pub fn gamma(n: u32) -> f32 {
    (n as f32 * MACHINE_EPSILON) / (1.0 - n as f32 * MACHINE_EPSILON)
}

/// Smallest representable float strictly less than 1
pub const ONE_MINUS_EPSILON: f32 = 0.99999994f32;

/// Linear interpolation between 2 values.
///
/// This version should be generic enough to linearly interpolate between 2 Spectrums using an f32
/// parameter.
pub fn lerp<S, T>(t: S, a: T, b: T) -> T
where
    S: One,
    S: Sub<S, Output = S>,
    S: Copy,
    T: Add<T, Output = T>,
    T: Mul<S, Output = T>,
{
    let one: S = num::one();
    a * (one - t) + b * t
}

/// Return the dimension index (0, 1 or 2) that contains the largest component.
pub fn max_dimension<T>(v: Vector3<T>) -> usize
where
    T: Scalar + PartialOrd,
{
    if v.x > v.y {
        if v.x > v.z {
            0
        } else {
            2
        }
    } else if v.y > v.z {
        1
    } else {
        2
    }
}

/// Permute the components of this vector based on the given indices for x, y and z.
pub fn permute_v<T>(v: &Vector3<T>, x: usize, y: usize, z: usize) -> Vector3<T>
where
    T: Scalar,
{
    Vector3::new(v[x], v[y], v[z])
}

/// Permute the components of this point based on the given indices for x, y and z.
pub fn permute_p<T>(v: &Point3<T>, x: usize, y: usize, z: usize) -> Point3<T>
where
    T: Scalar,
{
    Point3::new(v[x], v[y], v[z])
}

/// Create an orthogonal coordinate system from a single vector.
pub fn coordinate_system(v1: &Vector3f) -> (Vector3f, Vector3f) {
    let v2 = if v1.x.abs() > v1.y.abs() {
        Vector3::new(-v1.z, 0.0, v1.x) / (v1.x * v1.x + v1.z * v1.z).sqrt()
    } else {
        Vector3::new(0.0, v1.z, v1.y) / (v1.y * v1.y + v1.z * v1.z).sqrt()
    };

    let v3 = v1.cross(&v2);

    (v2, v3)
}

// TODO does this exist in std?
pub fn find_interval<P>(size: usize, pred: P) -> usize
where
    P: Fn(usize) -> bool,
{
    let mut first = 0;
    let mut len = size;
    while len > 0 {
        let half = len >> 1;
        let middle = first + half;
        // Bisect range based on value of _pred_ at _middle_
        if pred(middle) {
            first = middle + 1;
            len -= half + 1;
        } else {
            len = half;
        }
    }
    na::clamp(first - 1, 0, size - 2)
}

/// Version of min() that works on PartialOrd, so it works for both u32 and f32.
pub fn min<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.lt(&b) {
        a
    } else {
        b
    }
}

/// Version of max() that works on PartialOrd, so it works for both u32 and f32.
pub fn max<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.gt(&b) {
        a
    } else {
        b
    }
}

#[inline]
pub fn is_power_of_2(v: i32) -> bool {
    (v != 0) && (v & (v - 1)) == 0
}

#[inline]
pub fn round_up_pow_2(v: i32) -> i32 {
    let mut v = v;
    v -= 1;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v + 1
}

#[test]
fn test_gamma() {
    let g5 = gamma(5);
    let p = Point3f::new(-0.4, 0.9, 0.2);
    let v = g5 * &p.coords.abs();
    println!("gamma(5) = {}, p={:?}, v={:?}", gamma(5), p, v);
}

#[test]
fn test_is_power_of_2() {
    assert!(is_power_of_2(4));
    assert!(is_power_of_2(8));
    assert!(is_power_of_2(1024));
    assert!(!is_power_of_2(3));
    assert!(!is_power_of_2(7));
}

#[test]
fn test_round_up_pow_2() {
    assert_eq!(round_up_pow_2(1023), 1024);
    assert_eq!(round_up_pow_2(1024), 1024);
}
<|endoftext|>


<commit_before>//! Geometric types for 2D lattice-shaped puzzles.

#![feature(associated_consts)]

use std::ops::{Add, Mul, Sub, Neg, Index, IndexMut, Range};

/// A two-dimensional lattice point.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Point(pub i32, pub i32);

/// A size of a rectangle.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Size(pub i32, pub i32);

/// A difference between two `Point`s.
///
/// `Point(y0, x0)` - `Point(y1, x1) == `Move(y0 - y1, x0 - x1)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Move(pub i32, pub i32);

/// A 2x2 rotation matrix.
///
/// `Rotation(yy, yx, xy, xx) * Move(y, x) == Move(yy*y + yx*x, xy*y + xx*x)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Rotation(i32, i32, i32, i32);

impl Move {
    /// An upward `Move` vector.
    pub const UP:    Move = Move(-1, 0);

    /// A rightward `Move` vector.
    pub const RIGHT: Move = Move(0, 1);

    /// A downward `Move` vector.
    pub const DOWN:  Move = Move(1, 0);

    /// A leftward `Move` vector.
    pub const LEFT:  Move = Move(0, -1);

    /// `Move` vectors that is toward four adjacent points.
    pub const ALL_DIRECTIONS: [Move; 4] = [Move::UP, Move::RIGHT, Move::DOWN, Move::LEFT];

    /// `Move` vectors that is toward eight adjacent points.
    pub const ALL_ADJACENTS: [Move; 8] = [
        Move::UP, Move(-1, 1), Move::RIGHT, Move(1, 1),
        Move::DOWN, Move(1, -1), Move::LEFT, Move(-1, -1)];
}

impl Add<Move> for Point {
    type Output = Point;

    #[inline]
    fn add(self, other: Move) -> Point {
        Point(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Point> for Point {
    type Output = Move;

    #[inline]
    fn sub(self, other: Point) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Add<Move> for Move {
    type Output = Move;

    #[inline]
    fn add(self, other: Move) -> Move {
        Move(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Move> for Move {
    type Output = Move;

    #[inline]
    fn sub(self, other: Move) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Neg for Move {
    type Output = Move;

    #[inline]
    fn neg(self) -> Move {
        Move(-self.0, -self.1)
    }
}

impl Mul<i32> for Move {
    type Output = Move;

    #[inline]
    fn mul(self, other: i32) -> Move {
        Move(self.0 * other, self.1 * other)
    }
}

impl Rotation {
    /// A 0-degree `Rotation` to the left (counterclockwise).
    pub const CCW0:   Rotation = Rotation(1, 0, 0, 1);

    /// A 90-degree `Rotation` to the left (counterclockwise).
    pub const CCW90:  Rotation = Rotation(0, -1, 1, 0);

    /// A 180-degree `Rotation` to the left (counterclockwise).
    pub const CCW180: Rotation = Rotation(-1, 0, 0, -1);

    /// A 270-degree `Rotation` to the left (counterclockwise).
    pub const CCW270: Rotation = Rotation(0, 1, -1, 0);

    /// Flip horizontal.
    pub const H_FLIP: Rotation = Rotation(1, 0, 0, -1);

    /// Flip vertical.
    pub const V_FLIP: Rotation = Rotation(-1, 0, 0, 1);
}

impl Mul<Rotation> for Rotation {
    type Output = Rotation;

    #[inline]
    fn mul(self, other: Rotation) -> Rotation {
        Rotation(self.0 * other.0 + self.1 * other.2,
                 self.0 * other.1 + self.1 * other.3,
                 self.2 * other.0 + self.3 * other.2,
                 self.2 * other.1 + self.3 * other.3)
    }
}

impl Mul<Move> for Rotation {
    type Output = Move;

    #[inline]
    fn mul(self, other: Move) -> Move {
        Move(self.0 * other.0 + self.1 * other.1,
             self.2 * other.0 + self.3 * other.1)
    }
}

/// An ID identifying a cell in lattice rectangle.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CellId(usize);

impl CellId {
    /// An ID being given to cells on outside the rectangle.
    pub const OUTSIDE: CellId = CellId(0);

    /// Creates a new `CellId` from an ID.
    #[inline]
    pub fn new(id: usize) -> CellId {
        CellId(id)
    }

    /// Gets an ID of the cell.
    #[inline]
    pub fn id(self) -> usize {
        self.0
    }

    /// Returns if the cell is on outside of the rectangle.
    #[inline]
    pub fn is_outside(self) -> bool {
        self == CellId::OUTSIDE
    }
}

/// A representative `Point` of points on outside the rectangle.
const OUTSIDE_POINT: Point = Point(-1, -1);

/// A rectangle area.
pub trait Geom {
    /// Returns the rectangle's size.
    #[inline]
    fn size(&self) -> Size;

    /// Returns the number of the rectangle's rows.
    #[inline]
    fn row(&self) -> i32 {
        self.size().0
    }

    /// Returns the number of the rectangle's columns.
    #[inline]
    fn column(&self) -> i32 {
        self.size().1
    }

    /// Returns the cell length which is contained in the rectangle.
    #[inline]
    fn cell_len(&self) -> usize {
        (self.row() * self.column() + 1) as usize
    }

    /// Returns true if the point is contained in the rectangle.
    #[inline]
    fn contains(&self, p: Point) -> bool {
        let size = self.size();
        0 <= p.0 && p.0 < size.0 && 0 <= p.1 && p.1 < size.1
    }

    /// Convert a point to a corresponding cell ID.
    #[inline]
    fn point_to_cellid(&self, p: Point) -> CellId {
        if self.contains(p) {
            CellId::new((p.0 * self.column() + p.1 + 1) as usize)
        } else {
            CellId::OUTSIDE
        }
    }

    /// Convert a cell ID to a corresponding point.
    #[inline]
    fn cellid_to_point(&self, id: CellId) -> Point {
        if id.is_outside() {
            OUTSIDE_POINT
        } else {
            let idx = id.id() - 1;
            Point((idx as i32) / self.column(), (idx as i32) % self.column())
        }
    }

    /// Returns an iterator iterating all points.
    #[inline]
    fn points(&self) -> Points {
        if self.row() > 0 && self.column() > 0 {
            Points {
                point: Some(Point(0, 0)),
                size: self.size(),
            }
        } else {
            Points {
                point: None,
                size: self.size(),
            }
        }
    }

    /// Returns an iterator iterating all points in the row.
    #[inline]
    fn points_in_row(&self, row: i32) -> PointsInRow {
        PointsInRow {
            row: row,
            columns: 0..self.column(),
        }
    }

    /// Returns an iterator iterating all points in the column.
    #[inline]
    fn points_in_column(&self, column: i32) -> PointsInColumn {
        PointsInColumn {
            column: column,
            rows: 0..self.row(),
        }
    }
}

/// An iterator iterating all points in the rectangle.
#[derive(Copy, Clone, Debug)]
pub struct Points {
    point: Option<Point>,
    size: Size,
}

impl Iterator for Points {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(cur) = self.point {
            let mut next = cur;
            let mut end = false;
            next.1 += 1;
            if next.1 >= self.size.1 {
                next.0 += 1;
                next.1 = 0;
                if next.0 >= self.size.0 {
                    end = true;
                }
            }
            if !end {
                self.point = Some(next);
            } else {
                self.point = None;
            }
            return Some(cur);
        }
        None
    }
}

/// An iterator iterating all points in a row of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInRow {
    row: i32,
    columns: Range<i32>,
}

impl Iterator for PointsInRow {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(c) = self.columns.next() {
            Some(Point(self.row, c))
        } else {
            None
        }
    }
}

/// An iterator iterating all points in a column of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInColumn {
    rows: Range<i32>,
    column: i32,
}

impl Iterator for PointsInColumn {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(r) = self.rows.next() {
            Some(Point(r, self.column))
        } else {
            None
        }
    }
}

/// A table that stores values for each cells.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Table<T> {
    size: Size,
    data: Vec<T>,
}

impl<T> Table<T> {
    /// Creates a new table with data.
    #[inline]
    pub fn new(size: Size, outside: T, mut data: Vec<T>) -> Table<T> {
        assert_eq!((size.0 * size.1) as usize, data.len());
        data.insert(0, outside);
        Table {
            size: size,
            data: data,
        }
    }

    /// Creates a new empty table.
    #[inline]
    pub fn new_empty(size: Size, outside: T, init: T) -> Table<T>
        where T: Clone
    {
        let data = vec![init; (size.0 * size.1) as usize];
        Table::new(size, outside, data)
    }
}

impl<T> Geom for Table<T> {
    #[inline]
    fn size(&self) -> Size {
        self.size
    }
}

impl<T> Index<Point> for Table<T> {
    type Output = T;

    #[inline]
    fn index(&self, p: Point) -> &T {
        let idx = self.point_to_cellid(p).id();
        &self.data[idx]
    }
}

impl<T> IndexMut<Point> for Table<T> {
    #[inline]
    fn index_mut(&mut self, p: Point) -> &mut T {
        let idx = self.point_to_cellid(p).id();
        &mut self.data[idx]
    }
}

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

    struct Rect(Size);
    impl Geom for Rect {
        fn size(&self) -> Size {
            self.0
        }
    }

    #[test]
    fn points() {
        let pts = [Point(0, 0),
                   Point(0, 1),
                   Point(0, 2),
                   Point(1, 0),
                   Point(1, 1),
                   Point(1, 2),
                   Point(2, 0),
                   Point(2, 1),
                   Point(2, 2),
                   Point(3, 0),
                   Point(3, 1),
                   Point(3, 2)];
        let rect = Rect(Size(4, 3));
        assert_eq!(&pts[..], &rect.points().collect::<Vec<_>>()[..]);
    }

    #[test]
    fn rotate_mat() {
        let mat = [Rotation::CCW0, Rotation::CCW90, Rotation::CCW180, Rotation::CCW270];
        for i in 0..mat.len() {
            for j in 0..mat.len() {
                assert_eq!(mat[(i + j) % mat.len()], mat[i] * mat[j]);
            }
        }
    }

    #[test]
    fn rotate_point() {
        let mat = [Rotation::CCW0, Rotation::CCW90, Rotation::CCW180, Rotation::CCW270];
        let vec = [[Move::UP, Move::LEFT, Move::DOWN, Move::RIGHT],
                   [Move::UP + Move::RIGHT,
                    Move::LEFT + Move::UP,
                    Move::DOWN + Move::LEFT,
                    Move::RIGHT + Move::DOWN]];
        for i in 0..mat.len() {
            for v in &vec {
                for j in 0..v.len() {
                    assert_eq!(v[(i + j) % v.len()], mat[i] * v[j]);
                }
            }
        }
    }
}
<commit_msg>Stop using associated constsnts<commit_after>//! Geometric types for 2D lattice-shaped puzzles.

use std::ops::{Add, Mul, Sub, Neg, Index, IndexMut, Range};

/// A two-dimensional lattice point.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Point(pub i32, pub i32);

/// A size of a rectangle.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Size(pub i32, pub i32);

/// A difference between two `Point`s.
///
/// `Point(y0, x0)` - `Point(y1, x1) == `Move(y0 - y1, x0 - x1)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Move(pub i32, pub i32);

/// A 2x2 rotation matrix.
///
/// `Rotation(yy, yx, xy, xx) * Move(y, x) == Move(yy*y + yx*x, xy*y + xx*x)`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Rotation(i32, i32, i32, i32);

/// An upward `Move` vector.
pub const MOVE_UP: Move = Move(-1, 0);

/// A rightward `Move` vector.
pub const MOVE_RIGHT: Move = Move(0, 1);

/// A downward `Move` vector.
pub const MOVE_DOWN: Move = Move(1, 0);

/// A leftward `Move` vector.
pub const MOVE_LEFT: Move = Move(0, -1);

/// `Move` vectors that is toward four adjacent points.
pub const MOVE_ALL_DIRECTIONS: [Move; 4] = [MOVE_UP, MOVE_RIGHT, MOVE_DOWN, MOVE_LEFT];

/// `Move` vectors that is toward eight adjacent points.
pub const MOVE_ALL_ADJACENTS: [Move; 8] =
    [MOVE_UP, Move(-1, 1), MOVE_RIGHT, Move(1, 1), MOVE_DOWN, Move(1, -1), MOVE_LEFT, Move(-1, -1)];

impl Add<Move> for Point {
    type Output = Point;

    #[inline]
    fn add(self, other: Move) -> Point {
        Point(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Point> for Point {
    type Output = Move;

    #[inline]
    fn sub(self, other: Point) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Add<Move> for Move {
    type Output = Move;

    #[inline]
    fn add(self, other: Move) -> Move {
        Move(self.0 + other.0, self.1 + other.1)
    }
}

impl Sub<Move> for Move {
    type Output = Move;

    #[inline]
    fn sub(self, other: Move) -> Move {
        Move(self.0 - other.0, self.1 - other.1)
    }
}

impl Neg for Move {
    type Output = Move;

    #[inline]
    fn neg(self) -> Move {
        Move(-self.0, -self.1)
    }
}

impl Mul<i32> for Move {
    type Output = Move;

    #[inline]
    fn mul(self, other: i32) -> Move {
        Move(self.0 * other, self.1 * other)
    }
}

/// A 0-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW0: Rotation = Rotation(1, 0, 0, 1);

/// A 90-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW90: Rotation = Rotation(0, -1, 1, 0);

/// A 180-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW180: Rotation = Rotation(-1, 0, 0, -1);

/// A 270-degree `Rotation` to the left (counterclockwise).
pub const ROT_CCW270: Rotation = Rotation(0, 1, -1, 0);

/// Flip horizontal.
pub const ROT_H_FLIP: Rotation = Rotation(1, 0, 0, -1);

/// Flip vertical.
pub const ROT_V_FLIP: Rotation = Rotation(-1, 0, 0, 1);

impl Mul<Rotation> for Rotation {
    type Output = Rotation;

    #[inline]
    fn mul(self, other: Rotation) -> Rotation {
        Rotation(self.0 * other.0 + self.1 * other.2,
                 self.0 * other.1 + self.1 * other.3,
                 self.2 * other.0 + self.3 * other.2,
                 self.2 * other.1 + self.3 * other.3)
    }
}

impl Mul<Move> for Rotation {
    type Output = Move;

    #[inline]
    fn mul(self, other: Move) -> Move {
        Move(self.0 * other.0 + self.1 * other.1,
             self.2 * other.0 + self.3 * other.1)
    }
}

/// An ID identifying a cell in lattice rectangle.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct CellId(usize);

/// An ID being given to cells on outside the rectangle.
pub const CELL_ID_OUTSIDE: CellId = CellId(0);

impl CellId {
    /// Creates a new `CellId` from an ID.
    #[inline]
    pub fn new(id: usize) -> CellId {
        CellId(id)
    }

    /// Gets an ID of the cell.
    #[inline]
    pub fn id(self) -> usize {
        self.0
    }

    /// Returns if the cell is on outside of the rectangle.
    #[inline]
    pub fn is_outside(self) -> bool {
        self == CELL_ID_OUTSIDE
    }
}

/// A representative `Point` of points on outside the rectangle.
const OUTSIDE_POINT: Point = Point(-1, -1);

/// A rectangle area.
pub trait Geom {
    /// Returns the rectangle's size.
    #[inline]
    fn size(&self) -> Size;

    /// Returns the number of the rectangle's rows.
    #[inline]
    fn row(&self) -> i32 {
        self.size().0
    }

    /// Returns the number of the rectangle's columns.
    #[inline]
    fn column(&self) -> i32 {
        self.size().1
    }

    /// Returns the cell length which is contained in the rectangle.
    #[inline]
    fn cell_len(&self) -> usize {
        (self.row() * self.column() + 1) as usize
    }

    /// Returns true if the point is contained in the rectangle.
    #[inline]
    fn contains(&self, p: Point) -> bool {
        let size = self.size();
        0 <= p.0 && p.0 < size.0 && 0 <= p.1 && p.1 < size.1
    }

    /// Convert a point to a corresponding cell ID.
    #[inline]
    fn point_to_cellid(&self, p: Point) -> CellId {
        if self.contains(p) {
            CellId::new((p.0 * self.column() + p.1 + 1) as usize)
        } else {
            CELL_ID_OUTSIDE
        }
    }

    /// Convert a cell ID to a corresponding point.
    #[inline]
    fn cellid_to_point(&self, id: CellId) -> Point {
        if id.is_outside() {
            OUTSIDE_POINT
        } else {
            let idx = id.id() - 1;
            Point((idx as i32) / self.column(), (idx as i32) % self.column())
        }
    }

    /// Returns an iterator iterating all points.
    #[inline]
    fn points(&self) -> Points {
        if self.row() > 0 && self.column() > 0 {
            Points {
                point: Some(Point(0, 0)),
                size: self.size(),
            }
        } else {
            Points {
                point: None,
                size: self.size(),
            }
        }
    }

    /// Returns an iterator iterating all points in the row.
    #[inline]
    fn points_in_row(&self, row: i32) -> PointsInRow {
        PointsInRow {
            row: row,
            columns: 0..self.column(),
        }
    }

    /// Returns an iterator iterating all points in the column.
    #[inline]
    fn points_in_column(&self, column: i32) -> PointsInColumn {
        PointsInColumn {
            column: column,
            rows: 0..self.row(),
        }
    }
}

/// An iterator iterating all points in the rectangle.
#[derive(Copy, Clone, Debug)]
pub struct Points {
    point: Option<Point>,
    size: Size,
}

impl Iterator for Points {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(cur) = self.point {
            let mut next = cur;
            let mut end = false;
            next.1 += 1;
            if next.1 >= self.size.1 {
                next.0 += 1;
                next.1 = 0;
                if next.0 >= self.size.0 {
                    end = true;
                }
            }
            if !end {
                self.point = Some(next);
            } else {
                self.point = None;
            }
            return Some(cur);
        }
        None
    }
}

/// An iterator iterating all points in a row of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInRow {
    row: i32,
    columns: Range<i32>,
}

impl Iterator for PointsInRow {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(c) = self.columns.next() {
            Some(Point(self.row, c))
        } else {
            None
        }
    }
}

/// An iterator iterating all points in a column of the rectangle.
#[derive(Clone, Debug)]
pub struct PointsInColumn {
    rows: Range<i32>,
    column: i32,
}

impl Iterator for PointsInColumn {
    type Item = Point;

    #[inline]
    fn next(&mut self) -> Option<Point> {
        if let Some(r) = self.rows.next() {
            Some(Point(r, self.column))
        } else {
            None
        }
    }
}

/// A table that stores values for each cells.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Table<T> {
    size: Size,
    data: Vec<T>,
}

impl<T> Table<T> {
    /// Creates a new table with data.
    #[inline]
    pub fn new(size: Size, outside: T, mut data: Vec<T>) -> Table<T> {
        assert_eq!((size.0 * size.1) as usize, data.len());
        data.insert(0, outside);
        Table {
            size: size,
            data: data,
        }
    }

    /// Creates a new empty table.
    #[inline]
    pub fn new_empty(size: Size, outside: T, init: T) -> Table<T>
        where T: Clone
    {
        let data = vec![init; (size.0 * size.1) as usize];
        Table::new(size, outside, data)
    }
}

impl<T> Geom for Table<T> {
    #[inline]
    fn size(&self) -> Size {
        self.size
    }
}

impl<T> Index<Point> for Table<T> {
    type Output = T;

    #[inline]
    fn index(&self, p: Point) -> &T {
        let idx = self.point_to_cellid(p).id();
        &self.data[idx]
    }
}

impl<T> IndexMut<Point> for Table<T> {
    #[inline]
    fn index_mut(&mut self, p: Point) -> &mut T {
        let idx = self.point_to_cellid(p).id();
        &mut self.data[idx]
    }
}

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

    struct Rect(Size);
    impl Geom for Rect {
        fn size(&self) -> Size {
            self.0
        }
    }

    #[test]
    fn points() {
        let pts = [Point(0, 0),
                   Point(0, 1),
                   Point(0, 2),
                   Point(1, 0),
                   Point(1, 1),
                   Point(1, 2),
                   Point(2, 0),
                   Point(2, 1),
                   Point(2, 2),
                   Point(3, 0),
                   Point(3, 1),
                   Point(3, 2)];
        let rect = Rect(Size(4, 3));
        assert_eq!(&pts[..], &rect.points().collect::<Vec<_>>()[..]);
    }

    #[test]
    fn rotate_mat() {
        let mat = [ROT_CCW0, ROT_CCW90, ROT_CCW180, ROT_CCW270];
        for i in 0..mat.len() {
            for j in 0..mat.len() {
                assert_eq!(mat[(i + j) % mat.len()], mat[i] * mat[j]);
            }
        }
    }

    #[test]
    fn rotate_point() {
        let mat = [ROT_CCW0, ROT_CCW90, ROT_CCW180, ROT_CCW270];
        let vec = [[MOVE_UP, MOVE_LEFT, MOVE_DOWN, MOVE_RIGHT],
                   [MOVE_UP + MOVE_RIGHT,
                    MOVE_LEFT + MOVE_UP,
                    MOVE_DOWN + MOVE_LEFT,
                    MOVE_RIGHT + MOVE_DOWN]];
        for i in 0..mat.len() {
            for v in &vec {
                for j in 0..v.len() {
                    assert_eq!(v[(i + j) % v.len()], mat[i] * v[j]);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::fmt;

extern crate ansi_term;
extern crate image;
use self::ansi_term::Color as AnsiColor;

/// Type that associates a PixelColor to how many pixels in image
pub type ColorCounts = Vec<(PixelColor, usize)>;

/// A single color within an image, stored in rgb
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub struct PixelColor {
    r: u8,
    g: u8,
    b: u8,
}

/// Provides hex output for PixelColor
impl fmt::UpperHex for PixelColor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut ret = 0;
        ret = ret << 8 | self.r as u32;
        ret = ret << 8 | self.g as u32;
        ret = ret << 8 | self.b as u32;
        write!(f, "{:X}", ret)
    }
}

/// Returns a ColorCounts pixel to count vector
///
/// # Arguments
///
/// * 'filename' - A String reference to the filename of the picture
/// * 'depth' - How many pixels from the image to ingest
///
/// # Example
/// ```rust,no_run
/// # use std::path::Path;
/// let _colors = image_colors::fetch_colors(&Path::new("path/to/file.jpg"), 5);
/// ```
pub fn fetch_colors(filepath: &Path, depth: usize) -> ColorCounts {
    let img = image::open(filepath).expect("File couldn't be opened!");
    let raw_pixels = img.raw_pixels();
    let raw_pixels_size = raw_pixels.len();
    let mut pixel_map = HashMap::new();

    let mut i = 0;
    while i < raw_pixels_size - 3 {
        let color = PixelColor {
            r: raw_pixels[i],
            g: raw_pixels[i + 1],
            b: raw_pixels[i + 2],
        };
        *pixel_map.entry(color).or_insert(0) += 1;
        i += depth * 3;
    }

    Vec::from_iter(pixel_map)
}

/// Sorts a ColorCounts type by number of pixels
///
/// # Arguments
///
/// * 'colors' - A ColorCounts vector of colors to pixel count
/// * 'num_colors' - How many colors to return
///
/// # Example
/// ```rust,no_run
/// # use std::path::Path;
/// let _colors = image_colors::fetch_colors(&Path::new("path/to/file.jpg"), 5);
/// let _sorted_colors = image_colors::sort_colors(&_colors, 5);
/// ```
pub fn sort_colors(colors: &ColorCounts, num_colors: usize) -> ColorCounts {
    let mut color_copy = colors.clone();
    color_copy.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
    let pixels = if num_colors > color_copy.len() {
        color_copy.to_vec()
    } else {
        color_copy[0..num_colors].to_vec()
    };
    pixels
}

/// Prints colors from ColorCounts vector, options to use color, a delimiter, or print in rgb, not
/// hex.
///
/// # Arguments
///
/// * 'colors' - A ColorCounts vector of colors to pixel count
/// * 'with_ansi_color' - Print with color?
/// * 'delimiter' - Delimiter to be used when printing ColorCounts
/// * 'with_rgb' - Print in rgb
///
/// # Example
/// ```rust,no_run
/// # use std::path::Path;
/// let _colors = image_colors::fetch_colors(&Path::new("path/to/file.jpg"), 5);
/// let sorted_colors = image_colors::sort_colors(&_colors, 5);
/// image_colors::print_colors(sorted_colors, false, " - ", false);
/// ```
pub fn print_colors(colors: ColorCounts, with_ansi_color: bool, delimiter: &str, with_rgb: bool) {
    for (color, count) in colors {
        let display_color = if with_rgb {
            format!("r:{} g:{} b:{}", color.r, color.g, color.b)
        } else {
            format!("#{:X}", color)
        };

        if with_ansi_color {
            let ansi_color = AnsiColor::RGB(color.r, color.g, color.b);
            println!(
                "{} {}{}{}",
                ansi_color.paint("█"),
                display_color,
                delimiter,
                count
            );
        } else {
            println!("{}{}{}", display_color, delimiter, count);
        }
    }
}

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

    fn set_crate_dir() {
        let current = std::env::current_exe().unwrap();
        let deps = current.parent().unwrap();
        let debug = deps.parent().unwrap();
        let target = debug.parent().unwrap();
        let base = target.parent().unwrap();
        assert!(std::env::set_current_dir(base).is_ok());
    }

    #[test]
    fn fetch_tests_dir() {
        set_crate_dir();
        assert!(std::env::current_dir().unwrap().join("tests").is_dir());
    }

    #[test]
    fn fetch_test_image() {
        set_crate_dir();
        assert!(
            std::env::current_dir()
                .unwrap()
                .join("tests")
                .join("diamond.png")
                .is_file()
        )
    }

    #[test]
    fn basic_sort_colors() {
        set_crate_dir();
        let colors = fetch_colors(
            &Path::new(
                std::env::current_dir()
                    .unwrap()
                    .join("tests")
                    .join("diamond.png")
                    .as_path(),
            ),
            10,
        );
        let sorted_colors = sort_colors(&colors, 5);
        assert_eq!(sorted_colors.len(), 5);
        let pixel_colors = sorted_colors.get(0).unwrap().0.clone();
        let pixel_count = sorted_colors.get(0).unwrap().1.clone();
        assert_eq!(pixel_colors.r, 40);
        assert_eq!(pixel_colors.g, 44);
        assert_eq!(pixel_colors.b, 52);
        assert_eq!(pixel_count, 805174);
    }
}
<commit_msg>refractored fetch_colors for filename and image object<commit_after>use std::path::Path;
use std::collections::HashMap;
use std::iter::FromIterator;
use std::fmt;

extern crate ansi_term;
extern crate image;

use self::ansi_term::Color as AnsiColor;
use self::image::DynamicImage;

/// Type that associates a PixelColor to how many pixels in image
pub type ColorCounts = Vec<(PixelColor, usize)>;

/// A single color within an image, stored in rgb
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub struct PixelColor {
    r: u8,
    g: u8,
    b: u8,
}

/// Provides hex output for PixelColor
impl fmt::UpperHex for PixelColor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut ret = 0;
        ret = ret << 8 | self.r as u32;
        ret = ret << 8 | self.g as u32;
        ret = ret << 8 | self.b as u32;
        write!(f, "{:X}", ret)
    }
}

/// Returns a ColorCounts pixel to count vector for a file
///
/// # Arguments
///
/// * 'filename' - A String reference to the filename of the picture
/// * 'depth' - How many pixels from the image to ingest
///
/// # Example
/// ```rust,no_run
/// # use std::path::Path;
/// let _colors = image_colors::fetch_colors(&Path::new("path/to/file.jpg"), 5);
/// ```
pub fn fetch_colors(filepath: &Path, depth: usize) -> ColorCounts {
     let img = image::open(filepath).expect("File couldn't be opened!");
     fetch_colors_img(img, depth)
}
/// Returns a ColorCounts pixel to count vector for a image
///
/// # Arguments
///
/// * 'image' - A Image reference to the instance of the picture
/// * 'depth' - How many pixels from the image to ingest
///
/// # Example
/// ```rust,no_run
/// # extern crate image;
/// # extern crate image_colors;
/// # use std::path::Path;
/// fn main() {
///     let filepath = &Path::new("path/to/file.jpg");
///     let img = image::open(filepath).expect("File couldn't be opened!");
///     let _colors = image_colors::fetch_colors_img(img, 5);
/// }
/// ```
pub fn fetch_colors_img(img: DynamicImage, depth: usize) -> ColorCounts {
    let raw_pixels = img.raw_pixels();
    let raw_pixels_size = raw_pixels.len();
    let mut pixel_map = HashMap::new();

    let mut i = 0;
    while i < raw_pixels_size - 3 {
        let color = PixelColor {
            r: raw_pixels[i],
            g: raw_pixels[i + 1],
            b: raw_pixels[i + 2],
        };
        *pixel_map.entry(color).or_insert(0) += 1;
        i += depth * 3;
    }

    Vec::from_iter(pixel_map)
}

/// Sorts a ColorCounts type by number of pixels
///
/// # Arguments
///
/// * 'colors' - A ColorCounts vector of colors to pixel count
/// * 'num_colors' - How many colors to return
///
/// # Example
/// ```rust,no_run
/// # use std::path::Path;
/// let _colors = image_colors::fetch_colors(&Path::new("path/to/file.jpg"), 5);
/// let _sorted_colors = image_colors::sort_colors(&_colors, 5);
/// ```
pub fn sort_colors(colors: &ColorCounts, num_colors: usize) -> ColorCounts {
    let mut color_copy = colors.clone();
    color_copy.sort_by(|&(_, a), &(_, b)| b.cmp(&a));
    let pixels = if num_colors > color_copy.len() {
        color_copy.to_vec()
    } else {
        color_copy[0..num_colors].to_vec()
    };
    pixels
}

/// Prints colors from ColorCounts vector, options to use color, a delimiter, or print in rgb, not
/// hex.
///
/// # Arguments
///
/// * 'colors' - A ColorCounts vector of colors to pixel count
/// * 'with_ansi_color' - Print with color?
/// * 'delimiter' - Delimiter to be used when printing ColorCounts
/// * 'with_rgb' - Print in rgb
///
/// # Example
/// ```rust,no_run
/// # use std::path::Path;
/// let _colors = image_colors::fetch_colors(&Path::new("path/to/file.jpg"), 5);
/// let sorted_colors = image_colors::sort_colors(&_colors, 5);
/// image_colors::print_colors(sorted_colors, false, " - ", false);
/// ```
pub fn print_colors(colors: ColorCounts, with_ansi_color: bool, delimiter: &str, with_rgb: bool) {
    for (color, count) in colors {
        let display_color = if with_rgb {
            format!("r:{} g:{} b:{}", color.r, color.g, color.b)
        } else {
            format!("#{:X}", color)
        };

        if with_ansi_color {
            let ansi_color = AnsiColor::RGB(color.r, color.g, color.b);
            println!(
                "{} {}{}{}",
                ansi_color.paint("█"),
                display_color,
                delimiter,
                count
            );
        } else {
            println!("{}{}{}", display_color, delimiter, count);
        }
    }
}

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

    fn set_crate_dir() {
        let current = std::env::current_exe().unwrap();
        let deps = current.parent().unwrap();
        let debug = deps.parent().unwrap();
        let target = debug.parent().unwrap();
        let base = target.parent().unwrap();
        assert!(std::env::set_current_dir(base).is_ok());
    }

    #[test]
    fn fetch_tests_dir() {
        set_crate_dir();
        assert!(std::env::current_dir().unwrap().join("tests").is_dir());
    }

    #[test]
    fn fetch_test_image() {
        set_crate_dir();
        assert!(
            std::env::current_dir()
                .unwrap()
                .join("tests")
                .join("diamond.png")
                .is_file()
        )
    }

    #[test]
    fn basic_sort_colors() {
        set_crate_dir();
        let colors = fetch_colors(
            &Path::new(
                std::env::current_dir()
                    .unwrap()
                    .join("tests")
                    .join("diamond.png")
                    .as_path(),
            ),
            10,
        );
        let sorted_colors = sort_colors(&colors, 5);
        assert_eq!(sorted_colors.len(), 5);
        let pixel_colors = sorted_colors.get(0).unwrap().0.clone();
        let pixel_count = sorted_colors.get(0).unwrap().1.clone();
        assert_eq!(pixel_colors.r, 40);
        assert_eq!(pixel_colors.g, 44);
        assert_eq!(pixel_colors.b, 52);
        assert_eq!(pixel_count, 805174);
    }
}
<|endoftext|>