<commit_before>const TAG_OPENER: &'static str = "{{";
const TAG_CLOSER: &'static str = "}}";

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    Mismatch
}

#[derive(Debug, PartialEq, Eq)]
pub enum Token<'a> {
    Literal(&'a str),
    Interpolation(&'a str),
    SectionOpener(&'a str),
    SectionCloser(&'a str),
}

pub fn consume<'a>(input: &'a str, expected: &str) -> Result<&'a str, Error> {
    match input.starts_with(expected) {
        true => Ok(&input[expected.len()..]),
        false => Err(Error::Mismatch),
    }
}

pub fn bart_tag<'a>(input: &'a str) -> Result<(&'a str, Token<'a>), Error> {
    let input = consume(input, TAG_OPENER)?;

    enum TagType { // TODO Refactor into functions instead?
        Interpolation,
        SectionOpener,
        SectionCloser,
    };

    let (input, tag_type) = match input.chars().next() {
        Some('#') => (&input[1..], TagType::SectionOpener),
        Some('/') => (&input[1..], TagType::SectionCloser),
        Some(_) => (input, TagType::Interpolation),
        None => return Err(Error::Mismatch),
    };

    let (input, name) = match input.find(TAG_CLOSER) {
        Some(index) => Ok((&input[index..], &input[0..index])),
        None => Err(Error::Mismatch)
    }?;

    let input = consume(input, TAG_CLOSER)?;

    Ok((input, match tag_type {
        TagType::Interpolation => Token::Interpolation(name),
        TagType::SectionOpener => Token::SectionOpener(name),
        TagType::SectionCloser => Token::SectionCloser(name),
    }))
}

pub fn literal_text<'a>(input: &'a str) -> Result<(&'a str, Option<Token<'a>>), Error> {
    match input.find(TAG_OPENER) {
        Some(0) => Ok((input, None)),
        Some(index) => Ok((
            &input[index..],
            Some(Token::Literal(&input[0..index]))
        )),
        None => Ok((
            "",
            match input.len() {
                0 => None,
                _ => Some(Token::Literal(input))
            }
        ))
    }
}

pub fn sequence<'a>(mut input: &'a str) -> Result<Vec<Token<'a>>, Error> {
    let mut seq = vec![];

    loop {
        let (rest, literal_opt) = literal_text(input)?;

        if let Some(literal) = literal_opt {
            seq.push(literal);
        }

        if rest.is_empty() {
            break;
        }

        let (rest, tag) = bart_tag(rest)?;
        seq.push(tag);

        input = rest;
    }

    Ok(seq)
}

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

    #[test]
    fn consume_matches() {
        assert_eq!(Ok("ape}}"), consume("{{ape}}", "{{"));
    }

    #[test]
    fn consume_mismatches() {
        assert_eq!(Err(Error::Mismatch), consume("{{ape}}", "{a"));
    }

    #[test]
    fn bart_tag_matches() {
        assert_eq!(
            Ok(("tail", Token::Interpolation("ape"))),
            bart_tag("{{ape}}tail")
        );
    }

    #[test]
    fn bart_tag_mismatches() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("head{{ape}}")
        );
    }

    #[test]
    fn bart_tag_must_be_closed() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("{{ape")
        );
    }

    #[test]
    fn bart_tag_matches_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener("ape"))),
            bart_tag("{{#ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_section_closer() {
        assert_eq!(
            Ok(("", Token::SectionCloser("ape"))),
            bart_tag("{{/ape}}")
        );
    }

    #[test]
    fn literal_reads_until_tag() {
        assert_eq!(
            Ok(("{{ape}}", Some(Token::Literal("head")))),
            literal_text("head{{ape}}")
        );
    }

    #[test]
    fn literal_reads_until_end() {
        assert_eq!(
            Ok(("", Some(Token::Literal("head{ape}")))),
            literal_text("head{ape}")
        );
    }

    #[test]
    fn literal_returns_none_at_tag() {
        assert_eq!(
            Ok(("{{ape}}", None)),
            literal_text("{{ape}}")
        );
    }

    #[test]
    fn literal_returns_none_at_end() {
        assert_eq!(
            Ok(("", None)),
            literal_text("")
        );
    }

    #[test]
    fn template_with_tightly_packed_tags() {
        let parsed = sequence("{{a}}{{b}}{{c}}").unwrap();
        assert_eq!(vec![
            Interpolation("a".into()),
            Interpolation("b".into()),
            Interpolation("c".into()),
        ], parsed);
    }

    #[test]
    fn template_with_mixed_content() {
        let parsed = sequence("Hello {{name}}! {{#list}}Welcome{{/list}}").unwrap();
        assert_eq!(vec![
            Literal("Hello "),
            Interpolation("name"),
            Literal("! "),
            SectionOpener("list"),
            Literal("Welcome"),
            SectionCloser("list"),
        ], parsed);
    }
}
<commit_msg>Remove pubs that were added to silence compiler warnings<commit_after>const TAG_OPENER: &'static str = "{{";
const TAG_CLOSER: &'static str = "}}";

#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    Mismatch
}

#[derive(Debug, PartialEq, Eq)]
pub enum Token<'a> {
    Literal(&'a str),
    Interpolation(&'a str),
    SectionOpener(&'a str),
    SectionCloser(&'a str),
}

fn consume<'a>(input: &'a str, expected: &str) -> Result<&'a str, Error> {
    match input.starts_with(expected) {
        true => Ok(&input[expected.len()..]),
        false => Err(Error::Mismatch),
    }
}

fn bart_tag<'a>(input: &'a str) -> Result<(&'a str, Token<'a>), Error> {
    let input = consume(input, TAG_OPENER)?;

    enum TagType { // TODO Refactor into functions instead?
        Interpolation,
        SectionOpener,
        SectionCloser,
    };

    let (input, tag_type) = match input.chars().next() {
        Some('#') => (&input[1..], TagType::SectionOpener),
        Some('/') => (&input[1..], TagType::SectionCloser),
        Some(_) => (input, TagType::Interpolation),
        None => return Err(Error::Mismatch),
    };

    let (input, name) = match input.find(TAG_CLOSER) {
        Some(index) => Ok((&input[index..], &input[0..index])),
        None => Err(Error::Mismatch)
    }?;

    let input = consume(input, TAG_CLOSER)?;

    Ok((input, match tag_type {
        TagType::Interpolation => Token::Interpolation(name),
        TagType::SectionOpener => Token::SectionOpener(name),
        TagType::SectionCloser => Token::SectionCloser(name),
    }))
}

fn literal_text<'a>(input: &'a str) -> Result<(&'a str, Option<Token<'a>>), Error> {
    match input.find(TAG_OPENER) {
        Some(0) => Ok((input, None)),
        Some(index) => Ok((
            &input[index..],
            Some(Token::Literal(&input[0..index]))
        )),
        None => Ok((
            "",
            match input.len() {
                0 => None,
                _ => Some(Token::Literal(input))
            }
        ))
    }
}

pub fn sequence<'a>(mut input: &'a str) -> Result<Vec<Token<'a>>, Error> {
    let mut seq = vec![];

    loop {
        let (rest, literal_opt) = literal_text(input)?;

        if let Some(literal) = literal_opt {
            seq.push(literal);
        }

        if rest.is_empty() {
            break;
        }

        let (rest, tag) = bart_tag(rest)?;
        seq.push(tag);

        input = rest;
    }

    Ok(seq)
}

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

    #[test]
    fn consume_matches() {
        assert_eq!(Ok("ape}}"), consume("{{ape}}", "{{"));
    }

    #[test]
    fn consume_mismatches() {
        assert_eq!(Err(Error::Mismatch), consume("{{ape}}", "{a"));
    }

    #[test]
    fn bart_tag_matches() {
        assert_eq!(
            Ok(("tail", Token::Interpolation("ape"))),
            bart_tag("{{ape}}tail")
        );
    }

    #[test]
    fn bart_tag_mismatches() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("head{{ape}}")
        );
    }

    #[test]
    fn bart_tag_must_be_closed() {
        assert_eq!(
            Err(Error::Mismatch),
            bart_tag("{{ape")
        );
    }

    #[test]
    fn bart_tag_matches_section_opener() {
        assert_eq!(
            Ok(("", Token::SectionOpener("ape"))),
            bart_tag("{{#ape}}")
        );
    }

    #[test]
    fn bart_tag_matches_section_closer() {
        assert_eq!(
            Ok(("", Token::SectionCloser("ape"))),
            bart_tag("{{/ape}}")
        );
    }

    #[test]
    fn literal_reads_until_tag() {
        assert_eq!(
            Ok(("{{ape}}", Some(Token::Literal("head")))),
            literal_text("head{{ape}}")
        );
    }

    #[test]
    fn literal_reads_until_end() {
        assert_eq!(
            Ok(("", Some(Token::Literal("head{ape}")))),
            literal_text("head{ape}")
        );
    }

    #[test]
    fn literal_returns_none_at_tag() {
        assert_eq!(
            Ok(("{{ape}}", None)),
            literal_text("{{ape}}")
        );
    }

    #[test]
    fn literal_returns_none_at_end() {
        assert_eq!(
            Ok(("", None)),
            literal_text("")
        );
    }

    #[test]
    fn template_with_tightly_packed_tags() {
        let parsed = sequence("{{a}}{{b}}{{c}}").unwrap();
        assert_eq!(vec![
            Interpolation("a".into()),
            Interpolation("b".into()),
            Interpolation("c".into()),
        ], parsed);
    }

    #[test]
    fn template_with_mixed_content() {
        let parsed = sequence("Hello {{name}}! {{#list}}Welcome{{/list}}").unwrap();
        assert_eq!(vec![
            Literal("Hello "),
            Interpolation("name"),
            Literal("! "),
            SectionOpener("list"),
            Literal("Welcome"),
            SectionCloser("list"),
        ], parsed);
    }
}
<|endoftext|>


<commit_before>use snafu::{Backtrace, GenerateBacktrace, Snafu};
use std::io;
use std::path;
use std::result;
use std::string;

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum Error {
    #[snafu(display("file io error [{:?}]: {}", path, source))]
    FileIO {
        path: path::PathBuf,
        source: io::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("dir entry error [{:?}]: {}", dir, source))]
    EntryIO {
        dir: path::PathBuf,
        source: io::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("base64 decode error: {}", source))]
    Base64Decode {
        source: base64::DecodeError,
        backtrace: Backtrace,
    },

    #[snafu(display("utf8 decode error: {}", source))]
    UTF8Decode {
        source: string::FromUtf8Error,
        backtrace: Backtrace,
    },

    #[snafu(display("json decode error: {}", source))]
    JsonDecode {
        source: serde_json::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("failed to create regex pattern \"{}\": {}", pattern, source))]
    Regex {
        pattern: String,
        source: regex::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("http error: {}", msg))]
    Http {
        msg: String,
        status: u16,
        backtrace: Backtrace,
    },

    #[snafu(display("http io error: {}", source))]
    HttpIO {
        source: io::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("failed to parse episode title: {}", filename))]
    NoEpisodeTitle { filename: String },

    #[snafu(display("expected episode number for file: {}", filename))]
    ExpectedEpNumber { filename: String },

    #[snafu(display("failed to parse episode: {}", filename))]
    EpisodeParseFailed { filename: String },

    #[snafu(display(
        "found different episode titles:\n\texpecting: {}\n\tfound: {}",
        expecting,
        found
    ))]
    MultipleTitles { expecting: String, found: String },

    #[snafu(display("need existing series info to use offline backend"))]
    NeedExistingSeriesData,

    #[snafu(display("received bad response from AniList (code {}): {}", code, message))]
    BadAniListResponse { code: u16, message: String },

    #[snafu(display(
        "custom episode matcher must specify the episode and (optionally) the title group"
    ))]
    MissingMatcherGroups,

    #[snafu(display("title group must be specified to parse episodes"))]
    NeedTitleGroup,

    #[snafu(display("must be authorized to make this request"))]
    NeedAuthentication,
}

impl Error {
    pub fn is_http_code(&self, http_code: u16) -> bool {
        match self {
            Error::BadAniListResponse { code, .. } if http_code == *code => true,
            Error::Http { status, .. } => *status == http_code,
            _ => false,
        }
    }
}

impl From<&ureq::Error> for Error {
    fn from(source: &ureq::Error) -> Self {
        Self::Http {
            msg: format!("{}", source),
            status: source.status(),
            backtrace: Backtrace::generate(),
        }
    }
}
<commit_msg>anime: Simplify BadAniListResponse error message<commit_after>use snafu::{Backtrace, GenerateBacktrace, Snafu};
use std::io;
use std::path;
use std::result;
use std::string;

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)))]
pub enum Error {
    #[snafu(display("file io error [{:?}]: {}", path, source))]
    FileIO {
        path: path::PathBuf,
        source: io::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("dir entry error [{:?}]: {}", dir, source))]
    EntryIO {
        dir: path::PathBuf,
        source: io::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("base64 decode error: {}", source))]
    Base64Decode {
        source: base64::DecodeError,
        backtrace: Backtrace,
    },

    #[snafu(display("utf8 decode error: {}", source))]
    UTF8Decode {
        source: string::FromUtf8Error,
        backtrace: Backtrace,
    },

    #[snafu(display("json decode error: {}", source))]
    JsonDecode {
        source: serde_json::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("failed to create regex pattern \"{}\": {}", pattern, source))]
    Regex {
        pattern: String,
        source: regex::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("http error: {}", msg))]
    Http {
        msg: String,
        status: u16,
        backtrace: Backtrace,
    },

    #[snafu(display("http io error: {}", source))]
    HttpIO {
        source: io::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("failed to parse episode title: {}", filename))]
    NoEpisodeTitle { filename: String },

    #[snafu(display("expected episode number for file: {}", filename))]
    ExpectedEpNumber { filename: String },

    #[snafu(display("failed to parse episode: {}", filename))]
    EpisodeParseFailed { filename: String },

    #[snafu(display(
        "found different episode titles:\n\texpecting: {}\n\tfound: {}",
        expecting,
        found
    ))]
    MultipleTitles { expecting: String, found: String },

    #[snafu(display("need existing series info to use offline backend"))]
    NeedExistingSeriesData,

    #[snafu(display("bad AniList response ({}): {}", code, message))]
    BadAniListResponse { code: u16, message: String },

    #[snafu(display(
        "custom episode matcher must specify the episode and (optionally) the title group"
    ))]
    MissingMatcherGroups,

    #[snafu(display("title group must be specified to parse episodes"))]
    NeedTitleGroup,

    #[snafu(display("must be authorized to make this request"))]
    NeedAuthentication,
}

impl Error {
    pub fn is_http_code(&self, http_code: u16) -> bool {
        match self {
            Error::BadAniListResponse { code, .. } if http_code == *code => true,
            Error::Http { status, .. } => *status == http_code,
            _ => false,
        }
    }
}

impl From<&ureq::Error> for Error {
    fn from(source: &ureq::Error) -> Self {
        Self::Http {
            msg: format!("{}", source),
            status: source.status(),
            backtrace: Backtrace::generate(),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Serde YAML Developers
//
// 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.

use std::hash::{Hash, Hasher};
use std::mem;

use dtoa;
use linked_hash_map::LinkedHashMap;
use serde::{self, Serialize, Deserialize};
use yaml_rust::Yaml;

use super::{Error, Deserializer, Serializer};

#[derive(Clone, PartialOrd, Debug)]
pub enum Value {
    Null,
    Bool(bool),
    I64(i64),
    F64(f64),
    String(String),
    Sequence(Sequence),
    Mapping(Mapping),
}

pub type Sequence = Vec<Value>;
pub type Mapping = LinkedHashMap<Value, Value>;

/// Shortcut function to encode a `T` into a YAML `Value`.
///
/// ```rust
/// use serde_yaml::{Value, to_value};
/// let val = to_value("foo");
/// assert_eq!(val, Value::String("foo".to_owned()))
/// ```
pub fn to_value<T: ?Sized>(value: &T) -> Value
    where T: Serialize,
{
    let mut ser = Serializer::new();
    value.serialize(&mut ser).unwrap();
    ser.take().into()
}

/// Shortcut function to decode a YAML `Value` into a `T`.
///
/// ```rust
/// use serde_yaml::{Value, from_value};
/// let val = Value::String("foo".to_owned());
/// assert_eq!("foo", from_value::<String>(val).unwrap());
/// ```
pub fn from_value<T>(value: Value) -> Result<T, Error>
    where T: Deserialize,
{
    let yaml = value.into();
    let mut de = Deserializer::new(&yaml);
    Deserialize::deserialize(&mut de)
}

impl Value {
    pub fn is_null(&self) -> bool {
        if let Value::Null = *self {
            true
        } else {
            false
        }
    }

    pub fn is_bool(&self) -> bool {
        self.as_bool().is_some()
    }

    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Bool(b) => Some(b),
            _ => None,
        }
    }

    pub fn is_i64(&self) -> bool {
        self.as_i64().is_some()
    }

    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            Value::I64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_f64(&self) -> bool {
        self.as_f64().is_some()
    }

    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            Value::F64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_string(&self) -> bool {
        self.as_str().is_some()
    }

    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref s) => Some(&s),
            _ => None,
        }
    }

    pub fn is_sequence(&self) -> bool {
        self.as_sequence().is_some()
    }

    pub fn as_sequence(&self) -> Option<&Sequence> {
        match *self {
            Value::Sequence(ref seq) => Some(seq),
            _ => None,
        }
    }

    pub fn as_sequence_mut(&mut self) -> Option<&mut Sequence> {
        match *self {
            Value::Sequence(ref mut seq) => Some(seq),
            _ => None,
        }
    }

    pub fn is_mapping(&self) -> bool {
        self.as_mapping().is_some()
    }

    pub fn as_mapping(&self) -> Option<&Mapping> {
        match *self {
            Value::Mapping(ref map) => Some(map),
            _ => None,
        }
    }

    pub fn as_mapping_mut(&mut self) -> Option<&mut Mapping> {
        match *self {
            Value::Mapping(ref mut map) => Some(map),
            _ => None,
        }
    }
}

impl From<Yaml> for Value {
    fn from(yaml: Yaml) -> Self {
        match yaml {
            Yaml::Real(f) => {
                match f.parse() {
                    Ok(f) => Value::F64(f),
                    Err(_) => Value::String(f),
                }
            }
            Yaml::Integer(i) => Value::I64(i),
            Yaml::String(s) => Value::String(s),
            Yaml::Boolean(b) => Value::Bool(b),
            Yaml::Array(array) =>  {
                Value::Sequence(array.into_iter()
                                     .map(Into::into)
                                     .collect())
            }
            Yaml::Hash(hash) => {
                Value::Mapping(hash.into_iter()
                                   .map(|(k, v)| (k.into(), v.into()))
                                   .collect())
            }
            Yaml::Alias(_) => panic!("alias unsupported"),
            Yaml::Null => Value::Null,
            Yaml::BadValue => panic!("bad value"),
        }
    }
}

impl From<Value> for Yaml {
    fn from(value: Value) -> Self {
        match value {
            Value::Null => Yaml::Null,
            Value::Bool(b) => Yaml::Boolean(b),
            Value::I64(i) => Yaml::Integer(i),
            Value::F64(f) => {
                let mut buf = Vec::new();
                dtoa::write(&mut buf, f).unwrap();
                Yaml::Real(String::from_utf8(buf).unwrap())
            }
            Value::String(s) => Yaml::String(s),
            Value::Sequence(seq) => {
                Yaml::Array(seq.into_iter()
                               .map(Into::into)
                               .collect())
            }
            Value::Mapping(map) => {
                Yaml::Hash(map.into_iter()
                              .map(|(k, v)| (k.into(), v.into()))
                              .collect())
            }
        }
    }
}

impl Serialize for Value {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        match *self {
            Value::Null => serializer.serialize_unit(),
            Value::Bool(b) => serializer.serialize_bool(b),
            Value::I64(i) => serializer.serialize_i64(i),
            Value::F64(f) => serializer.serialize_f64(f),
            Value::String(ref s) => serializer.serialize_str(s),
            Value::Sequence(ref seq) => seq.serialize(serializer),
            Value::Mapping(ref map) => map.serialize(serializer),
        }
    }
}

impl Deserialize for Value {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: serde::Deserializer
    {
        struct ValueVisitor;

        impl serde::de::Visitor for ValueVisitor {
            type Value = Value;

            fn visit_bool<E>(&mut self, b: bool) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Bool(b))
            }

            fn visit_i64<E>(&mut self, i: i64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(i))
            }

            fn visit_u64<E>(&mut self, u: u64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(u as i64))
            }

            fn visit_f64<E>(&mut self, f: f64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::F64(f))
            }

            fn visit_str<E>(&mut self, s: &str) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s.to_owned()))
            }

            fn visit_string<E>(&mut self, s: String) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s))
            }

            fn visit_unit<E>(&mut self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_none<E>(&mut self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_some<D>(
                &mut self,
                deserializer: &mut D
            ) -> Result<Value, D::Error>
                where D: serde::Deserializer,
            {
                Deserialize::deserialize(deserializer)
            }

            fn visit_seq<V>(&mut self, visitor: V) -> Result<Value, V::Error>
                where V: serde::de::SeqVisitor,
            {
                use serde::de::impls::VecVisitor;
                let values = try!(VecVisitor::new().visit_seq(visitor));
                Ok(Value::Sequence(values))
            }

            fn visit_map<V>(&mut self, visitor: V) -> Result<Value, V::Error>
                where V: serde::de::MapVisitor,
            {
                use linked_hash_map::serde::LinkedHashMapVisitor;
                let values = try!(LinkedHashMapVisitor::new().visit_map(visitor));
                Ok(Value::Mapping(values))
            }
        }

        deserializer.deserialize(ValueVisitor)
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Value) -> bool {
        match (self, other) {
            (&Value::Null, &Value::Null) => true,
            (&Value::Bool(a), &Value::Bool(b)) => a == b,
            (&Value::I64(a), &Value::I64(b)) => a == b,
            (&Value::F64(a), &Value::F64(b)) => {
                if a.is_nan() && b.is_nan() {
                    // compare NaN for bitwise equality
                    let (a, b): (i64, i64) = unsafe {
                        (mem::transmute(a), mem::transmute(b))
                    };
                    a == b
                } else {
                    a == b
                }
            }
            (&Value::String(ref a), &Value::String(ref b)) => a == b,
            (&Value::Sequence(ref a), &Value::Sequence(ref b)) => a == b,
            (&Value::Mapping(ref a), &Value::Mapping(ref b)) => a == b,
            _ => false,
        }
    }
}

impl Eq for Value {}

impl Hash for Value {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            &Value::Null => 0.hash(state),
            &Value::Bool(b) => (1, b).hash(state),
            &Value::I64(i) => (2, i).hash(state),
            &Value::F64(_) => {
                // you should feel bad for using f64 as a map key
                3.hash(state);
            }
            &Value::String(ref s) => (4, s).hash(state),
            &Value::Sequence(ref seq) => (5, seq).hash(state),
            &Value::Mapping(ref map) => (6, map).hash(state),
        }
    }
}
<commit_msg>Fix clippy lints in Value implementation<commit_after>// Copyright 2016 Serde YAML Developers
//
// 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.

use std::hash::{Hash, Hasher};
use std::mem;

use dtoa;
use linked_hash_map::LinkedHashMap;
use serde::{self, Serialize, Deserialize};
use yaml_rust::Yaml;

use super::{Error, Deserializer, Serializer};

#[derive(Clone, PartialOrd, Debug)]
pub enum Value {
    Null,
    Bool(bool),
    I64(i64),
    F64(f64),
    String(String),
    Sequence(Sequence),
    Mapping(Mapping),
}

pub type Sequence = Vec<Value>;
pub type Mapping = LinkedHashMap<Value, Value>;

/// Shortcut function to encode a `T` into a YAML `Value`.
///
/// ```rust
/// use serde_yaml::{Value, to_value};
/// let val = to_value("foo");
/// assert_eq!(val, Value::String("foo".to_owned()))
/// ```
pub fn to_value<T: ?Sized>(value: &T) -> Value
    where T: Serialize,
{
    let mut ser = Serializer::new();
    value.serialize(&mut ser).unwrap();
    ser.take().into()
}

/// Shortcut function to decode a YAML `Value` into a `T`.
///
/// ```rust
/// use serde_yaml::{Value, from_value};
/// let val = Value::String("foo".to_owned());
/// assert_eq!("foo", from_value::<String>(val).unwrap());
/// ```
pub fn from_value<T>(value: Value) -> Result<T, Error>
    where T: Deserialize,
{
    let yaml = value.into();
    let mut de = Deserializer::new(&yaml);
    Deserialize::deserialize(&mut de)
}

impl Value {
    pub fn is_null(&self) -> bool {
        if let Value::Null = *self {
            true
        } else {
            false
        }
    }

    pub fn is_bool(&self) -> bool {
        self.as_bool().is_some()
    }

    pub fn as_bool(&self) -> Option<bool> {
        match *self {
            Value::Bool(b) => Some(b),
            _ => None,
        }
    }

    pub fn is_i64(&self) -> bool {
        self.as_i64().is_some()
    }

    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            Value::I64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_f64(&self) -> bool {
        self.as_f64().is_some()
    }

    pub fn as_f64(&self) -> Option<f64> {
        match *self {
            Value::F64(i) => Some(i),
            _ => None,
        }
    }

    pub fn is_string(&self) -> bool {
        self.as_str().is_some()
    }

    pub fn as_str(&self) -> Option<&str> {
        match *self {
            Value::String(ref s) => Some(s),
            _ => None,
        }
    }

    pub fn is_sequence(&self) -> bool {
        self.as_sequence().is_some()
    }

    pub fn as_sequence(&self) -> Option<&Sequence> {
        match *self {
            Value::Sequence(ref seq) => Some(seq),
            _ => None,
        }
    }

    pub fn as_sequence_mut(&mut self) -> Option<&mut Sequence> {
        match *self {
            Value::Sequence(ref mut seq) => Some(seq),
            _ => None,
        }
    }

    pub fn is_mapping(&self) -> bool {
        self.as_mapping().is_some()
    }

    pub fn as_mapping(&self) -> Option<&Mapping> {
        match *self {
            Value::Mapping(ref map) => Some(map),
            _ => None,
        }
    }

    pub fn as_mapping_mut(&mut self) -> Option<&mut Mapping> {
        match *self {
            Value::Mapping(ref mut map) => Some(map),
            _ => None,
        }
    }
}

impl From<Yaml> for Value {
    fn from(yaml: Yaml) -> Self {
        match yaml {
            Yaml::Real(f) => {
                match f.parse() {
                    Ok(f) => Value::F64(f),
                    Err(_) => Value::String(f),
                }
            }
            Yaml::Integer(i) => Value::I64(i),
            Yaml::String(s) => Value::String(s),
            Yaml::Boolean(b) => Value::Bool(b),
            Yaml::Array(array) =>  {
                Value::Sequence(array.into_iter()
                                     .map(Into::into)
                                     .collect())
            }
            Yaml::Hash(hash) => {
                Value::Mapping(hash.into_iter()
                                   .map(|(k, v)| (k.into(), v.into()))
                                   .collect())
            }
            Yaml::Alias(_) => panic!("alias unsupported"),
            Yaml::Null => Value::Null,
            Yaml::BadValue => panic!("bad value"),
        }
    }
}

impl From<Value> for Yaml {
    fn from(value: Value) -> Self {
        match value {
            Value::Null => Yaml::Null,
            Value::Bool(b) => Yaml::Boolean(b),
            Value::I64(i) => Yaml::Integer(i),
            Value::F64(f) => {
                let mut buf = Vec::new();
                dtoa::write(&mut buf, f).unwrap();
                Yaml::Real(String::from_utf8(buf).unwrap())
            }
            Value::String(s) => Yaml::String(s),
            Value::Sequence(seq) => {
                Yaml::Array(seq.into_iter()
                               .map(Into::into)
                               .collect())
            }
            Value::Mapping(map) => {
                Yaml::Hash(map.into_iter()
                              .map(|(k, v)| (k.into(), v.into()))
                              .collect())
            }
        }
    }
}

impl Serialize for Value {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        match *self {
            Value::Null => serializer.serialize_unit(),
            Value::Bool(b) => serializer.serialize_bool(b),
            Value::I64(i) => serializer.serialize_i64(i),
            Value::F64(f) => serializer.serialize_f64(f),
            Value::String(ref s) => serializer.serialize_str(s),
            Value::Sequence(ref seq) => seq.serialize(serializer),
            Value::Mapping(ref map) => map.serialize(serializer),
        }
    }
}

impl Deserialize for Value {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: serde::Deserializer
    {
        struct ValueVisitor;

        impl serde::de::Visitor for ValueVisitor {
            type Value = Value;

            fn visit_bool<E>(&mut self, b: bool) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Bool(b))
            }

            fn visit_i64<E>(&mut self, i: i64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(i))
            }

            fn visit_u64<E>(&mut self, u: u64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::I64(u as i64))
            }

            fn visit_f64<E>(&mut self, f: f64) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::F64(f))
            }

            fn visit_str<E>(&mut self, s: &str) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s.to_owned()))
            }

            fn visit_string<E>(&mut self, s: String) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::String(s))
            }

            fn visit_unit<E>(&mut self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_none<E>(&mut self) -> Result<Value, E>
                where E: serde::de::Error
            {
                Ok(Value::Null)
            }

            fn visit_some<D>(
                &mut self,
                deserializer: &mut D
            ) -> Result<Value, D::Error>
                where D: serde::Deserializer,
            {
                Deserialize::deserialize(deserializer)
            }

            fn visit_seq<V>(&mut self, visitor: V) -> Result<Value, V::Error>
                where V: serde::de::SeqVisitor,
            {
                use serde::de::impls::VecVisitor;
                let values = try!(VecVisitor::new().visit_seq(visitor));
                Ok(Value::Sequence(values))
            }

            fn visit_map<V>(&mut self, visitor: V) -> Result<Value, V::Error>
                where V: serde::de::MapVisitor,
            {
                use linked_hash_map::serde::LinkedHashMapVisitor;
                let values = try!(LinkedHashMapVisitor::new().visit_map(visitor));
                Ok(Value::Mapping(values))
            }
        }

        deserializer.deserialize(ValueVisitor)
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Value) -> bool {
        match (self, other) {
            (&Value::Null, &Value::Null) => true,
            (&Value::Bool(a), &Value::Bool(b)) => a == b,
            (&Value::I64(a), &Value::I64(b)) => a == b,
            (&Value::F64(a), &Value::F64(b)) => {
                if a.is_nan() && b.is_nan() {
                    // compare NaN for bitwise equality
                    let (a, b): (i64, i64) = unsafe {
                        (mem::transmute(a), mem::transmute(b))
                    };
                    a == b
                } else {
                    a == b
                }
            }
            (&Value::String(ref a), &Value::String(ref b)) => a == b,
            (&Value::Sequence(ref a), &Value::Sequence(ref b)) => a == b,
            (&Value::Mapping(ref a), &Value::Mapping(ref b)) => a == b,
            _ => false,
        }
    }
}

impl Eq for Value {}

impl Hash for Value {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match *self {
            Value::Null => 0.hash(state),
            Value::Bool(b) => (1, b).hash(state),
            Value::I64(i) => (2, i).hash(state),
            Value::F64(_) => {
                // you should feel bad for using f64 as a map key
                3.hash(state);
            }
            Value::String(ref s) => (4, s).hash(state),
            Value::Sequence(ref seq) => (5, seq).hash(state),
            Value::Mapping(ref map) => (6, map).hash(state),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 The Propverify authors
//
// 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.

/////////////////////////////////////////////////////////////////
// FFI wrapper for KLEE symbolic execution tool
/////////////////////////////////////////////////////////////////

use std::default::Default;
use std::os::raw;

#[link(name = "kleeRuntest")]
extern "C" {
    fn klee_make_symbolic(data: *mut raw::c_void, length: usize, name: *const raw::c_char);
    fn klee_assume(cond: usize);
    fn klee_abort() -> !;
    fn klee_silent_exit(_ignored: u32) -> !;
    fn klee_is_replay() -> i32;
}

// Create an abstract value of type <T>
//
// This should only be used on types that occupy contiguous memory
// and where all possible bit-patterns are legal.
// e.g., u8/i8, ... u128/i128, f32/f64
pub fn abstract_value<T: Default>() -> T {
    let mut r = T::default();
    unsafe {
        let data = std::mem::transmute(&mut r);
        let length = std::mem::size_of::<T>();
        let null = 0 as *const i8;
        klee_make_symbolic(data, length, null)
    }
    return r;
}

// Add an assumption
pub fn assume(cond: bool) {
    unsafe { klee_assume(if cond { 1 } else { 0 }) }
}

// Reject the current execution with a verification failure.
//
// In almost all circumstances, report_error should
// be used instead because it generates an error message.
pub fn abort() -> ! {
    unsafe { klee_abort() }
}

// Reject the current execution path with a verification success.
// This is equivalent to assume(false)
// and the opposite of report_error.
//
// Typical usage is in generating symbolic values when the value
// does not meet some criteria.
pub fn reject() -> ! {
    unsafe { klee_silent_exit(0) }
}

// Detect whether the program is being run symbolically in KLEE
// or being replayed using the kleeRuntest runtime.
//
// This is used to decide whether to display the values of
// variables that may be either symbolic or concrete.
pub fn is_replay() -> bool {
    unsafe { klee_is_replay() != 0 }
}

// Reject the current execution with a verification failure
// and an error message.
pub fn report_error(message: &str) -> ! {
    // Mimic the format of klee_report_error
    // (We don't use klee_report_error because it is not
    // supported by the kleeRuntest library.)
    eprintln!("KLEE: ERROR:{}", message);
    abort();
}

// Check an assertion
pub fn verify(cond: bool) {
    if !cond {
        report_error("verification failed");
    }
}

// Declare that failure is the expected behaviour
pub fn expect_raw(msg: &str) {
    eprintln!("VERIFIER_EXPECT: {}", msg)
}

// Declare that failure is the expected behaviour
pub fn expect(msg: Option<&str>) {
    match msg {
        None => eprintln!("VERIFIER_EXPECT: should_panic"),
        Some(msg) => eprintln!("VERIFIER_EXPECT: should_panic(expected = \"{}\")", msg)
    }
}

#[macro_export]
macro_rules! assert {
    ($cond:expr) => {
        if ! $cond {
            eprintln!("VERIFIER: assertion failed: {}", stringify!($cond));
            $crate::abort();
        }
    };
    // ($cond:expr,) => { ... };
    // ($cond:expr, $($arg:tt)+) => { ... };
}

#[macro_export]
macro_rules! assert_eq {
    ($left:expr, $right:expr) => { $crate::assert!(($left) == ($right)); };
    // ($left:expr, $right:expr,) => { ... };
    // ($left:expr, $right:expr, $($arg:tt)+) => { ... };
}

#[macro_export]
macro_rules! assert_ne {
    ($left:expr, $right:expr) => { $crate::assert!(($left) != ($right)); };
    // ($left:expr, $right:expr,) => { ... };
    // ($left:expr, $right:expr, $($arg:tt)+) => { ... };
}

/////////////////////////////////////////////////////////////////
// End
/////////////////////////////////////////////////////////////////
<commit_msg>Fix KLEE assertion message<commit_after>// Copyright 2020 The Propverify authors
//
// 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.

/////////////////////////////////////////////////////////////////
// FFI wrapper for KLEE symbolic execution tool
/////////////////////////////////////////////////////////////////

use std::default::Default;
use std::os::raw;

#[link(name = "kleeRuntest")]
extern "C" {
    fn klee_make_symbolic(data: *mut raw::c_void, length: usize, name: *const raw::c_char);
    fn klee_assume(cond: usize);
    fn klee_abort() -> !;
    fn klee_silent_exit(_ignored: u32) -> !;
    fn klee_is_replay() -> i32;
}

// Create an abstract value of type <T>
//
// This should only be used on types that occupy contiguous memory
// and where all possible bit-patterns are legal.
// e.g., u8/i8, ... u128/i128, f32/f64
pub fn abstract_value<T: Default>() -> T {
    let mut r = T::default();
    unsafe {
        let data = std::mem::transmute(&mut r);
        let length = std::mem::size_of::<T>();
        let null = 0 as *const i8;
        klee_make_symbolic(data, length, null)
    }
    return r;
}

// Add an assumption
pub fn assume(cond: bool) {
    unsafe { klee_assume(if cond { 1 } else { 0 }) }
}

// Reject the current execution with a verification failure.
//
// In almost all circumstances, report_error should
// be used instead because it generates an error message.
pub fn abort() -> ! {
    unsafe { klee_abort() }
}

// Reject the current execution path with a verification success.
// This is equivalent to assume(false)
// and the opposite of report_error.
//
// Typical usage is in generating symbolic values when the value
// does not meet some criteria.
pub fn reject() -> ! {
    unsafe { klee_silent_exit(0) }
}

// Detect whether the program is being run symbolically in KLEE
// or being replayed using the kleeRuntest runtime.
//
// This is used to decide whether to display the values of
// variables that may be either symbolic or concrete.
pub fn is_replay() -> bool {
    unsafe { klee_is_replay() != 0 }
}

// Reject the current execution with a verification failure
// and an error message.
pub fn report_error(message: &str) -> ! {
    // Mimic the format of klee_report_error
    // (We don't use klee_report_error because it is not
    // supported by the kleeRuntest library.)
    eprintln!("KLEE: ERROR:{}", message);
    abort();
}

// Check an assertion
pub fn verify(cond: bool) {
    if !cond {
        report_error("verification failed");
    }
}

// Declare that failure is the expected behaviour
pub fn expect_raw(msg: &str) {
    eprintln!("VERIFIER_EXPECT: {}", msg)
}

// Declare that failure is the expected behaviour
pub fn expect(msg: Option<&str>) {
    match msg {
        None => eprintln!("VERIFIER_EXPECT: should_panic"),
        Some(msg) => eprintln!("VERIFIER_EXPECT: should_panic(expected = \"{}\")", msg)
    }
}

#[macro_export]
macro_rules! assert {
    ($cond:expr) => {
        if ! $cond {
            eprintln!("VERIFIER: panicked at 'assertion failed: {}', {}:{}:{}", stringify!($cond), std::file!(), std::line!(), std::column!());
            $crate::abort();
        }
    };
    // ($cond:expr,) => { ... };
    // ($cond:expr, $($arg:tt)+) => { ... };
}

#[macro_export]
macro_rules! assert_eq {
    ($left:expr, $right:expr) => { $crate::assert!(($left) == ($right)); };
    // ($left:expr, $right:expr,) => { ... };
    // ($left:expr, $right:expr, $($arg:tt)+) => { ... };
}

#[macro_export]
macro_rules! assert_ne {
    ($left:expr, $right:expr) => { $crate::assert!(($left) != ($right)); };
    // ($left:expr, $right:expr,) => { ... };
    // ($left:expr, $right:expr, $($arg:tt)+) => { ... };
}

/////////////////////////////////////////////////////////////////
// End
/////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>use std::num::SignedInt;

#[derive(FromPrimitive, Debug)]
pub enum FilterType {
    NoFilter = 0,
    Sub = 1,
    Up = 2,
    Avg = 3,
    Paeth = 4
}

fn filter_paeth(a: u8, b: u8, c: u8) -> u8 {
    let ia = a as i16;
    let ib = b as i16;
    let ic = c as i16;

    let p = ia + ib - ic;

    let pa = (p - ia).abs();
    let pb = (p - ib).abs();
    let pc = (p - ic).abs();

    if pa <= pb && pa <= pc {
        a
    } else if pb <= pc {
        b
    } else {
        c
    }
}

pub fn unfilter(filter: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
    let len = current.len();

    match filter {
        FilterType::NoFilter => (),
        FilterType::Sub => {
            for i in (bpp..len) {
                current[i] += current[i - bpp];
            }
        }
        FilterType::Up => {
            for i in (0..len) {
                current[i] += previous[i];
            }
        }
        FilterType::Avg => {
            for i in (0..bpp) {
                current[i] += previous[i] / 2;
            }

            for i in (bpp..len) {
                current[i] += ((current[i - bpp] as i16 + previous[i] as i16) / 2) as u8;
            }
        }
        FilterType::Paeth => {
            for i in (0..bpp) {
                current[i] += filter_paeth(0, previous[i], 0);
            }

            for i in (bpp..len) {
                current[i] += filter_paeth(current[i - bpp], previous[i], previous[i - bpp]);
            }
        }
    }
}

pub fn filter(method: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
    let len  = current.len();

    match method {
        FilterType::NoFilter => (),
        FilterType::Sub      => {
            for i in (bpp..len).rev() {
                current[i] = current[i] - current[i - bpp];
            }
        }
        FilterType::Up       => {
            for i in (0..len) {
                current[i] = current[i] - previous[i];
            }
        }
        FilterType::Avg  => {
            for i in (bpp..len) {
                current[i] = current[i] - ((current[i - bpp] as i16 + previous[i] as i16) / 2) as u8;
            }

            for i in (0..bpp) {
                current[i] = current[i] - previous[i] / 2;
            }
        }
        FilterType::Paeth    => {
            for i in (bpp..len) {
                current[i] = current[i] - filter_paeth(current[i - bpp], previous[i], previous[i - bpp]);
            }

            for i in (0..bpp) {
                current[i] = current[i] - filter_paeth(0, previous[i], 0);
            }
        }
    }
}
<commit_msg>Add some missing rev() calls<commit_after>use std::num::SignedInt;

#[derive(FromPrimitive, Debug)]
pub enum FilterType {
    NoFilter = 0,
    Sub = 1,
    Up = 2,
    Avg = 3,
    Paeth = 4
}

fn filter_paeth(a: u8, b: u8, c: u8) -> u8 {
    let ia = a as i16;
    let ib = b as i16;
    let ic = c as i16;

    let p = ia + ib - ic;

    let pa = (p - ia).abs();
    let pb = (p - ib).abs();
    let pc = (p - ic).abs();

    if pa <= pb && pa <= pc {
        a
    } else if pb <= pc {
        b
    } else {
        c
    }
}

pub fn unfilter(filter: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
    let len = current.len();

    match filter {
        FilterType::NoFilter => (),
        FilterType::Sub => {
            for i in (bpp..len) {
                current[i] += current[i - bpp];
            }
        }
        FilterType::Up => {
            for i in (0..len) {
                current[i] += previous[i];
            }
        }
        FilterType::Avg => {
            for i in (0..bpp) {
                current[i] += previous[i] / 2;
            }

            for i in (bpp..len) {
                current[i] += ((current[i - bpp] as i16 + previous[i] as i16) / 2) as u8;
            }
        }
        FilterType::Paeth => {
            for i in (0..bpp) {
                current[i] += filter_paeth(0, previous[i], 0);
            }

            for i in (bpp..len) {
                current[i] += filter_paeth(current[i - bpp], previous[i], previous[i - bpp]);
            }
        }
    }
}

pub fn filter(method: FilterType, bpp: usize, previous: &[u8], current: &mut [u8]) {
    let len  = current.len();

    match method {
        FilterType::NoFilter => (),
        FilterType::Sub      => {
            for i in (bpp..len).rev() {
                current[i] = current[i] - current[i - bpp];
            }
        }
        FilterType::Up       => {
            for i in (0..len) {
                current[i] = current[i] - previous[i];
            }
        }
        FilterType::Avg  => {
            for i in (bpp..len).rev() {
                current[i] = current[i] - ((current[i - bpp] as i16 + previous[i] as i16) / 2) as u8;
            }

            for i in (0..bpp) {
                current[i] = current[i] - previous[i] / 2;
            }
        }
        FilterType::Paeth    => {
            for i in (bpp..len).rev() {
                current[i] = current[i] - filter_paeth(current[i - bpp], previous[i], previous[i - bpp]);
            }

            for i in (0..bpp) {
                current[i] = current[i] - filter_paeth(0, previous[i], 0);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! The SpaceAPI server struct.

use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::time::Duration;

use r2d2;
use r2d2_redis::RedisConnectionManager;
use iron::Iron;
use router::Router;
use redis::{IntoConnectionInfo, ConnectionInfo};

use serde_json::Value;
use serde_json::map::Map;

mod handlers;

use crate::api;

use crate::sensors;
use crate::modifiers;
use crate::errors::SpaceapiServerError;
use crate::types::RedisPool;

pub struct SpaceapiServerBuilder {
    status: api::Status,
    redis_connection_info: Result<ConnectionInfo, SpaceapiServerError>,
    sensor_specs: Vec<sensors::SensorSpec>,
    status_modifiers: Vec<Box<modifiers::StatusModifier>>,
}

impl SpaceapiServerBuilder {

    pub fn new(mut status: api::Status) -> SpaceapiServerBuilder {
        // Instantiate versions object
        let mut versions = Map::new();
        versions.insert("spaceapi-rs".into(), api::get_version().into());
        versions.insert("spaceapi-server-rs".into(), crate::get_version().into());

        // Add to extensions
        status.extensions.insert("versions".into(), Value::Object(versions));

        SpaceapiServerBuilder {
            status,
            redis_connection_info: Err("redis_connection_info missing".into()),
            sensor_specs: vec![],
            status_modifiers: vec![],
        }
    }

    pub fn redis_connection_info<R: IntoConnectionInfo>(mut self, redis_connection_info: R) -> Self {
        self.redis_connection_info = redis_connection_info.into_connection_info().map_err(|e| e.into());
        self
    }

    pub fn add_status_modifier<M: modifiers::StatusModifier + 'static>(mut self, modifier: M) -> Self {
        self.status_modifiers.push(Box::new(modifier));
        self
    }

    /// Add a new sensor.
    ///
    /// The first argument is a ``api::SensorTemplate`` instance containing all static data.
    /// The second argument specifies how to get the actual sensor value from Redis.
    pub fn add_sensor<T: api::SensorTemplate + 'static>(mut self, template: T, data_key: String) -> Self {
        self.sensor_specs.push(
            sensors::SensorSpec { template: Box::new(template), data_key }
        );
        self
    }

    pub fn build(self) -> Result<SpaceapiServer, SpaceapiServerError> {
        // Log some useful debug information
        debug!("Redis connection info: {:?}", &self.redis_connection_info);

        // Create redis pool
        let redis_config = r2d2::Config::builder()
            // Provide up to 6 connections in connection pool
            .pool_size(6)
            // At least 1 connection must be active
            .min_idle(Some(2))
            // Initialize connection pool lazily. This allows the SpaceAPI
            // server to work even without a database connection.
            .initialization_fail_fast(false)
            // Try to get a connection for max 1 second
            .connection_timeout(Duration::from_secs(1))
            // Don't log errors directly.
            // They can get quite verbose, and we're already catching and
            // logging the corresponding results anyways.
            .error_handler(Box::new(r2d2::NopErrorHandler))
            .build();
        let redis_manager = RedisConnectionManager::new(self.redis_connection_info?)?;
        let pool = r2d2::Pool::new(redis_config, redis_manager)?;

        Ok(SpaceapiServer {
            status: self.status,
            redis_pool: pool,
            sensor_specs: Arc::new(self.sensor_specs),
            status_modifiers: self.status_modifiers,
        })
    }
}

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a redis connection info object.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    status: api::Status,
    redis_pool: RedisPool,
    sensor_specs: sensors::SafeSensorSpecs,
    status_modifiers: Vec<Box<modifiers::StatusModifier>>,
}

impl SpaceapiServer {

    /// Create and return a Router instance.
    fn route(self) -> Router {
        let mut router = Router::new();

        router.get("/", handlers::ReadHandler::new(
            self.status.clone(), self.redis_pool.clone(),
            self.sensor_specs.clone(), self.status_modifiers), "root");

        router.put("/sensors/:sensor/", handlers::UpdateHandler::new(
            self.redis_pool.clone(), self.sensor_specs.clone()), "sensors");

        router
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// The call returns an `HttpResult<Listening>` object, see
    /// http://ironframework.io/doc/hyper/server/struct.Listening.html
    /// for more information.
    pub fn serve<S: ToSocketAddrs>(self, socket_addr: S) -> crate::HttpResult<crate::Listening> {
        // Launch server process
        let router = self.route();
        println!("Starting HTTP server on:");
        for a in socket_addr.to_socket_addrs()? {
            println!("\thttp://{}", a);
        }
        Iron::new(router).http(socket_addr)
    }
}
<commit_msg>Add tentative implementation of init with pool<commit_after>//! The SpaceAPI server struct.

use std::net::ToSocketAddrs;
use std::sync::Arc;
use std::time::Duration;

use r2d2;
use r2d2_redis::RedisConnectionManager;
use iron::Iron;
use router::Router;
use redis::{IntoConnectionInfo, ConnectionInfo};

use serde_json::Value;
use serde_json::map::Map;

mod handlers;

use crate::api;

use crate::sensors;
use crate::modifiers;
use crate::errors::SpaceapiServerError;
use crate::types::RedisPool;

pub struct SpaceapiServerBuilder {
    status: api::Status,
    redis_connection_info: Result<ConnectionInfo, SpaceapiServerError>,
    pool: Option<r2d2::Pool<r2d2_redis::RedisConnectionManager>>,
    sensor_specs: Vec<sensors::SensorSpec>,
    status_modifiers: Vec<Box<modifiers::StatusModifier>>,
}

impl SpaceapiServerBuilder {

    pub fn new(mut status: api::Status) -> SpaceapiServerBuilder {
        // Instantiate versions object
        let mut versions = Map::new();
        versions.insert("spaceapi-rs".into(), api::get_version().into());
        versions.insert("spaceapi-server-rs".into(), crate::get_version().into());

        // Add to extensions
        status.extensions.insert("versions".into(), Value::Object(versions));

        SpaceapiServerBuilder {
            status,
            redis_connection_info: Err("redis_connection_info missing".into()),
            pool: None,
            sensor_specs: vec![],
            status_modifiers: vec![],
        }
    }

    pub fn redis_connection_info<R: IntoConnectionInfo>(mut self, redis_connection_info: R) -> Self {
        self.redis_connection_info = redis_connection_info.into_connection_info().map_err(|e| e.into());
        self
    }

    pub fn redis_pool(mut self, redis_pool: r2d2::Pool<r2d2_redis::RedisConnectionManager>) -> Self {
        self.pool = Ok(redis_pool);
        self
    }

    pub fn add_status_modifier<M: modifiers::StatusModifier + 'static>(mut self, modifier: M) -> Self {
        self.status_modifiers.push(Box::new(modifier));
        self
    }

    /// Add a new sensor.
    ///
    /// The first argument is a ``api::SensorTemplate`` instance containing all static data.
    /// The second argument specifies how to get the actual sensor value from Redis.
    pub fn add_sensor<T: api::SensorTemplate + 'static>(mut self, template: T, data_key: String) -> Self {
        self.sensor_specs.push(
            sensors::SensorSpec { template: Box::new(template), data_key }
        );
        self
    }

    pub fn build(self) -> Result<SpaceapiServer, SpaceapiServerError> {
        // Log some useful debug information
        debug!("Redis connection info: {:?}", &self.redis_connection_info);

        let pool = match self.pool {
            Some(p) => p,
            None => {
                // Create redis pool
                let redis_config = r2d2::Config::builder()
                    // Provide up to 6 connections in connection pool
                    .pool_size(6)
                    // At least 1 connection must be active
                    .min_idle(Some(2))
                    // Initialize connection pool lazily. This allows the SpaceAPI
                    // server to work even without a database connection.
                    .initialization_fail_fast(false)
                    // Try to get a connection for max 1 second
                    .connection_timeout(Duration::from_secs(1))
                    // Don't log errors directly.
                    // They can get quite verbose, and we're already catching and
                    // logging the corresponding results anyways.
                    .error_handler(Box::new(r2d2::NopErrorHandler))
                    .build();
                let redis_manager = RedisConnectionManager::new(self.redis_connection_info?)?;
                r2d2::Pool::new(redis_config, redis_manager)?
            },
        };

        Ok(SpaceapiServer {
            status: self.status,
            redis_pool: pool,
            sensor_specs: Arc::new(self.sensor_specs),
            status_modifiers: self.status_modifiers,
        })
    }
}

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a redis connection info object.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    status: api::Status,
    redis_pool: RedisPool,
    sensor_specs: sensors::SafeSensorSpecs,
    status_modifiers: Vec<Box<modifiers::StatusModifier>>,
}

impl SpaceapiServer {

    /// Create and return a Router instance.
    fn route(self) -> Router {
        let mut router = Router::new();

        router.get("/", handlers::ReadHandler::new(
            self.status.clone(), self.redis_pool.clone(),
            self.sensor_specs.clone(), self.status_modifiers), "root");

        router.put("/sensors/:sensor/", handlers::UpdateHandler::new(
            self.redis_pool.clone(), self.sensor_specs.clone()), "sensors");

        router
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// The call returns an `HttpResult<Listening>` object, see
    /// http://ironframework.io/doc/hyper/server/struct.Listening.html
    /// for more information.
    pub fn serve<S: ToSocketAddrs>(self, socket_addr: S) -> crate::HttpResult<crate::Listening> {
        // Launch server process
        let router = self.route();
        println!("Starting HTTP server on:");
        for a in socket_addr.to_socket_addrs()? {
            println!("\thttp://{}", a);
        }
        Iron::new(router).http(socket_addr)
    }
}
<|endoftext|>


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

//! The `server` module contains things needed to build an SMTP server,
//! but useless for an SMTP client.

extern crate libc;

use super::common::stream::{InputStream, OutputStream};
use std::io::net::tcp::{TcpListener, TcpAcceptor, TcpStream};
use std::io::net::ip::{SocketAddr, IpAddr, Port};
use std::io::{Acceptor, Listener, IoResult};
use std::sync::Arc;
use std::collections::DList;
use std::thread::Thread;
use std::borrow::ToOwned;

/// Core SMTP commands
pub mod commands;

extern {
    pub fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int;
}

fn rust_gethostname() -> Result<String, ()> {
    let len = 255;
    let mut buf = Vec::<u8>::with_capacity(len);

    let ptr = buf.as_mut_slice().as_mut_ptr();

    let err = unsafe {
        gethostname(ptr as *mut libc::c_char, len as libc::size_t)
    } as isize;

    match err {
        0 => {
            let mut real_len = len;
            let mut i = 0;
            loop {
                if i >= len {
                    break;
                }
                let byte = unsafe { *(((ptr as u64) + (i as u64)) as *const u8) };
                if byte == 0 {
                    real_len = i;
                    break;
                }

                i += 1;
            }
            unsafe { buf.set_len(real_len) }
            Ok(String::from_utf8_lossy(buf.as_slice()).into_owned())
        },
        _ => {
            Err(())
        }
    }
}

/// Gives access to the next middleware for a command.
pub struct NextMiddleware<CT, ST> {
    callback: MiddlewareFn<CT, ST>,
    next: Box<Option<NextMiddleware<CT, ST>>>
}

impl<CT, ST> Clone for NextMiddleware<CT, ST> {
    fn clone(&self) -> NextMiddleware<CT, ST> {
        NextMiddleware {
            callback: self.callback,
            next: Box::new(*self.next.clone())
        }
    }
}

impl<CT, ST> NextMiddleware<CT, ST> {
    /// Call a command middleware.
    pub fn call(&self, container: &mut CT, i: &mut InputStream<ST>, o: &mut OutputStream<ST>, l: &str) {
        let opt = match *self.next {
            Some(ref next) => Some(next.clone()),
            None => None
        };
        (self.callback)(container, i, o, l, opt);
    }
}

/// A command middleware callback.
pub type MiddlewareFn<CT, ST> = fn(
    &mut CT,
    &mut InputStream<ST>,
    &mut OutputStream<ST>,
    &str,
    Option<NextMiddleware<CT, ST>>
) -> ();

/// An email server command.
///
/// It is defined by the string you find at the start of the command, for
/// example "MAIL FROM:" or "EHLO ", as well as a bunch of middleware parts
/// that are executed sequentially until one says to stop.
#[derive(Clone)]
pub struct Command<CT, ST> {
    start: Option<String>,
    front_middleware: Option<NextMiddleware<CT, ST>>,
}

impl<CT, ST> Command<CT, ST> {
    /// Creates a new command
    pub fn new() -> Command<CT, ST> {
        Command {
            start: None,
            front_middleware: None
        }
    }

    /// Describes the start of the command line for this command.
    pub fn starts_with(&mut self, start: &str) {
        self.start = Some(start.to_owned());
    }

    fn last_middleware<'a>(prev: &'a mut NextMiddleware<CT, ST>) -> &'a mut NextMiddleware<CT, ST> {
        match *prev.next {
            None => prev,
            Some(ref mut next) => Command::last_middleware(next)
        }
    }

    /// Add a middleware to call for this command.
    pub fn middleware(&mut self, callback: MiddlewareFn<CT, ST>) {
        // The upcoming item in the middleware chain.
        let next = Some(NextMiddleware {
            callback: callback,
            next: Box::new(None)
        });

        // Get the current last item, so we can append the new item.
        match self.front_middleware {
            None => {
                self.front_middleware = next;
            },
            Some(_) => {
                Command::last_middleware(self.front_middleware.as_mut().unwrap()).next = Box::new(next);
            }
        }
    }

    fn ready(&self) -> bool {
        // TODO: complete this
        true
    }
}

/// An SMTP server, with no commands by default.
pub struct Server<CT> {
    hostname: String,
    max_recipients: usize,
    max_message_size: usize,
    max_command_line_size: usize,
    max_text_line_size: usize,
    commands: Arc<Vec<Command<CT, TcpStream>>>,
    container: CT
}

/// An error that occures when a server starts up
pub enum ServerError {
    /// The hostname could not be retrieved from the system
    Hostname,
    /// Could not bind the socket
    Bind,
    /// Could not listen on the socket
    Listen
}

/// Tells whether an error occured during server setup.
pub type ServerResult<T> = Result<T, ServerError>;

// TODO: logging, via a Trait on the container?
// TODO: fatal error handling
// TODO: actual TCP listening and command handling

impl<CT: Send + Clone> Server<CT> {
    /// Creates a new SMTP server.
    ///
    /// The container can be of any type and can be used to get access to a
    /// bunch of things inside your commands, like database connections,
    /// a logger and more.
    pub fn new(container: CT) -> Server<CT> {
        Server {
            hostname: String::new(),
            max_recipients: 100,
            max_message_size: 65536,
            max_command_line_size: 512,
            max_text_line_size: 1000,
            commands: Arc::new(Vec::with_capacity(16)),
            container: container
        }
    }

    fn set_hostname(&mut self, hostname: &str) {
        self.hostname = hostname.to_owned();
    }

    fn set_max_recipients(&mut self, max: usize) {
        if max < 100 {
            panic!("Maximum number of recipients must be >= 100.");
        }
        self.max_recipients = max;
    }

    fn set_max_message_size(&mut self, max: usize) {
        if max < 65536 {
            panic!("Maximum message size must be >= 65536.");
        }
        self.max_message_size = max;
    }

    /// Adds a command to the server.
    pub fn add_command(&mut self, command: Command<CT, TcpStream>) {
        self.commands.make_unique().push(command);
    }

    // TODO: allow saying which extensions are supported by this server
    // for use in EHLO response.

    fn increase_max_command_line_size(&mut self, bytes: usize) {
        self.max_command_line_size += bytes;
    }

    fn increase_max_text_line_size(&mut self, bytes: usize) {
        self.max_text_line_size += bytes;
    }

    fn get_hostname_from_system(&mut self) -> ServerResult<String> {
        match rust_gethostname() {
            Ok(s) => {
                Ok(s)
            },
            Err(_) => {
                Err(ServerError::Hostname)
            }
        }
    }

    fn get_listener_for_address(&mut self, address: SocketAddr) -> ServerResult<TcpListener> {
        match TcpListener::bind(address) {
            Ok(listener) => Ok(listener),
            Err(_) => Err(ServerError::Bind)
        }
    }

    fn get_acceptor_for_listener(&mut self, listener: TcpListener) -> ServerResult<TcpAcceptor> {
        match listener.listen() {
            Ok(acceptor) => Ok(acceptor),
            Err(_) => Err(ServerError::Listen)
        }
    }

    fn handle_commands(input: &mut InputStream<TcpStream>, output: &mut OutputStream<TcpStream>, container: &mut CT, commands: &[Command<CT, TcpStream>]) {
        'main: loop {
            let line = match input.read_line() {
                Ok(buffer) => {
                    // The commands expect a regular human readable string.
                    // Also, we need to make this an owned string because
                    // the stream uses the same buffer for command lines and
                    // text lines.
                    //
                    // TODO: use a different buffer for text lines and command
                    // lines?
                    String::from_utf8_lossy(buffer).into_owned()
                },
                Err(err) => {
                    panic!("Could not read command: {}", err);
                }
            };

            // Find the right handler for this command line.
            for command in commands.iter() {
                // The right command starts with whatever we have set
                // when we created the command. We use unwrap here, but
                // the commands are checked before the server starts
                // so this is always OK.
                match command.start {
                    Some(ref start) => {
                        // TODO: make this case insensitive
                        if line.as_slice().starts_with(start.as_slice()) {
                            match command.front_middleware {
                                Some(ref next) => {
                                    next.call(container, input, output, line.as_slice().slice_from(start.len()));
                                },
                                None => {
                                    // TODO: improve error message
                                    panic!("Found a command with no middleware");
                                }
                            }
                            continue 'main;
                        }
                    },
                    None => {
                        // TODO: improve error message
                        panic!("Found a command with no start string");
                    }
                }
            }

            // If we get here, it means that no command matched.
            output.write_line("500 Command unrecognized").unwrap();
        }
    }

    fn handle_connection(&self, stream_res: IoResult<TcpStream>) {
        let mut container = self.container.clone();
        let commands = self.commands.clone();
        Thread::spawn(move || {
            match stream_res {
                Ok(stream) => {
                    let mut input = InputStream::new(stream.clone(), 1000, false);
                    let mut output = OutputStream::new(stream.clone(), false);

                    Server::<CT>::handle_commands(
                        &mut input,
                        &mut output,
                        &mut container,
                        commands.as_slice()
                    );
                },
                Err(err) => {
                    panic!("Could not accept client: {}", err);
                }
            }
        });
    }

    /// Start the SMTP server on the given address and port.
    pub fn listen(&mut self, ip: IpAddr, port: Port) -> ServerResult<()> {
        // TODO: check that commands all are valid

        if self.hostname.len() == 0 {
            self.hostname = try!(self.get_hostname_from_system());
        }

        let address = SocketAddr {
            ip: ip,
            port: port
        };

        let listener = try!(self.get_listener_for_address(address));

        let mut acceptor = try!(self.get_acceptor_for_listener(listener));

        println!("Server '{}' listening on {}...", self.hostname, address);

        for conn in acceptor.incoming() {
            self.handle_connection(conn);
        }

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

//! The `server` module contains things needed to build an SMTP server,
//! but useless for an SMTP client.

extern crate libc;

use super::common::stream::{InputStream, OutputStream};
use std::io::net::tcp::{TcpListener, TcpAcceptor, TcpStream};
use std::io::net::ip::{SocketAddr, IpAddr, Port};
use std::io::{Acceptor, Listener, IoResult};
use std::sync::Arc;
use std::collections::DList;
use std::thread::Thread;
use std::borrow::ToOwned;

/// Core SMTP commands
pub mod commands;

extern {
    pub fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int;
}

fn rust_gethostname() -> Result<String, ()> {
    let len = 255;
    let mut buf = Vec::<u8>::with_capacity(len);

    let ptr = buf.as_mut_slice().as_mut_ptr();

    let err = unsafe {
        gethostname(ptr as *mut libc::c_char, len as libc::size_t)
    } as isize;

    match err {
        0 => {
            let mut real_len = len;
            let mut i = 0;
            loop {
                if i >= len {
                    break;
                }
                let byte = unsafe { *(((ptr as u64) + (i as u64)) as *const u8) };
                if byte == 0 {
                    real_len = i;
                    break;
                }

                i += 1;
            }
            unsafe { buf.set_len(real_len) }
            Ok(String::from_utf8_lossy(buf.as_slice()).into_owned())
        },
        _ => {
            Err(())
        }
    }
}

/// Gives access to the next middleware for a command.
pub struct NextMiddleware<CT, ST> {
    callback: MiddlewareFn<CT, ST>,
    next: Box<Option<NextMiddleware<CT, ST>>>
}

impl<CT, ST> Clone for NextMiddleware<CT, ST> {
    fn clone(&self) -> NextMiddleware<CT, ST> {
        NextMiddleware {
            callback: self.callback,
            next: Box::new(*self.next.clone())
        }
    }
}

impl<CT, ST> NextMiddleware<CT, ST> {
    /// Call a command middleware.
    pub fn call(&self, container: &mut CT, i: &mut InputStream<ST>, o: &mut OutputStream<ST>, l: &str) {
        let opt = match *self.next {
            Some(ref next) => Some(next.clone()),
            None => None
        };
        (self.callback)(container, i, o, l, opt);
    }
}

/// A command middleware callback.
pub type MiddlewareFn<CT, ST> = fn(
    &mut CT,
    &mut InputStream<ST>,
    &mut OutputStream<ST>,
    &str,
    Option<NextMiddleware<CT, ST>>
) -> ();

/// An email server command.
///
/// It is defined by the string you find at the start of the command, for
/// example "MAIL FROM:" or "EHLO ", as well as a bunch of middleware parts
/// that are executed sequentially until one says to stop.
#[derive(Clone)]
pub struct Command<CT, ST> {
    start: Option<String>,
    front_middleware: Option<NextMiddleware<CT, ST>>,
}

impl<CT, ST> Command<CT, ST> {
    /// Creates a new command
    pub fn new() -> Command<CT, ST> {
        Command {
            start: None,
            front_middleware: None
        }
    }

    /// Describes the start of the command line for this command.
    pub fn starts_with(&mut self, start: &str) {
        self.start = Some(start.to_owned());
    }

    fn last_middleware<'a>(prev: &'a mut NextMiddleware<CT, ST>) -> &'a mut NextMiddleware<CT, ST> {
        match *prev.next {
            None => prev,
            Some(ref mut next) => Command::last_middleware(next)
        }
    }

    /// Add a middleware to call for this command.
    pub fn middleware(&mut self, callback: MiddlewareFn<CT, ST>) {
        // The upcoming item in the middleware chain.
        let next = Some(NextMiddleware {
            callback: callback,
            next: Box::new(None)
        });

        // Get the current last item, so we can append the new item.
        match self.front_middleware {
            None => {
                self.front_middleware = next;
            },
            Some(_) => {
                Command::last_middleware(self.front_middleware.as_mut().unwrap()).next = Box::new(next);
            }
        }
    }

    fn ready(&self) -> bool {
        // TODO: complete this
        true
    }
}

/// An SMTP server, with no commands by default.
pub struct Server<CT> {
    hostname: String,
    max_recipients: usize,
    max_message_size: usize,
    max_command_line_size: usize,
    max_text_line_size: usize,
    commands: Arc<Vec<Command<CT, TcpStream>>>,
    container: CT
}

/// An error that occures when a server starts up
pub enum ServerError {
    /// The hostname could not be retrieved from the system
    Hostname,
    /// Could not bind the socket
    Bind,
    /// Could not listen on the socket
    Listen
}

/// Tells whether an error occured during server setup.
pub type ServerResult<T> = Result<T, ServerError>;

// TODO: logging, via a Trait on the container?
// TODO: fatal error handling
// TODO: actual TCP listening and command handling

impl<CT: Send + Clone> Server<CT> {
    /// Creates a new SMTP server.
    ///
    /// The container can be of any type and can be used to get access to a
    /// bunch of things inside your commands, like database connections,
    /// a logger and more.
    pub fn new(container: CT) -> Server<CT> {
        Server {
            hostname: String::new(),
            max_recipients: 100,
            max_message_size: 65536,
            max_command_line_size: 512,
            max_text_line_size: 1000,
            commands: Arc::new(Vec::with_capacity(16)),
            container: container
        }
    }

    fn set_hostname(&mut self, hostname: &str) {
        self.hostname = hostname.to_owned();
    }

    fn set_max_recipients(&mut self, max: usize) {
        if max < 100 {
            panic!("Maximum number of recipients must be >= 100.");
        }
        self.max_recipients = max;
    }

    fn set_max_message_size(&mut self, max: usize) {
        if max < 65536 {
            panic!("Maximum message size must be >= 65536.");
        }
        self.max_message_size = max;
    }

    /// Adds a command to the server.
    pub fn add_command(&mut self, command: Command<CT, TcpStream>) {
        self.commands.make_unique().push(command);
    }

    // TODO: allow saying which extensions are supported by this server
    // for use in EHLO response.

    fn increase_max_command_line_size(&mut self, bytes: usize) {
        self.max_command_line_size += bytes;
    }

    fn increase_max_text_line_size(&mut self, bytes: usize) {
        self.max_text_line_size += bytes;
    }

    fn get_hostname_from_system(&mut self) -> ServerResult<String> {
        match rust_gethostname() {
            Ok(s) => {
                Ok(s)
            },
            Err(_) => {
                Err(ServerError::Hostname)
            }
        }
    }

    fn get_listener_for_address(&mut self, address: SocketAddr) -> ServerResult<TcpListener> {
        match TcpListener::bind(address) {
            Ok(listener) => Ok(listener),
            Err(_) => Err(ServerError::Bind)
        }
    }

    fn get_acceptor_for_listener(&mut self, listener: TcpListener) -> ServerResult<TcpAcceptor> {
        match listener.listen() {
            Ok(acceptor) => Ok(acceptor),
            Err(_) => Err(ServerError::Listen)
        }
    }

    fn handle_commands(input: &mut InputStream<TcpStream>, output: &mut OutputStream<TcpStream>, container: &mut CT, commands: &[Command<CT, TcpStream>]) {
        'main: loop {
            let line = match input.read_line() {
                Ok(buffer) => {
                    // The commands expect a regular human readable string.
                    // Also, we need to make this an owned string because
                    // the stream uses the same buffer for command lines and
                    // text lines.
                    //
                    // TODO: use a different buffer for text lines and command
                    // lines?
                    String::from_utf8_lossy(buffer).into_owned()
                },
                Err(err) => {
                    panic!("Could not read command: {}", err);
                }
            };

            // Find the right handler for this command line.
            for command in commands.iter() {
                // The right command starts with whatever we have set
                // when we created the command. We use unwrap here, but
                // the commands are checked before the server starts
                // so this is always OK.
                match command.start {
                    Some(ref start) => {
                        // TODO: make this case insensitive
                        if line.as_slice().starts_with(start.as_slice()) {
                            match command.front_middleware {
                                Some(ref next) => {
                                    next.call(container, input, output, line.as_slice().slice_from(start.len()));
                                },
                                None => {
                                    // TODO: improve error message
                                    panic!("Found a command with no middleware");
                                }
                            }
                            continue 'main;
                        }
                    },
                    None => {
                        // TODO: improve error message
                        panic!("Found a command with no start string");
                    }
                }
            }

            // If we get here, it means that no command matched.
            output.write_line("500 Command unrecognized").unwrap();
        }
    }

    fn handle_connection(&self, stream_res: IoResult<TcpStream>) {
        let mut container = self.container.clone();
        let commands = self.commands.clone();
        let thread = Thread::spawn(move || {
            match stream_res {
                Ok(stream) => {
                    let mut input = InputStream::new(stream.clone(), 1000, false);
                    let mut output = OutputStream::new(stream.clone(), false);

                    Server::<CT>::handle_commands(
                        &mut input,
                        &mut output,
                        &mut container,
                        commands.as_slice()
                    );
                },
                Err(err) => {
                    panic!("Could not accept client: {}", err);
                }
            }
        });
        println!("Connection being handled in thread: {:?}", thread.name());
    }

    /// Start the SMTP server on the given address and port.
    pub fn listen(&mut self, ip: IpAddr, port: Port) -> ServerResult<()> {
        // TODO: check that commands all are valid

        if self.hostname.len() == 0 {
            self.hostname = try!(self.get_hostname_from_system());
        }

        let address = SocketAddr {
            ip: ip,
            port: port
        };

        let listener = try!(self.get_listener_for_address(address));

        let mut acceptor = try!(self.get_acceptor_for_listener(listener));

        println!("Server '{}' listening on {}...", self.hostname, address);

        for conn in acceptor.incoming() {
            self.handle_connection(conn);
        }

        Ok(())
    }
}
<|endoftext|>