<commit_before>// (c) 2017 Productize SPRL <joost@productize.be>

use std::iter::Peekable;
use std::slice::Iter;

use Sexp;
pub use Result as SResult;

/// convert an `&Sexp` to something
pub trait FromSexp
    where Self: Sized
{
    /// convert from a symbolic-expression to something
    fn from_sexp(&Sexp) -> SResult<Self>;
}

/// convert from a symbolic-expression to something (dispatcher)
pub fn from_sexp<T: FromSexp>(s: &Sexp) -> SResult<T> {
    T::from_sexp(s)
}

/// Atom iterator wrapper
pub struct IterAtom<'a> {
    name: String,
    /// containing iterable (pub for now)
    pub iter: Peekable<Iter<'a, Sexp>>,
}

impl<'a> IterAtom<'a> {
    /// deconstruct a `Sexp` that is a list and starts with 'name'
    pub fn new(s: &'a Sexp, name: &str) -> SResult<IterAtom<'a>> {
        let v = s.list()?;
        let mut i = v.iter();
        let st = match i.next() {
            None => return Err(format!("missing first element {} in list {}", name, s).into()),
            Some(e) => e.string()?,
        };
        if st != name {
            return Err(format!("list {} doesn't start with {}, but with {}", s, name, st).into());
        }
        let i = i.peekable();
        Ok(IterAtom {
            name: name.into(),
            iter: i,
        })
    }

    /// deconstruct a `Sexp` that is a list and doesn't start with a name
    pub fn new_nameless(s: &'a Sexp, name:&str) -> SResult<IterAtom<'a>> {
        Ok(IterAtom {
            name: name.into(),
            iter: s.list()?.iter().peekable(),
        })
    }

    fn expect<T, F>(&mut self, name: &str, get: F) -> SResult<T>
        where F: Fn(&Sexp) -> SResult<T>
    {
        match self.iter.next() {
            Some(x) => get(x),
            None => return Err(format!("missing {} field in {}", name, self.name).into()),
        }
    }

    /// expect an integer while iterating a `Sexp` list
    pub fn i(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.i().map_err(From::from))
    }

    /// expect a float while iterating a `Sexp` list
    pub fn f(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.f().map_err(From::from))
    }

    /// expect a String while iterating a `Sexp` list
    pub fn s(&mut self, name: &str) -> SResult<String> {
        self.expect(name, |x| x.s().map_err(From::from))
    }

    /// expect a list contained String while iterating a `Sexp` list
    pub fn s_in_list(&mut self, name: &str) -> SResult<String> {
        self.expect(name,
                    |x| x.named_value_s(name).map_err(From::from))
    }

    /// expect a list contained i64 while iterating a `Sexp` list
    pub fn i_in_list(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.named_value_i(name).map_err(From::from))
    }

    /// expect a list contained f64 while iterating a `Sexp` list
    pub fn f_in_list(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.named_value_f(name).map_err(From::from))
    }

    /// expect a `Sexp` while iterating a `Sexp` list
    pub fn t<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x))
    }

        /// expect a list containing a `Sexp` while iterating a `Sexp` list
    pub fn t_in_list<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x.named_value(name)?))
    }

    /// expect remainder of iterator to be a `Vec<T>`
    pub fn vec<T: FromSexp>(&mut self) -> SResult<Vec<T>> {
        let mut res = Vec::new();
        loop {
            match self.iter.next() {
                None => break,
                Some(e) => {
                    let p = from_sexp(e)?;
                    res.push(p);
                }
            }
        }
        Ok(res)
    }

    /// maybe something while iterating a `Sexp` list
    fn maybe<X, F>(&mut self, convert: F) -> Option<X>
        where F: Fn(&Sexp) -> SResult<X>
    {
        let res = match self.iter.peek() {
            None => None,
            Some(s) => {
                match convert(s) {
                    Ok(t) => Some(t),
                    Err(_) => None,
                }
            }
        };
        match res {
            Some(x) => {
                let _ = self.iter.next();
                Some(x)
            }
            x => x,
        }
    }

    /// maybe a `FromSexp` while iterating a `Sexp` list
    pub fn maybe_t<T: FromSexp>(&mut self) -> Option<T> {
        self.maybe(|x| T::from_sexp(x))
    }

    /// maybe a `String` while iterating a `Sexp` list
    pub fn maybe_s(&mut self) -> Option<String> {
        self.maybe(|x| x.s())
    }

    /// maybe an `i64` while iterating a `Sexp` list
    pub fn maybe_i(&mut self) -> Option<i64> {
        self.maybe(|x| x.i())
    }

    /// maybe an `f64` while iterating a `Sexp` list
    pub fn maybe_f(&mut self) -> Option<f64> {
        self.maybe(|x| x.f())
    }

    /// maybe a list containing a `String` while iterating a `Sexp` list
    pub fn maybe_s_in_list(&mut self, name:&str) -> Option<String> {
        self.maybe(|x| x.named_value_s(name))
    }

    /// maybe a list containing an `i64` while iterating a `Sexp` list
    pub fn maybe_i_in_list(&mut self, name:&str) -> Option<i64> {
        self.maybe(|x| x.named_value_i(name))
    }

    /// maybe a list containing an `f64` while iterating a `Sexp` list
    pub fn maybe_f_in_list(&mut self, name:&str) -> Option<f64> {
        self.maybe(|x| x.named_value_f(name))
    }

    /// make sure we consumed all of the iterator
    pub fn close<T>(&mut self, x:T) -> SResult<T> {
        match self.iter.next() {
            Some(x) => Err(format!("Unexpected {} in {}", x, self.name).into()),
            None => Ok(x)
        }
    }
}
<commit_msg>maybe_literal_s experiment<commit_after>// (c) 2017 Productize SPRL <joost@productize.be>

use std::iter::Peekable;
use std::slice::Iter;

use Sexp;
pub use Result as SResult;

/// convert an `&Sexp` to something
pub trait FromSexp
    where Self: Sized
{
    /// convert from a symbolic-expression to something
    fn from_sexp(&Sexp) -> SResult<Self>;
}

/// convert from a symbolic-expression to something (dispatcher)
pub fn from_sexp<T: FromSexp>(s: &Sexp) -> SResult<T> {
    T::from_sexp(s)
}

/// Atom iterator wrapper
pub struct IterAtom<'a> {
    name: String,
    /// containing iterable (pub for now)
    pub iter: Peekable<Iter<'a, Sexp>>,
}

impl<'a> IterAtom<'a> {
    /// deconstruct a `Sexp` that is a list and starts with 'name'
    pub fn new(s: &'a Sexp, name: &str) -> SResult<IterAtom<'a>> {
        let v = s.list()?;
        let mut i = v.iter();
        let st = match i.next() {
            None => return Err(format!("missing first element {} in list {}", name, s).into()),
            Some(e) => e.string()?,
        };
        if st != name {
            return Err(format!("list {} doesn't start with {}, but with {}", s, name, st).into());
        }
        let i = i.peekable();
        Ok(IterAtom {
            name: name.into(),
            iter: i,
        })
    }

    /// deconstruct a `Sexp` that is a list and doesn't start with a name
    pub fn new_nameless(s: &'a Sexp, name:&str) -> SResult<IterAtom<'a>> {
        Ok(IterAtom {
            name: name.into(),
            iter: s.list()?.iter().peekable(),
        })
    }

    fn expect<T, F>(&mut self, name: &str, get: F) -> SResult<T>
        where F: Fn(&Sexp) -> SResult<T>
    {
        match self.iter.next() {
            Some(x) => get(x),
            None => return Err(format!("missing {} field in {}", name, self.name).into()),
        }
    }

    /// expect an integer while iterating a `Sexp` list
    pub fn i(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.i().map_err(From::from))
    }

    /// expect a float while iterating a `Sexp` list
    pub fn f(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.f().map_err(From::from))
    }

    /// expect a String while iterating a `Sexp` list
    pub fn s(&mut self, name: &str) -> SResult<String> {
        self.expect(name, |x| x.s().map_err(From::from))
    }

    /// expect a list contained String while iterating a `Sexp` list
    pub fn s_in_list(&mut self, name: &str) -> SResult<String> {
        self.expect(name,
                    |x| x.named_value_s(name).map_err(From::from))
    }

    /// expect a list contained i64 while iterating a `Sexp` list
    pub fn i_in_list(&mut self, name: &str) -> SResult<i64> {
        self.expect(name, |x| x.named_value_i(name).map_err(From::from))
    }

    /// expect a list contained f64 while iterating a `Sexp` list
    pub fn f_in_list(&mut self, name: &str) -> SResult<f64> {
        self.expect(name, |x| x.named_value_f(name).map_err(From::from))
    }

    /// expect a `Sexp` while iterating a `Sexp` list
    pub fn t<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x))
    }

        /// expect a list containing a `Sexp` while iterating a `Sexp` list
    pub fn t_in_list<T: FromSexp>(&mut self, name: &str) -> SResult<T> {
        self.expect(name, |x| T::from_sexp(x.named_value(name)?))
    }

    /// expect remainder of iterator to be a `Vec<T>`
    pub fn vec<T: FromSexp>(&mut self) -> SResult<Vec<T>> {
        let mut res = Vec::new();
        loop {
            match self.iter.next() {
                None => break,
                Some(e) => {
                    let p = from_sexp(e)?;
                    res.push(p);
                }
            }
        }
        Ok(res)
    }

    /// maybe something while iterating a `Sexp` list
    fn maybe<X, F>(&mut self, convert: F) -> Option<X>
        where F: Fn(&Sexp) -> SResult<X>
    {
        let res = match self.iter.peek() {
            None => None,
            Some(s) => {
                match convert(s) {
                    Ok(t) => Some(t),
                    Err(_) => None,
                }
            }
        };
        match res {
            Some(x) => {
                let _ = self.iter.next();
                Some(x)
            }
            x => x,
        }
    }

    /// maybe a `FromSexp` while iterating a `Sexp` list
    pub fn maybe_t<T: FromSexp>(&mut self) -> Option<T> {
        self.maybe(|x| T::from_sexp(x))
    }

    /// maybe a `String` while iterating a `Sexp` list
    pub fn maybe_s(&mut self) -> Option<String> {
        self.maybe(|x| x.s())
    }

    /// maybe a `String` while iterating a `Sexp` list
    pub fn maybe_literal_s(&mut self, l:&str) -> Option<String> {
        self.maybe(|x| {
            let z = x.s()?;
            if &z == l {
                Ok(z)
            } else {
                Err("unexpected".into())
            }
        })
    }

    /// maybe an `i64` while iterating a `Sexp` list
    pub fn maybe_i(&mut self) -> Option<i64> {
        self.maybe(|x| x.i())
    }

    /// maybe an `f64` while iterating a `Sexp` list
    pub fn maybe_f(&mut self) -> Option<f64> {
        self.maybe(|x| x.f())
    }

    /// maybe a list containing a `String` while iterating a `Sexp` list
    pub fn maybe_s_in_list(&mut self, name:&str) -> Option<String> {
        self.maybe(|x| x.named_value_s(name))
    }

    /// maybe a list containing an `i64` while iterating a `Sexp` list
    pub fn maybe_i_in_list(&mut self, name:&str) -> Option<i64> {
        self.maybe(|x| x.named_value_i(name))
    }

    /// maybe a list containing an `f64` while iterating a `Sexp` list
    pub fn maybe_f_in_list(&mut self, name:&str) -> Option<f64> {
        self.maybe(|x| x.named_value_f(name))
    }

    /// make sure we consumed all of the iterator
    pub fn close<T>(&mut self, x:T) -> SResult<T> {
        match self.iter.next() {
            Some(x) => Err(format!("Unexpected {} in {}", x, self.name).into()),
            None => Ok(x)
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use common::{Atom, Sexp};

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError,
    IncorrectSpecialForm,
    IncorrectNumberOfArguments,
    IncorrectTypeOfArgument
}

struct Env {
    current: HashMap<String, Sexp>,
}

impl Env {
    fn new() -> Env {
        let mut env = Env {
            current: HashMap::new(),
        };
        env.current.insert("nil".to_string(),
                           Sexp::Atom(Atom::Symbol("nil".to_string())));
        env.current.insert("true".to_string(),
                           Sexp::Atom(Atom::Bool(true)));
        env.current.insert("false".to_string(),
                           Sexp::Atom(Atom::Bool(false)));
        env
    }

    fn get(&self, k: &String) -> Option<&Sexp> {
        self.current.get(k)
    }

    fn insert(&mut self, k: String, v: Sexp) -> Option<Sexp> {
        self.current.insert(k, v)
    }
}

pub struct Interpreter {
    env: Env
}

impl Interpreter {
    pub fn new() -> Interpreter {
        Interpreter {
            env: Env::new(),
        }
    }

    pub fn eval(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        match *s {
            Sexp::Atom(Atom::Number(_)) => {
                Ok(s.clone())
            },
            Sexp::Atom(Atom::Bool(_)) => {
                Ok(s.clone())
            },
            Sexp::Atom(Atom::Symbol(ref name)) => {
                if let Some(s) = self.env.get(name) {
                    Ok(s.clone())
                } else {
                    Err(EvalError::EvalError)
                }
            },
            Sexp::Atom(Atom::String(ref s)) => {
                Ok(Sexp::Atom(Atom::String(s.clone())))
            },
            Sexp::List(ref l) => {
                if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                    match &n[..] {
                        "def" => {
                            self.eval_def(s)
                        },
                        "+" => {
                            self.eval_plus(s)
                        },
                        "-" => {
                            self.eval_minus(s)
                        },
                        "/" => {
                            self.eval_div(s)
                        },
                        "*" => {
                            self.eval_mul(s)
                        },
                        _ => {
                            Err(EvalError::EvalError)
                        }
                    }
                } else {
                    Err(EvalError::EvalError)
                }
            }
        }
    }

    fn eval_def(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "def" => {
                        if l.len() == 3 {
                            if let Sexp::Atom(Atom::Symbol(ref n)) = l[1] {
                                self.eval(&l[2]).and_then(|a| {
                                    self.env.insert(n.clone(), a.clone());
                                    Ok(a)
                                })
                            } else {
                                Err(EvalError::EvalError)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_plus(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "+" => {
                        if l.len() > 1 {
                            let mut a = 0_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Sexp::Atom(Atom::Number(n))) => {
                                        a += n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Sexp::Atom(Atom::Number(a)))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_minus(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "-" => {
                        if l.len() > 1 {
                            if let Sexp::Atom(Atom::Number(n)) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Sexp::Atom(Atom::Number(n))) => {
                                            a -= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Sexp::Atom(Atom::Number(a)))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_div(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "/" => {
                        if l.len() > 1 {
                            if let Sexp::Atom(Atom::Number(n)) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Sexp::Atom(Atom::Number(n))) => {
                                            a /= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Sexp::Atom(Atom::Number(a)))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_mul(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "*" => {
                        if l.len() > 1 {
                            let mut a = 1_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Sexp::Atom(Atom::Number(n))) => {
                                        a *= n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Sexp::Atom(Atom::Number(a)))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use common::{Atom, Sexp};
    use super::Interpreter;
    use super::EvalError::EvalError;

    #[test]
    fn test_eval_number_to_itself() {
        let num = 10_f64;
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Number(num));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Number(num)));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_string_to_itself() {
        let s = "rust is awesome";
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::String(s.to_string()));
        let actual_result = intr.eval(&Sexp::Atom(Atom::String(s.to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_undefined_symbol_to_error() {
        let mut intr = Interpreter::new();
        let expected_result = EvalError;
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("a".to_string())));
        assert_eq!(expected_result, actual_result.err().unwrap());
    }

    #[test]
    fn test_eval_true_to_matching_bool() {
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Bool(true));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("true".to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_false_to_matching_bool() {
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Bool(false));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("false".to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nil_to_itself() {
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Symbol("nil".to_string()));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("nil".to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_def_special_form() {
        let num = 1_f64;
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Number(num));
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("def".to_string())),
                                           Sexp::Atom(Atom::Symbol("a".to_string())),
                                           Sexp::Atom(Atom::Number(num))]);
        let actual_result = intr.eval(&actual_input);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nested_plus_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("+".to_string())),
                                           Sexp::List(vec![Sexp::Atom(Atom::Symbol("+".to_string())),
                                                           Sexp::Atom(Atom::Number(1_f64)),
                                                           Sexp::Atom(Atom::Number(2_f64))]),
                                           Sexp::Atom(Atom::Number(3_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(6_f64));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_minus_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("-".to_string())),
                                           Sexp::Atom(Atom::Number(3_f64)),
                                           Sexp::Atom(Atom::Number(2_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(1_f64));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_div_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("/".to_string())),
                                           Sexp::Atom(Atom::Number(3_f64)),
                                           Sexp::Atom(Atom::Number(2_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(1.5));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_mul_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("*".to_string())),
                                           Sexp::Atom(Atom::Number(3.5)),
                                           Sexp::Atom(Atom::Number(2_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(7_f64));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_plus_special_form_using_defined_symbols() {
        let mut intr = Interpreter::new();
        intr.eval(&Sexp::List(vec![Sexp::Atom(Atom::Symbol("def".to_string())),
                                   Sexp::Atom(Atom::Symbol("a".to_string())),
                                   Sexp::Atom(Atom::Number(1_f64))])).ok().unwrap();
        intr.eval(&Sexp::List(vec![Sexp::Atom(Atom::Symbol("def".to_string())),
                                   Sexp::Atom(Atom::Symbol("b".to_string())),
                                   Sexp::Atom(Atom::Number(2_f64))])).ok().unwrap();
        let expected_result = Sexp::Atom(Atom::Number(3_f64));
        let actual_result = intr.eval(&Sexp::List(vec![Sexp::Atom(Atom::Symbol("+".to_string())),
                                                       Sexp::Atom(Atom::Symbol("a".to_string())),
                                                       Sexp::Atom(Atom::Symbol("b".to_string()))]));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }
}
<commit_msg>Remove Env entity<commit_after>use std::collections::HashMap;
use common::{Atom, Sexp};

#[derive(Debug, PartialEq)]
enum EvalError{
    EvalError,
    IncorrectSpecialForm,
    IncorrectNumberOfArguments,
    IncorrectTypeOfArgument
}

pub struct Interpreter {
    env: HashMap<String, Sexp>
}

impl Interpreter {
    pub fn new() -> Interpreter {
        let mut intr = Interpreter {
            env: HashMap::new(),
        };
        intr.env.insert("nil".to_string(),
                        Sexp::Atom(Atom::Symbol("nil".to_string())));
        intr.env.insert("true".to_string(),
                        Sexp::Atom(Atom::Bool(true)));
        intr.env.insert("false".to_string(),
                        Sexp::Atom(Atom::Bool(false)));
        intr
    }

    pub fn eval(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        match *s {
            Sexp::Atom(Atom::Number(_)) => {
                Ok(s.clone())
            },
            Sexp::Atom(Atom::Bool(_)) => {
                Ok(s.clone())
            },
            Sexp::Atom(Atom::Symbol(ref name)) => {
                if let Some(s) = self.env.get(name) {
                    Ok(s.clone())
                } else {
                    Err(EvalError::EvalError)
                }
            },
            Sexp::Atom(Atom::String(ref s)) => {
                Ok(Sexp::Atom(Atom::String(s.clone())))
            },
            Sexp::List(ref l) => {
                if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                    match &n[..] {
                        "def" => {
                            self.eval_def(s)
                        },
                        "+" => {
                            self.eval_plus(s)
                        },
                        "-" => {
                            self.eval_minus(s)
                        },
                        "/" => {
                            self.eval_div(s)
                        },
                        "*" => {
                            self.eval_mul(s)
                        },
                        _ => {
                            Err(EvalError::EvalError)
                        }
                    }
                } else {
                    Err(EvalError::EvalError)
                }
            }
        }
    }

    fn eval_def(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "def" => {
                        if l.len() == 3 {
                            if let Sexp::Atom(Atom::Symbol(ref n)) = l[1] {
                                self.eval(&l[2]).and_then(|a| {
                                    self.env.insert(n.clone(), a.clone());
                                    Ok(a)
                                })
                            } else {
                                Err(EvalError::EvalError)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_plus(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "+" => {
                        if l.len() > 1 {
                            let mut a = 0_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Sexp::Atom(Atom::Number(n))) => {
                                        a += n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Sexp::Atom(Atom::Number(a)))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_minus(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "-" => {
                        if l.len() > 1 {
                            if let Sexp::Atom(Atom::Number(n)) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Sexp::Atom(Atom::Number(n))) => {
                                            a -= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Sexp::Atom(Atom::Number(a)))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_div(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "/" => {
                        if l.len() > 1 {
                            if let Sexp::Atom(Atom::Number(n)) = l[1] {
                                let mut a = n;
                                for i in l.iter().skip(2) {
                                    match self.eval(i) {
                                        Ok(Sexp::Atom(Atom::Number(n))) => {
                                            a /= n
                                        },
                                        _ => {
                                            return Err(EvalError::IncorrectTypeOfArgument)
                                        }
                                    }
                                }
                                Ok(Sexp::Atom(Atom::Number(a)))
                            } else {
                                Err(EvalError::IncorrectTypeOfArgument)
                            }
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }

    fn eval_mul(&mut self, s: &Sexp) -> Result<Sexp, EvalError> {
        if let Sexp::List(ref l) = *s {
            if let Sexp::Atom(Atom::Symbol(ref n)) = *l.first().unwrap() {
                match &n[..] {
                    "*" => {
                        if l.len() > 1 {
                            let mut a = 1_f64;
                            for i in l.iter().skip(1) {
                                match self.eval(i) {
                                    Ok(Sexp::Atom(Atom::Number(n))) => {
                                        a *= n
                                    },
                                    _ => {
                                        return Err(EvalError::IncorrectTypeOfArgument)
                                    }
                                }
                            }
                            Ok(Sexp::Atom(Atom::Number(a)))
                        } else {
                            Err(EvalError::IncorrectNumberOfArguments)
                        }
                    },
                    _ => {
                        Err(EvalError::IncorrectSpecialForm)
                    }
                }
            } else {
                Err(EvalError::EvalError)
            }
        } else {
            Err(EvalError::EvalError)
        }
    }
}

#[cfg(test)]
mod tests {
    use common::{Atom, Sexp};
    use super::Interpreter;
    use super::EvalError::EvalError;

    #[test]
    fn test_eval_number_to_itself() {
        let num = 10_f64;
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Number(num));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Number(num)));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_string_to_itself() {
        let s = "rust is awesome";
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::String(s.to_string()));
        let actual_result = intr.eval(&Sexp::Atom(Atom::String(s.to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_undefined_symbol_to_error() {
        let mut intr = Interpreter::new();
        let expected_result = EvalError;
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("a".to_string())));
        assert_eq!(expected_result, actual_result.err().unwrap());
    }

    #[test]
    fn test_eval_true_to_matching_bool() {
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Bool(true));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("true".to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_false_to_matching_bool() {
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Bool(false));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("false".to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nil_to_itself() {
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Symbol("nil".to_string()));
        let actual_result = intr.eval(&Sexp::Atom(Atom::Symbol("nil".to_string())));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_def_special_form() {
        let num = 1_f64;
        let mut intr = Interpreter::new();
        let expected_result = Sexp::Atom(Atom::Number(num));
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("def".to_string())),
                                           Sexp::Atom(Atom::Symbol("a".to_string())),
                                           Sexp::Atom(Atom::Number(num))]);
        let actual_result = intr.eval(&actual_input);
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_nested_plus_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("+".to_string())),
                                           Sexp::List(vec![Sexp::Atom(Atom::Symbol("+".to_string())),
                                                           Sexp::Atom(Atom::Number(1_f64)),
                                                           Sexp::Atom(Atom::Number(2_f64))]),
                                           Sexp::Atom(Atom::Number(3_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(6_f64));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_minus_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("-".to_string())),
                                           Sexp::Atom(Atom::Number(3_f64)),
                                           Sexp::Atom(Atom::Number(2_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(1_f64));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_div_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("/".to_string())),
                                           Sexp::Atom(Atom::Number(3_f64)),
                                           Sexp::Atom(Atom::Number(2_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(1.5));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_mul_special_form() {
        let mut intr = Interpreter::new();
        let actual_input = Sexp::List(vec![Sexp::Atom(Atom::Symbol("*".to_string())),
                                           Sexp::Atom(Atom::Number(3.5)),
                                           Sexp::Atom(Atom::Number(2_f64))]);
        let actual_result = intr.eval(&actual_input);
        let expected_result = Sexp::Atom(Atom::Number(7_f64));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }

    #[test]
    fn test_eval_plus_special_form_using_defined_symbols() {
        let mut intr = Interpreter::new();
        intr.eval(&Sexp::List(vec![Sexp::Atom(Atom::Symbol("def".to_string())),
                                   Sexp::Atom(Atom::Symbol("a".to_string())),
                                   Sexp::Atom(Atom::Number(1_f64))])).ok().unwrap();
        intr.eval(&Sexp::List(vec![Sexp::Atom(Atom::Symbol("def".to_string())),
                                   Sexp::Atom(Atom::Symbol("b".to_string())),
                                   Sexp::Atom(Atom::Number(2_f64))])).ok().unwrap();
        let expected_result = Sexp::Atom(Atom::Number(3_f64));
        let actual_result = intr.eval(&Sexp::List(vec![Sexp::Atom(Atom::Symbol("+".to_string())),
                                                       Sexp::Atom(Atom::Symbol("a".to_string())),
                                                       Sexp::Atom(Atom::Symbol("b".to_string()))]));
        assert_eq!(expected_result, actual_result.ok().unwrap());
    }
}
<|endoftext|>


<commit_before>use crate::{
    analysis::{
        bounds::{Bound, BoundType},
        ref_mode::RefMode,
    },
    library::Nullable,
};

impl Bound {
    /// Returns the type parameter reference.
    /// Currently always returns the alias.
    pub(super) fn type_parameter_reference(&self) -> Option<char> {
        self.alias
    }

    /// Returns the type parameter reference, with [`BoundType::IsA`] wrapped
    /// in `ref_mode` and `nullable` as appropriate.
    pub(super) fn full_type_parameter_reference(
        &self,
        ref_mode: RefMode,
        nullable: Nullable,
        r#async: bool,
    ) -> String {
        let ref_str = ref_mode.for_rust_type();

        // Generate `impl Trait` if this bound does not have an alias
        let trait_bound = match self.type_parameter_reference() {
            Some(t) => t.to_string(),
            None => {
                let trait_bound = self.trait_bound(r#async);
                let trait_bound = format!("impl {}", trait_bound);

                // Combining a ref mode and lifetime requires parentheses for disambiguation
                match self.bound_type {
                    BoundType::IsA(lifetime) => {
                        // TODO: This is fragile
                        let has_lifetime = r#async || lifetime.is_some();

                        if !ref_str.is_empty() && has_lifetime {
                            format!("({})", trait_bound)
                        } else {
                            trait_bound
                        }
                    }
                    _ => trait_bound,
                }
            }
        };

        match self.bound_type {
            BoundType::IsA(_) if *nullable => {
                format!("Option<{}{}>", ref_str, trait_bound)
            }
            BoundType::IsA(_) => format!("{}{}", ref_str, trait_bound),
            BoundType::NoWrapper | BoundType::AsRef(_) => trait_bound,
        }
    }

    /// Returns the type parameter definition for this bound, usually
    /// of the form `T: SomeTrait` or `T: IsA<Foo>`.
    pub(super) fn type_parameter_definition(&self, r#async: bool) -> Option<String> {
        self.alias
            .map(|alias| format!("{}: {}", alias, self.trait_bound(r#async)))
    }

    /// Returns the trait bound, usually of the form `SomeTrait`
    /// or `IsA<Foo>`.
    pub(super) fn trait_bound(&self, r#async: bool) -> String {
        match self.bound_type {
            BoundType::NoWrapper => self.type_str.clone(),
            BoundType::IsA(lifetime) => {
                if r#async {
                    assert!(lifetime.is_none(), "Async overwrites lifetime");
                }
                let is_a = format!("IsA<{}>", self.type_str);

                let lifetime = r#async
                    .then(|| " + Clone + 'static".to_string())
                    .or_else(|| lifetime.map(|l| format!(" + '{}", l)))
                    .unwrap_or_default();

                format!("{}{}", is_a, lifetime)
            }
            BoundType::AsRef(Some(_ /*lifetime*/)) => panic!("AsRef cannot have a lifetime"),
            BoundType::AsRef(None) => format!("AsRef<{}>", self.type_str),
        }
    }
}
<commit_msg>Nullable AsRef parameters need to be placed into an Option<commit_after>use crate::{
    analysis::{
        bounds::{Bound, BoundType},
        ref_mode::RefMode,
    },
    library::Nullable,
};

impl Bound {
    /// Returns the type parameter reference.
    /// Currently always returns the alias.
    pub(super) fn type_parameter_reference(&self) -> Option<char> {
        self.alias
    }

    /// Returns the type parameter reference, with [`BoundType::IsA`] wrapped
    /// in `ref_mode` and `nullable` as appropriate.
    pub(super) fn full_type_parameter_reference(
        &self,
        ref_mode: RefMode,
        nullable: Nullable,
        r#async: bool,
    ) -> String {
        let ref_str = ref_mode.for_rust_type();

        // Generate `impl Trait` if this bound does not have an alias
        let trait_bound = match self.type_parameter_reference() {
            Some(t) => t.to_string(),
            None => {
                let trait_bound = self.trait_bound(r#async);
                let trait_bound = format!("impl {}", trait_bound);

                // Combining a ref mode and lifetime requires parentheses for disambiguation
                match self.bound_type {
                    BoundType::IsA(lifetime) => {
                        // TODO: This is fragile
                        let has_lifetime = r#async || lifetime.is_some();

                        if !ref_str.is_empty() && has_lifetime {
                            format!("({})", trait_bound)
                        } else {
                            trait_bound
                        }
                    }
                    _ => trait_bound,
                }
            }
        };

        match self.bound_type {
            BoundType::IsA(_) if *nullable => {
                format!("Option<{}{}>", ref_str, trait_bound)
            }
            BoundType::IsA(_) => format!("{}{}", ref_str, trait_bound),
            BoundType::AsRef(_) if *nullable => {
                format!("Option<{}>", trait_bound)
            }
            BoundType::NoWrapper | BoundType::AsRef(_) => trait_bound,
        }
    }

    /// Returns the type parameter definition for this bound, usually
    /// of the form `T: SomeTrait` or `T: IsA<Foo>`.
    pub(super) fn type_parameter_definition(&self, r#async: bool) -> Option<String> {
        self.alias
            .map(|alias| format!("{}: {}", alias, self.trait_bound(r#async)))
    }

    /// Returns the trait bound, usually of the form `SomeTrait`
    /// or `IsA<Foo>`.
    pub(super) fn trait_bound(&self, r#async: bool) -> String {
        match self.bound_type {
            BoundType::NoWrapper => self.type_str.clone(),
            BoundType::IsA(lifetime) => {
                if r#async {
                    assert!(lifetime.is_none(), "Async overwrites lifetime");
                }
                let is_a = format!("IsA<{}>", self.type_str);

                let lifetime = r#async
                    .then(|| " + Clone + 'static".to_string())
                    .or_else(|| lifetime.map(|l| format!(" + '{}", l)))
                    .unwrap_or_default();

                format!("{}{}", is_a, lifetime)
            }
            BoundType::AsRef(Some(_ /*lifetime*/)) => panic!("AsRef cannot have a lifetime"),
            BoundType::AsRef(None) => format!("AsRef<{}>", self.type_str),
        }
    }
}
<|endoftext|>


<commit_before>
extern mod sqlite;

use core::result;
use core::io;
use core::path;
use core::clone;
use core::to_str;

struct Price {
    date: ~str,
    close: ~str
}

impl clone::Clone for Price {
    fn clone(&self) -> Price {
        Price{date: self.date.clone(), close: self.close.clone()}
    }
}
impl to_str::ToStr for Price {
    fn to_str(&self) -> ~str {
        fmt!("%s: %s", self.date, self.close)
    }
}

fn load(filename: &str) -> ~[~str] {

    let fres = io::file_reader(~path::Path(filename));
    let f = result::unwrap(fres);
    return f.read_lines();
}

fn parse(lines: &[~str]) -> ~[Price] {

    let mut date = ~"";
    let mut close = ~"";
    let mut i = 0;
    let mut prices: ~[Price] = ~[];

    for lines.each |&line| {

        i = 0;
        for line.each_split_char(',') |piece| {
            if i == 0 {
                date = piece.to_str();
            } else if i == 4 {
                close = piece.to_str();
            }
            i += 1;
        }
        prices += [Price{date: date.clone(), close: close.clone()}]
    }

    return prices;
}

struct DB {
    filename: ~str,
    database: sqlite::Database
}

impl DB {

    fn open(name: &str) -> Result<DB, ~str> {

        match sqlite::open(name) {
            // Here 'name' is borrowed. Calling .to_str() makes a new
            // owned string, which the object will hold.
            Ok(db) => Ok(DB{filename: name.to_str(), database: db}),
            Err(e) => {
                println(fmt!("Error opening test.db: %?", e));
                Err(e.to_str())
            }
        }
    }

    fn create(&self) -> Result<sqlite::ResultCode, sqlite::ResultCode>  {
        self.database.exec("CREATE TABLE prices (date text, price real)")
    }

    fn write_price(&self, date: ~str, price: float) {
        let res = self.database.exec(fmt!(
            "INSERT INTO prices VALUES ('%s', %f)", date, price));
        if ! res.is_ok() {
            println(fmt!("INSERT error: %s", res.unwrap_err().to_str()));
        }
    }

    fn max_price(&self) -> ~Price {
        return ~Price{date:~"TODO", close:~"TODO"};
        /*
        let st: sqlite::Cursor = self.database.prepare("SELECT date, price FROM prices ORDER BY price DESC LIMIT 1", &None).unwrap();
        st.step_row();
        let date = st.get_text(0);
        let price = st.get_num(1);
        return ~Price{date:date, close:fmt!("%f", price)}
        */
    }

    fn min_price(&self) -> ~Price {
        return ~Price{date:~"TODO", close:~"TODO"};
    }
}

fn save(prices: ~[Price]) {
    println("Start save");

    let res = DB::open("test.db");
    if ! res.is_ok() {
        return;
    }
    let db = res.unwrap();

    println("Opened");

    match db.create() {
        Err(code) => { println(fmt!("Error creating tables: %s",
                                    code.to_str())); return; }
        _ => {}
    };

    println("Created");

    let mut val: float = 0.0;
    for prices.each |&price| {
        println(price.to_str());

        val = float::from_str(price.close).unwrap();
        db.write_price(price.date.to_str(), val);
    }

}

fn cmd_init() {
    let all_lines: ~[~str] = load("vt.csv");

    // 'tail' because the first line is the column headers, which we
    // don't want.
    let lines: &[~str] = all_lines.tail();

    let prices: ~[Price] = parse(lines);
    println(fmt!("%?", prices));

    save(prices);
}

fn cmd_max() {

    let res = DB::open("test.db");
    if ! res.is_ok() {
        return;
    }
    let db = res.unwrap();

    println(db.max_price().to_str());
}

fn cmd_min() {
    let res = DB::open("test.db");
    if ! res.is_ok() {
        return;
    }
    let db = res.unwrap();

    println(db.min_price().to_str());
}

fn main() {

    static USAGE: &'static str = "Usage: highlow init|max|min";

    let args: &[~str] = os::args();
    if args.len() != 2 {
        println(USAGE);
        return;
    }

    let cmd: ~str = args[1].clone();

    match cmd {
        ~"init" => cmd_init(),
        ~"max" => cmd_max(),
        ~"min" => cmd_min(),
        other => println(fmt!("Invalid cmd: %s", other))
    }
}

<commit_msg>min / max working (with rustsqlite fork)<commit_after>
extern mod sqlite;

use core::result;
use core::io;
use core::path;
use core::clone;
use core::to_str;

struct Price {
    date: ~str,
    close: ~str
}

impl clone::Clone for Price {
    fn clone(&self) -> Price {
        Price{date: self.date.clone(), close: self.close.clone()}
    }
}
impl to_str::ToStr for Price {
    fn to_str(&self) -> ~str {
        fmt!("%s: %s", self.date, self.close)
    }
}

fn load(filename: &str) -> ~[~str] {

    let fres = io::file_reader(~path::Path(filename));
    let f = result::unwrap(fres);
    return f.read_lines();
}

fn parse(lines: &[~str]) -> ~[Price] {

    let mut date = ~"";
    let mut close = ~"";
    let mut i = 0;
    let mut prices: ~[Price] = ~[];

    for lines.each |&line| {

        i = 0;
        for line.each_split_char(',') |piece| {
            if i == 0 {
                date = piece.to_str();
            } else if i == 4 {
                close = piece.to_str();
            }
            i += 1;
        }
        prices += [Price{date: date.clone(), close: close.clone()}]
    }

    return prices;
}

struct DB {
    filename: ~str,
    database: sqlite::Database
}

impl DB {

    fn open(name: &str) -> Result<DB, ~str> {

        match sqlite::open(name) {
            // Here 'name' is borrowed. Calling .to_str() makes a new
            // owned string, which the object will hold.
            Ok(db) => Ok(DB{filename: name.to_str(), database: db}),
            Err(e) => {
                println(fmt!("Error opening test.db: %?", e));
                Err(e.to_str())
            }
        }
    }

    fn create(&self) -> Result<sqlite::ResultCode, sqlite::ResultCode>  {
        self.database.exec("CREATE TABLE prices (date text, price real)")
    }

    fn write_price(&self, date: ~str, price: float) {
        let res = self.database.exec(fmt!(
            "INSERT INTO prices VALUES ('%s', %f)", date, price));
        if ! res.is_ok() {
            println(fmt!("INSERT error: %s", res.unwrap_err().to_str()));
        }
    }

    fn _min_max(&self, min_max: &str) -> ~Price {

        let extra =
            match min_max {
                "MAX" => "DESC",
                _ => ""
            };
        let sql = fmt!(
            "SELECT date, price FROM prices ORDER BY price %s LIMIT 1",
            extra);

        let st: sqlite::Cursor = self.database.prepare(sql, &None).unwrap();
        st.step();
        let date = st.get_text(0);
        let price = st.get_num(1);
        return ~Price{date:date, close:fmt!("%f", price)}
    }

    fn max_price(&self) -> ~Price {
        self._min_max("MAX")
    }

    fn min_price(&self) -> ~Price {
        self._min_max("MIN")
    }
}

fn save(prices: ~[Price]) {
    println("Start save");

    let res = DB::open("test.db");
    if ! res.is_ok() {
        return;
    }
    let db = res.unwrap();

    println("Opened");

    match db.create() {
        Err(code) => { println(fmt!("Error creating tables: %s",
                                    code.to_str())); return; }
        _ => {}
    };

    println("Created");

    let mut val: float = 0.0;
    for prices.each |&price| {
        println(price.to_str());

        val = float::from_str(price.close).unwrap();
        db.write_price(price.date.to_str(), val);
    }

}

fn cmd_init() {
    let all_lines: ~[~str] = load("vt.csv");

    // 'tail' because the first line is the column headers, which we
    // don't want.
    let lines: &[~str] = all_lines.tail();

    let prices: ~[Price] = parse(lines);
    println(fmt!("%?", prices));

    save(prices);
}

fn cmd_max() {

    let res = DB::open("test.db");
    if ! res.is_ok() {
        return;
    }
    let db = res.unwrap();

    println(db.max_price().to_str());
}

fn cmd_min() {
    let res = DB::open("test.db");
    if ! res.is_ok() {
        return;
    }
    let db = res.unwrap();

    println(db.min_price().to_str());
}

fn main() {

    static USAGE: &'static str = "Usage: highlow init|max|min";

    let args: &[~str] = os::args();
    if args.len() != 2 {
        println(USAGE);
        return;
    }

    let cmd: ~str = args[1].clone();

    match cmd {
        ~"init" => cmd_init(),
        ~"max" => cmd_max(),
        ~"min" => cmd_min(),
        other => println(fmt!("Invalid cmd: %s", other))
    }
}

<|endoftext|>


<commit_before>use super::{MockAnonymousUser, MockCookieUser, MockTokenUser};
use crate::git::UpstreamIndex;
use crate::record;
use crate::util::{chaosproxy::ChaosProxy, fresh_schema::FreshSchema};
use cargo_registry::config;
use cargo_registry::{
    background_jobs::Environment,
    db::DieselPool,
    git::{Credentials, RepositoryConfig},
    App, Emails,
};
use std::{rc::Rc, sync::Arc, time::Duration};

use cargo_registry::git::Repository as WorkerRepository;
use diesel::PgConnection;
use reqwest::{blocking::Client, Proxy};
use std::collections::HashSet;
use swirl::Runner;

struct TestAppInner {
    app: Arc<App>,
    // The bomb (if created) needs to be held in scope until the end of the test.
    _bomb: Option<record::Bomb>,
    middle: conduit_middleware::MiddlewareBuilder,
    index: Option<UpstreamIndex>,
    runner: Option<Runner<Environment, DieselPool>>,
    db_chaosproxy: Option<Arc<ChaosProxy>>,

    // Must be the last field of the struct!
    _fresh_schema: Option<FreshSchema>,
}

impl Drop for TestAppInner {
    fn drop(&mut self) {
        use diesel::prelude::*;
        use swirl::schema::background_jobs::dsl::*;

        // Avoid a double-panic if the test is already failing
        if std::thread::panicking() {
            return;
        }

        // Lazily run any remaining jobs
        if let Some(runner) = &self.runner {
            runner.run_all_pending_jobs().expect("Could not run jobs");
            runner.check_for_failed_jobs().expect("Failed jobs remain");
        }

        // Manually verify that all jobs have completed successfully
        // This will catch any tests that enqueued a job but forgot to initialize the runner
        let conn = self.app.primary_database.get().unwrap();
        let job_count: i64 = background_jobs.count().get_result(&*conn).unwrap();
        assert_eq!(
            0, job_count,
            "Unprocessed or failed jobs remain in the queue"
        );

        // TODO: If a runner was started, obtain the clone from it and ensure its HEAD matches the upstream index HEAD
    }
}

/// A representation of the app and its database transaction
#[derive(Clone)]
pub struct TestApp(Rc<TestAppInner>);

impl TestApp {
    /// Initialize an application with an `Uploader` that panics
    pub fn init() -> TestAppBuilder {
        init_logger();

        TestAppBuilder {
            config: simple_config(),
            proxy: None,
            bomb: None,
            index: None,
            build_job_runner: false,
        }
    }

    /// Initialize the app and a proxy that can record and playback outgoing HTTP requests
    pub fn with_proxy() -> TestAppBuilder {
        Self::init().with_proxy()
    }

    /// Initialize a full application, with a proxy, index, and background worker
    pub fn full() -> TestAppBuilder {
        Self::with_proxy().with_git_index().with_job_runner()
    }

    /// Obtain the database connection and pass it to the closure
    ///
    /// Within each test, the connection pool only has 1 connection so it is necessary to drop the
    /// connection before making any API calls.  Once the closure returns, the connection is
    /// dropped, ensuring it is returned to the pool and available for any future API calls.
    pub fn db<T, F: FnOnce(&PgConnection) -> T>(&self, f: F) -> T {
        let conn = self.0.app.primary_database.get().unwrap();
        f(&conn)
    }

    /// Create a new user with a verified email address in the database and return a mock user
    /// session
    ///
    /// This method updates the database directly
    pub fn db_new_user(&self, username: &str) -> MockCookieUser {
        use cargo_registry::schema::emails;
        use diesel::prelude::*;

        let user = self.db(|conn| {
            let email = "something@example.com";

            let user = crate::new_user(username)
                .create_or_update(None, &self.0.app.emails, conn)
                .unwrap();
            diesel::insert_into(emails::table)
                .values((
                    emails::user_id.eq(user.id),
                    emails::email.eq(email),
                    emails::verified.eq(true),
                ))
                .execute(conn)
                .unwrap();
            user
        });
        MockCookieUser {
            app: self.clone(),
            user,
        }
    }

    /// Obtain a reference to the upstream repository ("the index")
    pub fn upstream_index(&self) -> &UpstreamIndex {
        assert_some!(self.0.index.as_ref())
    }

    /// Obtain a reference to the upstream repository ("the index")
    pub fn upstream_repository(&self) -> &git2::Repository {
        &self.upstream_index().repository
    }

    /// Obtain a list of crates from the index HEAD
    pub fn crates_from_index_head(&self, crate_name: &str) -> Vec<cargo_registry::git::Crate> {
        self.upstream_index().crates_from_index_head(crate_name)
    }

    pub fn run_pending_background_jobs(&self) {
        let runner = &self.0.runner;
        let runner = runner.as_ref().expect("Index has not been initialized");

        runner.run_all_pending_jobs().expect("Could not run jobs");
        runner
            .check_for_failed_jobs()
            .expect("Could not determine if jobs failed");
    }

    /// Obtain a reference to the inner `App` value
    pub fn as_inner(&self) -> &App {
        &self.0.app
    }

    /// Obtain a reference to the inner middleware builder
    pub fn as_middleware(&self) -> &conduit_middleware::MiddlewareBuilder {
        &self.0.middle
    }

    pub(crate) fn db_chaosproxy(&self) -> Arc<ChaosProxy> {
        self.0.db_chaosproxy.clone().expect(
            "ChaosProxy is not enabled on this test, call with_slow_real_pool during app init",
        )
    }
}

pub struct TestAppBuilder {
    config: config::Server,
    proxy: Option<String>,
    bomb: Option<record::Bomb>,
    index: Option<UpstreamIndex>,
    build_job_runner: bool,
}

impl TestAppBuilder {
    /// Create a `TestApp` with an empty database
    pub fn empty(mut self) -> (TestApp, MockAnonymousUser) {
        // Run each test inside a fresh database schema, deleted at the end of the test,
        // The schema will be cleared up once the app is dropped.
        let (db_chaosproxy, fresh_schema) = if !self.config.use_test_database_pool {
            let fresh_schema = FreshSchema::new(&self.config.db.primary.url);
            let (proxy, url) = ChaosProxy::proxy_database_url(fresh_schema.database_url()).unwrap();
            self.config.db.primary.url = url;

            (Some(proxy), Some(fresh_schema))
        } else {
            (None, None)
        };

        let (app, middle) = build_app(self.config, self.proxy);

        let runner = if self.build_job_runner {
            let repository_config = RepositoryConfig {
                index_location: UpstreamIndex::url(),
                credentials: Credentials::Missing,
            };
            let index = WorkerRepository::open(&repository_config).expect("Could not clone index");
            let environment = Environment::new(
                index,
                app.config.uploader().clone(),
                app.http_client().clone(),
            );

            Some(
                Runner::builder(environment)
                    // We only have 1 connection in tests, so trying to run more than
                    // 1 job concurrently will just block
                    .thread_count(1)
                    .connection_pool(app.primary_database.clone())
                    .job_start_timeout(Duration::from_secs(5))
                    .build(),
            )
        } else {
            None
        };

        let test_app_inner = TestAppInner {
            app,
            _fresh_schema: fresh_schema,
            _bomb: self.bomb,
            middle,
            index: self.index,
            runner,
            db_chaosproxy,
        };
        let test_app = TestApp(Rc::new(test_app_inner));
        let anon = MockAnonymousUser {
            app: test_app.clone(),
        };
        (test_app, anon)
    }

    /// Create a proxy for use with this app
    pub fn with_proxy(mut self) -> Self {
        let (proxy, bomb) = record::proxy();
        self.proxy = Some(proxy);
        self.bomb = Some(bomb);
        self
    }

    // Create a `TestApp` with a database including a default user
    pub fn with_user(self) -> (TestApp, MockAnonymousUser, MockCookieUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        (app, anon, user)
    }

    /// Create a `TestApp` with a database including a default user and its token
    pub fn with_token(self) -> (TestApp, MockAnonymousUser, MockCookieUser, MockTokenUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        let token = user.db_new_token("bar");
        (app, anon, user, token)
    }

    pub fn with_config(mut self, f: impl FnOnce(&mut config::Server)) -> Self {
        f(&mut self.config);
        self
    }

    pub fn with_publish_rate_limit(self, rate: Duration, burst: i32) -> Self {
        self.with_config(|config| {
            config.publish_rate_limit.rate = rate;
            config.publish_rate_limit.burst = burst;
        })
    }

    pub fn with_git_index(mut self) -> Self {
        self.index = Some(UpstreamIndex::new().unwrap());
        self
    }

    pub fn with_job_runner(mut self) -> Self {
        self.build_job_runner = true;
        self
    }

    pub fn with_slow_real_db_pool(mut self) -> Self {
        self.config.use_test_database_pool = false;
        self
    }
}

pub fn init_logger() {
    let _ = tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .without_time()
        .with_test_writer()
        .try_init();
}

fn simple_config() -> config::Server {
    config::Server {
        base: config::Base::test(),
        db: config::DatabasePools::test_from_environment(),
        session_key: "test this has to be over 32 bytes long".to_string(),
        gh_client_id: dotenv::var("GH_CLIENT_ID").unwrap_or_default(),
        gh_client_secret: dotenv::var("GH_CLIENT_SECRET").unwrap_or_default(),
        gh_base_url: "http://api.github.com".to_string(),
        max_upload_size: 3000,
        max_unpack_size: 2000,
        publish_rate_limit: Default::default(),
        blocked_traffic: Default::default(),
        max_allowed_page_offset: 200,
        page_offset_ua_blocklist: vec![],
        domain_name: "crates.io".into(),
        allowed_origins: Vec::new(),
        downloads_persist_interval_ms: 1000,
        ownership_invitations_expiration_days: 30,
        metrics_authorization_token: None,
        use_test_database_pool: true,
        instance_metrics_log_every_seconds: None,
        force_unconditional_redirects: false,
        blocked_routes: HashSet::new(),
        version_id_cache_size: 10000,
        version_id_cache_ttl: Duration::from_secs(5 * 60),
    }
}

fn build_app(
    config: config::Server,
    proxy: Option<String>,
) -> (Arc<App>, conduit_middleware::MiddlewareBuilder) {
    let client = if let Some(proxy) = proxy {
        let mut builder = Client::builder();
        builder = builder
            .proxy(Proxy::all(&proxy).expect("Unable to configure proxy with the provided URL"));
        Some(builder.build().expect("TLS backend cannot be initialized"))
    } else {
        None
    };

    let mut app = App::new(config, client);

    // Use the in-memory email backend for all tests, allowing tests to analyze the emails sent by
    // the application. This will also prevent cluttering the filesystem.
    app.emails = Emails::new_in_memory();

    let app = Arc::new(app);
    let handler = cargo_registry::build_handler(Arc::clone(&app));
    (app, handler)
}
<commit_msg>TestApp: Remove unused `upstream_repository()` method<commit_after>use super::{MockAnonymousUser, MockCookieUser, MockTokenUser};
use crate::git::UpstreamIndex;
use crate::record;
use crate::util::{chaosproxy::ChaosProxy, fresh_schema::FreshSchema};
use cargo_registry::config;
use cargo_registry::{
    background_jobs::Environment,
    db::DieselPool,
    git::{Credentials, RepositoryConfig},
    App, Emails,
};
use std::{rc::Rc, sync::Arc, time::Duration};

use cargo_registry::git::Repository as WorkerRepository;
use diesel::PgConnection;
use reqwest::{blocking::Client, Proxy};
use std::collections::HashSet;
use swirl::Runner;

struct TestAppInner {
    app: Arc<App>,
    // The bomb (if created) needs to be held in scope until the end of the test.
    _bomb: Option<record::Bomb>,
    middle: conduit_middleware::MiddlewareBuilder,
    index: Option<UpstreamIndex>,
    runner: Option<Runner<Environment, DieselPool>>,
    db_chaosproxy: Option<Arc<ChaosProxy>>,

    // Must be the last field of the struct!
    _fresh_schema: Option<FreshSchema>,
}

impl Drop for TestAppInner {
    fn drop(&mut self) {
        use diesel::prelude::*;
        use swirl::schema::background_jobs::dsl::*;

        // Avoid a double-panic if the test is already failing
        if std::thread::panicking() {
            return;
        }

        // Lazily run any remaining jobs
        if let Some(runner) = &self.runner {
            runner.run_all_pending_jobs().expect("Could not run jobs");
            runner.check_for_failed_jobs().expect("Failed jobs remain");
        }

        // Manually verify that all jobs have completed successfully
        // This will catch any tests that enqueued a job but forgot to initialize the runner
        let conn = self.app.primary_database.get().unwrap();
        let job_count: i64 = background_jobs.count().get_result(&*conn).unwrap();
        assert_eq!(
            0, job_count,
            "Unprocessed or failed jobs remain in the queue"
        );

        // TODO: If a runner was started, obtain the clone from it and ensure its HEAD matches the upstream index HEAD
    }
}

/// A representation of the app and its database transaction
#[derive(Clone)]
pub struct TestApp(Rc<TestAppInner>);

impl TestApp {
    /// Initialize an application with an `Uploader` that panics
    pub fn init() -> TestAppBuilder {
        init_logger();

        TestAppBuilder {
            config: simple_config(),
            proxy: None,
            bomb: None,
            index: None,
            build_job_runner: false,
        }
    }

    /// Initialize the app and a proxy that can record and playback outgoing HTTP requests
    pub fn with_proxy() -> TestAppBuilder {
        Self::init().with_proxy()
    }

    /// Initialize a full application, with a proxy, index, and background worker
    pub fn full() -> TestAppBuilder {
        Self::with_proxy().with_git_index().with_job_runner()
    }

    /// Obtain the database connection and pass it to the closure
    ///
    /// Within each test, the connection pool only has 1 connection so it is necessary to drop the
    /// connection before making any API calls.  Once the closure returns, the connection is
    /// dropped, ensuring it is returned to the pool and available for any future API calls.
    pub fn db<T, F: FnOnce(&PgConnection) -> T>(&self, f: F) -> T {
        let conn = self.0.app.primary_database.get().unwrap();
        f(&conn)
    }

    /// Create a new user with a verified email address in the database and return a mock user
    /// session
    ///
    /// This method updates the database directly
    pub fn db_new_user(&self, username: &str) -> MockCookieUser {
        use cargo_registry::schema::emails;
        use diesel::prelude::*;

        let user = self.db(|conn| {
            let email = "something@example.com";

            let user = crate::new_user(username)
                .create_or_update(None, &self.0.app.emails, conn)
                .unwrap();
            diesel::insert_into(emails::table)
                .values((
                    emails::user_id.eq(user.id),
                    emails::email.eq(email),
                    emails::verified.eq(true),
                ))
                .execute(conn)
                .unwrap();
            user
        });
        MockCookieUser {
            app: self.clone(),
            user,
        }
    }

    /// Obtain a reference to the upstream repository ("the index")
    pub fn upstream_index(&self) -> &UpstreamIndex {
        assert_some!(self.0.index.as_ref())
    }

    /// Obtain a list of crates from the index HEAD
    pub fn crates_from_index_head(&self, crate_name: &str) -> Vec<cargo_registry::git::Crate> {
        self.upstream_index().crates_from_index_head(crate_name)
    }

    pub fn run_pending_background_jobs(&self) {
        let runner = &self.0.runner;
        let runner = runner.as_ref().expect("Index has not been initialized");

        runner.run_all_pending_jobs().expect("Could not run jobs");
        runner
            .check_for_failed_jobs()
            .expect("Could not determine if jobs failed");
    }

    /// Obtain a reference to the inner `App` value
    pub fn as_inner(&self) -> &App {
        &self.0.app
    }

    /// Obtain a reference to the inner middleware builder
    pub fn as_middleware(&self) -> &conduit_middleware::MiddlewareBuilder {
        &self.0.middle
    }

    pub(crate) fn db_chaosproxy(&self) -> Arc<ChaosProxy> {
        self.0.db_chaosproxy.clone().expect(
            "ChaosProxy is not enabled on this test, call with_slow_real_pool during app init",
        )
    }
}

pub struct TestAppBuilder {
    config: config::Server,
    proxy: Option<String>,
    bomb: Option<record::Bomb>,
    index: Option<UpstreamIndex>,
    build_job_runner: bool,
}

impl TestAppBuilder {
    /// Create a `TestApp` with an empty database
    pub fn empty(mut self) -> (TestApp, MockAnonymousUser) {
        // Run each test inside a fresh database schema, deleted at the end of the test,
        // The schema will be cleared up once the app is dropped.
        let (db_chaosproxy, fresh_schema) = if !self.config.use_test_database_pool {
            let fresh_schema = FreshSchema::new(&self.config.db.primary.url);
            let (proxy, url) = ChaosProxy::proxy_database_url(fresh_schema.database_url()).unwrap();
            self.config.db.primary.url = url;

            (Some(proxy), Some(fresh_schema))
        } else {
            (None, None)
        };

        let (app, middle) = build_app(self.config, self.proxy);

        let runner = if self.build_job_runner {
            let repository_config = RepositoryConfig {
                index_location: UpstreamIndex::url(),
                credentials: Credentials::Missing,
            };
            let index = WorkerRepository::open(&repository_config).expect("Could not clone index");
            let environment = Environment::new(
                index,
                app.config.uploader().clone(),
                app.http_client().clone(),
            );

            Some(
                Runner::builder(environment)
                    // We only have 1 connection in tests, so trying to run more than
                    // 1 job concurrently will just block
                    .thread_count(1)
                    .connection_pool(app.primary_database.clone())
                    .job_start_timeout(Duration::from_secs(5))
                    .build(),
            )
        } else {
            None
        };

        let test_app_inner = TestAppInner {
            app,
            _fresh_schema: fresh_schema,
            _bomb: self.bomb,
            middle,
            index: self.index,
            runner,
            db_chaosproxy,
        };
        let test_app = TestApp(Rc::new(test_app_inner));
        let anon = MockAnonymousUser {
            app: test_app.clone(),
        };
        (test_app, anon)
    }

    /// Create a proxy for use with this app
    pub fn with_proxy(mut self) -> Self {
        let (proxy, bomb) = record::proxy();
        self.proxy = Some(proxy);
        self.bomb = Some(bomb);
        self
    }

    // Create a `TestApp` with a database including a default user
    pub fn with_user(self) -> (TestApp, MockAnonymousUser, MockCookieUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        (app, anon, user)
    }

    /// Create a `TestApp` with a database including a default user and its token
    pub fn with_token(self) -> (TestApp, MockAnonymousUser, MockCookieUser, MockTokenUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        let token = user.db_new_token("bar");
        (app, anon, user, token)
    }

    pub fn with_config(mut self, f: impl FnOnce(&mut config::Server)) -> Self {
        f(&mut self.config);
        self
    }

    pub fn with_publish_rate_limit(self, rate: Duration, burst: i32) -> Self {
        self.with_config(|config| {
            config.publish_rate_limit.rate = rate;
            config.publish_rate_limit.burst = burst;
        })
    }

    pub fn with_git_index(mut self) -> Self {
        self.index = Some(UpstreamIndex::new().unwrap());
        self
    }

    pub fn with_job_runner(mut self) -> Self {
        self.build_job_runner = true;
        self
    }

    pub fn with_slow_real_db_pool(mut self) -> Self {
        self.config.use_test_database_pool = false;
        self
    }
}

pub fn init_logger() {
    let _ = tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .without_time()
        .with_test_writer()
        .try_init();
}

fn simple_config() -> config::Server {
    config::Server {
        base: config::Base::test(),
        db: config::DatabasePools::test_from_environment(),
        session_key: "test this has to be over 32 bytes long".to_string(),
        gh_client_id: dotenv::var("GH_CLIENT_ID").unwrap_or_default(),
        gh_client_secret: dotenv::var("GH_CLIENT_SECRET").unwrap_or_default(),
        gh_base_url: "http://api.github.com".to_string(),
        max_upload_size: 3000,
        max_unpack_size: 2000,
        publish_rate_limit: Default::default(),
        blocked_traffic: Default::default(),
        max_allowed_page_offset: 200,
        page_offset_ua_blocklist: vec![],
        domain_name: "crates.io".into(),
        allowed_origins: Vec::new(),
        downloads_persist_interval_ms: 1000,
        ownership_invitations_expiration_days: 30,
        metrics_authorization_token: None,
        use_test_database_pool: true,
        instance_metrics_log_every_seconds: None,
        force_unconditional_redirects: false,
        blocked_routes: HashSet::new(),
        version_id_cache_size: 10000,
        version_id_cache_ttl: Duration::from_secs(5 * 60),
    }
}

fn build_app(
    config: config::Server,
    proxy: Option<String>,
) -> (Arc<App>, conduit_middleware::MiddlewareBuilder) {
    let client = if let Some(proxy) = proxy {
        let mut builder = Client::builder();
        builder = builder
            .proxy(Proxy::all(&proxy).expect("Unable to configure proxy with the provided URL"));
        Some(builder.build().expect("TLS backend cannot be initialized"))
    } else {
        None
    };

    let mut app = App::new(config, client);

    // Use the in-memory email backend for all tests, allowing tests to analyze the emails sent by
    // the application. This will also prevent cluttering the filesystem.
    app.emails = Emails::new_in_memory();

    let app = Arc::new(app);
    let handler = cargo_registry::build_handler(Arc::clone(&app));
    (app, handler)
}
<|endoftext|>


<commit_before>use super::make_request;
use super::{import_contest_mutation::*, ImportContestMutation};
use failure::Error;
use graphql_client::GraphQLQuery;
use juniper::http::GraphQLRequest;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs;
use std::path::{Path, PathBuf};
use turingarena_core::archive::pack_archive;

#[derive(Debug, Serialize, Deserialize)]
struct ContestFile {
    /// Title of the contest
    title: String,

    /// Start time of the contest
    start: Option<String>,

    /// End time of the contest
    end: Option<String>,

    /// Users of the contest
    users: Vec<ContestUser>,

    /// Problems of the contest
    problems: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ContestUser {
    /// Id of the user
    id: String,

    /// Name of the user
    name: String,

    /// Access token of the user
    token: String,

    /// Role of the user. Currently supported: user, admin
    role: Option<String>,
}

pub struct Importer {
    contest: ContestFile,
    path: PathBuf,
}

impl Importer {
    pub fn new<P: Into<PathBuf>>(path: P) -> Result<Self, Error> {
        let path = path.into();
        let metadata_path = path.join("turingarena.yaml");
        let metadata_data = fs::read(metadata_path)?;
        let contest = serde_yaml::from_slice::<ContestFile>(&metadata_data)?;

        Ok(Importer { contest, path })
    }

    fn contest(&self) -> ContestInput {
        let files = self.path.join("files");
        ContestInput {
            start_time: self.contest.start.as_ref().unwrap().clone(), // TODO: handle not specified date
            end_time: self.contest.end.as_ref().unwrap().clone(),
            archive_content: FileContentInput {
                base64: base64::encode(&pack_archive(files)),
            },
        }
    }

    fn problems(&self) -> Vec<ProblemInput> {
        let mut problems = Vec::new();

        for problem in &self.contest.problems {
            let path = self.path.join(problem);
            problems.push(ProblemInput {
                name: problem.clone(),
                archive_content: FileContentInput {
                    base64: base64::encode(&pack_archive(path)),
                },
            });
        }

        problems
    }

    fn users(&self) -> Vec<UserInput> {
        let mut users = Vec::new();

        for user in &self.contest.users {
            users.push(UserInput {
                id: user.id.clone(),
                token: user.token.clone(),
                display_name: user.name.clone(),
            });
        }

        users
    }
}

impl TryInto<GraphQLRequest> for Importer {
    type Error = Error;

    fn try_into(self) -> Result<GraphQLRequest, Self::Error> {
        let variables = Variables {
            contest: self.contest(),
            problems: self.problems(),
            users: self.users(),
        };
        Ok(make_request(ImportContestMutation::build_query, variables))
    }
}
<commit_msg>Fix compilation error<commit_after>use super::make_request;
use super::{import_contest_mutation::*, ImportContestMutation};
use failure::Error;
use graphql_client::GraphQLQuery;
use juniper::http::GraphQLRequest;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs;
use std::path::{Path, PathBuf};
use turingarena_core::archive::pack_archive;

#[derive(Debug, Serialize, Deserialize)]
struct ContestFile {
    /// Title of the contest
    title: String,

    /// Start time of the contest
    start: Option<String>,

    /// End time of the contest
    end: Option<String>,

    /// Users of the contest
    users: Vec<ContestUser>,

    /// Problems of the contest
    problems: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct ContestUser {
    /// Id of the user
    id: String,

    /// Name of the user
    name: String,

    /// Access token of the user
    token: String,

    /// Role of the user. Currently supported: user, admin
    role: Option<String>,
}

pub struct Importer {
    contest: ContestFile,
    path: PathBuf,
}

impl Importer {
    pub fn new<P: Into<PathBuf>>(path: P) -> Result<Self, Error> {
        let path = path.into();
        let metadata_path = path.join("turingarena.yaml");
        let metadata_data = fs::read(metadata_path)?;
        let contest = serde_yaml::from_slice::<ContestFile>(&metadata_data)?;

        Ok(Importer { contest, path })
    }

    fn contest(&self) -> ContestInput {
        let files = self.path.join("files");
        ContestInput {
            start_time: self.contest.start.as_ref().unwrap().clone(), // TODO: handle not specified date
            end_time: self.contest.end.as_ref().unwrap().clone(),
            archive_content: FileContentInput {
                base64: base64::encode(&pack_archive(files)),
            },
        }
    }

    fn problems(&self) -> Vec<ProblemInput> {
        let mut problems = Vec::new();

        for problem in &self.contest.problems {
            let path = self.path.join(problem);
            problems.push(ProblemInput {
                name: problem.clone(),
                archive_content: FileContentInput {
                    base64: base64::encode(&pack_archive(path)),
                },
            });
        }

        problems
    }

    fn users(&self) -> Vec<UserInput> {
        let mut users = Vec::new();

        for user in &self.contest.users {
            users.push(UserInput {
                id: user.id.clone(),
                token: user.token.clone(),
                display_name: user.name.clone(),
                admin: match user.role.as_ref() {
                    Some(role) => role == "admin",
                    _ => false,
                },
            });
        }

        users
    }
}

impl TryInto<GraphQLRequest> for Importer {
    type Error = Error;

    fn try_into(self) -> Result<GraphQLRequest, Self::Error> {
        let variables = Variables {
            contest: self.contest(),
            problems: self.problems(),
            users: self.users(),
        };
        Ok(make_request(ImportContestMutation::build_query, variables))
    }
}
<|endoftext|>