<commit_before>use std;
use std::collections::{BTreeSet, HashMap};
use std::fmt::{Debug, Display, Formatter, Result};
use std::iter::{Extend, FromIterator};

use serde::{Deserialize, Serialize};

use sexp;

use constraints;

#[cfg(test)]
mod tests;

#[derive(Debug, Clone)]
pub struct FromSexpError {
    pub message: String,
}

pub trait ToSexp: Sized {
    fn to_sexp(&self) -> sexp::Sexp;
    fn from_sexp(&sexp::Sexp) -> std::result::Result<Self, FromSexpError>;

    fn from_sexp_string(s: &String) -> std::result::Result<Self, FromSexpError> {
        Self::from_sexp_str(s.as_str())
    }

    fn from_sexp_str(s: &str) -> std::result::Result<Self, FromSexpError> {
        match sexp::parse(s) {
            Ok(s_exp) => Self::from_sexp(&s_exp),
            Err(boxed_err) => {
                match *boxed_err {
                    sexp::Error { message, .. } => Err(FromSexpError { message: message.to_string() }),
                }
            }
        }
    }

    fn to_sexp_string(&self) -> String {
        self.to_sexp().to_string()
    }
}

pub trait BindingsValue
    : Clone + Debug + Deserialize + Display + Eq + PartialEq + PartialOrd + Serialize + ToSexp
    {
    fn to_float(&self) -> Option<f64>;
    fn from_float(f64) -> Self;
    fn variable(&self) -> Option<String>;
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct Bindings<T: BindingsValue> {
    data: HashMap<String, T>,
    equivalences: HashMap<String, BTreeSet<String>>,
}

impl<T: BindingsValue> PartialEq for Bindings<T> {
    fn eq(&self, other: &Bindings<T>) -> bool {
        self.data.eq(&other.data) && self.equivalences.eq(&other.equivalences)
    }
}
impl<T: BindingsValue> Eq for Bindings<T> {}

impl<T: BindingsValue> Bindings<T> {
    pub fn new() -> Bindings<T> {
        Bindings {
            data: HashMap::new(),
            equivalences: HashMap::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

    pub fn new_from_vec(data: Vec<(String, T)>) -> Bindings<T> {
        data.into_iter().collect()
    }

    pub fn has_binding(&self, variable: &String) -> bool {
        self.data.contains_key(variable)
    }

    pub fn set_binding(&self, variable: &String, val: T) -> Bindings<T> {
        let mut bindings_copy = self.clone();

        bindings_copy.set_binding_mut(variable, val);

        bindings_copy
    }

    pub fn set_binding_mut(&mut self, variable: &String, val: T) {
        self.ensure_equivalence_exists_mut(variable);

        if let Some(variable2) = val.variable() {
            self.add_equivalence(variable, &variable2);
        } else {
            for equivalent_variable in self.equivalences.get(variable).unwrap().iter() {
                self.data.insert(equivalent_variable.clone(), val.clone());
            }
        }
    }

    fn add_equivalence(&mut self, variable: &String, variable2: &String) {
        self.ensure_equivalence_exists_mut(&variable2);
        self.merge_equivalences_mut(variable, &variable2);
    }

    fn ensure_equivalence_exists_mut(&mut self, variable: &String) {
        if !self.equivalences.contains_key(variable) {
            self.equivalences.insert(variable.clone(),
                                     vec![variable.clone()].into_iter().collect());
        }
    }

    fn merge_equivalences_mut(&mut self, variable: &String, variable2: &String) {
        let mut merge = self.equivalences.get(variable).cloned().unwrap();
        merge.extend(self.equivalences.get(variable2).cloned().unwrap());

        self.equivalences.insert(variable.clone(), merge.clone());
        self.equivalences.insert(variable2.clone(), merge);
    }

    pub fn get_binding(&self, variable: &String) -> Option<T> {
        match self.data.get(variable) {
            Some(val) => Some(val.clone()),
            None => None,
        }
    }

    pub fn update_bindings(&self, variable: &String, value: &T) -> Option<Self> {
        // If we are setting a variable to itself, then do nothing
        if Some(variable.clone()) == value.variable() {
            return Some(self.clone());
        }
        match self.get_binding(&variable) {
            Some(ref val) if val == value => Some(self.clone()),
            Some(_) => None,
            None => Some(self.set_binding(variable, value.clone())),
        }
    }
}

impl<T: BindingsValue> FromIterator<(String, T)> for Bindings<T> {
    fn from_iter<I: IntoIterator<Item = (String, T)>>(iter: I) -> Bindings<T> {
        let mut bindings: Bindings<T> = Bindings::new();
        for (key, value) in iter {
            bindings.set_binding_mut(&key, value);
        }
        bindings
    }
}

impl<T: BindingsValue> Extend<(String, T)> for Bindings<T> {
    fn extend<It>(&mut self, iter: It)
        where It: IntoIterator<Item = (String, T)>
    {
        for (key, value) in iter {
            self.set_binding_mut(&key, value);
        }
    }
}

impl<T: BindingsValue> Display for Bindings<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        try!(write!(f, "("));
        for (key, val) in self.data.iter() {
            try!(write!(f, "{} => {}, ", key, val));
        }
        write!(f, ")")
    }
}

pub trait Unify<T: BindingsValue>
    : Clone + Debug + Display + Eq + Serialize + Deserialize + PartialEq {
    fn unify(&self, &Self, &Bindings<T>) -> Option<Bindings<T>>;
    fn apply_bindings(&self, &Bindings<T>) -> Option<Self>;
    fn constraints<'a>(&'a self) -> Vec<&'a constraints::Constraint<T>> {
        Vec::new()
    }
    fn variables(&self) -> Vec<String>;
    fn get_variable(&self, &String) -> Option<&T>;
    fn rename_variables(&self, &HashMap<String, String>) -> Self;
    fn nil() -> Self;
    fn equiv(&self, other: &Self) -> bool {
        self.unify(other, &Bindings::new()).is_some()
    }
}

pub trait Apply<T: BindingsValue, U: Unify<T>>
    : Clone + Debug + Display + Eq + PartialEq + Deserialize + Serialize {
    fn arg_count(&self) -> usize;
    fn apply(&self, &Vec<&U>, &Bindings<T>) -> Option<(U, Bindings<T>)>;
    fn constraints<'a>(&'a self) -> Vec<&'a constraints::Constraint<T>>;
    fn r_apply(&self, &U, &Bindings<T>) -> Option<(Vec<U>, Bindings<T>)>;
    fn snowflake(&self, String) -> Self;
}
<commit_msg>Removed unneeded trait<commit_after>use std;
use std::collections::{BTreeSet, HashMap};
use std::fmt::{Debug, Display, Formatter, Result};
use std::iter::{Extend, FromIterator};

use serde::{Deserialize, Serialize};

use sexp;

use constraints;

#[cfg(test)]
mod tests;

#[derive(Debug, Clone)]
pub struct FromSexpError {
    pub message: String,
}

pub trait ToSexp: Sized {
    fn to_sexp(&self) -> sexp::Sexp;
    fn from_sexp(&sexp::Sexp) -> std::result::Result<Self, FromSexpError>;

    fn from_sexp_string(s: &String) -> std::result::Result<Self, FromSexpError> {
        Self::from_sexp_str(s.as_str())
    }

    fn from_sexp_str(s: &str) -> std::result::Result<Self, FromSexpError> {
        match sexp::parse(s) {
            Ok(s_exp) => Self::from_sexp(&s_exp),
            Err(boxed_err) => {
                match *boxed_err {
                    sexp::Error { message, .. } => Err(FromSexpError { message: message.to_string() }),
                }
            }
        }
    }

    fn to_sexp_string(&self) -> String {
        self.to_sexp().to_string()
    }
}

pub trait BindingsValue
    : Clone + Debug + Deserialize + Display + Eq + PartialEq + PartialOrd + Serialize
    {
    fn to_float(&self) -> Option<f64>;
    fn from_float(f64) -> Self;
    fn variable(&self) -> Option<String>;
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct Bindings<T: BindingsValue> {
    data: HashMap<String, T>,
    equivalences: HashMap<String, BTreeSet<String>>,
}

impl<T: BindingsValue> PartialEq for Bindings<T> {
    fn eq(&self, other: &Bindings<T>) -> bool {
        self.data.eq(&other.data) && self.equivalences.eq(&other.equivalences)
    }
}
impl<T: BindingsValue> Eq for Bindings<T> {}

impl<T: BindingsValue> Bindings<T> {
    pub fn new() -> Bindings<T> {
        Bindings {
            data: HashMap::new(),
            equivalences: HashMap::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

    pub fn new_from_vec(data: Vec<(String, T)>) -> Bindings<T> {
        data.into_iter().collect()
    }

    pub fn has_binding(&self, variable: &String) -> bool {
        self.data.contains_key(variable)
    }

    pub fn set_binding(&self, variable: &String, val: T) -> Bindings<T> {
        let mut bindings_copy = self.clone();

        bindings_copy.set_binding_mut(variable, val);

        bindings_copy
    }

    pub fn set_binding_mut(&mut self, variable: &String, val: T) {
        self.ensure_equivalence_exists_mut(variable);

        if let Some(variable2) = val.variable() {
            self.add_equivalence(variable, &variable2);
        } else {
            for equivalent_variable in self.equivalences.get(variable).unwrap().iter() {
                self.data.insert(equivalent_variable.clone(), val.clone());
            }
        }
    }

    fn add_equivalence(&mut self, variable: &String, variable2: &String) {
        self.ensure_equivalence_exists_mut(&variable2);
        self.merge_equivalences_mut(variable, &variable2);
    }

    fn ensure_equivalence_exists_mut(&mut self, variable: &String) {
        if !self.equivalences.contains_key(variable) {
            self.equivalences.insert(variable.clone(),
                                     vec![variable.clone()].into_iter().collect());
        }
    }

    fn merge_equivalences_mut(&mut self, variable: &String, variable2: &String) {
        let mut merge = self.equivalences.get(variable).cloned().unwrap();
        merge.extend(self.equivalences.get(variable2).cloned().unwrap());

        self.equivalences.insert(variable.clone(), merge.clone());
        self.equivalences.insert(variable2.clone(), merge);
    }

    pub fn get_binding(&self, variable: &String) -> Option<T> {
        match self.data.get(variable) {
            Some(val) => Some(val.clone()),
            None => None,
        }
    }

    pub fn update_bindings(&self, variable: &String, value: &T) -> Option<Self> {
        // If we are setting a variable to itself, then do nothing
        if Some(variable.clone()) == value.variable() {
            return Some(self.clone());
        }
        match self.get_binding(&variable) {
            Some(ref val) if val == value => Some(self.clone()),
            Some(_) => None,
            None => Some(self.set_binding(variable, value.clone())),
        }
    }
}

impl<T: BindingsValue> FromIterator<(String, T)> for Bindings<T> {
    fn from_iter<I: IntoIterator<Item = (String, T)>>(iter: I) -> Bindings<T> {
        let mut bindings: Bindings<T> = Bindings::new();
        for (key, value) in iter {
            bindings.set_binding_mut(&key, value);
        }
        bindings
    }
}

impl<T: BindingsValue> Extend<(String, T)> for Bindings<T> {
    fn extend<It>(&mut self, iter: It)
        where It: IntoIterator<Item = (String, T)>
    {
        for (key, value) in iter {
            self.set_binding_mut(&key, value);
        }
    }
}

impl<T: BindingsValue> Display for Bindings<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        try!(write!(f, "("));
        for (key, val) in self.data.iter() {
            try!(write!(f, "{} => {}, ", key, val));
        }
        write!(f, ")")
    }
}

pub trait Unify<T: BindingsValue>
    : Clone + Debug + Display + Eq + Serialize + Deserialize + PartialEq {
    fn unify(&self, &Self, &Bindings<T>) -> Option<Bindings<T>>;
    fn apply_bindings(&self, &Bindings<T>) -> Option<Self>;
    fn constraints<'a>(&'a self) -> Vec<&'a constraints::Constraint<T>> {
        Vec::new()
    }
    fn variables(&self) -> Vec<String>;
    fn get_variable(&self, &String) -> Option<&T>;
    fn rename_variables(&self, &HashMap<String, String>) -> Self;
    fn nil() -> Self;
    fn equiv(&self, other: &Self) -> bool {
        self.unify(other, &Bindings::new()).is_some()
    }
}

pub trait Apply<T: BindingsValue, U: Unify<T>>
    : Clone + Debug + Display + Eq + PartialEq + Deserialize + Serialize {
    fn arg_count(&self) -> usize;
    fn apply(&self, &Vec<&U>, &Bindings<T>) -> Option<(U, Bindings<T>)>;
    fn constraints<'a>(&'a self) -> Vec<&'a constraints::Constraint<T>>;
    fn r_apply(&self, &U, &Bindings<T>) -> Option<(Vec<U>, Bindings<T>)>;
    fn snowflake(&self, String) -> Self;
}
<|endoftext|>


<commit_before>use std::collections::{BTreeSet, HashMap};
use std::fmt::{Debug, Display, Formatter, Result};
use std::iter::{Extend, FromIterator};

use serde::{Deserialize, Serialize};

#[cfg(feature = "with-constraint")]
use constraints;

#[cfg(test)]
mod tests;

pub trait BindingsValue
    : Clone + Debug + Default + Deserialize + Display + Eq + PartialEq + PartialOrd + Serialize
    {
    /// Construct a BindingsValue variable using the specified string as it's name
    fn variable(_s: &String) -> Option<Self> {
        None
    }
    /// Extract the name of this BindingsValue variable (if it is a variable)
    fn to_variable(&self) -> Option<String> {
        None
    }
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct Bindings<T: BindingsValue> {
    #[serde(default)]
    data: HashMap<String, T>,
    #[serde(default)]
    equivalences: HashMap<String, BTreeSet<String>>,
}

impl<T: BindingsValue> PartialEq for Bindings<T> {
    fn eq(&self, other: &Bindings<T>) -> bool {
        self.data.eq(&other.data) && self.equivalences.eq(&other.equivalences)
    }
}
impl<T: BindingsValue> Eq for Bindings<T> {}

impl<T: BindingsValue> Default for Bindings<T> {
    fn default() -> Self {
        Bindings {
            data: HashMap::new(),
            equivalences: HashMap::new(),
        }
    }
}

impl<T: BindingsValue> Bindings<T> {
    pub fn new() -> Bindings<T> {
        Bindings::default()
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

    pub fn new_from_vec(data: Vec<(String, T)>) -> Bindings<T> {
        data.into_iter().collect()
    }

    pub fn has_binding(&self, variable: &String) -> bool {
        self.data.contains_key(variable)
    }

    pub fn set_binding(&self, variable: &String, val: T) -> Bindings<T> {
        let mut bindings_copy = self.clone();

        bindings_copy.set_binding_mut(variable, val);

        bindings_copy
    }

    pub fn set_binding_mut(&mut self, variable: &String, val: T) {
        self.ensure_equivalence_exists_mut(variable);

        if let Some(variable2) = val.to_variable() {
            self.add_equivalence(variable, &variable2);
        } else {
            for equivalent_variable in self.equivalences.get(variable).unwrap().iter() {
                self.data.insert(equivalent_variable.clone(), val.clone());
            }
        }
    }

    fn add_equivalence(&mut self, variable: &String, variable2: &String) {
        self.ensure_equivalence_exists_mut(&variable2);
        self.merge_equivalences_mut(variable, &variable2);
    }

    fn ensure_equivalence_exists_mut(&mut self, variable: &String) {
        if !self.equivalences.contains_key(variable) {
            self.equivalences.insert(variable.clone(),
                                     vec![variable.clone()].into_iter().collect());
        }
    }

    fn merge_equivalences_mut(&mut self, variable: &String, variable2: &String) {
        let mut merge = self.equivalences.get(variable).cloned().unwrap();
        merge.extend(self.equivalences.get(variable2).cloned().unwrap());

        self.equivalences.insert(variable.clone(), merge.clone());
        self.equivalences.insert(variable2.clone(), merge);
    }

    pub fn get_binding(&self, variable: &String) -> Option<T> {
        match self.data.get(variable) {
            Some(val) => Some(val.clone()),
            None => None,
        }
    }

    pub fn update_bindings(&self, variable: &String, value: &T) -> Option<Self> {
        // If we are setting a variable to itself, then do nothing
        if Some(variable.clone()) == value.to_variable() {
            return Some(self.clone());
        }
        match self.get_binding(&variable) {
            Some(ref val) if val == value => Some(self.clone()),
            Some(_) => None,
            None => Some(self.set_binding(variable, value.clone())),
        }
    }

    pub fn merge(&self, other: &Self) -> Self {
        let mut bindings = self.clone();

        // Merge in equivalences
        for (ref key, ref equivalences) in other.equivalences.iter() {
            for equivalent_key in equivalences.iter() {
                bindings.ensure_equivalence_exists_mut(key);
                bindings.add_equivalence(key, equivalent_key);
            }
        }

        // Merge in values
        for (key, value) in other.data.iter() {
            bindings.set_binding_mut(&key, value.clone())
        }

        bindings
    }

    pub fn equivalences_string(&self) -> String {
        let equivalent_v: Vec<String> = self.equivalences.iter().map(|(key, value)| format!("{} => {:?}", key, value)).collect();
        equivalent_v.join(",")
    }
}

impl<T: BindingsValue> FromIterator<(String, T)> for Bindings<T> {
    fn from_iter<I: IntoIterator<Item = (String, T)>>(iter: I) -> Bindings<T> {
        let mut bindings: Bindings<T> = Bindings::new();
        for (key, value) in iter {
            bindings.set_binding_mut(&key, value);
        }
        bindings
    }
}

impl<T: BindingsValue> Extend<(String, T)> for Bindings<T> {
    fn extend<It>(&mut self, iter: It)
        where It: IntoIterator<Item = (String, T)>
    {
        for (key, value) in iter {
            self.set_binding_mut(&key, value);
        }
    }
}

impl<T: BindingsValue> Display for Bindings<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        try!(write!(f, "("));
        let mut sorted_keys: Vec<String> = self.data.keys().cloned().collect();
        sorted_keys.sort();
        for key in sorted_keys.into_iter() {
            let ref val = self.data[&key];
            try!(write!(f, "{} => {}, ", key, val));
        }
        try!(write!(f, "Equivalences: {}", self.equivalences_string()));
        write!(f, ")")
    }
}

pub trait Unify<T: BindingsValue>
    : Clone + Debug + Display + Eq + Serialize + Deserialize + PartialEq {
    fn unify(&self, &Self, &Bindings<T>) -> Option<Bindings<T>>;
    fn apply_bindings(&self, &Bindings<T>) -> Option<Self>;
    // NOTE: replace constraints with validate_bindings
    #[cfg(feature = "with-constraint")]
    fn constraints<'a>(&'a self) -> Vec<&'a constraints::Constraint> {
        Vec::new()
    }
    fn variables(&self) -> Vec<String>;
    fn get_variable(&self, &String) -> Option<&T>;
    fn rename_variables(&self, &HashMap<String, String>) -> Self;
    fn nil() -> Self;
    fn equiv(&self, other: &Self) -> bool {
        self.unify(other, &Bindings::new()).is_some()
    }
}

pub trait Apply<T: BindingsValue, U: Unify<T>>
    : Clone + Debug + Display + Eq + PartialEq + Deserialize + Serialize {
    fn arg_count(&self) -> usize;
    fn apply(&self, &Vec<&U>, &Bindings<T>) -> Option<(U, Bindings<T>)>;
    #[cfg(feature = "with-constraint")]
    fn constraints<'a>(&'a self) -> Vec<&'a constraints::Constraint>;
    fn r_apply(&self, &U, &Bindings<T>) -> Option<(Vec<U>, Bindings<T>)>;
    fn snowflake(&self, String) -> Self;
}
<commit_msg>Removed unneeded method<commit_after>use std::collections::{BTreeSet, HashMap};
use std::fmt::{Debug, Display, Formatter, Result};
use std::iter::{Extend, FromIterator};

use serde::{Deserialize, Serialize};

#[cfg(feature = "with-constraint")]
use constraints;

#[cfg(test)]
mod tests;

pub trait BindingsValue
    : Clone + Debug + Default + Deserialize + Display + Eq + PartialEq + PartialOrd + Serialize
    {
    /// Construct a BindingsValue variable using the specified string as it's name
    fn variable(_s: &String) -> Option<Self> {
        None
    }
    /// Extract the name of this BindingsValue variable (if it is a variable)
    fn to_variable(&self) -> Option<String> {
        None
    }
}

#[derive(Debug, Deserialize, Clone, Serialize)]
pub struct Bindings<T: BindingsValue> {
    #[serde(default)]
    data: HashMap<String, T>,
    #[serde(default)]
    equivalences: HashMap<String, BTreeSet<String>>,
}

impl<T: BindingsValue> PartialEq for Bindings<T> {
    fn eq(&self, other: &Bindings<T>) -> bool {
        self.data.eq(&other.data) && self.equivalences.eq(&other.equivalences)
    }
}
impl<T: BindingsValue> Eq for Bindings<T> {}

impl<T: BindingsValue> Default for Bindings<T> {
    fn default() -> Self {
        Bindings {
            data: HashMap::new(),
            equivalences: HashMap::new(),
        }
    }
}

impl<T: BindingsValue> Bindings<T> {
    pub fn new() -> Bindings<T> {
        Bindings::default()
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }

    pub fn new_from_vec(data: Vec<(String, T)>) -> Bindings<T> {
        data.into_iter().collect()
    }

    pub fn has_binding(&self, variable: &String) -> bool {
        self.data.contains_key(variable)
    }

    pub fn set_binding(&self, variable: &String, val: T) -> Bindings<T> {
        let mut bindings_copy = self.clone();

        bindings_copy.set_binding_mut(variable, val);

        bindings_copy
    }

    pub fn set_binding_mut(&mut self, variable: &String, val: T) {
        self.ensure_equivalence_exists_mut(variable);

        if let Some(variable2) = val.to_variable() {
            self.add_equivalence(variable, &variable2);
        } else {
            for equivalent_variable in self.equivalences.get(variable).unwrap().iter() {
                self.data.insert(equivalent_variable.clone(), val.clone());
            }
        }
    }

    fn add_equivalence(&mut self, variable: &String, variable2: &String) {
        self.ensure_equivalence_exists_mut(&variable2);
        self.merge_equivalences_mut(variable, &variable2);
    }

    fn ensure_equivalence_exists_mut(&mut self, variable: &String) {
        if !self.equivalences.contains_key(variable) {
            self.equivalences.insert(variable.clone(),
                                     vec![variable.clone()].into_iter().collect());
        }
    }

    fn merge_equivalences_mut(&mut self, variable: &String, variable2: &String) {
        let mut merge = self.equivalences.get(variable).cloned().unwrap();
        merge.extend(self.equivalences.get(variable2).cloned().unwrap());

        self.equivalences.insert(variable.clone(), merge.clone());
        self.equivalences.insert(variable2.clone(), merge);
    }

    pub fn get_binding(&self, variable: &String) -> Option<T> {
        match self.data.get(variable) {
            Some(val) => Some(val.clone()),
            None => None,
        }
    }

    pub fn update_bindings(&self, variable: &String, value: &T) -> Option<Self> {
        // If we are setting a variable to itself, then do nothing
        if Some(variable.clone()) == value.to_variable() {
            return Some(self.clone());
        }
        match self.get_binding(&variable) {
            Some(ref val) if val == value => Some(self.clone()),
            Some(_) => None,
            None => Some(self.set_binding(variable, value.clone())),
        }
    }

    pub fn merge(&self, other: &Self) -> Self {
        let mut bindings = self.clone();

        // Merge in equivalences
        for (ref key, ref equivalences) in other.equivalences.iter() {
            for equivalent_key in equivalences.iter() {
                bindings.ensure_equivalence_exists_mut(key);
                bindings.add_equivalence(key, equivalent_key);
            }
        }

        // Merge in values
        for (key, value) in other.data.iter() {
            bindings.set_binding_mut(&key, value.clone())
        }

        bindings
    }

    pub fn equivalences_string(&self) -> String {
        let equivalent_v: Vec<String> = self.equivalences.iter().map(|(key, value)| format!("{} => {:?}", key, value)).collect();
        equivalent_v.join(",")
    }
}

impl<T: BindingsValue> FromIterator<(String, T)> for Bindings<T> {
    fn from_iter<I: IntoIterator<Item = (String, T)>>(iter: I) -> Bindings<T> {
        let mut bindings: Bindings<T> = Bindings::new();
        for (key, value) in iter {
            bindings.set_binding_mut(&key, value);
        }
        bindings
    }
}

impl<T: BindingsValue> Extend<(String, T)> for Bindings<T> {
    fn extend<It>(&mut self, iter: It)
        where It: IntoIterator<Item = (String, T)>
    {
        for (key, value) in iter {
            self.set_binding_mut(&key, value);
        }
    }
}

impl<T: BindingsValue> Display for Bindings<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        try!(write!(f, "("));
        let mut sorted_keys: Vec<String> = self.data.keys().cloned().collect();
        sorted_keys.sort();
        for key in sorted_keys.into_iter() {
            let ref val = self.data[&key];
            try!(write!(f, "{} => {}, ", key, val));
        }
        try!(write!(f, "Equivalences: {}", self.equivalences_string()));
        write!(f, ")")
    }
}

pub trait Unify<T: BindingsValue>
    : Clone + Debug + Display + Eq + Serialize + Deserialize + PartialEq {
    fn unify(&self, &Self, &Bindings<T>) -> Option<Bindings<T>>;
    fn apply_bindings(&self, &Bindings<T>) -> Option<Self>;
    fn variables(&self) -> Vec<String>;
    fn get_variable(&self, &String) -> Option<&T>;
    fn rename_variables(&self, &HashMap<String, String>) -> Self;
    fn nil() -> Self;
    fn equiv(&self, other: &Self) -> bool {
        self.unify(other, &Bindings::new()).is_some()
    }
}

pub trait Apply<T: BindingsValue, U: Unify<T>>
    : Clone + Debug + Display + Eq + PartialEq + Deserialize + Serialize {
    fn arg_count(&self) -> usize;
    fn apply(&self, &Vec<&U>, &Bindings<T>) -> Option<(U, Bindings<T>)>;
    #[cfg(feature = "with-constraint")]
    fn constraints<'a>(&'a self) -> Vec<&'a constraints::Constraint>;
    fn r_apply(&self, &U, &Bindings<T>) -> Option<(Vec<U>, Bindings<T>)>;
    fn snowflake(&self, String) -> Self;
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

// Passing structs via FFI should work regardless of whether
// they get passed in multiple registers, byval pointers or the stack

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct Rect {
    a: i32,
    b: i32,
    c: i32,
    d: i32
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct BiggerRect {
    s: Rect,
    a: i32,
    b: i32
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct FloatRect {
    a: i32,
    b: i32,
    c: f64
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct Huge {
    a: i32,
    b: i32,
    c: i32,
    d: i32,
    e: i32
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct FloatPoint {
    x: f64,
    y: f64
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct FloatOne {
    x: f64,
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct IntOdd {
    a: i8,
    b: i8,
    c: i8,
}

#[link(name = "test", kind = "static")]
extern {
    fn byval_rect(a: i32, b: i32, c: i32, d: i32, e: i32, s: Rect);

    fn byval_many_rect(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, s: Rect);

    fn byval_rect_floats(a: f32, b: f32, c: f64, d: f32, e: f32,
                         f: f32, g: f64, s: Rect, t: FloatRect);

    fn byval_rect_with_float(a: i32, b: i32, c: f32, d: i32, e: i32, f: i32, s: Rect);

    fn byval_rect_with_many_huge(a: Huge, b: Huge, c: Huge, d: Huge, e: Huge, f: Huge, g: Rect);

    fn split_rect(a: i32, b: i32, s: Rect);

    fn split_rect_floats(a: f32, b: f32, s: FloatRect);

    fn split_rect_with_floats(a: i32, b: i32, c: f32, d: i32, e: f32, f: i32, s: Rect);

    fn split_and_byval_rect(a: i32, b: i32, c: i32, s: Rect, t: Rect);

    fn split_ret_byval_struct(a: i32, b: i32, s: Rect) -> Rect;

    fn sret_byval_struct(a: i32, b: i32, c: i32, d: i32, s: Rect) -> BiggerRect;

    fn sret_split_struct(a: i32, b: i32, s: Rect) -> BiggerRect;

    fn huge_struct(s: Huge) -> Huge;

    fn float_point(p: FloatPoint) -> FloatPoint;

    fn float_one(f: FloatOne) -> FloatOne;

    fn int_odd(i: IntOdd) -> IntOdd;
}

fn main() {
    let s = Rect { a: 553, b: 554, c: 555, d: 556 };
    let t = BiggerRect { s: s, a: 27834, b: 7657 };
    let u = FloatRect { a: 3489, b: 3490, c: 8. };
    let v = Huge { a: 5647, b: 5648, c: 5649, d: 5650, e: 5651 };
    let p = FloatPoint { x: 5., y: -3. };
    let f1 = FloatOne { x: 7. };
    let i = IntOdd { a: 1, b: 2, c: 3 };

    unsafe {
        byval_rect(1, 2, 3, 4, 5, s);
        byval_many_rect(1, 2, 3, 4, 5, 6, s);
        byval_rect_floats(1., 2., 3., 4., 5., 6., 7., s, u);
        byval_rect_with_float(1, 2, 3.0, 4, 5, 6, s);
        byval_rect_with_many_huge(v, v, v, v, v, v, Rect {
            a: 123,
            b: 456,
            c: 789,
            d: 420
        });
        split_rect(1, 2, s);
        split_rect_floats(1., 2., u);
        split_rect_with_floats(1, 2, 3.0, 4, 5.0, 6, s);
        split_and_byval_rect(1, 2, 3, s, s);
        split_rect(1, 2, s);
        assert_eq!(huge_struct(v), v);
        assert_eq!(split_ret_byval_struct(1, 2, s), s);
        assert_eq!(sret_byval_struct(1, 2, 3, 4, s), t);
        assert_eq!(sret_split_struct(1, 2, s), t);
        assert_eq!(float_point(p), p);
        assert_eq!(float_one(f1), f1);
        assert_eq!(int_odd(i), i);
    }
}
<commit_msg>Exclude x86_64-pc-windows-gnu from the float_one test<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

// Passing structs via FFI should work regardless of whether
// they get passed in multiple registers, byval pointers or the stack

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct Rect {
    a: i32,
    b: i32,
    c: i32,
    d: i32
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct BiggerRect {
    s: Rect,
    a: i32,
    b: i32
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct FloatRect {
    a: i32,
    b: i32,
    c: f64
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct Huge {
    a: i32,
    b: i32,
    c: i32,
    d: i32,
    e: i32
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct FloatPoint {
    x: f64,
    y: f64
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct FloatOne {
    x: f64,
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
struct IntOdd {
    a: i8,
    b: i8,
    c: i8,
}

#[link(name = "test", kind = "static")]
extern {
    fn byval_rect(a: i32, b: i32, c: i32, d: i32, e: i32, s: Rect);

    fn byval_many_rect(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, s: Rect);

    fn byval_rect_floats(a: f32, b: f32, c: f64, d: f32, e: f32,
                         f: f32, g: f64, s: Rect, t: FloatRect);

    fn byval_rect_with_float(a: i32, b: i32, c: f32, d: i32, e: i32, f: i32, s: Rect);

    fn byval_rect_with_many_huge(a: Huge, b: Huge, c: Huge, d: Huge, e: Huge, f: Huge, g: Rect);

    fn split_rect(a: i32, b: i32, s: Rect);

    fn split_rect_floats(a: f32, b: f32, s: FloatRect);

    fn split_rect_with_floats(a: i32, b: i32, c: f32, d: i32, e: f32, f: i32, s: Rect);

    fn split_and_byval_rect(a: i32, b: i32, c: i32, s: Rect, t: Rect);

    fn split_ret_byval_struct(a: i32, b: i32, s: Rect) -> Rect;

    fn sret_byval_struct(a: i32, b: i32, c: i32, d: i32, s: Rect) -> BiggerRect;

    fn sret_split_struct(a: i32, b: i32, s: Rect) -> BiggerRect;

    fn huge_struct(s: Huge) -> Huge;

    fn float_point(p: FloatPoint) -> FloatPoint;

    fn float_one(f: FloatOne) -> FloatOne;

    fn int_odd(i: IntOdd) -> IntOdd;
}

fn main() {
    let s = Rect { a: 553, b: 554, c: 555, d: 556 };
    let t = BiggerRect { s: s, a: 27834, b: 7657 };
    let u = FloatRect { a: 3489, b: 3490, c: 8. };
    let v = Huge { a: 5647, b: 5648, c: 5649, d: 5650, e: 5651 };
    let p = FloatPoint { x: 5., y: -3. };
    let f1 = FloatOne { x: 7. };
    let i = IntOdd { a: 1, b: 2, c: 3 };

    unsafe {
        byval_rect(1, 2, 3, 4, 5, s);
        byval_many_rect(1, 2, 3, 4, 5, 6, s);
        byval_rect_floats(1., 2., 3., 4., 5., 6., 7., s, u);
        byval_rect_with_float(1, 2, 3.0, 4, 5, 6, s);
        byval_rect_with_many_huge(v, v, v, v, v, v, Rect {
            a: 123,
            b: 456,
            c: 789,
            d: 420
        });
        split_rect(1, 2, s);
        split_rect_floats(1., 2., u);
        split_rect_with_floats(1, 2, 3.0, 4, 5.0, 6, s);
        split_and_byval_rect(1, 2, 3, s, s);
        split_rect(1, 2, s);
        assert_eq!(huge_struct(v), v);
        assert_eq!(split_ret_byval_struct(1, 2, s), s);
        assert_eq!(sret_byval_struct(1, 2, 3, 4, s), t);
        assert_eq!(sret_split_struct(1, 2, s), t);
        assert_eq!(float_point(p), p);
        assert_eq!(int_odd(i), i);

        // mingw64-gcc uses the wrong ABI:
        // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82028
        #[cfg(not(all(windows, target_arch = "x86_64", target_env = "gnu")))]
        assert_eq!(float_one(f1), f1);
    }
}
<|endoftext|>


<commit_before>use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use postgres::types::{FromSql, ToSql, Type, SessionInfo, IsNull};
use postgres::error::Error;
use std::io::{Read, Write};
use std::ops::{Add, Sub};
use std::fmt;
use num::integer::{div_rem};

// This is a really stupid currency type. We'll improve this later.
// It mainly exists to provide an implementation of the MONEY type in Postgres.
#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug)]
pub struct Currency(i64);

impl Add for Currency {
    type Output = Currency;

    fn add(self, other: Currency) -> Currency {
        Currency(self.0 + other.0)
    }
}

impl Sub for Currency {
    type Output = Currency;

    fn sub(self, other: Currency) -> Currency {
        Currency(self.0 - other.0)
    }
}

// TODO: Have a currency display that understands locale.
impl fmt::Display for Currency {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (top, rem) = div_rem(self.0, 100);
        write!(f, "£{}.{:02}", top, rem)
    }
}

impl FromSql for Currency {
    fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<Currency, Error> {
        // Money is sent simply as an int64.
        let t = try!(raw.read_i64::<BigEndian>());
        Ok(Currency(t))
    }

    accepts!(Type::Money);
}

impl ToSql for Currency {
    fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull, Error> {
        let Currency(val) = *self;
        try!(w.write_i64::<BigEndian>(val));
        Ok(IsNull::No)
    }

    accepts!(Type::Money);
    to_sql_checked!();
}<commit_msg>Add test from_str implementation for Currency<commit_after>use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian};
use postgres::types::{FromSql, ToSql, Type, SessionInfo, IsNull};
use postgres::error::Error;
use std::io::{Read, Write};
use std::ops::{Add, Sub};
use std::fmt;
use num::integer::{div_rem};

// This is a really stupid currency type. We'll improve this later.
// It mainly exists to provide an implementation of the MONEY type in Postgres.
#[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug)]
pub struct Currency(i64);

impl Add for Currency {
    type Output = Currency;

    fn add(self, other: Currency) -> Currency {
        Currency(self.0 + other.0)
    }
}

impl Sub for Currency {
    type Output = Currency;

    fn sub(self, other: Currency) -> Currency {
        Currency(self.0 - other.0)
    }
}

// TODO: Have a currency display that understands locale.
impl fmt::Display for Currency {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (top, rem) = div_rem(self.0, 100);
        write!(f, "£{}.{:02}", top, rem)
    }
}

impl ::std::str::FromStr for Currency {
    type Err = &'static str;

    fn from_str(s: &str) -> Result<Currency, &'static str> {
        let mut result: i64 = 0;
        let mut chars: Vec<char> = s.chars().collect();

        // Remove currency symbol.
        chars.remove(0);

        for c in chars {
            match c {
                '0' ... '9' => {result = (result * 10) + (c as i64 - '0' as i64)}
                '.' | ',' | ' ' => {}  // Ignore punctuation
                _ => {return Err("invalid character")}
            }
        }

        Ok(Currency(result))
    }
}

impl FromSql for Currency {
    fn from_sql<R: Read>(_: &Type, raw: &mut R, _: &SessionInfo) -> Result<Currency, Error> {
        // Money is sent simply as an int64.
        let t = try!(raw.read_i64::<BigEndian>());
        Ok(Currency(t))
    }

    accepts!(Type::Money);
}

impl ToSql for Currency {
    fn to_sql<W: Write+?Sized>(&self, _: &Type, mut w: &mut W, _: &SessionInfo) -> Result<IsNull, Error> {
        let Currency(val) = *self;
        try!(w.write_i64::<BigEndian>(val));
        Ok(IsNull::No)
    }

    accepts!(Type::Money);
    to_sql_checked!();
}<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::default::Default;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

use digest::Digest;
use ignore::WalkBuilder;
use serde_bytes;
use serde_cbor;
use serde_json;

use sha2;
use sha3;

use error;

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Database(Entry);

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Entry {
    Directory(BTreeMap<PathBuf, Entry>),
    File(Metrics),
}

impl Default for Entry {
    fn default() -> Entry {
        Entry::Directory(BTreeMap::default())
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Metrics {
    sha2: HashSum,
    sha3: HashSum,
    size: u64,      // File size
    nul: bool,      // Does the file contain a NUL byte?
    nonascii: bool, // Does the file contain non-ASCII bytes?
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HashSum(#[serde(with = "serde_bytes")] Vec<u8>);

#[derive(Default)]
struct EngineSize(u64);
impl EngineSize {
    fn input(&mut self, input: &[u8]) {
        self.0 += input.len() as u64;
    }
    fn result(self) -> u64 {
        self.0
    }
}

#[derive(Default)]
struct EngineNul(bool);
impl EngineNul {
    fn input(&mut self, input: &[u8]) {
        self.0 = self.0 || input.iter().any(|x| *x == 0);
    }
    fn result(self) -> bool {
        self.0
    }
}

#[derive(Default)]
struct EngineNonascii(bool);
impl EngineNonascii {
    fn input(&mut self, input: &[u8]) {
        self.0 = self.0 || input.iter().any(|x| x & 0x80 != 0);
    }
    fn result(self) -> bool {
        self.0
    }
}

#[derive(Default)]
struct Engines {
    sha2: sha2::Sha256,
    sha3: sha3::Sha3_256,
    size: EngineSize,
    nul: EngineNul,
    nonascii: EngineNonascii,
}

impl Engines {
    fn input(&mut self, input: &[u8]) {
        self.sha2.input(input);
        self.sha3.input(input);
        self.size.input(input);
        self.nul.input(input);
        self.nonascii.input(input);
    }
    fn result(self) -> Metrics {
        Metrics {
            sha2: HashSum(Vec::from(self.sha2.result().as_slice())),
            sha3: HashSum(Vec::from(self.sha3.result().as_slice())),
            size: self.size.result(),
            nul: self.nul.result(),
            nonascii: self.nonascii.result(),
        }
    }
}

fn compute_metrics<P>(path: P) -> Result<Metrics, error::Error>
where
    P: AsRef<Path>
{
    let mut f = File::open(path)?;

    let mut engines = Engines::default();

    let mut buffer = [0; 4096];
    loop {
        let n = f.read(&mut buffer[..])?;
        if n == 0 { break }
        engines.input(&buffer[0..n]);
    }
    Ok(engines.result())
}

trait BTreeMapExt<K, V> where K: Ord, V: Default {
    fn get_mut_default(&mut self, key: K) -> &mut V;
}

impl<K, V> BTreeMapExt<K, V> for BTreeMap<K, V>
where
    K: Ord + Clone,
    V: Default,
{
    fn get_mut_default(&mut self, key: K) -> &mut V {
        self.entry(key).or_insert(V::default())
    }
}

impl Entry {
    fn insert(&mut self, path: PathBuf, file: Entry) {
        // Inner nodes in the tree should always be directories. If
        // the node is not a directory, that means we are inserting a
        // duplicate file. However, this function is only called from
        // the directory walker, which makes it impossible to observe
        // any duplicates. (And the database, after construction, is
        // always immutable.)
        match self {
            &mut Entry::Directory(ref mut entries) => {
                let mut components = path.components();
                let count = components.clone().count();
                let first = Path::new(components.next().expect("unreachable").as_os_str()).to_owned();
                let rest = components.as_path().to_owned();
                if count > 1 {
                    let mut subentry = entries.get_mut_default(first);
                    subentry.insert(rest, file);
                } else {
                    match entries.insert(first, file) {
                        Some(_) => unreachable!(), // See above
                        None => (),
                    }
                }
            }
            &mut Entry::File(_) => unreachable!()
        }
    }

    fn lookup(&self, path: &PathBuf) -> Option<&Entry> {
        match *self {
            Entry::Directory(ref entries) => {
                let mut components = path.components();
                let count = components.clone().count();
                let first = Path::new(components.next().expect("unreachable").as_os_str()).to_owned();
                let rest = components.as_path().to_owned();
                if count > 1 {
                    entries.get(&first).and_then(
                        |subentry| subentry.lookup(&rest))
                } else {
                    entries.get(&first)
                }
            }
            Entry::File(_) => unreachable!()
        }
    }
}

fn summarize<P>(path: P, expected: &Metrics, actual: &Metrics)
where
    P: AsRef<Path>,
{
    let diff = expected.size != actual.size ||
        expected.sha2 != actual.sha2 ||
        expected.sha3 != actual.sha3;
    if diff {
        let suspicious_size = expected.size > 0 && actual.size == 0;
        let suspicious_nul = expected.nul != actual.nul;
        let suspicious_nonascii = expected.nonascii != actual.nonascii;
        println!("{} differs", path.as_ref().display());
        if suspicious_size {
            println!("  suspicious: file was truncated");
        }
        if suspicious_nul {
            println!("  suspicious: original had no NUL bytes, but now does");
        }
        if suspicious_nonascii {
            println!("  suspicious: original had no non-ASCII bytes, but now does");
        }
    }
}

impl Database {
    fn insert(&mut self, path: PathBuf, entry: Entry) {
        self.0.insert(path, entry);
    }

    fn lookup(&self, path: &PathBuf) -> Option<&Entry> {
        self.0.lookup(path)
    }

    pub fn build<P>(root: P) -> Result<Database, error::Error>
    where
        P: AsRef<Path>,
    {
        let mut database = Database::default();
        for entry in WalkBuilder::new(&root).build() {
            let entry = entry?;
            if entry.file_type().map_or(false, |t| t.is_file()) {
                let metrics = compute_metrics(entry.path())?;
                let result = Entry::File(metrics);
                let short_path = if entry.path() == root.as_ref() {
                    Path::new(entry.path().file_name().expect("unreachable"))
                } else {
                    entry.path().strip_prefix(&root)?
                };
                database.insert(short_path.to_owned(), result);
            }
        }
        Ok(database)
    }

    pub fn check<P>(&self, root: P) -> Result<(), error::Error>
    where
        P: AsRef<Path>,
    {
        // FIXME: Doesn't check for missing entries
        for entry in WalkBuilder::new(&root).build() {
            let entry = entry?;
            if entry.file_type().map_or(false, |t| t.is_file()) {
                let actual = compute_metrics(entry.path())?;

                let short_path = if entry.path() == root.as_ref() {
                    Path::new(entry.path().file_name().expect("unreachable"))
                } else {
                    entry.path().strip_prefix(&root)?
                };
                let expected = self.lookup(&short_path.to_owned());
                match expected {
                    Some(&Entry::File(ref metrics)) => summarize(
                        short_path, metrics, &actual),
                    Some(&Entry::Directory(_)) => unreachable!(),
                    None => println!("{} was created", short_path.display()),
                }
            }
        }
        Ok(())
    }

    pub fn load_json<P>(path: P) -> Result<Database, error::Error>
    where
        P: AsRef<Path>
    {
        let f = File::open(path)?;
        Ok(serde_json::from_reader(f)?)
    }

    pub fn dump_json<P>(&self, path: P) -> Result<(), error::Error>
    where
        P: AsRef<Path>
    {
        let json = serde_json::to_string(self)?;
        let mut f = File::create(path)?;
        write!(f, "{}", json)?;
        Ok(())
    }

    pub fn load_cbor<P>(path: P) -> Result<Database, error::Error>
    where
        P: AsRef<Path>
    {
        let f = File::open(path)?;
        Ok(serde_cbor::from_reader(f)?)
    }

    pub fn dump_cbor<P>(&self, path: P) -> Result<(), error::Error>
    where
        P: AsRef<Path>
    {
        let cbor = serde_cbor::to_vec(self)?;
        let mut f = File::create(path)?;
        f.write_all(cbor.as_slice())?;
        Ok(())
    }
}

// impl std::fmt::Display for Database {
//     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
//         for (path, entry) in self.0.iter() {
//             match entry {
//                 &Entry::File(ref hashes) => {
//                     let hash: Vec<_> = hashes.sha2.0.iter().map(
//                         |b| format!("{:02x}", b)).collect();
//                     writeln!(f, "{} {}", hash.join(""), Path::new(path).display())?
//                 }
//             }
//         }
//         Ok(())
//     }
// }
<commit_msg>Avoid creating an empty on get_default when it isn't needed.<commit_after>use std::collections::BTreeMap;
use std::default::Default;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

use digest::Digest;
use ignore::WalkBuilder;
use serde_bytes;
use serde_cbor;
use serde_json;

use sha2;
use sha3;

use error;

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Database(Entry);

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Entry {
    Directory(BTreeMap<PathBuf, Entry>),
    File(Metrics),
}

impl Default for Entry {
    fn default() -> Entry {
        Entry::Directory(BTreeMap::default())
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Metrics {
    sha2: HashSum,
    sha3: HashSum,
    size: u64,      // File size
    nul: bool,      // Does the file contain a NUL byte?
    nonascii: bool, // Does the file contain non-ASCII bytes?
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HashSum(#[serde(with = "serde_bytes")] Vec<u8>);

#[derive(Default)]
struct EngineSize(u64);
impl EngineSize {
    fn input(&mut self, input: &[u8]) {
        self.0 += input.len() as u64;
    }
    fn result(self) -> u64 {
        self.0
    }
}

#[derive(Default)]
struct EngineNul(bool);
impl EngineNul {
    fn input(&mut self, input: &[u8]) {
        self.0 = self.0 || input.iter().any(|x| *x == 0);
    }
    fn result(self) -> bool {
        self.0
    }
}

#[derive(Default)]
struct EngineNonascii(bool);
impl EngineNonascii {
    fn input(&mut self, input: &[u8]) {
        self.0 = self.0 || input.iter().any(|x| x & 0x80 != 0);
    }
    fn result(self) -> bool {
        self.0
    }
}

#[derive(Default)]
struct Engines {
    sha2: sha2::Sha256,
    sha3: sha3::Sha3_256,
    size: EngineSize,
    nul: EngineNul,
    nonascii: EngineNonascii,
}

impl Engines {
    fn input(&mut self, input: &[u8]) {
        self.sha2.input(input);
        self.sha3.input(input);
        self.size.input(input);
        self.nul.input(input);
        self.nonascii.input(input);
    }
    fn result(self) -> Metrics {
        Metrics {
            sha2: HashSum(Vec::from(self.sha2.result().as_slice())),
            sha3: HashSum(Vec::from(self.sha3.result().as_slice())),
            size: self.size.result(),
            nul: self.nul.result(),
            nonascii: self.nonascii.result(),
        }
    }
}

fn compute_metrics<P>(path: P) -> Result<Metrics, error::Error>
where
    P: AsRef<Path>
{
    let mut f = File::open(path)?;

    let mut engines = Engines::default();

    let mut buffer = [0; 4096];
    loop {
        let n = f.read(&mut buffer[..])?;
        if n == 0 { break }
        engines.input(&buffer[0..n]);
    }
    Ok(engines.result())
}

trait BTreeMapExt<K, V> where K: Ord, V: Default {
    fn get_default(&mut self, key: K) -> &mut V;
}

impl<K, V> BTreeMapExt<K, V> for BTreeMap<K, V>
where
    K: Ord + Clone,
    V: Default,
{
    fn get_default(&mut self, key: K) -> &mut V {
        self.entry(key).or_insert_with(|| V::default())
    }
}

impl Entry {
    fn insert(&mut self, path: PathBuf, file: Entry) {
        // Inner nodes in the tree should always be directories. If
        // the node is not a directory, that means we are inserting a
        // duplicate file. However, this function is only called from
        // the directory walker, which makes it impossible to observe
        // any duplicates. (And the database, after construction, is
        // always immutable.)
        match self {
            &mut Entry::Directory(ref mut entries) => {
                let mut components = path.components();
                let count = components.clone().count();
                let first = Path::new(components.next().expect("unreachable").as_os_str()).to_owned();
                let rest = components.as_path().to_owned();
                if count > 1 {
                    let mut subentry = entries.get_default(first);
                    subentry.insert(rest, file);
                } else {
                    match entries.insert(first, file) {
                        Some(_) => unreachable!(), // See above
                        None => (),
                    }
                }
            }
            &mut Entry::File(_) => unreachable!()
        }
    }

    fn lookup(&self, path: &PathBuf) -> Option<&Entry> {
        match *self {
            Entry::Directory(ref entries) => {
                let mut components = path.components();
                let count = components.clone().count();
                let first = Path::new(components.next().expect("unreachable").as_os_str()).to_owned();
                let rest = components.as_path().to_owned();
                if count > 1 {
                    entries.get(&first).and_then(
                        |subentry| subentry.lookup(&rest))
                } else {
                    entries.get(&first)
                }
            }
            Entry::File(_) => unreachable!()
        }
    }
}

fn summarize<P>(path: P, expected: &Metrics, actual: &Metrics)
where
    P: AsRef<Path>,
{
    let diff = expected.size != actual.size ||
        expected.sha2 != actual.sha2 ||
        expected.sha3 != actual.sha3;
    if diff {
        let suspicious_size = expected.size > 0 && actual.size == 0;
        let suspicious_nul = expected.nul != actual.nul;
        let suspicious_nonascii = expected.nonascii != actual.nonascii;
        println!("{} differs", path.as_ref().display());
        if suspicious_size {
            println!("  suspicious: file was truncated");
        }
        if suspicious_nul {
            println!("  suspicious: original had no NUL bytes, but now does");
        }
        if suspicious_nonascii {
            println!("  suspicious: original had no non-ASCII bytes, but now does");
        }
    }
}

impl Database {
    fn insert(&mut self, path: PathBuf, entry: Entry) {
        self.0.insert(path, entry);
    }

    fn lookup(&self, path: &PathBuf) -> Option<&Entry> {
        self.0.lookup(path)
    }

    pub fn build<P>(root: P) -> Result<Database, error::Error>
    where
        P: AsRef<Path>,
    {
        let mut database = Database::default();
        for entry in WalkBuilder::new(&root).build() {
            let entry = entry?;
            if entry.file_type().map_or(false, |t| t.is_file()) {
                let metrics = compute_metrics(entry.path())?;
                let result = Entry::File(metrics);
                let short_path = if entry.path() == root.as_ref() {
                    Path::new(entry.path().file_name().expect("unreachable"))
                } else {
                    entry.path().strip_prefix(&root)?
                };
                database.insert(short_path.to_owned(), result);
            }
        }
        Ok(database)
    }

    pub fn check<P>(&self, root: P) -> Result<(), error::Error>
    where
        P: AsRef<Path>,
    {
        // FIXME: Doesn't check for missing entries
        for entry in WalkBuilder::new(&root).build() {
            let entry = entry?;
            if entry.file_type().map_or(false, |t| t.is_file()) {
                let actual = compute_metrics(entry.path())?;

                let short_path = if entry.path() == root.as_ref() {
                    Path::new(entry.path().file_name().expect("unreachable"))
                } else {
                    entry.path().strip_prefix(&root)?
                };
                let expected = self.lookup(&short_path.to_owned());
                match expected {
                    Some(&Entry::File(ref metrics)) => summarize(
                        short_path, metrics, &actual),
                    Some(&Entry::Directory(_)) => unreachable!(),
                    None => println!("{} was created", short_path.display()),
                }
            }
        }
        Ok(())
    }

    pub fn load_json<P>(path: P) -> Result<Database, error::Error>
    where
        P: AsRef<Path>
    {
        let f = File::open(path)?;
        Ok(serde_json::from_reader(f)?)
    }

    pub fn dump_json<P>(&self, path: P) -> Result<(), error::Error>
    where
        P: AsRef<Path>
    {
        let json = serde_json::to_string(self)?;
        let mut f = File::create(path)?;
        write!(f, "{}", json)?;
        Ok(())
    }

    pub fn load_cbor<P>(path: P) -> Result<Database, error::Error>
    where
        P: AsRef<Path>
    {
        let f = File::open(path)?;
        Ok(serde_cbor::from_reader(f)?)
    }

    pub fn dump_cbor<P>(&self, path: P) -> Result<(), error::Error>
    where
        P: AsRef<Path>
    {
        let cbor = serde_cbor::to_vec(self)?;
        let mut f = File::create(path)?;
        f.write_all(cbor.as_slice())?;
        Ok(())
    }
}

// impl std::fmt::Display for Database {
//     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
//         for (path, entry) in self.0.iter() {
//             match entry {
//                 &Entry::File(ref hashes) => {
//                     let hash: Vec<_> = hashes.sha2.0.iter().map(
//                         |b| format!("{:02x}", b)).collect();
//                     writeln!(f, "{} {}", hash.join(""), Path::new(path).display())?
//                 }
//             }
//         }
//         Ok(())
//     }
// }
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::mem;

use serde_xml;

use credentials::Credentials;
use command::Command;
use region::Region;
use request::{Request, Headers, Query};
use serde_types::{ListBucketResult, BucketLocationResult};
use error::S3Result;
use serde_types::Tagging;

/// # Example
/// ```
/// # // Fake  credentials so we don't access user's real credentials in tests
/// # use std::env;
/// # env::set_var("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE");
/// # env::set_var("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
/// use s3::bucket::Bucket;
/// use s3::credentials::Credentials;
///
/// let bucket_name = "rust-s3-test";
/// let region = "us-east-1".parse().unwrap();
/// let credentials = Credentials::default();
///
/// let bucket = Bucket::new(bucket_name, region, credentials);
/// ```
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Bucket {
    pub name: String,
    pub region: Region,
    pub credentials: Credentials,
    pub extra_headers: Headers,
    pub extra_query: Query,
}

impl Bucket {
    /// Instantiate a new `Bucket`.
    ///
    /// # Example
    /// ```
    /// # // Fake  credentials so we don't access user's real credentials in tests
    /// # use std::env;
    /// # env::set_var("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE");
    /// # env::set_var("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = "rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    ///
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    /// ```
    pub fn new(name: &str, region: Region, credentials: Credentials) -> S3Result<Bucket> {
        Ok(Bucket {
            name: name.into(),
            region,
            credentials,
            extra_headers: HashMap::new(),
            extra_query: HashMap::new(),
        })
    }

    /// Gets file from an S3 path.
    ///
    /// # Example:
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = "rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (data, code) = bucket.get_object("/test.file").unwrap();
    /// println!("Code: {}\nData: {:?}", code, data);
    /// ```
    pub fn get_object(&self, path: &str) -> S3Result<(Vec<u8>, u32)> {
        let command = Command::GetObject;
        let request = Request::new(self, path, command);
        request.execute()
    }

    //// Get bucket location from S3
////
    /// # Example
    /// ```rust,no_run
    /// # // Fake  credentials so we don't access user's real credentials in tests
    /// # use std::env;
    /// # env::set_var("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE");
    /// # env::set_var("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = "rust-s3-test";
    /// let region = "eu-central-1".parse().unwrap();
    /// let credentials = Credentials::default();
    ///
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    /// println!("{}", bucket.location().unwrap().0)
    /// ```
    pub fn location(&self) -> S3Result<(Region, u32)> {
        let request = Request::new(self, "?location", Command::GetBucketLocation);
        let result = request.execute()?;
        let result_string = String::from_utf8_lossy(&result.0);
        let region = match serde_xml::deserialize(result_string.as_bytes()) {
            Ok(r) => {
                let location_result: BucketLocationResult = r;
                location_result.region.parse()?
            }
            Err(e) => {
                if e.to_string() == "missing field `$value`" {
                    "us-east-1".parse()?
                } else {
                    bail!(e)
                }
            }
        };
        Ok((region, result.1))
    }

    /// Delete file from an S3 path.
    ///
    /// # Example:
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (_, code) = bucket.delete_object("/test.file").unwrap();
    /// assert_eq!(204, code);
    /// ```
    pub fn delete_object(&self, path: &str) -> S3Result<(Vec<u8>, u32)> {
        let command = Command::DeleteObject;
        let request = Request::new(self, path, command);
        request.execute()
    }

    /// Put into an S3 bucket.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let content = "I want to go to S3".as_bytes();
    /// let (_, code) = bucket.put_object("/test.file", content, "text/plain").unwrap();
    /// assert_eq!(201, code);
    /// ```
    pub fn put_object(&self, path: &str, content: &[u8], content_type: &str) -> S3Result<(Vec<u8>, u32)> {
        let command = Command::PutObject {
            content,
            content_type,
        };
        let request = Request::new(self, path, command);
        request.execute()
    }

    fn _tags_xml(&self, tags: &[(&str, &str)]) -> String {
        let mut s = String::new();
        let content = tags
            .iter()
            .map(|&(name, value)| format!("<Tag><Key>{}</Key><Value>{}</Value></Tag>", name, value))
            .fold(String::new(), |mut a, b| {
                a.push_str(b.as_str());
                a
            });
        s.push_str("<Tagging><TagSet>");
        s.push_str(&content);
        s.push_str("</TagSet></Tagging>");
        s
    }

    /// Tag an S3 object.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (_, code) = bucket.put_object_tagging("/test.file", &[("Tag1", "Value1"), ("Tag2", "Value2")]).unwrap();
    /// assert_eq!(201, code);
    /// ```
    pub fn put_object_tagging(&self, path: &str, tags: &[(&str, &str)]) -> S3Result<(Vec<u8>, u32)> {
        let content = self._tags_xml(&tags);
        let command = Command::PutObjectTagging {
            tags: &content.to_string()
        };
        let request = Request::new(self, path, command);
        request.execute()
    }

    /// Retrieve an S3 object list of tags.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (tags, code) = bucket.get_object_tagging("/test.file").unwrap();
    /// if code == 200 {
    ///     for tag in tags.expect("No tags found").tag_set {
    ///         println!("{}={}", tag.key(), tag.value());
    ///     }
    /// }
    /// ```
    pub fn get_object_tagging(&self, path: &str) -> S3Result<(Option<Tagging>, u32)> {
        let command = Command::GetObjectTagging {};
        let request = Request::new(self, path, command);
        let result = request.execute()?;

        let tagging = if result.1 == 200 {
            let result_string = String::from_utf8_lossy(&result.0);
            println!("{}", result_string);
            Some(serde_xml::deserialize(result_string.as_bytes())?)
        } else {
            None
        };

        Ok((tagging, result.1))
    }

    fn _list(&self,
             prefix: &str,
             delimiter: Option<&str>,
             continuation_token: Option<&str>)
             -> S3Result<(ListBucketResult, u32)> {
        let command = Command::ListBucket {
            prefix,
            delimiter,
            continuation_token,
        };
        let request = Request::new(self, "/", command);
        let result = request.execute()?;
        let result_string = String::from_utf8_lossy(&result.0);
        let deserialized: ListBucketResult = serde_xml::deserialize(result_string.as_bytes())?;
        Ok((deserialized, result.1))
    }

    /// List the contents of an S3 bucket.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use std::str;
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let results = bucket.list("/", Some("/")).unwrap();
    /// for (list, code) in results {
    ///     assert_eq!(200, code);
    ///     println!("{:?}", list);
    /// }
    /// ```
    pub fn list(&self, prefix: &str, delimiter: Option<&str>) -> S3Result<Vec<(ListBucketResult, u32)>> {
        let mut results = Vec::new();
        let mut result = self._list(prefix, delimiter, None)?;
        loop {
            results.push(result.clone());
            match result.0.next_continuation_token {
                Some(token) => result = self._list(prefix, delimiter, Some(&token))?,
                None => break,
            }
        }

        Ok(results)
    }

    /// Get a reference to the name of the S3 bucket.
    pub fn name(&self) -> String {
        self.name.to_string()
    }

    /// Get a reference to the hostname of the S3 API endpoint.
    pub fn host(&self) -> String {
        self.region.host()
    }

    pub fn self_host(&self) -> String {
        format!("{}.s3.amazonaws.com", self.name)
    }

    pub fn scheme(&self) -> String {
        self.region.scheme()
    }

    /// Get the region this object will connect to.
    pub fn region(&self) -> Region {
        self.region.clone()
    }

    /// Get a reference to the AWS access key.
    pub fn access_key(&self) -> String {
        self.credentials.access_key.clone()
    }

    /// Get a reference to the AWS secret key.
    pub fn secret_key(&self) -> String {
        self.credentials.secret_key.clone()
    }

    /// Get a reference to the AWS token.
    pub fn token(&self) -> Option<&str> {
        self.credentials.token.as_ref().map(|s| s.as_str())
    }

    /// Get a reference to the full [`Credentials`](struct.Credentials.html)
    /// object used by this `Bucket`.
    pub fn credentials(&self) -> &Credentials {
        &self.credentials
    }

    /// Change the credentials used by the Bucket, returning the existing
    /// credentials.
    pub fn set_credentials(&mut self, credentials: Credentials) -> Credentials {
        mem::replace(&mut self.credentials, credentials)
    }

    /// Add an extra header to send with requests to S3.
    ///
    /// Add an extra header to send with requests. Note that the library
    /// already sets a number of headers - headers set with this method will be
    /// overridden by the library headers:
    ///   * Host
    ///   * Content-Type
    ///   * Date
    ///   * Content-Length
    ///   * Authorization
    ///   * X-Amz-Content-Sha256
    ///   * X-Amz-Date
    pub fn add_header(&mut self, key: &str, value: &str) {
        self.extra_headers.insert(key.into(), value.into());
    }

    /// Get a reference to the extra headers to be passed to the S3 API.
    pub fn extra_headers(&self) -> &Headers {
        &self.extra_headers
    }

    /// Get a mutable reference to the extra headers to be passed to the S3
    /// API.
    pub fn extra_headers_mut(&mut self) -> &mut Headers {
        &mut self.extra_headers
    }

    /// Add an extra query pair to the URL used for S3 API access.
    pub fn add_query(&mut self, key: &str, value: &str) {
        self.extra_query.insert(key.into(), value.into());
    }

    /// Get a reference to the extra query pairs to be passed to the S3 API.
    pub fn extra_query(&self) -> &Query {
        &self.extra_query
    }

    /// Get a mutable reference to the extra query pairs to be passed to the S3
    /// API.
    pub fn extra_query_mut(&mut self) -> &mut Query {
        &mut self.extra_query
    }
}
<commit_msg>Add DeleteObjectTagging test<commit_after>use std::collections::HashMap;
use std::mem;

use serde_xml;

use credentials::Credentials;
use command::Command;
use region::Region;
use request::{Request, Headers, Query};
use serde_types::{ListBucketResult, BucketLocationResult};
use error::S3Result;
use serde_types::Tagging;

/// # Example
/// ```
/// # // Fake  credentials so we don't access user's real credentials in tests
/// # use std::env;
/// # env::set_var("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE");
/// # env::set_var("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
/// use s3::bucket::Bucket;
/// use s3::credentials::Credentials;
///
/// let bucket_name = "rust-s3-test";
/// let region = "us-east-1".parse().unwrap();
/// let credentials = Credentials::default();
///
/// let bucket = Bucket::new(bucket_name, region, credentials);
/// ```
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Bucket {
    pub name: String,
    pub region: Region,
    pub credentials: Credentials,
    pub extra_headers: Headers,
    pub extra_query: Query,
}

impl Bucket {
    /// Instantiate a new `Bucket`.
    ///
    /// # Example
    /// ```
    /// # // Fake  credentials so we don't access user's real credentials in tests
    /// # use std::env;
    /// # env::set_var("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE");
    /// # env::set_var("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = "rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    ///
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    /// ```
    pub fn new(name: &str, region: Region, credentials: Credentials) -> S3Result<Bucket> {
        Ok(Bucket {
            name: name.into(),
            region,
            credentials,
            extra_headers: HashMap::new(),
            extra_query: HashMap::new(),
        })
    }

    /// Gets file from an S3 path.
    ///
    /// # Example:
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = "rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (data, code) = bucket.get_object("/test.file").unwrap();
    /// println!("Code: {}\nData: {:?}", code, data);
    /// ```
    pub fn get_object(&self, path: &str) -> S3Result<(Vec<u8>, u32)> {
        let command = Command::GetObject;
        let request = Request::new(self, path, command);
        request.execute()
    }

    //// Get bucket location from S3
////
    /// # Example
    /// ```rust,no_run
    /// # // Fake  credentials so we don't access user's real credentials in tests
    /// # use std::env;
    /// # env::set_var("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE");
    /// # env::set_var("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = "rust-s3-test";
    /// let region = "eu-central-1".parse().unwrap();
    /// let credentials = Credentials::default();
    ///
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    /// println!("{}", bucket.location().unwrap().0)
    /// ```
    pub fn location(&self) -> S3Result<(Region, u32)> {
        let request = Request::new(self, "?location", Command::GetBucketLocation);
        let result = request.execute()?;
        let result_string = String::from_utf8_lossy(&result.0);
        let region = match serde_xml::deserialize(result_string.as_bytes()) {
            Ok(r) => {
                let location_result: BucketLocationResult = r;
                location_result.region.parse()?
            }
            Err(e) => {
                if e.to_string() == "missing field `$value`" {
                    "us-east-1".parse()?
                } else {
                    bail!(e)
                }
            }
        };
        Ok((region, result.1))
    }

    /// Delete file from an S3 path.
    ///
    /// # Example:
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (_, code) = bucket.delete_object("/test.file").unwrap();
    /// assert_eq!(204, code);
    /// ```
    pub fn delete_object(&self, path: &str) -> S3Result<(Vec<u8>, u32)> {
        let command = Command::DeleteObject;
        let request = Request::new(self, path, command);
        request.execute()
    }

    /// Put into an S3 bucket.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let content = "I want to go to S3".as_bytes();
    /// let (_, code) = bucket.put_object("/test.file", content, "text/plain").unwrap();
    /// assert_eq!(201, code);
    /// ```
    pub fn put_object(&self, path: &str, content: &[u8], content_type: &str) -> S3Result<(Vec<u8>, u32)> {
        let command = Command::PutObject {
            content,
            content_type,
        };
        let request = Request::new(self, path, command);
        request.execute()
    }

    fn _tags_xml(&self, tags: &[(&str, &str)]) -> String {
        let mut s = String::new();
        let content = tags
            .iter()
            .map(|&(name, value)| format!("<Tag><Key>{}</Key><Value>{}</Value></Tag>", name, value))
            .fold(String::new(), |mut a, b| {
                a.push_str(b.as_str());
                a
            });
        s.push_str("<Tagging><TagSet>");
        s.push_str(&content);
        s.push_str("</TagSet></Tagging>");
        s
    }

    /// Tag an S3 object.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (_, code) = bucket.put_object_tagging("/test.file", &[("Tag1", "Value1"), ("Tag2", "Value2")]).unwrap();
    /// assert_eq!(201, code);
    /// ```
    pub fn put_object_tagging(&self, path: &str, tags: &[(&str, &str)]) -> S3Result<(Vec<u8>, u32)> {
        let content = self._tags_xml(&tags);
        let command = Command::PutObjectTagging {
            tags: &content.to_string()
        };
        let request = Request::new(self, path, command);
        request.execute()
    }

    /// Delete tags from an S3 object.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (_, code) = bucket.delete_object_tagging("/test.file").unwrap();
    /// assert_eq!(201, code);
    /// ```
    pub fn delete_object_tagging(&self, path: &str) -> S3Result<(Vec<u8>, u32)> {
        let command = Command::DeleteObjectTagging;
        let request = Request::new(self, path, command);
        request.execute()
    }

    /// Retrieve an S3 object list of tags.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let (tags, code) = bucket.get_object_tagging("/test.file").unwrap();
    /// if code == 200 {
    ///     for tag in tags.expect("No tags found").tag_set {
    ///         println!("{}={}", tag.key(), tag.value());
    ///     }
    /// }
    /// ```
    pub fn get_object_tagging(&self, path: &str) -> S3Result<(Option<Tagging>, u32)> {
        let command = Command::GetObjectTagging {};
        let request = Request::new(self, path, command);
        let result = request.execute()?;

        let tagging = if result.1 == 200 {
            let result_string = String::from_utf8_lossy(&result.0);
            println!("{}", result_string);
            Some(serde_xml::deserialize(result_string.as_bytes())?)
        } else {
            None
        };

        Ok((tagging, result.1))
    }

    fn _list(&self,
             prefix: &str,
             delimiter: Option<&str>,
             continuation_token: Option<&str>)
             -> S3Result<(ListBucketResult, u32)> {
        let command = Command::ListBucket {
            prefix,
            delimiter,
            continuation_token,
        };
        let request = Request::new(self, "/", command);
        let result = request.execute()?;
        let result_string = String::from_utf8_lossy(&result.0);
        let deserialized: ListBucketResult = serde_xml::deserialize(result_string.as_bytes())?;
        Ok((deserialized, result.1))
    }

    /// List the contents of an S3 bucket.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use std::str;
    /// use s3::bucket::Bucket;
    /// use s3::credentials::Credentials;
    /// let bucket_name = &"rust-s3-test";
    /// let aws_access = &"access_key";
    /// let aws_secret = &"secret_key";
    ///
    /// let bucket_name = &"rust-s3-test";
    /// let region = "us-east-1".parse().unwrap();
    /// let credentials = Credentials::default();
    /// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
    ///
    /// let results = bucket.list("/", Some("/")).unwrap();
    /// for (list, code) in results {
    ///     assert_eq!(200, code);
    ///     println!("{:?}", list);
    /// }
    /// ```
    pub fn list(&self, prefix: &str, delimiter: Option<&str>) -> S3Result<Vec<(ListBucketResult, u32)>> {
        let mut results = Vec::new();
        let mut result = self._list(prefix, delimiter, None)?;
        loop {
            results.push(result.clone());
            match result.0.next_continuation_token {
                Some(token) => result = self._list(prefix, delimiter, Some(&token))?,
                None => break,
            }
        }

        Ok(results)
    }

    /// Get a reference to the name of the S3 bucket.
    pub fn name(&self) -> String {
        self.name.to_string()
    }

    /// Get a reference to the hostname of the S3 API endpoint.
    pub fn host(&self) -> String {
        self.region.host()
    }

    pub fn self_host(&self) -> String {
        format!("{}.s3.amazonaws.com", self.name)
    }

    pub fn scheme(&self) -> String {
        self.region.scheme()
    }

    /// Get the region this object will connect to.
    pub fn region(&self) -> Region {
        self.region.clone()
    }

    /// Get a reference to the AWS access key.
    pub fn access_key(&self) -> String {
        self.credentials.access_key.clone()
    }

    /// Get a reference to the AWS secret key.
    pub fn secret_key(&self) -> String {
        self.credentials.secret_key.clone()
    }

    /// Get a reference to the AWS token.
    pub fn token(&self) -> Option<&str> {
        self.credentials.token.as_ref().map(|s| s.as_str())
    }

    /// Get a reference to the full [`Credentials`](struct.Credentials.html)
    /// object used by this `Bucket`.
    pub fn credentials(&self) -> &Credentials {
        &self.credentials
    }

    /// Change the credentials used by the Bucket, returning the existing
    /// credentials.
    pub fn set_credentials(&mut self, credentials: Credentials) -> Credentials {
        mem::replace(&mut self.credentials, credentials)
    }

    /// Add an extra header to send with requests to S3.
    ///
    /// Add an extra header to send with requests. Note that the library
    /// already sets a number of headers - headers set with this method will be
    /// overridden by the library headers:
    ///   * Host
    ///   * Content-Type
    ///   * Date
    ///   * Content-Length
    ///   * Authorization
    ///   * X-Amz-Content-Sha256
    ///   * X-Amz-Date
    pub fn add_header(&mut self, key: &str, value: &str) {
        self.extra_headers.insert(key.into(), value.into());
    }

    /// Get a reference to the extra headers to be passed to the S3 API.
    pub fn extra_headers(&self) -> &Headers {
        &self.extra_headers
    }

    /// Get a mutable reference to the extra headers to be passed to the S3
    /// API.
    pub fn extra_headers_mut(&mut self) -> &mut Headers {
        &mut self.extra_headers
    }

    /// Add an extra query pair to the URL used for S3 API access.
    pub fn add_query(&mut self, key: &str, value: &str) {
        self.extra_query.insert(key.into(), value.into());
    }

    /// Get a reference to the extra query pairs to be passed to the S3 API.
    pub fn extra_query(&self) -> &Query {
        &self.extra_query
    }

    /// Get a mutable reference to the extra query pairs to be passed to the S3
    /// API.
    pub fn extra_query_mut(&mut self) -> &mut Query {
        &mut self.extra_query
    }
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::io::BufReader;
use std::io::BufRead;
use std::fs::read_dir;
use std::fs::File;
use std::path::Path;
use std::process::Command;
use std::process::Stdio;
use std::str;

fn main() {
    for x in link_haskell_package("rust-haskell-hsbits", Path::new("hsbits")) {
        println!("{}", x);
    }
}

fn link_haskell_package(name: &str, path: &Path) -> Vec<String> {
    let mut ret: Vec<String> = Vec::new();

    // Build the package
    call_command(Command::new("cabal")
                         .arg("build")
                         .current_dir(&path),
                 "failed to build haskell package");

    // Link to the dependencies
    let opts_file   = File::open(path.join("dist").join("build").join("cargoOpts")).unwrap();
    let opts_reader = BufReader::new(opts_file);
    for x in opts_reader.lines() {
        ret.push(x.unwrap());
    }

    // Link to the package
    let mut compiler = gcc::Config::new();
    for entry in read_dir(path.join("dist").join("build")).unwrap() {
        let entry = entry.unwrap();
        if let Some(_) = entry.file_name().to_str().and_then(|x| strip_prefix_suffix("", ".dyn_o", x)) {
            compiler.object(entry.path());
        }
    }
    compiler.compile(&format!("libHS{}.a", name));

    return ret;
}

fn strip_prefix_suffix<'a>(prefix : &str, suffix : &str, string : &'a str) -> Option<&'a str> {
    if string.starts_with(prefix) && string.ends_with(suffix) {
        Some(&string[prefix.len() .. string.len() - suffix.len()])
    } else {
        None
    }
}

fn call_command(cmd: &mut Command, failmsg: &str) -> std::process::Output {
    let output = cmd.stderr(Stdio::inherit()).output().expect(failmsg);
    if output.status.success() {
        return output;
    } else {
        panic!(String::from(failmsg));
    }
}
<commit_msg>build.rs: Output options directly<commit_after>extern crate gcc;

use std::fs::read_dir;
use std::fs::File;
use std::path::Path;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::io;

fn main() {
    link_haskell_package("rust-haskell-hsbits", Path::new("hsbits"));
}

fn link_haskell_package(name: &str, path: &Path) {
    // Build the package
    call_command(Command::new("cabal")
                         .arg("build")
                         .current_dir(&path),
                 "failed to build haskell package");

    // Link to the dependencies
    let mut opts_file = File::open(path.join("dist").join("build").join("cargoOpts")).unwrap();
    io::copy(&mut opts_file, &mut io::stdout()).unwrap();

    // Link to the package
    let mut compiler = gcc::Config::new();
    for entry in read_dir(path.join("dist").join("build")).unwrap() {
        let entry = entry.unwrap();
        if let Some(_) = entry.file_name().to_str().and_then(|x| strip_prefix_suffix("", ".dyn_o", x)) {
            compiler.object(entry.path());
        }
    }
    compiler.compile(&format!("libHS{}.a", name));
}

fn strip_prefix_suffix<'a>(prefix : &str, suffix : &str, string : &'a str) -> Option<&'a str> {
    if string.starts_with(prefix) && string.ends_with(suffix) {
        Some(&string[prefix.len() .. string.len() - suffix.len()])
    } else {
        None
    }
}

fn call_command(cmd: &mut Command, failmsg: &str) -> std::process::Output {
    let output = cmd.stderr(Stdio::inherit()).output().expect(failmsg);
    if output.status.success() {
        return output;
    } else {
        panic!(String::from(failmsg));
    }
}
<|endoftext|>


<commit_before>use error::{self, SaveShaderError, InvalidShaderIdError};

use serde_json::{self, Value};

use reqwest::{Client, header};

use std::io::{self, Read, Write};
use std::fs::File;

pub fn download(id: &str) -> error::Result<(String, String)> {
    let (name, code) = get_shader_name_and_code(id)?;

    File::create(&name).or_else(|err| {
        return_save_shader_error(&name, err)
    })?.write_all(code.as_bytes()).or_else(|err| {
        return_save_shader_error(&name, err)
    })?;

    Ok((name, code))
}

fn return_save_shader_error<E>(name: &str, err: io::Error) -> error::Result<E> {
    Err(SaveShaderError::new(name, err).into())
}

fn get_shader_name_and_code(mut id: &str) -> error::Result<(String, String)> {
    let https_url = "https://www.shadertoy.com/view/";
    let http_url  = "http://www.shadertoy.com/view/";
    let url       = "www.shadertoy.com/view/";

    if id.starts_with(https_url) || id.starts_with(http_url) || id.starts_with(url) {
        id = id.split_at(id.rfind("view/").unwrap() + 5).1;
    }

    let json = serde_json::from_str::<Value>(&get_json_string(id)?)?;

    extract_from_json(&json)
}

fn get_json_string(id: &str) -> error::Result<String> {
    let client = Client::new();
    use reqwest::header::*;
    let mut headers=HeaderMap::new();
    headers.insert(REFERER,HeaderValue::from_static("https://www.shadertoy.com/"));
    let mut res = client.post("https://www.shadertoy.com/shadertoy/")
        .headers(headers)
//        .header(header::ContentType::form_url_encoded())
        .form(&[("s", format!("{{\"shaders\": [\"{}\"]}}", id))])
        .send()?;

    let mut buf = String::new();

    match res.read_to_string(&mut buf) {
        Ok(_) => {
            if buf == "[]" {
                Err(InvalidShaderIdError::new(id).into())
            } else {
                Ok(buf)
            }
        },
        Err(err) => {
            Err(err.into())
        }
    }
}

fn extract_from_json(json: &Value) -> error::Result<(String, String)> {
    let name = format!("{}.frag", json[0]["info"]["name"].as_str().unwrap().replace(" ", "_")).to_lowercase();
    let mut code = String::new();

    let shaders = json[0]["renderpass"].as_array().unwrap();

    if shaders.len() > 1 {
        for shader in shaders {
            if shader["name"] == "Image" {
                code = String::from(shader["code"].as_str().unwrap());
            }
        }
    } else {
        code = String::from(shaders[0]["code"].as_str().unwrap());
    }

    Ok((name, code))
}
<commit_msg>Address review comments :3<commit_after>use error::{self, SaveShaderError, InvalidShaderIdError};

use serde_json::{self, Value};

use reqwest::{Client, header};

use std::io::{self, Read, Write};
use std::fs::File;

pub fn download(id: &str) -> error::Result<(String, String)> {
    let (name, code) = get_shader_name_and_code(id)?;

    File::create(&name).or_else(|err| {
        return_save_shader_error(&name, err)
    })?.write_all(code.as_bytes()).or_else(|err| {
        return_save_shader_error(&name, err)
    })?;

    Ok((name, code))
}

fn return_save_shader_error<E>(name: &str, err: io::Error) -> error::Result<E> {
    Err(SaveShaderError::new(name, err).into())
}

fn get_shader_name_and_code(mut id: &str) -> error::Result<(String, String)> {
    let https_url = "https://www.shadertoy.com/view/";
    let http_url  = "http://www.shadertoy.com/view/";
    let url       = "www.shadertoy.com/view/";

    if id.starts_with(https_url) || id.starts_with(http_url) || id.starts_with(url) {
        id = id.split_at(id.rfind("view/").unwrap() + 5).1;
    }

    let json = serde_json::from_str::<Value>(&get_json_string(id)?)?;

    extract_from_json(&json)
}

fn get_json_string(id: &str) -> error::Result<String> {
    let client = Client::new();
    use reqwest::header::*;
    let mut headers = HeaderMap::new();
    headers.insert(REFERER, HeaderValue::from_static("https://www.shadertoy.com/"));
    let mut res = client.post("https://www.shadertoy.com/shadertoy/")
        .headers(headers)
        .form(&[("s", format!("{{\"shaders\": [\"{}\"]}}", id))])
        .send()?;

    let mut buf = String::new();

    match res.read_to_string(&mut buf) {
        Ok(_) => {
            if buf == "[]" {
                Err(InvalidShaderIdError::new(id).into())
            } else {
                Ok(buf)
            }
        },
        Err(err) => {
            Err(err.into())
        }
    }
}

fn extract_from_json(json: &Value) -> error::Result<(String, String)> {
    let name = format!("{}.frag", json[0]["info"]["name"].as_str().unwrap().replace(" ", "_")).to_lowercase();
    let mut code = String::new();

    let shaders = json[0]["renderpass"].as_array().unwrap();

    if shaders.len() > 1 {
        for shader in shaders {
            if shader["name"] == "Image" {
                code = String::from(shader["code"].as_str().unwrap());
            }
        }
    } else {
        code = String::from(shaders[0]["code"].as_str().unwrap());
    }

    Ok((name, code))
}
<|endoftext|>


<commit_before>use nom::{self, IResult};
use std::str;

pub enum Expr<'a> {
     Var(&'a [u8]),
     Filter(&'a str, Box<Expr<'a>>),
}

pub enum Node<'a> {
    Lit(&'a [u8]),
    Expr(Expr<'a>),
    Cond(Vec<(Option<Expr<'a>>, Vec<Node<'a>>)>),
}

pub type Nodes<'a> = Vec<Node<'a>>;
pub type Conds<'a> = Vec<(Option<Expr<'a>>, Nodes<'a>)>;

fn take_content(i: &[u8]) -> IResult<&[u8], Node> {
    if i.len() < 1 || i[0] == b'{' {
        return IResult::Error(error_position!(nom::ErrorKind::TakeUntil, i));
    }
    for (j, c) in i.iter().enumerate() {
        if *c == b'{' {
            if i.len() < j + 2 {
                return IResult::Done(&i[..0], Node::Lit(&i[..]));
            } else if i[j + 1] == '{' as u8 {
                return IResult::Done(&i[j..], Node::Lit(&i[..j]));
            } else if i[j + 1] == '%' as u8 {
                return IResult::Done(&i[j..], Node::Lit(&i[..j]));
            }
        }
    }
    IResult::Done(&i[..0], Node::Lit(&i[..]))
}

named!(expr_var<Expr>, map!(nom::alphanumeric, Expr::Var));

fn expr_filtered(i: &[u8]) -> IResult<&[u8], Expr> {
    let (mut left, mut expr) = match expr_var(i) {
        IResult::Error(err) => { return IResult::Error(err); },
        IResult::Incomplete(needed) => { return IResult::Incomplete(needed); },
        IResult::Done(left, res) => (left, res),
    };
    while left[0] == b'|' {
        match nom::alphanumeric(&left[1..]) {
            IResult::Error(err) => {
                return IResult::Error(err);
            },
            IResult::Incomplete(needed) => {
                return IResult::Incomplete(needed);
            },
            IResult::Done(new_left, res) => {
                left = new_left;
                expr = Expr::Filter(str::from_utf8(res).unwrap(), Box::new(expr));
            },
        };
    }
    return IResult::Done(left, expr);
}

named!(expr_node<Node>, map!(
    delimited!(tag_s!("{{"), ws!(expr_filtered), tag_s!("}}")),
    Node::Expr));

named!(cond_blocks<Conds>, do_parse!(
    tag_s!("{%") >>
    ws!(tag_s!("else")) >>
    tag_s!("%}") >>
    block: parse_template >>
    (vec![(None, block)])));

named!(block_if<Node>, do_parse!(
    tag_s!("{%") >>
    ws!(tag_s!("if")) >>
    cond: ws!(expr_filtered) >>
    tag_s!("%}") >>
    block: parse_template >>
    rest: opt!(cond_blocks) >>
    tag_s!("{%") >>
    ws!(tag_s!("endif")) >>
    tag_s!("%}") >>
    ({
        let mut res = Vec::new();
        res.push((Some(cond), block));
        if let Some(blocks) = rest {
            res.extend(blocks);
        }
        Node::Cond(res)
    })));

named!(parse_template< Vec<Node> >, many1!(alt!(
    take_content |
    expr_node |
    block_if)));

pub fn parse<'a>(src: &'a str) -> Vec<Node> {
    match parse_template(src.as_bytes()) {
        IResult::Done(_, res) => res,
        IResult::Error(err) => panic!("problems parsing template source: {}", err),
        IResult::Incomplete(_) => panic!("parsing incomplete"),
    }
}
<commit_msg>Support elif blocks in parser and generator<commit_after>use nom::{self, IResult};
use std::str;

pub enum Expr<'a> {
     Var(&'a [u8]),
     Filter(&'a str, Box<Expr<'a>>),
}

pub enum Node<'a> {
    Lit(&'a [u8]),
    Expr(Expr<'a>),
    Cond(Vec<(Option<Expr<'a>>, Vec<Node<'a>>)>),
}

pub type Nodes<'a> = Vec<Node<'a>>;
pub type Conds<'a> = Vec<(Option<Expr<'a>>, Nodes<'a>)>;

fn take_content(i: &[u8]) -> IResult<&[u8], Node> {
    if i.len() < 1 || i[0] == b'{' {
        return IResult::Error(error_position!(nom::ErrorKind::TakeUntil, i));
    }
    for (j, c) in i.iter().enumerate() {
        if *c == b'{' {
            if i.len() < j + 2 {
                return IResult::Done(&i[..0], Node::Lit(&i[..]));
            } else if i[j + 1] == '{' as u8 {
                return IResult::Done(&i[j..], Node::Lit(&i[..j]));
            } else if i[j + 1] == '%' as u8 {
                return IResult::Done(&i[j..], Node::Lit(&i[..j]));
            }
        }
    }
    IResult::Done(&i[..0], Node::Lit(&i[..]))
}

named!(expr_var<Expr>, map!(nom::alphanumeric, Expr::Var));

fn expr_filtered(i: &[u8]) -> IResult<&[u8], Expr> {
    let (mut left, mut expr) = match expr_var(i) {
        IResult::Error(err) => { return IResult::Error(err); },
        IResult::Incomplete(needed) => { return IResult::Incomplete(needed); },
        IResult::Done(left, res) => (left, res),
    };
    while left[0] == b'|' {
        match nom::alphanumeric(&left[1..]) {
            IResult::Error(err) => {
                return IResult::Error(err);
            },
            IResult::Incomplete(needed) => {
                return IResult::Incomplete(needed);
            },
            IResult::Done(new_left, res) => {
                left = new_left;
                expr = Expr::Filter(str::from_utf8(res).unwrap(), Box::new(expr));
            },
        };
    }
    return IResult::Done(left, expr);
}

named!(expr_node<Node>, map!(
    delimited!(tag_s!("{{"), ws!(expr_filtered), tag_s!("}}")),
    Node::Expr));

named!(cond_elif<(Option<Expr>, Nodes)>, do_parse!(
    tag_s!("{%") >>
    ws!(tag_s!("elif")) >>
    cond: ws!(expr_filtered) >>
    tag_s!("%}") >>
    block: parse_template >>
    (Some(cond), block)));

named!(cond_else<Nodes>, do_parse!(
    tag_s!("{%") >>
    ws!(tag_s!("else")) >>
    tag_s!("%}") >>
    block: parse_template >>
    (block)));

named!(block_if<Node>, do_parse!(
    tag_s!("{%") >>
    ws!(tag_s!("if")) >>
    cond: ws!(expr_filtered) >>
    tag_s!("%}") >>
    block: parse_template >>
    elifs: many0!(cond_elif) >>
    rest: opt!(cond_else) >>
    tag_s!("{%") >>
    ws!(tag_s!("endif")) >>
    tag_s!("%}") >>
    ({
        let mut res = Vec::new();
        res.push((Some(cond), block));
        res.extend(elifs);
        if let Some(else_block) = rest {
            res.push((None, else_block));
        }
        Node::Cond(res)
    })));

named!(parse_template< Vec<Node> >, many1!(alt!(
    take_content |
    expr_node |
    block_if)));

pub fn parse<'a>(src: &'a str) -> Vec<Node> {
    match parse_template(src.as_bytes()) {
        IResult::Done(_, res) => res,
        IResult::Error(err) => panic!("problems parsing template source: {}", err),
        IResult::Incomplete(_) => panic!("parsing incomplete"),
    }
}
<|endoftext|>