<commit_before>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

use std::collections::{BTreeSet, BTreeMap, LinkedList};
use std::cmp::{Ordering, Ord, PartialOrd};
use std::fmt::{Display, Formatter};

use symbols;
use num::BigInt;
use ordered_float::OrderedFloat;

/// Value represents one of the allowed values in an EDN string.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum Value {
    Nil,
    Boolean(bool),
    Integer(i64),
    BigInteger(BigInt),
    // https://users.rust-lang.org/t/hashmap-key-cant-be-float-number-type-why/7892
    Float(OrderedFloat<f64>),
    Text(String),
    PlainSymbol(symbols::PlainSymbol),
    NamespacedSymbol(symbols::NamespacedSymbol),
    Keyword(symbols::Keyword),
    NamespacedKeyword(symbols::NamespacedKeyword),
    Vector(Vec<Value>),
    // We're using a LinkedList here instead of a Vec or VecDeque because the
    // LinkedList is faster for appending (which we do a lot of).
    // See https://github.com/mozilla/mentat/issues/231
    List(LinkedList<Value>),
    // We're using BTree{Set, Map} rather than Hash{Set, Map} because the BTree variants
    // implement Hash. The Hash variants don't in order to preserve O(n) hashing
    // time, which is hard given recursive data structures.
    // See https://internals.rust-lang.org/t/implementing-hash-for-hashset-hashmap/3817/1
    Set(BTreeSet<Value>),
    Map(BTreeMap<Value, Value>),
}

use self::Value::*;

impl Display for Value {
    // TODO: Make sure float syntax is correct, handle NaN and escaping.
    // See https://github.com/mozilla/mentat/issues/232
    fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
        match *self {
            Nil => write!(f, "null"),
            Boolean(v) => write!(f, "{}", v),
            Integer(v) => write!(f, "{}", v),
            BigInteger(ref v) => write!(f, "{}N", v),
            Float(OrderedFloat(v)) => write!(f, "{}", v),
            Text(ref v) => write!(f, "{}", v),
            PlainSymbol(ref v) => v.fmt(f),
            NamespacedSymbol(ref v) => v.fmt(f),
            Keyword(ref v) => v.fmt(f),
            NamespacedKeyword(ref v) => v.fmt(f),
            Vector(ref v) => {
                write!(f, "[")?;
                for x in v {
                    write!(f, " {}", x)?;
                }
                write!(f, " ]")
            }
            List(ref v) => {
                write!(f, "(")?;
                for x in v {
                    write!(f, " {}", x)?;
                }
                write!(f, " )")
            }
            Set(ref v) => {
                write!(f, "#{{")?;
                for x in v {
                    write!(f, " {}", x)?;
                }
                write!(f, " }}")
            }
            Map(ref v) => {
                write!(f, "{{")?;
                for (key, val) in v {
                    write!(f, " :{} {}", key, val)?;
                }
                write!(f, " }}")
            }
        }
    }
}

/// Creates `is_$TYPE` helper functions for Value, like
/// `is_big_integer()` or `is_text()`.
macro_rules! def_is {
    ($name: ident, $pat: pat) => {
        pub fn $name(&self) -> bool {
            match *self { $pat => true, _ => false }
        }
    }
}

/// Creates `as_$TYPE` helper functions for Value, like `as_big_integer()`,
/// which returns the underlying value representing this Value wrapped
/// in an Option, like `<Option<&BigInt>`.
macro_rules! def_as {
    ($name: ident, $kind: path, $t: ty) => {
        pub fn $name(&self) -> Option<&$t> {
            match *self { $kind(ref v) => Some(v), _ => None }
        }
    }
}

impl Value {
    def_is!(is_nil, Nil);
    def_is!(is_boolean, Boolean(_));
    def_is!(is_integer, Integer(_));
    def_is!(is_big_integer, BigInteger(_));
    def_is!(is_float, Float(_));
    def_is!(is_text, Text(_));
    def_is!(is_symbol, PlainSymbol(_));
    def_is!(is_namespaced_symbol, NamespacedSymbol(_));
    def_is!(is_keyword, Keyword(_));
    def_is!(is_namespaced_keyword, NamespacedKeyword(_));
    def_is!(is_vector, Vector(_));
    def_is!(is_list, List(_));
    def_is!(is_set, Set(_));
    def_is!(is_map, Map(_));

    /// `as_nil` does not use the macro as it does not have an underlying
    /// value, and returns `Option<()>`.
    pub fn as_nil(&self) -> Option<()> {
        match *self { Nil => Some(()), _ => None }
    }

    def_as!(as_boolean, Boolean, bool);
    def_as!(as_integer, Integer, i64);
    def_as!(as_big_integer, BigInteger, BigInt);
    def_as!(as_float, Float, OrderedFloat<f64>);
    def_as!(as_text, Text, String);
    def_as!(as_symbol, PlainSymbol, symbols::PlainSymbol);
    def_as!(as_namespaced_symbol, NamespacedSymbol, symbols::NamespacedSymbol);
    def_as!(as_keyword, Keyword, symbols::Keyword);
    def_as!(as_namespaced_keyword, NamespacedKeyword, symbols::NamespacedKeyword);
    def_as!(as_vector, Vector, Vec<Value>);
    def_as!(as_list, List, LinkedList<Value>);
    def_as!(as_set, Set, BTreeSet<Value>);
    def_as!(as_map, Map, BTreeMap<Value, Value>);

    pub fn from_bigint(src: &str) -> Option<Value> {
        src.parse::<BigInt>().map(Value::BigInteger).ok()
    }

    pub fn from_symbol<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
        to_symbol(namespace, name)
    }

    pub fn from_keyword<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
        to_keyword(namespace, name)
    }
}

impl From<f64> for Value {
    fn from(src: f64) -> Value {
        Value::Float(OrderedFloat::from(src))
    }
}

impl PartialOrd for Value {
    fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Value {
    fn cmp(&self, other: &Value) -> Ordering {
        match (self, other) {
            (&Nil, &Nil) => Ordering::Equal,
            (&Boolean(a), &Boolean(b)) => b.cmp(&a),
            (&Integer(a), &Integer(b)) => b.cmp(&a),
            (&BigInteger(ref a), &BigInteger(ref b)) => b.cmp(a),
            (&Float(ref a), &Float(ref b)) => b.cmp(a),
            (&Text(ref a), &Text(ref b)) => b.cmp(a),
            (&PlainSymbol(ref a), &PlainSymbol(ref b)) => b.cmp(a),
            (&NamespacedSymbol(ref a), &NamespacedSymbol(ref b)) => b.cmp(a),
            (&Keyword(ref a), &Keyword(ref b)) => b.cmp(a),
            (&NamespacedKeyword(ref a), &NamespacedKeyword(ref b)) => b.cmp(a),
            (&Vector(ref a), &Vector(ref b)) => b.cmp(a),
            (&List(ref a), &List(ref b)) => b.cmp(a),
            (&Set(ref a), &Set(ref b)) => b.cmp(a),
            (&Map(ref a), &Map(ref b)) => b.cmp(a),
            _ => to_ord(self).cmp(&to_ord(other))
        }
    }
}

fn to_ord(value: &Value) -> i32 {
    match *value {
        Nil => 0,
        Boolean(_) => 1,
        Integer(_) => 2,
        BigInteger(_) => 3,
        Float(_) => 4,
        Text(_) => 5,
        PlainSymbol(_) => 6,
        NamespacedSymbol(_) => 7,
        Keyword(_) => 8,
        NamespacedKeyword(_) => 9,
        Vector(_) => 10,
        List(_) => 11,
        Set(_) => 12,
        Map(_) => 13,
    }
}

/// Converts `name` into a plain or namespaced value symbol, depending on
/// whether or not `namespace` is given.
///
/// # Examples
///
/// ```
/// # use edn::types::to_symbol;
/// # use edn::types::Value;
/// # use edn::symbols;
/// let value = to_symbol("foo", "bar");
/// assert_eq!(value, Value::NamespacedSymbol(symbols::NamespacedSymbol::new("foo", "bar")));
///
/// let value = to_symbol(None, "baz");
/// assert_eq!(value, Value::PlainSymbol(symbols::PlainSymbol::new("baz")));
/// ```
pub fn to_symbol<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
    namespace.into().map_or_else(
        || Value::PlainSymbol(symbols::PlainSymbol::new(name)),
        |ns| Value::NamespacedSymbol(symbols::NamespacedSymbol::new(ns, name)))
}

/// Converts `name` into a plain or namespaced value keyword, depending on
/// whether or not `namespace` is given.
///
/// # Examples
///
/// ```
/// # use edn::types::to_keyword;
/// # use edn::types::Value;
/// # use edn::symbols;
/// let value = to_keyword("foo", "bar");
/// assert_eq!(value, Value::NamespacedKeyword(symbols::NamespacedKeyword::new("foo", "bar")));
///
/// let value = to_keyword(None, "baz");
/// assert_eq!(value, Value::Keyword(symbols::Keyword::new("baz")));
/// ```
pub fn to_keyword<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
    namespace.into().map_or_else(
        || Value::Keyword(symbols::Keyword::new(name)),
        |ns| Value::NamespacedKeyword(symbols::NamespacedKeyword::new(ns, name)))
}

#[cfg(test)]
mod test {
    extern crate ordered_float;
    extern crate num;

    use super::*;

    use std::collections::{BTreeSet, BTreeMap, LinkedList};
    use std::cmp::{Ordering};

    use symbols;
    use num::BigInt;
    use ordered_float::OrderedFloat;
    use std::iter::FromIterator;

    #[test]
    fn test_value_from() {
        assert_eq!(Value::from(42f64), Value::Float(OrderedFloat::from(42f64)));
        assert_eq!(Value::from_bigint("42").unwrap(), Value::BigInteger(BigInt::from(42)));
    }

    #[test]
    fn test_print_edn() {
        assert_eq!("[ 1 2 ( 3.14 ) #{ 4N } { :foo/bar 42 } [ ] :five :six/seven eight nine/ten true false null ]",
            Value::Vector(vec![
                Value::Integer(1),
                Value::Integer(2),
                Value::List(LinkedList::from_iter(vec![
                    Value::Float(OrderedFloat(3.14))
                ])),
                Value::Set(BTreeSet::from_iter(vec![
                    Value::from_bigint("4").unwrap()
                ])),
                Value::Map(BTreeMap::from_iter(vec![
                    (Value::from_symbol("foo", "bar"), Value::Integer(42))
                ])),
                Value::Vector(vec![]),
                Value::Keyword(symbols::Keyword::new("five")),
                Value::NamespacedKeyword(symbols::NamespacedKeyword::new("six", "seven")),
                Value::PlainSymbol(symbols::PlainSymbol::new("eight")),
                Value::NamespacedSymbol(symbols::NamespacedSymbol::new("nine", "ten")),
                Value::Boolean(true),
                Value::Boolean(false),
                Value::Nil
            ]
        ).to_string());
    }

    #[test]
    fn test_ord() {
        // TODO: Check we follow the equality rules at the bottom of https://github.com/edn-format/edn
        assert_eq!(Value::Nil.cmp(&Value::Nil), Ordering::Equal);
        assert_eq!(Value::Boolean(false).cmp(&Value::Boolean(true)), Ordering::Greater);
        assert_eq!(Value::Integer(1).cmp(&Value::Integer(2)), Ordering::Greater);
        assert_eq!(Value::from_bigint("1").cmp(&Value::from_bigint("2")), Ordering::Greater);
        assert_eq!(Value::from(1f64).cmp(&Value::from(2f64)), Ordering::Greater);
        assert_eq!(Value::Text("1".to_string()).cmp(&Value::Text("2".to_string())), Ordering::Greater);
        assert_eq!(Value::from_symbol("a", "b").cmp(&Value::from_symbol("c", "d")), Ordering::Greater);
        assert_eq!(Value::from_symbol(None, "a").cmp(&Value::from_symbol(None, "b")), Ordering::Greater);
        assert_eq!(Value::from_keyword(":a", ":b").cmp(&Value::from_keyword(":c", ":d")), Ordering::Greater);
        assert_eq!(Value::from_keyword(None, ":a").cmp(&Value::from_keyword(None, ":b")), Ordering::Greater);
        assert_eq!(Value::Vector(vec![]).cmp(&Value::Vector(vec![])), Ordering::Equal);
        assert_eq!(Value::List(LinkedList::new()).cmp(&Value::List(LinkedList::new())), Ordering::Equal);
        assert_eq!(Value::Set(BTreeSet::new()).cmp(&Value::Set(BTreeSet::new())), Ordering::Equal);
        assert_eq!(Value::Map(BTreeMap::new()).cmp(&Value::Map(BTreeMap::new())), Ordering::Equal);
    }
}<commit_msg>Properly print null edn values as "nil", to allow for isomorphic write/parse<commit_after>// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

use std::collections::{BTreeSet, BTreeMap, LinkedList};
use std::cmp::{Ordering, Ord, PartialOrd};
use std::fmt::{Display, Formatter};

use symbols;
use num::BigInt;
use ordered_float::OrderedFloat;

/// Value represents one of the allowed values in an EDN string.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum Value {
    Nil,
    Boolean(bool),
    Integer(i64),
    BigInteger(BigInt),
    // https://users.rust-lang.org/t/hashmap-key-cant-be-float-number-type-why/7892
    Float(OrderedFloat<f64>),
    Text(String),
    PlainSymbol(symbols::PlainSymbol),
    NamespacedSymbol(symbols::NamespacedSymbol),
    Keyword(symbols::Keyword),
    NamespacedKeyword(symbols::NamespacedKeyword),
    Vector(Vec<Value>),
    // We're using a LinkedList here instead of a Vec or VecDeque because the
    // LinkedList is faster for appending (which we do a lot of).
    // See https://github.com/mozilla/mentat/issues/231
    List(LinkedList<Value>),
    // We're using BTree{Set, Map} rather than Hash{Set, Map} because the BTree variants
    // implement Hash. The Hash variants don't in order to preserve O(n) hashing
    // time, which is hard given recursive data structures.
    // See https://internals.rust-lang.org/t/implementing-hash-for-hashset-hashmap/3817/1
    Set(BTreeSet<Value>),
    Map(BTreeMap<Value, Value>),
}

use self::Value::*;

impl Display for Value {
    // TODO: Make sure float syntax is correct, handle NaN and escaping.
    // See https://github.com/mozilla/mentat/issues/232
    fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
        match *self {
            Nil => write!(f, "nil"),
            Boolean(v) => write!(f, "{}", v),
            Integer(v) => write!(f, "{}", v),
            BigInteger(ref v) => write!(f, "{}N", v),
            Float(OrderedFloat(v)) => write!(f, "{}", v),
            Text(ref v) => write!(f, "{}", v),
            PlainSymbol(ref v) => v.fmt(f),
            NamespacedSymbol(ref v) => v.fmt(f),
            Keyword(ref v) => v.fmt(f),
            NamespacedKeyword(ref v) => v.fmt(f),
            Vector(ref v) => {
                write!(f, "[")?;
                for x in v {
                    write!(f, " {}", x)?;
                }
                write!(f, " ]")
            }
            List(ref v) => {
                write!(f, "(")?;
                for x in v {
                    write!(f, " {}", x)?;
                }
                write!(f, " )")
            }
            Set(ref v) => {
                write!(f, "#{{")?;
                for x in v {
                    write!(f, " {}", x)?;
                }
                write!(f, " }}")
            }
            Map(ref v) => {
                write!(f, "{{")?;
                for (key, val) in v {
                    write!(f, " :{} {}", key, val)?;
                }
                write!(f, " }}")
            }
        }
    }
}

/// Creates `is_$TYPE` helper functions for Value, like
/// `is_big_integer()` or `is_text()`.
macro_rules! def_is {
    ($name: ident, $pat: pat) => {
        pub fn $name(&self) -> bool {
            match *self { $pat => true, _ => false }
        }
    }
}

/// Creates `as_$TYPE` helper functions for Value, like `as_big_integer()`,
/// which returns the underlying value representing this Value wrapped
/// in an Option, like `<Option<&BigInt>`.
macro_rules! def_as {
    ($name: ident, $kind: path, $t: ty) => {
        pub fn $name(&self) -> Option<&$t> {
            match *self { $kind(ref v) => Some(v), _ => None }
        }
    }
}

impl Value {
    def_is!(is_nil, Nil);
    def_is!(is_boolean, Boolean(_));
    def_is!(is_integer, Integer(_));
    def_is!(is_big_integer, BigInteger(_));
    def_is!(is_float, Float(_));
    def_is!(is_text, Text(_));
    def_is!(is_symbol, PlainSymbol(_));
    def_is!(is_namespaced_symbol, NamespacedSymbol(_));
    def_is!(is_keyword, Keyword(_));
    def_is!(is_namespaced_keyword, NamespacedKeyword(_));
    def_is!(is_vector, Vector(_));
    def_is!(is_list, List(_));
    def_is!(is_set, Set(_));
    def_is!(is_map, Map(_));

    /// `as_nil` does not use the macro as it does not have an underlying
    /// value, and returns `Option<()>`.
    pub fn as_nil(&self) -> Option<()> {
        match *self { Nil => Some(()), _ => None }
    }

    def_as!(as_boolean, Boolean, bool);
    def_as!(as_integer, Integer, i64);
    def_as!(as_big_integer, BigInteger, BigInt);
    def_as!(as_float, Float, OrderedFloat<f64>);
    def_as!(as_text, Text, String);
    def_as!(as_symbol, PlainSymbol, symbols::PlainSymbol);
    def_as!(as_namespaced_symbol, NamespacedSymbol, symbols::NamespacedSymbol);
    def_as!(as_keyword, Keyword, symbols::Keyword);
    def_as!(as_namespaced_keyword, NamespacedKeyword, symbols::NamespacedKeyword);
    def_as!(as_vector, Vector, Vec<Value>);
    def_as!(as_list, List, LinkedList<Value>);
    def_as!(as_set, Set, BTreeSet<Value>);
    def_as!(as_map, Map, BTreeMap<Value, Value>);

    pub fn from_bigint(src: &str) -> Option<Value> {
        src.parse::<BigInt>().map(Value::BigInteger).ok()
    }

    pub fn from_symbol<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
        to_symbol(namespace, name)
    }

    pub fn from_keyword<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
        to_keyword(namespace, name)
    }
}

impl From<f64> for Value {
    fn from(src: f64) -> Value {
        Value::Float(OrderedFloat::from(src))
    }
}

impl PartialOrd for Value {
    fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Value {
    fn cmp(&self, other: &Value) -> Ordering {
        match (self, other) {
            (&Nil, &Nil) => Ordering::Equal,
            (&Boolean(a), &Boolean(b)) => b.cmp(&a),
            (&Integer(a), &Integer(b)) => b.cmp(&a),
            (&BigInteger(ref a), &BigInteger(ref b)) => b.cmp(a),
            (&Float(ref a), &Float(ref b)) => b.cmp(a),
            (&Text(ref a), &Text(ref b)) => b.cmp(a),
            (&PlainSymbol(ref a), &PlainSymbol(ref b)) => b.cmp(a),
            (&NamespacedSymbol(ref a), &NamespacedSymbol(ref b)) => b.cmp(a),
            (&Keyword(ref a), &Keyword(ref b)) => b.cmp(a),
            (&NamespacedKeyword(ref a), &NamespacedKeyword(ref b)) => b.cmp(a),
            (&Vector(ref a), &Vector(ref b)) => b.cmp(a),
            (&List(ref a), &List(ref b)) => b.cmp(a),
            (&Set(ref a), &Set(ref b)) => b.cmp(a),
            (&Map(ref a), &Map(ref b)) => b.cmp(a),
            _ => to_ord(self).cmp(&to_ord(other))
        }
    }
}

fn to_ord(value: &Value) -> i32 {
    match *value {
        Nil => 0,
        Boolean(_) => 1,
        Integer(_) => 2,
        BigInteger(_) => 3,
        Float(_) => 4,
        Text(_) => 5,
        PlainSymbol(_) => 6,
        NamespacedSymbol(_) => 7,
        Keyword(_) => 8,
        NamespacedKeyword(_) => 9,
        Vector(_) => 10,
        List(_) => 11,
        Set(_) => 12,
        Map(_) => 13,
    }
}

/// Converts `name` into a plain or namespaced value symbol, depending on
/// whether or not `namespace` is given.
///
/// # Examples
///
/// ```
/// # use edn::types::to_symbol;
/// # use edn::types::Value;
/// # use edn::symbols;
/// let value = to_symbol("foo", "bar");
/// assert_eq!(value, Value::NamespacedSymbol(symbols::NamespacedSymbol::new("foo", "bar")));
///
/// let value = to_symbol(None, "baz");
/// assert_eq!(value, Value::PlainSymbol(symbols::PlainSymbol::new("baz")));
/// ```
pub fn to_symbol<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
    namespace.into().map_or_else(
        || Value::PlainSymbol(symbols::PlainSymbol::new(name)),
        |ns| Value::NamespacedSymbol(symbols::NamespacedSymbol::new(ns, name)))
}

/// Converts `name` into a plain or namespaced value keyword, depending on
/// whether or not `namespace` is given.
///
/// # Examples
///
/// ```
/// # use edn::types::to_keyword;
/// # use edn::types::Value;
/// # use edn::symbols;
/// let value = to_keyword("foo", "bar");
/// assert_eq!(value, Value::NamespacedKeyword(symbols::NamespacedKeyword::new("foo", "bar")));
///
/// let value = to_keyword(None, "baz");
/// assert_eq!(value, Value::Keyword(symbols::Keyword::new("baz")));
/// ```
pub fn to_keyword<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
    namespace.into().map_or_else(
        || Value::Keyword(symbols::Keyword::new(name)),
        |ns| Value::NamespacedKeyword(symbols::NamespacedKeyword::new(ns, name)))
}

#[cfg(test)]
mod test {
    extern crate ordered_float;
    extern crate num;

    use super::*;

    use std::collections::{BTreeSet, BTreeMap, LinkedList};
    use std::cmp::{Ordering};

    use symbols;
    use num::BigInt;
    use ordered_float::OrderedFloat;
    use std::iter::FromIterator;

    #[test]
    fn test_value_from() {
        assert_eq!(Value::from(42f64), Value::Float(OrderedFloat::from(42f64)));
        assert_eq!(Value::from_bigint("42").unwrap(), Value::BigInteger(BigInt::from(42)));
    }

    #[test]
    fn test_print_edn() {
        assert_eq!("[ 1 2 ( 3.14 ) #{ 4N } { :foo/bar 42 } [ ] :five :six/seven eight nine/ten true false nil ]",
            Value::Vector(vec![
                Value::Integer(1),
                Value::Integer(2),
                Value::List(LinkedList::from_iter(vec![
                    Value::Float(OrderedFloat(3.14))
                ])),
                Value::Set(BTreeSet::from_iter(vec![
                    Value::from_bigint("4").unwrap()
                ])),
                Value::Map(BTreeMap::from_iter(vec![
                    (Value::from_symbol("foo", "bar"), Value::Integer(42))
                ])),
                Value::Vector(vec![]),
                Value::Keyword(symbols::Keyword::new("five")),
                Value::NamespacedKeyword(symbols::NamespacedKeyword::new("six", "seven")),
                Value::PlainSymbol(symbols::PlainSymbol::new("eight")),
                Value::NamespacedSymbol(symbols::NamespacedSymbol::new("nine", "ten")),
                Value::Boolean(true),
                Value::Boolean(false),
                Value::Nil
            ]
        ).to_string());
    }

    #[test]
    fn test_ord() {
        // TODO: Check we follow the equality rules at the bottom of https://github.com/edn-format/edn
        assert_eq!(Value::Nil.cmp(&Value::Nil), Ordering::Equal);
        assert_eq!(Value::Boolean(false).cmp(&Value::Boolean(true)), Ordering::Greater);
        assert_eq!(Value::Integer(1).cmp(&Value::Integer(2)), Ordering::Greater);
        assert_eq!(Value::from_bigint("1").cmp(&Value::from_bigint("2")), Ordering::Greater);
        assert_eq!(Value::from(1f64).cmp(&Value::from(2f64)), Ordering::Greater);
        assert_eq!(Value::Text("1".to_string()).cmp(&Value::Text("2".to_string())), Ordering::Greater);
        assert_eq!(Value::from_symbol("a", "b").cmp(&Value::from_symbol("c", "d")), Ordering::Greater);
        assert_eq!(Value::from_symbol(None, "a").cmp(&Value::from_symbol(None, "b")), Ordering::Greater);
        assert_eq!(Value::from_keyword(":a", ":b").cmp(&Value::from_keyword(":c", ":d")), Ordering::Greater);
        assert_eq!(Value::from_keyword(None, ":a").cmp(&Value::from_keyword(None, ":b")), Ordering::Greater);
        assert_eq!(Value::Vector(vec![]).cmp(&Value::Vector(vec![])), Ordering::Equal);
        assert_eq!(Value::List(LinkedList::new()).cmp(&Value::List(LinkedList::new())), Ordering::Equal);
        assert_eq!(Value::Set(BTreeSet::new()).cmp(&Value::Set(BTreeSet::new())), Ordering::Equal);
        assert_eq!(Value::Map(BTreeMap::new()).cmp(&Value::Map(BTreeMap::new())), Ordering::Equal);
    }
}<|endoftext|>


<commit_before>#![feature(core)]

use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::ops::Deref;
use std::path::Path;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::thread;

fn load_matrix(file_path: &Path) -> Vec<Vec<u32>> {

    let mat_file = File::open(file_path).unwrap();

    let mat_reader = BufReader::new(mat_file);

    let mut mat_line_iterator = mat_reader.lines();

    let mut mat_content: Vec<Vec<u32>> = Vec::new();

    loop {
        match mat_line_iterator.next() {
            Some(result) => {
                                let mat_line = result.unwrap();
                                let slice_iter = mat_line.as_slice().trim_matches(',').split(',')
                                    .map(|x| {
                                                FromStr::from_str(x).unwrap()
                                            });
                                mat_content.push(slice_iter.collect());
                            },

            None => break,
        }
    }

    mat_content
}

fn single_thread_mul(m1: &Vec<Vec<u32>>, m2: &Vec<Vec<u32>>) -> Vec<Vec<u32>> {

    let mut result: Vec<Vec<u32>> = Vec::new();
    let mul_length = m2.len();

    for y in 0..m1.len() {

        let mut result_row: Vec<u32> = Vec::new();
        let mut entry_value: u32 = 0;

        for x in 0..m2[0].len() {

            for i in 0..mul_length {
                entry_value += m1[y][i] * m2[i][x];
            }

            result_row.push(entry_value);
        }

        result.push(result_row);
    }

    result
}

fn multi_thread_mul(m1: Vec<Vec<u32>>, m2: Vec<Vec<u32>>, thread_num: usize) -> Vec<Vec<u32>> {

    let rows_to_process = m1.len();
    let rows_per_thread = rows_to_process / thread_num;

    let shared_m1 = Arc::new(m1);
    let shared_m2 = Arc::new(m2);

    // is there a better way to initialize a vector?
    let mut result: Vec<Vec<u32>> = Vec::with_capacity(rows_to_process);
    //result.resize(rows_to_process, Vec::new());
    for _ in 0..rows_to_process {
        result.push(Vec::new());
    }

    let mul_length = shared_m2.len();

    let shared_result = Arc::new(Mutex::new(result));

    let mut threads = vec![];
    for thread_idx in 0..thread_num {

        let child_m1 = shared_m1.clone();
        let child_m2 = shared_m2.clone();
        let child_result = shared_result.clone();

        let join_handle = thread::spawn(move || {

            let start_row_idx = thread_idx * rows_per_thread;
            let end_row_idx =
                if start_row_idx + rows_per_thread < rows_to_process - 1 {
                    start_row_idx + rows_per_thread
                }
                else {
                    rows_to_process
                };

            println!("thread #{}: start={}, end={}", thread_idx, start_row_idx, end_row_idx);

            for y in start_row_idx..end_row_idx {

                let mut result_row: Vec<u32> = Vec::new();
                let mut entry_value: u32 = 0;

                for x in 0..child_m2[0].len() {

                    for i in 0..mul_length {
                        entry_value += child_m1[y][i] * child_m2[i][x];
                    }

                    result_row.push(entry_value);
                }

                let mut child_result = child_result.lock().unwrap();
                child_result[y] = result_row;
            }

            println!("thread #{} finished", thread_idx);

        });

        threads.push(join_handle);
    }

    for t in threads {
        let _ = t.join();
    }

    // could it be more straightforward? put this mutex guard and the threads in a separate block?
    let guard = shared_result.lock().unwrap();
    guard.deref().clone()
}

fn main() {

    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        println!("incorrect arguments");
        return;
    }

    let m1: Vec<Vec<u32>> = load_matrix(Path::new(&args[1]));
    let m2: Vec<Vec<u32>> = load_matrix(Path::new(&args[2]));

    assert!(m1[0].len() == m2.len());

    //let result: Vec<Vec<u32>> = single_thread_mul(&m1, &m2);
/*
    for y in 0..result.len() {
        for x in 0..result[y].len() {
            print!("{},", result[y][x]);
        }
        print!("\n");
    }

*/
    let result: Vec<Vec<u32>> = multi_thread_mul(m1, m2, 100);
/*
    for y in 0..result.len() {
        for x in 0..result[y].len() {
            print!("{},", result[y][x]);
        }
        print!("\n");
    }
*/
}
<commit_msg>Use an Option inside the mutex guard in order to get the result vector ownership back at the end of the function<commit_after>#![feature(core)]

use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::ops::Deref;
use std::path::Path;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::thread;

fn load_matrix(file_path: &Path) -> Vec<Vec<u32>> {

    let mat_file = File::open(file_path).unwrap();

    let mat_reader = BufReader::new(mat_file);

    let mut mat_line_iterator = mat_reader.lines();

    let mut mat_content: Vec<Vec<u32>> = Vec::new();

    loop {
        match mat_line_iterator.next() {
            Some(result) => {
                                let mat_line = result.unwrap();
                                let slice_iter = mat_line.as_slice().trim_matches(',').split(',')
                                    .map(|x| {
                                                FromStr::from_str(x).unwrap()
                                            });
                                mat_content.push(slice_iter.collect());
                            },

            None => break,
        }
    }

    mat_content
}

fn single_thread_mul(m1: &Vec<Vec<u32>>, m2: &Vec<Vec<u32>>) -> Vec<Vec<u32>> {

    let mut result: Vec<Vec<u32>> = Vec::new();
    let mul_length = m2.len();

    for y in 0..m1.len() {

        let mut result_row: Vec<u32> = Vec::new();
        let mut entry_value: u32 = 0;

        for x in 0..m2[0].len() {

            for i in 0..mul_length {
                entry_value += m1[y][i] * m2[i][x];
            }

            result_row.push(entry_value);
        }

        result.push(result_row);
    }

    result
}

fn multi_thread_mul(m1: Vec<Vec<u32>>, m2: Vec<Vec<u32>>, thread_num: usize) -> Vec<Vec<u32>> {

    let rows_to_process = m1.len();
    let rows_per_thread = rows_to_process / thread_num;

    let shared_m1 = Arc::new(m1);
    let shared_m2 = Arc::new(m2);

    // is there a better way to initialize a vector?
    let mut result: Vec<Vec<u32>> = Vec::with_capacity(rows_to_process);
    //result.resize(rows_to_process, Vec::new());
    for _ in 0..rows_to_process {
        result.push(Vec::new());
    }

    let mul_length = shared_m2.len();

    let shared_result = Arc::new(Mutex::new(Some(result)));

    let mut threads = vec![];
    for thread_idx in 0..thread_num {

        let child_m1 = shared_m1.clone();
        let child_m2 = shared_m2.clone();
        let child_result_arcptr = shared_result.clone();

        let join_handle = thread::spawn(move || {

            let start_row_idx = thread_idx * rows_per_thread;
            let end_row_idx =
                if start_row_idx + rows_per_thread < rows_to_process - 1 {
                    start_row_idx + rows_per_thread
                }
                else {
                    rows_to_process
                };

            println!("thread #{}: start={}, end={}", thread_idx, start_row_idx, end_row_idx);

            for y in start_row_idx..end_row_idx {

                let mut result_row: Vec<u32> = Vec::new();
                let mut entry_value: u32 = 0;

                for x in 0..child_m2[0].len() {

                    for i in 0..mul_length {
                        entry_value += child_m1[y][i] * child_m2[i][x];
                    }

                    result_row.push(entry_value);
                }

                // get the guard
                let mut result_guard = child_result_arcptr.lock().unwrap();
                // extract the result vector from the Option and substitute it wth a None
                let mut child_result_vector = result_guard.take().unwrap();
                // alter the result vector
                child_result_vector[y] = result_row;
                // put the vector back to the Option
                *result_guard = Some(child_result_vector);
            }

            println!("thread #{} finished", thread_idx);

        });

        threads.push(join_handle);
    }

    for t in threads {
        let _ = t.join();
    }

    // get the guard and extract the result vector from the Option, leave a None there(take())
    let mut guard = shared_result.lock().unwrap();
    guard.take().unwrap()
}

fn main() {

    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        println!("incorrect arguments");
        return;
    }

    let m1: Vec<Vec<u32>> = load_matrix(Path::new(&args[1]));
    let m2: Vec<Vec<u32>> = load_matrix(Path::new(&args[2]));

    assert!(m1[0].len() == m2.len());

    //let result: Vec<Vec<u32>> = single_thread_mul(&m1, &m2);
/*
    for y in 0..result.len() {
        for x in 0..result[y].len() {
            print!("{},", result[y][x]);
        }
        print!("\n");
    }

*/
    let result: Vec<Vec<u32>> = multi_thread_mul(m1, m2, 2);
/*
    for y in 0..result.len() {
        for x in 0..result[y].len() {
            print!("{},", result[y][x]);
        }
        print!("\n");
    }
*/
}
<|endoftext|>


<commit_before>extern crate mustache;

use std::str;
use std::sync::{Arc, RWLock};
use std::collections::HashMap;
use std::io::{IoResult, File};
use std::io::util::copy;
use std::path::BytesContainer;
use http;
use time;
use mimes::get_media_type;

///A container for the response
pub struct Response<'a, 'b: 'a> {
    ///the original `http::server::ResponseWriter`
    pub origin: &'a mut http::server::ResponseWriter<'b>,
    templates: Arc<RWLock<HashMap<&'static str, mustache::Template>>>
}

impl<'a, 'b> Response<'a, 'b> {

    pub fn from_internal<'c, 'd>(response: &'c mut http::server::ResponseWriter<'d>,
                                 templates: Arc<RWLock<HashMap<&'static str, mustache::Template>>>
                                ) -> Response<'c, 'd>
    {
        Response {
            origin: response,
            templates: templates
        }
    }

    /// Writes a response
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.send("hello world");
    /// ```
    pub fn send<T: BytesContainer> (&mut self, text: T) {
        // TODO: This needs to be more sophisticated to return the correct headers
        // not just "some headers" :)
        Response::set_headers(self.origin);
        let _ = self.origin.write(text.container_as_bytes());
    }

    /// sets the content type by it's short form.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.set_content_type("html");
    /// ```
    pub fn set_content_type(&mut self, text: &str) {
        // TODO: make this a chaining API. (Fight the lifetime hell!)
        self.origin.headers.content_type = get_media_type(text);
    }

    fn set_headers(response_writer: &mut http::server::ResponseWriter) {
        response_writer.headers.date = Some(time::now_utc());

        // we don't need to set this https://github.com/Ogeon/rustful/issues/3#issuecomment-44787613
        response_writer.headers.content_length = None;
        response_writer.headers.content_type = response_writer.headers.content_type
                                                                      .clone()
                                                                      .or(get_media_type("txt"));

        response_writer.headers.server = Some(String::from_str("Nickel"));
    }

    pub fn send_file(&mut self, path: &Path) -> IoResult<()> {
        let mut file = try!(File::open(path));
        self.origin.headers.content_length = None;

        self.origin.headers.content_type = path.extension_str().and_then(get_media_type);
        self.origin.headers.server = Some(String::from_str("Nickel"));
        copy(&mut file, self.origin)
    }

    /// Renders the given template bound with the given data.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// let mut data = HashMap::<&'static str, &'static str>::new();
    /// data.insert("name", "user");
    /// response.render("examples/assets/template.tpl", &data);
    /// ```
    pub fn render(&mut self, path: &'static str, data: &HashMap<&'static str, &'static str>)
    {
        let mut templates = self.templates.write();
        let template = templates.find_or_insert_with(path, |_|
                     {
                         mustache::compile_str(match str::from_utf8(
                            match File::open(&Path::new(path)).read_to_end()
                            {
                                Ok(s) => s,
                                Err(e) => fail!("Couldn't open the template file: {}", e)
                            }.as_slice()
                        )
                        {
                            Some(s) => s,
                            None => fail!("Coulnt't read template file as utf8"),
                        }
                    )
                }
            );
        let _ = template.render(self.origin, data);
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_media_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<commit_msg>chore(response): minor cleanup<commit_after>extern crate mustache;

use std::str;
use std::sync::{Arc, RWLock};
use std::collections::HashMap;
use std::io::{IoResult, File};
use std::io::util::copy;
use std::path::BytesContainer;
use http;
use time;
use mimes::get_media_type;

///A container for the response
pub struct Response<'a, 'b: 'a> {
    ///the original `http::server::ResponseWriter`
    pub origin: &'a mut http::server::ResponseWriter<'b>,
    templates: Arc<RWLock<HashMap<&'static str, mustache::Template>>>
}

impl<'a, 'b> Response<'a, 'b> {

    pub fn from_internal<'c, 'd>(response: &'c mut http::server::ResponseWriter<'d>,
                                 templates: Arc<RWLock<HashMap<&'static str, mustache::Template>>>
                                ) -> Response<'c, 'd>
    {
        Response {
            origin: response,
            templates: templates
        }
    }

    /// Writes a response
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.send("hello world");
    /// ```
    pub fn send<T: BytesContainer> (&mut self, text: T) {
        // TODO: This needs to be more sophisticated to return the correct headers
        // not just "some headers" :)
        Response::set_headers(self.origin);
        let _ = self.origin.write(text.container_as_bytes());
    }

    /// sets the content type by it's short form.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// response.set_content_type("html");
    /// ```
    pub fn set_content_type(&mut self, text: &str) {
        // TODO: make this a chaining API. (Fight the lifetime hell!)
        self.origin.headers.content_type = get_media_type(text);
    }

    fn set_headers(response_writer: &mut http::server::ResponseWriter) {
        response_writer.headers.date = Some(time::now_utc());

        // we don't need to set this https://github.com/Ogeon/rustful/issues/3#issuecomment-44787613
        response_writer.headers.content_length = None;
        response_writer.headers.content_type = response_writer.headers.content_type
                                                                      .clone()
                                                                      .or(get_media_type("txt"));

        response_writer.headers.server = Some(String::from_str("Nickel"));
    }

    pub fn send_file(&mut self, path: &Path) -> IoResult<()> {
        let mut file = try!(File::open(path));
        self.origin.headers.content_length = None;

        self.origin.headers.content_type = path.extension_str().and_then(get_media_type);
        self.origin.headers.server = Some(String::from_str("Nickel"));
        copy(&mut file, self.origin)
    }

    /// Renders the given template bound with the given data.
    ///
    /// # Example
    /// ```{rust,ignore}
    /// let mut data = HashMap::<&'static str, &'static str>::new();
    /// data.insert("name", "user");
    /// response.render("examples/assets/template.tpl", &data);
    /// ```
    pub fn render(&mut self, path: &'static str, data: &HashMap<&'static str, &'static str>)
    {
        let mut templates = self.templates.write();
        let template = templates.find_or_insert_with(path, |_|
                     {
                         mustache::compile_str(str::from_utf8(
                            File::open(&Path::new(path))
                                 .read_to_end().ok().expect(format!("Couldn't open the template file: {}", path).as_slice()).as_slice()
                        ).expect("Coulnt't read template file as utf8")
                    )
                }
            );
        let _ = template.render(self.origin, data);
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_media_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<|endoftext|>


<commit_before>
#[derive(PartialEq, Clone, Debug)]
pub enum Token {
   Name(String),
   Int(i64),
   Float(f64),
   NewLine,
   Function,
   Address,
   SpaceOffset,
   Assign,
   Add,
   Subtract,
   Multiply,
   Divide,
   Not,
   ParenLeft,
   ParenRight,
   BracketLeft,
   BracketRight,
   AngleBracketLeft,
   AngleBracketRight,
   Equal,
   Unequal,
   LessEqual,
   GreaterEqual,
   True,
   False,
   Range,
}

pub type TokenResult = Option<(Token, usize)>;

pub fn tokenize(source: &str) -> Result<Vec<Token>, String> {
   let mut tokens = Vec::new();

   let original = source;

   let mut source = source;

   let mut consumed = 0;

   let mut after_new_line = true;

   let mut spaces_offset = 0;

   loop {
      let taken = consume_spaces(source);

      consumed += taken;
      source = &source[taken..];

      if after_new_line && taken > 0 {
         if spaces_offset == 0 {
            spaces_offset = taken;
         }

         if taken != spaces_offset {
            return Err(wrong_space_offset_error(original, consumed));
         }

         tokens.push(Token::SpaceOffset);
      }

      let taken = consume_comment(source);

      consumed += taken;
      source = &source[taken..];

      if let Some((token, taken)) = single_token(source) {
         consumed += taken;
         source = &source[taken..];

         after_new_line = Token::NewLine == token;

         tokens.push(token);
      } else {
         if consumed != original.len() {
            return Err(unrecognized_char_error(original, consumed));
         }

         break;
      }
   }

   let has_new_line = match tokens.last() {
      Some(token) => {
         match token {
            &Token::NewLine => true,
            _ => false,
         }
      },
      _ => false,
   };

   if !has_new_line {
      tokens.push(Token::NewLine);
   }

   Ok(tokens)
}

fn wrong_space_offset_error(source: &str, consumed: usize) -> String {
   let (_, line, column) = error_position(source, consumed);

   format!(
      "Wrong space offset at line {}, col {}", line, column
   )
}

fn unrecognized_char_error(source: &str, consumed: usize) -> String {
   let (error_ch, line, column) = error_position(source, consumed);

   format!(
      "Unrecognized character '{}' at line {}, col {}", error_ch, line, column
   )
}

fn error_position(source: &str, consumed: usize) -> (char, usize, usize) {
   let mut line = 1;
   let mut column = 1;
   let mut error_ch = ' ';

   for (index, ch) in source.chars().enumerate() {
      if consumed == index {
         error_ch = ch;
         break;
      }

      if ch == '\n' {
         line += 1;
         column = 1;
      } else {
         column += 1;
      }
   }

   return (error_ch, line, column)
}

fn consume_spaces(source: &str) -> usize {
   for (index, ch) in source.chars().enumerate() {
      if ch != ' ' {
         return index;
      }
   }

   source.len()
}

fn consume_comment(source: &str) -> usize {
   let mut end = 0;

   let mut chars = source.chars();

   if let Some(ch) = chars.next() {
      if ch == '#' {
         end += 1;

         loop {
            match chars.next() {
               Some(ch) => match ch {
                  '\n' => break,
                  _ => end += 1,
               },
               None => break,
            }
         }
      }
   }

   end
}

fn single_token(source: &str) -> TokenResult {
   if let Some(result) = extract_name(source) {
      Some(result)
   } else if let Some(result) = extract_number(source) {
      Some(result)
   } else if let Some(result) = extract_symbol_token(source) {
      Some(result)
   } else {
      None
   }
}

fn extract_name(source: &str) -> TokenResult {
   let mut end = 0;

   let mut chars = source.chars();

   match chars.next() {
      Some(ch) => match ch {
         'a' ... 'z' | 'A' ... 'Z' | '$' => end += 1,
         _ => return None,
      },
      None => return None,
   }

   loop {
      match chars.next() {
         Some(ch) => match ch {
            'a' ... 'z' | 'A' ... 'Z' | '0' ... '9' | '-' | '_' => end += 1,
            _ => break,
         },
         None => break,
      }
   }

   let name = &source[0..end];

   let token = match name {
      "true" => Token::True,
      "false" => Token::False,
      _ => Token::Name(String::from(name)),
   };

   Some((token, end))
}

fn extract_symbol_token(source: &str) -> TokenResult {
   let mut chars = source.chars();

   match chars.next() {
      Some(ch) => {
         let token = match ch {
            '\n' => Token::NewLine,
            '=' => match chars.next() {
               Some(ch) => match ch {
                  '=' => return Some((Token::Equal, 2)),
                  _ => Token::Assign,
               },
               None => Token::Assign,
            },
            '+' => Token::Add,
            '-' => Token::Subtract,
            '*' => Token::Multiply,
            '/' => Token::Divide,
            '!' => match chars.next() {
               Some(ch) => match ch {
                  '=' => return Some((Token::Unequal, 2)),
                  _ => Token::Not,
               },
               None => Token::Not,
            },
            '(' => Token::ParenLeft,
            ')' => Token::ParenRight,
            '[' => Token::BracketLeft,
            ']' => Token::BracketRight,
            '<' => match chars.next() {
               Some(ch) => match ch {
                  '=' => return Some((Token::LessEqual, 2)),
                  _ => Token::AngleBracketLeft,
               },
               None => Token::AngleBracketLeft,
            },
            '>' => match chars.next() {
               Some(ch) => match ch {
                  '=' => return Some((Token::GreaterEqual, 2)),
                  '>' => return Some((Token::Function, 2)),
                  _ => Token::AngleBracketRight,
               },
               None => Token::AngleBracketRight,
            },
            '@' => Token::Address,
            '.' => match chars.next() {
               Some(ch) => match ch {
                  '.' => return Some((Token::Range, 2)),
                  _ => return None,
               },
               None => return None,
            },
            _ => return None,
         };

         Some((token, 1))
      },
      None => None,
   }
}

fn extract_number(source: &str) -> TokenResult {
   let full_len = source.len();

   let mut end = 0;

   let positive = match source.chars().next() {
      None => {
         return None;
      },
      Some('-') => {
         end += 1;
         false
      },
      Some(_) => {
         true
      },
   };

   let mut integral = 0;

   let source = &source[end..];
   end = 0;

   for ch in source.chars() {
      if let Some(digit) = to_digit(ch) {
         integral = 10 * integral + digit;
         end += 1;
      } else {
         break;
      }
   }

   if end == 0 {
      return None;
   }

   let source = &source[end..];

   let len = source.len();

   let mut chars = source.chars();
   let first = chars.next();
   let second = chars.next();

   let float = match (first, second) {
      (Some('.'), Some(ch)) if ch != '.' => true,
      (Some('.'), None) => true,
      _ => false,
   };

   if !float {
      if !positive {
         integral = -integral
      }

      return Some((
         Token::Int(integral),
         full_len - len
      ));
   }

   // Dot here

   let source = &source[1..];

   end = 0;

   let mut fractional = 0;
   let mut divisor = 1;

   for ch in source.chars() {
      if let Some(digit) = to_digit(ch) {
         fractional = 10 * fractional + digit;
         divisor = 10 * divisor;

         end += 1;
      } else {
         break;
      }
   }

   let mut value = integral as f64 + fractional as f64 / divisor as f64;

   if !positive {
      value = -value
   }

   let source = &source[end..];

   Some((
      Token::Float(value),
      full_len - source.len()
   ))
}

fn to_digit(ch: char) -> Option<i64> {
   match ch {
      '0' ... '9' => Some((ch as u32 - '0' as u32) as i64),
      _ =>  None
   }
}

<commit_msg>Using a Vec of chars instead of str chars iterator<commit_after>
#[derive(PartialEq, Clone, Debug)]
pub enum Token {
   Name(String),
   Int(i64),
   Float(f64),
   NewLine,
   Function,
   Address,
   SpaceOffset,
   Assign,
   Add,
   Subtract,
   Multiply,
   Divide,
   Not,
   ParenLeft,
   ParenRight,
   BracketLeft,
   BracketRight,
   AngleBracketLeft,
   AngleBracketRight,
   Equal,
   Unequal,
   LessEqual,
   GreaterEqual,
   True,
   False,
   Range,
}

pub type TokenResult = Option<(Token, usize)>;

pub fn tokenize(string: &str) -> Result<Vec<Token>, String> {
   let mut tokens = Vec::new();

   let chars_vec: Vec<char> = string.chars().collect();

   let mut source: &[char] = &chars_vec;

   let original = source;

   let mut consumed = 0;

   let mut after_new_line = true;

   let mut spaces_offset = 0;

   loop {
      let taken = consume_spaces(source);

      consumed += taken;
      source = &source[taken..];

      if after_new_line && taken > 0 {
         if spaces_offset == 0 {
            spaces_offset = taken;
         }

         if taken != spaces_offset {
            return Err(wrong_space_offset_error(original, consumed));
         }

         tokens.push(Token::SpaceOffset);
      }

      let taken = consume_comment(source);

      consumed += taken;
      source = &source[taken..];

      if let Some((token, taken)) = single_token(source) {
         consumed += taken;
         source = &source[taken..];

         after_new_line = Token::NewLine == token;

         tokens.push(token);
      } else {
         if consumed != original.len() {
            return Err(unrecognized_char_error(original, consumed));
         }

         break;
      }
   }

   let has_new_line = match tokens.last() {
      Some(token) => {
         match token {
            &Token::NewLine => true,
            _ => false,
         }
      },
      _ => false,
   };

   if !has_new_line {
      tokens.push(Token::NewLine);
   }

   Ok(tokens)
}

fn wrong_space_offset_error(source: &[char], consumed: usize) -> String {
   let (_, line, column) = error_position(source, consumed);

   format!(
      "Wrong space offset at line {}, col {}", line, column
   )
}

fn unrecognized_char_error(source: &[char], consumed: usize) -> String {
   let (error_ch, line, column) = error_position(source, consumed);

   format!(
      "Unrecognized character '{}' at line {}, col {}", error_ch, line, column
   )
}

fn error_position(source: &[char], consumed: usize) -> (char, usize, usize) {
   let mut line = 1;
   let mut column = 1;
   let mut error_ch = ' ';

   for (index, &ch) in source.iter().enumerate() {
      if consumed == index {
         error_ch = ch;
         break;
      }

      if ch == '\n' {
         line += 1;
         column = 1;
      } else {
         column += 1;
      }
   }

   return (error_ch, line, column)
}

fn consume_spaces(source: &[char]) -> usize {
   for (index, ch) in source.iter().enumerate() {
      if *ch != ' ' {
         return index;
      }
   }

   source.len()
}

fn consume_comment(source: &[char]) -> usize {
   let mut end = 0;

   let mut chars = source.iter();

   if let Some(ch) = chars.next() {
      if *ch == '#' {
         end += 1;

         loop {
            match chars.next() {
               Some(ch) => match *ch {
                  '\n' => break,
                  _ => end += 1,
               },
               None => break,
            }
         }
      }
   }

   end
}

fn single_token(source: &[char]) -> TokenResult {
   if let Some(result) = extract_name(source) {
      Some(result)
   } else if let Some(result) = extract_number(source) {
      Some(result)
   } else if let Some(result) = extract_symbol_token(source) {
      Some(result)
   } else {
      None
   }
}

fn extract_name(source: &[char]) -> TokenResult {
   let mut end = 0;

   let mut chars = source.iter();

   match chars.next() {
      Some(ch) => match *ch {
         'a' ... 'z' | 'A' ... 'Z' | '$' => end += 1,
         _ => return None,
      },
      None => return None,
   }

   loop {
      match chars.next() {
         Some(ch) => match *ch {
            'a' ... 'z' | 'A' ... 'Z' | '0' ... '9' | '-' | '_' => end += 1,
            _ => break,
         },
         None => break,
      }
   }

   let name = as_string(&source[0..end]);

   let token = match &name as &str {
      "true" => Token::True,
      "false" => Token::False,
      _ => Token::Name(name),
   };

   Some((token, end))
}

fn as_string(source: &[char]) -> String {
   let mut result = String::with_capacity(source.len());

   for ch in source.iter() {
      result.push(*ch);
   }

   result
}

fn extract_symbol_token(source: &[char]) -> TokenResult {
   let mut chars = source.iter();

   match chars.next() {
      Some(ch) => {
         let token = match *ch {
            '\n' => Token::NewLine,
            '=' => match chars.next() {
               Some(ch) => match *ch {
                  '=' => return Some((Token::Equal, 2)),
                  _ => Token::Assign,
               },
               None => Token::Assign,
            },
            '+' => Token::Add,
            '-' => Token::Subtract,
            '*' => Token::Multiply,
            '/' => Token::Divide,
            '!' => match chars.next() {
               Some(ch) => match *ch {
                  '=' => return Some((Token::Unequal, 2)),
                  _ => Token::Not,
               },
               None => Token::Not,
            },
            '(' => Token::ParenLeft,
            ')' => Token::ParenRight,
            '[' => Token::BracketLeft,
            ']' => Token::BracketRight,
            '<' => match chars.next() {
               Some(ch) => match *ch {
                  '=' => return Some((Token::LessEqual, 2)),
                  _ => Token::AngleBracketLeft,
               },
               None => Token::AngleBracketLeft,
            },
            '>' => match chars.next() {
               Some(ch) => match *ch {
                  '=' => return Some((Token::GreaterEqual, 2)),
                  '>' => return Some((Token::Function, 2)),
                  _ => Token::AngleBracketRight,
               },
               None => Token::AngleBracketRight,
            },
            '@' => Token::Address,
            '.' => match chars.next() {
               Some(ch) => match *ch {
                  '.' => return Some((Token::Range, 2)),
                  _ => return None,
               },
               None => return None,
            },
            _ => return None,
         };

         Some((token, 1))
      },
      None => None,
   }
}

fn extract_number(source: &[char]) -> TokenResult {
   let full_len = source.len();

   let mut end = 0;

   let positive = match source.iter().next() {
      None => {
         return None;
      },
      Some(&'-') => {
         end += 1;
         false
      },
      Some(_) => {
         true
      },
   };

   let mut integral = 0;

   let source = &source[end..];
   end = 0;

   for ch in source.iter() {
      if let Some(digit) = to_digit(*ch) {
         integral = 10 * integral + digit;
         end += 1;
      } else {
         break;
      }
   }

   if end == 0 {
      return None;
   }

   let source = &source[end..];

   let len = source.len();

   let mut chars = source.iter();
   let first = chars.next();
   let second = chars.next();

   let float = match (first, second) {
      (Some(&'.'), Some(&ch)) if ch != '.' => true,
      (Some(&'.'), None) => true,
      _ => false,
   };

   if !float {
      if !positive {
         integral = -integral
      }

      return Some((
         Token::Int(integral),
         full_len - len
      ));
   }

   // Dot here

   let source = &source[1..];

   end = 0;

   let mut fractional = 0;
   let mut divisor = 1;

   for ch in source.iter() {
      if let Some(digit) = to_digit(*ch) {
         fractional = 10 * fractional + digit;
         divisor = 10 * divisor;

         end += 1;
      } else {
         break;
      }
   }

   let mut value = integral as f64 + fractional as f64 / divisor as f64;

   if !positive {
      value = -value
   }

   let source = &source[end..];

   Some((
      Token::Float(value),
      full_len - source.len()
   ))
}

fn to_digit(ch: char) -> Option<i64> {
   match ch {
      '0' ... '9' => Some((ch as u32 - '0' as u32) as i64),
      _ =>  None
   }
}

<|endoftext|>


<commit_before><commit_msg>when deleting a queue, wait for all other references to go away<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2019 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use futures_util::{
    future::FutureExt,
    stream::{StreamExt, StreamFuture},
};
use pin_project::pin_project;
use tokio::time::{self, Interval};

use std::{
    future::Future,
    sync::{atomic::Ordering, Arc},
};

use super::Inner;
use crate::{prelude::Queryable, PoolOptions};
use futures_core::task::{Context, Poll};
use std::pin::Pin;

/// Idling connections TTL check interval.
///
/// The purpose of this interval is to remove idling connections that both:
/// * overflows min bound of the pool;
/// * idles longer then `inactive_connection_ttl`.
#[pin_project]
pub struct TtlCheckInterval {
    inner: Arc<Inner>,
    #[pin]
    interval: StreamFuture<Interval>,
    pool_options: PoolOptions,
}

impl TtlCheckInterval {
    /// Creates new `TtlCheckInterval`.
    pub fn new(pool_options: PoolOptions, inner: Arc<Inner>) -> Self {
        let interval = time::interval(pool_options.ttl_check_interval()).into_future();
        Self {
            inner,
            interval,
            pool_options,
        }
    }

    /// Perform the check.
    pub fn check_ttl(&self) {
        let mut exchange = self.inner.exchange.lock().unwrap();

        let num_idling = exchange.available.len();
        let num_to_drop = num_idling.saturating_sub(self.pool_options.constraints().min());

        for _ in 0..num_to_drop {
            let idling_conn = exchange.available.pop_front().unwrap();
            if idling_conn.elapsed() > self.pool_options.inactive_connection_ttl() {
                assert!(idling_conn.conn.inner.pool.is_none());
                tokio::spawn(idling_conn.conn.disconnect().map(drop));
                exchange.exist -= 1;
            } else {
                exchange.available.push_back(idling_conn);
            }
        }

        if num_to_drop > 0 {
            // there were available connections, so no tasks should be waiting
            assert_eq!(exchange.waiting.len(), 0);
        }
    }
}

impl Future for TtlCheckInterval {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        loop {
            let (_, interval) = futures_core::ready!(self.as_mut().project().interval.poll(cx));
            let close = self.inner.close.load(Ordering::Acquire);

            if !close {
                self.check_ttl();
                self.interval = interval.into_future();
            } else {
                return Poll::Ready(());
            }
        }
    }
}
<commit_msg>Remove assert of waiting tasks count<commit_after>// Copyright (c) 2019 Anatoly Ikorsky
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use futures_util::{
    future::FutureExt,
    stream::{StreamExt, StreamFuture},
};
use pin_project::pin_project;
use tokio::time::{self, Interval};

use std::{
    future::Future,
    sync::{atomic::Ordering, Arc},
};

use super::Inner;
use crate::{prelude::Queryable, PoolOptions};
use futures_core::task::{Context, Poll};
use std::pin::Pin;

/// Idling connections TTL check interval.
///
/// The purpose of this interval is to remove idling connections that both:
/// * overflows min bound of the pool;
/// * idles longer then `inactive_connection_ttl`.
#[pin_project]
pub struct TtlCheckInterval {
    inner: Arc<Inner>,
    #[pin]
    interval: StreamFuture<Interval>,
    pool_options: PoolOptions,
}

impl TtlCheckInterval {
    /// Creates new `TtlCheckInterval`.
    pub fn new(pool_options: PoolOptions, inner: Arc<Inner>) -> Self {
        let interval = time::interval(pool_options.ttl_check_interval()).into_future();
        Self {
            inner,
            interval,
            pool_options,
        }
    }

    /// Perform the check.
    pub fn check_ttl(&self) {
        let mut exchange = self.inner.exchange.lock().unwrap();

        let num_idling = exchange.available.len();
        let num_to_drop = num_idling.saturating_sub(self.pool_options.constraints().min());

        for _ in 0..num_to_drop {
            let idling_conn = exchange.available.pop_front().unwrap();
            if idling_conn.elapsed() > self.pool_options.inactive_connection_ttl() {
                assert!(idling_conn.conn.inner.pool.is_none());
                tokio::spawn(idling_conn.conn.disconnect().map(drop));
                exchange.exist -= 1;
            } else {
                exchange.available.push_back(idling_conn);
            }
        }
    }
}

impl Future for TtlCheckInterval {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        loop {
            let (_, interval) = futures_core::ready!(self.as_mut().project().interval.poll(cx));
            let close = self.inner.close.load(Ordering::Acquire);

            if !close {
                self.check_ttl();
                self.interval = interval.into_future();
            } else {
                return Poll::Ready(());
            }
        }
    }
}
<|endoftext|>


<commit_before>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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/.

use std::io;
use std::io::{BufRead, Write};
use std::str::FromStr;

use domain::DomainDispatcher;
use io::*;
use proto::*;
use types::*;

mod input;
mod output;

use self::input::RawInputMessage;
use self::output::RawOutputMessage;

pub struct Oacsp {
    dev: Device,
    domains: DomainDispatcher,
    client_id: Option<String>
}

impl Oacsp {

    pub fn new(dev: Device, domains: DomainDispatcher) -> Oacsp {
        Oacsp { dev: dev, domains: domains, client_id: None }
    }

    fn line_is_ready(&self) -> bool {
        self.dev.recv_bytes().contains(&b'\n')
    }

    fn process_input(&mut self) -> io::Result<usize> {
        assert!(self.line_is_ready());
        let dev_id = self.dev.id();
        let mut buf = io::BufReader::new(self.dev.recv_bytes());
        let mut line = String::new();
        let nbytes = try!(buf.read_line(&mut line));
        let begin_received = self.client_id.is_some();
        match (try!(RawInputMessage::from_str(&line)), begin_received) {
            (RawInputMessage::Begin { version: _, client_id }, false) => {
                info!("received a begin message from client {}", client_id);
            	self.client_id = Some(client_id);
            	Ok(nbytes)
            },
            (RawInputMessage::Begin { version: _, client_id: _ }, true) => {
				Err(io::Error::new(io::ErrorKind::InvalidData, "begin message already received"))
            }
            (RawInputMessage::WriteLvar { lvar, value }, true) => {
                debug!("received a WRITE_LVAR message from client {}: {} <- {}",
                    self.client_id_str(), lvar, value);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.write(&Var::Named(lvar), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::WriteOffset { offset, value }, true) => {
                debug!("received a WRITE_OFFSET message from client {}: {} <- {}",
                    self.client_id_str(), offset, value);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.write(&Var::Offset(offset), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveLvar { lvar }, true) => {
                debug!("received a OBSERVE_LVAR message from client {}: {}",
                    self.client_id_str(), lvar);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.subscribe(dev_id, &Var::Named(lvar))
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveOffset { offset }, true) => {
                debug!("received a OBSERVE_OFFSET message from client {}: {}",
                    self.client_id_str(), offset);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.subscribe(dev_id, &Var::Offset(offset))
                }));
                Ok(nbytes)
            }
            (_, false) =>  {
                let error = io::Error::new(
                    io::ErrorKind::InvalidData,
                    "unexpected message while waiting for begin");
				Err(error)
            }
        }
    }

    fn client_id_str(&self) -> &str {
        self.client_id
        	.as_ref()
        	.map(|id| id.as_str())
        	.unwrap_or("none")
    }
}

impl DeviceHandler for Oacsp {
    fn device(&mut self) -> &mut Device { &mut self.dev }

    fn process_event(&mut self, event: Event) -> io::Result<()> {
        match event {
            Event::Ready => self.dev.request_read(),
            Event::BytesRead(_) => {
                while self.line_is_ready() {
                    let nread = try!(self.process_input());
                    self.dev.consume_recv_buffer(nread);
                }
                self.dev.request_read()
            }
            Event::BytesWritten(_) => Ok(()),
        }
    }
}

impl Protocol for Oacsp {

    fn send_update(&mut self, domain: &str, variable: Var, value: Value) -> io::Result<()> {
        let raw = try!(match variable {
            Var::Offset(offset) if domain == "fsuipc" =>
                Ok(RawOutputMessage::EventOffset { offset: offset, value: value }),
            Var::Named(ref lvar) if domain == "lvar" =>
                Ok(RawOutputMessage::EventLvar { lvar: lvar.clone(), value: value }),
            _ => {
                Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("cannot encode a message for domain '{}', var '{:?}'",
                        domain, variable)))
            }
        });
        let mut buf = Vec::new();
        try!(write!(&mut buf, "{}\n", raw));
        self.dev.request_write(&buf)
    }
}
<commit_msg>Make OACSP reject unsupported protocol versions<commit_after>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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/.

use std::io;
use std::io::{BufRead, Write};
use std::str::FromStr;

use domain::DomainDispatcher;
use io::*;
use proto::*;
use types::*;

mod input;
mod output;

use self::input::RawInputMessage;
use self::output::RawOutputMessage;

const PROTOCOL_VERSION: u16 = 2;

pub struct Oacsp {
    dev: Device,
    domains: DomainDispatcher,
    client_id: Option<String>
}

impl Oacsp {

    pub fn new(dev: Device, domains: DomainDispatcher) -> Oacsp {
        Oacsp { dev: dev, domains: domains, client_id: None }
    }

    fn line_is_ready(&self) -> bool {
        self.dev.recv_bytes().contains(&b'\n')
    }

    fn process_input(&mut self) -> io::Result<usize> {
        assert!(self.line_is_ready());
        let dev_id = self.dev.id();
        let mut buf = io::BufReader::new(self.dev.recv_bytes());
        let mut line = String::new();
        let nbytes = try!(buf.read_line(&mut line));
        let begin_received = self.client_id.is_some();
        match (try!(RawInputMessage::from_str(&line)), begin_received) {
            (RawInputMessage::Begin { version, client_id }, false) => {
                if version != PROTOCOL_VERSION {
                    let error = io::Error::new(
                        io::ErrorKind::InvalidInput,
                        format!("OACSP version {} is not supported by this version of FlightVars",
                        	version));
                    return Err(error);
                }
                info!("received a begin message from client {}", client_id);
            	self.client_id = Some(client_id);
            	Ok(nbytes)
            },
            (RawInputMessage::Begin { version: _, client_id: _ }, true) => {
				Err(io::Error::new(io::ErrorKind::InvalidData, "begin message already received"))
            }
            (RawInputMessage::WriteLvar { lvar, value }, true) => {
                debug!("received a WRITE_LVAR message from client {}: {} <- {}",
                    self.client_id_str(), lvar, value);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.write(&Var::Named(lvar), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::WriteOffset { offset, value }, true) => {
                debug!("received a WRITE_OFFSET message from client {}: {} <- {}",
                    self.client_id_str(), offset, value);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.write(&Var::Offset(offset), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveLvar { lvar }, true) => {
                debug!("received a OBSERVE_LVAR message from client {}: {}",
                    self.client_id_str(), lvar);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.subscribe(dev_id, &Var::Named(lvar))
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveOffset { offset }, true) => {
                debug!("received a OBSERVE_OFFSET message from client {}: {}",
                    self.client_id_str(), offset);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.subscribe(dev_id, &Var::Offset(offset))
                }));
                Ok(nbytes)
            }
            (_, false) =>  {
                let error = io::Error::new(
                    io::ErrorKind::InvalidData,
                    "unexpected message while waiting for begin");
				Err(error)
            }
        }
    }

    fn client_id_str(&self) -> &str {
        self.client_id
        	.as_ref()
        	.map(|id| id.as_str())
        	.unwrap_or("none")
    }
}

impl DeviceHandler for Oacsp {
    fn device(&mut self) -> &mut Device { &mut self.dev }

    fn process_event(&mut self, event: Event) -> io::Result<()> {
        match event {
            Event::Ready => self.dev.request_read(),
            Event::BytesRead(_) => {
                while self.line_is_ready() {
                    let nread = try!(self.process_input());
                    self.dev.consume_recv_buffer(nread);
                }
                self.dev.request_read()
            }
            Event::BytesWritten(_) => Ok(()),
        }
    }
}

impl Protocol for Oacsp {

    fn send_update(&mut self, domain: &str, variable: Var, value: Value) -> io::Result<()> {
        let raw = try!(match variable {
            Var::Offset(offset) if domain == "fsuipc" =>
                Ok(RawOutputMessage::EventOffset { offset: offset, value: value }),
            Var::Named(ref lvar) if domain == "lvar" =>
                Ok(RawOutputMessage::EventLvar { lvar: lvar.clone(), value: value }),
            _ => {
                Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("cannot encode a message for domain '{}', var '{:?}'",
                        domain, variable)))
            }
        });
        let mut buf = Vec::new();
        try!(write!(&mut buf, "{}\n", raw));
        self.dev.request_write(&buf)
    }
}
<|endoftext|>


<commit_before>//! The rule module provides a data structure, Rule, that implements the Operation trait.
//! Rule aims to be a drop-in for any algorithm in ai_kit that operates on the Operation trait.

use constraints::{Constraint, ConstraintValue};
use core::{Bindings, Operation, Unify};
use std;
use std::collections::HashMap;
use std::marker::PhantomData;
use utils;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Rule<T: ConstraintValue, U: Unify<T>> {
    #[serde(default)]
    pub constraints: Vec<Constraint>,
    #[serde(default = "Vec::default")]
    pub lhs: Vec<U>,
    pub rhs: U,
    #[serde(default)]
    pub _marker: PhantomData<T>,
}

impl<T, U> Operation<T, U> for Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
    fn input_patterns(&self) -> Vec<U> {
        self.lhs.clone()
    }

    fn apply_match(&self, bindings: &Bindings<T>) -> Option<Vec<U>> {
        self.solve_constraints(&bindings).and_then(|bindings| {
            self.rhs
                .apply_bindings(&bindings)
                .and_then(|bound_rhs| Some(vec![bound_rhs]))
        })
    }

    fn r_apply_match(&self, fact: &U) -> Option<(Vec<U>, Bindings<T>)> {
        self.rhs
            .unify(fact, &Bindings::new())
            .and_then(|bindings| self.solve_constraints(&bindings))
            .and_then(|bindings| {
                utils::map_while_some(&mut self.lhs.iter(), &|f| {
                    f.apply_bindings(&bindings)
                }).and_then(|inputs| Some((inputs, bindings)))
            })
    }

    fn constraints<'a>(&'a self) -> Vec<&'a Constraint> {
        self.constraints.iter().collect()
    }

    /// Construct a new version of this rule but with all variables updated to be unique for this invocation
    fn snowflake(&self, suffix: String) -> Self {
        // Gather all variables
        let mut variables = self.rhs.variables();
        for lhs in self.lhs.iter() {
            variables.extend(lhs.variables());
        }
        for constraint in self.constraints.iter() {
            variables.extend(constraint.variables());
        }

        let renamed_variable: HashMap<String, String> = variables
            .into_iter()
            .map(|var| (var.clone(), format!("{}::{}", var, suffix)))
            .collect();

        let rhs = self.rhs.rename_variables(&renamed_variable);
        let lhs: Vec<U> = self.lhs
            .iter()
            .map(|lhs| lhs.rename_variables(&renamed_variable))
            .collect();
        let constraints: Vec<Constraint> = self.constraints
            .iter()
            .map(|constraint| constraint.rename_variables(&renamed_variable))
            .collect();

        Rule {
            constraints: constraints,
            lhs: lhs,
            rhs: rhs,
            _marker: PhantomData,
        }
    }
}

impl<T, U> Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
    pub fn unify(&self, facts: &Vec<&U>, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        utils::fold_while_some(
            bindings.clone(),
            &mut self.lhs.iter().zip(facts.iter()),
            &|bindings, (t1, t2)| t1.unify(t2, &bindings),
        )
    }

    pub fn apply_bindings(&self, bindings: &Bindings<T>) -> Option<U> {
        self.rhs.apply_bindings(bindings)
    }

    fn solve_constraints(&self, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        Constraint::solve_many(self.constraints.iter().collect(), bindings).ok()
    }

    pub fn pprint(&self) -> String {
        let lhs_string_vec: Vec<String> = self.lhs.iter().map(|o| format!("{}", o)).collect();
        let lhs_string = lhs_string_vec.join("\n\t\t");
        let constraints_string_vec: Vec<String> = self.constraints.iter().map(|o| format!("{}", o)).collect();
        let constraints_string = constraints_string_vec.join("\n");
        format!(
            "Rule {{\n\tlhs:\n\t\t{},\n\trhs: {},\n\tconstraints: {} }}",
            lhs_string, self.rhs, constraints_string
        )
    }
}

impl<T, U> std::fmt::Display for Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.pprint())
    }
}

impl<T, U> Eq for Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
}

#[cfg(test)]
mod tests;
<commit_msg>Added helpful `new` function  for the `Rule` type<commit_after>//! The rule module provides a data structure, Rule, that implements the Operation trait.
//! Rule aims to be a drop-in for any algorithm in ai_kit that operates on the Operation trait.

use constraints::{Constraint, ConstraintValue};
use core::{Bindings, Operation, Unify};
use std;
use std::collections::HashMap;
use std::marker::PhantomData;
use utils;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Rule<T: ConstraintValue, U: Unify<T>> {
    #[serde(default)]
    pub constraints: Vec<Constraint>,
    #[serde(default = "Vec::default")]
    pub lhs: Vec<U>,
    pub rhs: U,
    #[serde(default)]
    pub _marker: PhantomData<T>,
}

impl<T, U> Operation<T, U> for Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
    fn input_patterns(&self) -> Vec<U> {
        self.lhs.clone()
    }

    fn apply_match(&self, bindings: &Bindings<T>) -> Option<Vec<U>> {
        self.solve_constraints(&bindings).and_then(|bindings| {
            self.rhs
                .apply_bindings(&bindings)
                .and_then(|bound_rhs| Some(vec![bound_rhs]))
        })
    }

    fn r_apply_match(&self, fact: &U) -> Option<(Vec<U>, Bindings<T>)> {
        self.rhs
            .unify(fact, &Bindings::new())
            .and_then(|bindings| self.solve_constraints(&bindings))
            .and_then(|bindings| {
                utils::map_while_some(&mut self.lhs.iter(), &|f| {
                    f.apply_bindings(&bindings)
                }).and_then(|inputs| Some((inputs, bindings)))
            })
    }

    fn constraints<'a>(&'a self) -> Vec<&'a Constraint> {
        self.constraints.iter().collect()
    }

    /// Construct a new version of this rule but with all variables updated to be unique for this invocation
    fn snowflake(&self, suffix: String) -> Self {
        // Gather all variables
        let mut variables = self.rhs.variables();
        for lhs in self.lhs.iter() {
            variables.extend(lhs.variables());
        }
        for constraint in self.constraints.iter() {
            variables.extend(constraint.variables());
        }

        let renamed_variable: HashMap<String, String> = variables
            .into_iter()
            .map(|var| (var.clone(), format!("{}::{}", var, suffix)))
            .collect();

        let rhs = self.rhs.rename_variables(&renamed_variable);
        let lhs: Vec<U> = self.lhs
            .iter()
            .map(|lhs| lhs.rename_variables(&renamed_variable))
            .collect();
        let constraints: Vec<Constraint> = self.constraints
            .iter()
            .map(|constraint| constraint.rename_variables(&renamed_variable))
            .collect();

        Rule {
            constraints: constraints,
            lhs: lhs,
            rhs: rhs,
            _marker: PhantomData,
        }
    }
}

impl<T, U> Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
    pub fn new(lhs: Vec<U>, rhs: U, constraints: Vec<Constraint>) -> Self {
        Rule {
            constraints: constraints,
            lhs: lhs,
            rhs: rhs,
            _marker: PhantomData,
        }
    }

    pub fn unify(&self, facts: &Vec<&U>, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        utils::fold_while_some(
            bindings.clone(),
            &mut self.lhs.iter().zip(facts.iter()),
            &|bindings, (t1, t2)| t1.unify(t2, &bindings),
        )
    }

    pub fn apply_bindings(&self, bindings: &Bindings<T>) -> Option<U> {
        self.rhs.apply_bindings(bindings)
    }

    fn solve_constraints(&self, bindings: &Bindings<T>) -> Option<Bindings<T>> {
        Constraint::solve_many(self.constraints.iter().collect(), bindings).ok()
    }

    pub fn pprint(&self) -> String {
        let lhs_string_vec: Vec<String> = self.lhs.iter().map(|o| format!("{}", o)).collect();
        let lhs_string = lhs_string_vec.join("\n\t\t");
        let constraints_string_vec: Vec<String> = self.constraints.iter().map(|o| format!("{}", o)).collect();
        let constraints_string = constraints_string_vec.join("\n");
        format!(
            "Rule {{\n\tlhs:\n\t\t{},\n\trhs: {},\n\tconstraints: {} }}",
            lhs_string, self.rhs, constraints_string
        )
    }
}

impl<T, U> std::fmt::Display for Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.pprint())
    }
}

impl<T, U> Eq for Rule<T, U>
where
    T: ConstraintValue,
    U: Unify<T>,
{
}

#[cfg(test)]
mod tests;
<|endoftext|>