<commit_before>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            if tokens.has_next() {
                                resolve(sub_document, tokens.next().unwrap().deref())
                            } else {
                                Ok(sub_document)
                            }
                        },
                    }
                },

                &Token::Index { idx: i, .. } => {
                    let kind = ErrorKind::NoIndexInTable(i);
                    Err(Error::from(kind))
                },
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => {
                    if tokens.has_next() {
                        resolve(ary.get_mut(i).unwrap(), tokens.next().unwrap().deref())
                    } else {
                        Ok(ary.index_mut(i))
                    }
                },
                &Token::Identifier { .. } => unimplemented!(),
            }
        },

        _ => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    Err(Error::from(ErrorKind::IdentifierNotFoundInDocument(ident.clone())))
                },

                &Token::Index { .. } => unimplemented!(),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_member_of_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_query_index_in_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIndexInTable { .. }));
    }

    #[test]
    fn test_resolve_query_identifier_in_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = [ 1, 2, 3 ]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIdentifierInArray { .. }));
    }

}
<commit_msg>Impl resolve to fix test_resolve_query_identifier_in_array<commit_after>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            if tokens.has_next() {
                                resolve(sub_document, tokens.next().unwrap().deref())
                            } else {
                                Ok(sub_document)
                            }
                        },
                    }
                },

                &Token::Index { idx: i, .. } => {
                    let kind = ErrorKind::NoIndexInTable(i);
                    Err(Error::from(kind))
                },
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => {
                    if tokens.has_next() {
                        resolve(ary.get_mut(i).unwrap(), tokens.next().unwrap().deref())
                    } else {
                        Ok(ary.index_mut(i))
                    }
                },
                &Token::Identifier { ident: ref ident, .. } => {
                    let kind = ErrorKind::NoIdentifierInArray(ident.clone());
                    Err(Error::from(kind))
                },
            }
        },

        _ => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    Err(Error::from(ErrorKind::IdentifierNotFoundInDocument(ident.clone())))
                },

                &Token::Index { .. } => unimplemented!(),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_member_of_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_query_index_in_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIndexInTable { .. }));
    }

    #[test]
    fn test_resolve_query_identifier_in_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = [ 1, 2, 3 ]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIdentifierInArray { .. }));
    }

}
<|endoftext|>


<commit_before>use std::sync::RwLock;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::old_path::BytesContainer;
use std::path::Path;
use std::fmt::Debug;
use serialize::Encodable;
use hyper::status::StatusCode;
use hyper::server::Response as HyperResponse;
use hyper::header::{Date, Server, ContentType, ContentLength, Header, HeaderFormat};
use hyper::net::{Fresh, Streaming};
use time;
use mimes::{get_media_type, MediaType};
use mustache;
use mustache::Template;
use std::io;
use std::io::{Read, Write, ErrorKind, copy};
use std::fs::File;

pub type TemplateCache = RwLock<HashMap<&'static str, Template>>;

///A container for the response
pub struct Response<'a, T=Fresh> {
    ///the original `hyper::server::Response`
    pub origin: HyperResponse<'a, T>,
    templates: &'a TemplateCache
}

impl<'a> Response<'a, Fresh> {
    pub fn from_internal<'c, 'd>(response: HyperResponse<'c, Fresh>,
                                 templates: &'c TemplateCache)
                                -> Response<'c, Fresh> {
        Response {
            origin: response,
            templates: templates
        }
    }

    /// Sets the content type by it's short form.
    /// Returns the response for chaining.
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Continue};
    /// use nickel::mimes::MediaType;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     res.content_type(MediaType::Html);
    ///     Ok(Continue(res))
    /// }
    /// ```
    pub fn content_type(&mut self, mt: MediaType) -> &mut Response<'a> {
        self.origin.headers_mut().set(ContentType(get_media_type(mt)));
        self
    }

    /// Sets the status code and returns the response for chaining
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Continue};
    /// use nickel::status::StatusCode;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     res.status_code(StatusCode::NotFound);
    ///     Ok(Continue(res))
    /// }
    /// ```
    pub fn status_code(&mut self, status: StatusCode) -> &mut Response<'a> {
        *self.origin.status_mut() = status;
        self
    }

    /// Writes a response
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Halt};
    ///
    /// fn handler<'a>(_: &mut Request, res: Response<'a>) -> MiddlewareResult<'a> {
    ///     Ok(Halt(try!(res.send("hello world"))))
    /// }
    /// ```
    pub fn send<T: BytesContainer> (self, text: T) -> io::Result<Response<'a, Streaming>> {
        let mut stream = try!(self.start());
        try!(stream.write_all(text.container_as_bytes()));
        Ok(stream)
    }

    /// Writes a file to the output.
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Halt};
    /// use nickel::status::StatusCode;
    /// use std::path::Path;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     let favicon = Path::new("/assets/favicon.ico");
    ///     Ok(Halt(try!(res.send_file(favicon))))
    /// }
    /// ```
    pub fn send_file(mut self, path: &Path) -> io::Result<Response<'a, Streaming>> {
        // Chunk the response
        self.origin.headers_mut().remove::<ContentLength>();
        // Determine content type by file extension or default to binary
        self.content_type(path.extension()
                              .and_then(|os| os.to_str())
                              .and_then(|s| s.parse().ok())
                              .unwrap_or(MediaType::Bin));

        let mut stream = try!(self.start());
        let mut file = try!(File::open(path));
        try!(copy(&mut file, &mut stream));
        Ok(stream)
    }

    // TODO: This needs to be more sophisticated to return the correct headers
    // not just "some headers" :)
    //
    // Also, it should only set them if not already set.
    fn set_fallback_headers(&mut self) {
        self.set_header_fallback(|| Date(time::now_utc()));
        self.set_header_fallback(|| Server("Nickel".to_string()));
        self.set_header_fallback(|| ContentType(get_media_type(MediaType::Html)));
    }

    /// Sets the header if not already set.
    ///
    /// If the header is not set then `f` will be called.
    /// Renders the given template bound with the given data.
    ///
    /// # Examples
    /// ```{rust}
    /// # extern crate nickel;
    /// # extern crate hyper;
    ///
    /// # fn main() {
    /// use nickel::{Request, Response, MiddlewareResult, Halt, MediaType, get_media_type};
    /// use hyper::header::ContentType;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     res.content_type(MediaType::Html);
    ///     res.set_header_fallback(|| {
    ///         panic!("Should not get called");
    ///         ContentType(get_media_type(MediaType::Txt))
    ///     });
    ///     let stream = try!(res.send("<h1>Hello World</h1>"));
    ///     Ok(Halt(stream))
    /// }
    /// # }
    /// ```
    pub fn set_header_fallback<F, H>(&mut self, f: F)
            where H: Header + HeaderFormat, F: FnOnce() -> H {
        let headers = self.origin.headers_mut();
        if !headers.has::<H>() { headers.set(f()) }
    }

    /// Renders the given template bound with the given data.
    ///
    /// # Examples
    /// ```{rust}
    /// use std::collections::HashMap;
    /// use nickel::{Request, Response, MiddlewareResult, Halt};
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     let mut data = HashMap::new();
    ///     data.insert("name", "user");
    ///     let stream = try!(res.render("examples/assets/template.tpl", &data));
    ///     Ok(Halt(stream))
    /// }
    /// ```
    pub fn render<T>(self, path: &'static str, data: &T)
            -> io::Result<Response<'a, Streaming>>
            where T: Encodable {
        fn to_ioerr<U: Debug>(r: Result<(), U>) -> io::Result<()> {
            r.map_err(|e| io::Error::new(ErrorKind::Other,
                                         "Problem rendering template",
                                         Some(format!("{:?}", e))))
        }

        // Fast path doesn't need writer lock
        if let Some(t) = self.templates.read().unwrap().get(&path) {
            let mut stream = try!(self.start());
            try!(to_ioerr(t.render(&mut stream, data)));
            return Ok(stream);
        }

        // We didn't find the template, get writers lock
        let mut templates = self.templates.write().unwrap();
        // Search again incase there was a race to compile the template
        let template = match templates.entry(path) {
            Vacant(entry) => {
                let mut file = File::open(&Path::new(path))
                                     .ok().expect(&*format!("Couldn't open the template file: {}", path));
                let mut raw_template = String::new();

                file.read_to_string(&mut raw_template)
                    .ok().expect(&*format!("Couldn't open the template file: {}", path));

                entry.insert(mustache::compile_str(&*raw_template))
            },
            Occupied(entry) => entry.into_mut()
        };

        let mut stream = try!(self.start());
        try!(to_ioerr(template.render(&mut stream, data)));
        Ok(stream)
    }

    pub fn start(mut self) -> io::Result<Response<'a, Streaming>> {
        self.set_fallback_headers();

        let Response { origin, templates } = self;
        let origin = try!(origin.start());

        Ok(Response { origin: origin, templates: templates })
    }
}

impl<'a, 'b> Write for Response<'a, Streaming> {
    #[inline(always)]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.origin.write(buf)
    }
    #[inline(always)]
    fn flush(&mut self) -> io::Result<()> {
        self.origin.flush()
    }
}

impl<'a, 'b> Response<'a, Streaming> {
    /// Flushes all writing of a response to the client.
    pub fn end(self) -> io::Result<()> {
        self.origin.end()
    }
}

#[test]
fn matches_content_type () {
    use hyper::mime::{Mime, TopLevel, SubLevel};
    let path = &Path::new("test.txt");
    let content_type = path.extension()
                           .and_then(|os| os.to_str())
                           .and_then(|s| s.parse().ok());

    assert_eq!(content_type, Some(MediaType::Txt));
    let content_type = content_type.map(get_media_type).unwrap();

    match content_type {
        Mime(TopLevel::Text, SubLevel::Plain, _) => {}, // OK
        wrong => panic!("Wrong mime: {}", wrong)
    }
}
<commit_msg>fix(send_file): dont deadlock when send_file has a bad path<commit_after>use std::sync::RwLock;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::old_path::BytesContainer;
use std::path::Path;
use std::fmt::Debug;
use serialize::Encodable;
use hyper::status::StatusCode;
use hyper::server::Response as HyperResponse;
use hyper::header::{Date, Server, ContentType, ContentLength, Header, HeaderFormat};
use hyper::net::{Fresh, Streaming};
use time;
use mimes::{get_media_type, MediaType};
use mustache;
use mustache::Template;
use std::io;
use std::io::{Read, Write, ErrorKind, copy};
use std::fs::File;

pub type TemplateCache = RwLock<HashMap<&'static str, Template>>;

///A container for the response
pub struct Response<'a, T=Fresh> {
    ///the original `hyper::server::Response`
    pub origin: HyperResponse<'a, T>,
    templates: &'a TemplateCache
}

impl<'a> Response<'a, Fresh> {
    pub fn from_internal<'c, 'd>(response: HyperResponse<'c, Fresh>,
                                 templates: &'c TemplateCache)
                                -> Response<'c, Fresh> {
        Response {
            origin: response,
            templates: templates
        }
    }

    /// Sets the content type by it's short form.
    /// Returns the response for chaining.
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Continue};
    /// use nickel::mimes::MediaType;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     res.content_type(MediaType::Html);
    ///     Ok(Continue(res))
    /// }
    /// ```
    pub fn content_type(&mut self, mt: MediaType) -> &mut Response<'a> {
        self.origin.headers_mut().set(ContentType(get_media_type(mt)));
        self
    }

    /// Sets the status code and returns the response for chaining
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Continue};
    /// use nickel::status::StatusCode;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     res.status_code(StatusCode::NotFound);
    ///     Ok(Continue(res))
    /// }
    /// ```
    pub fn status_code(&mut self, status: StatusCode) -> &mut Response<'a> {
        *self.origin.status_mut() = status;
        self
    }

    /// Writes a response
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Halt};
    ///
    /// fn handler<'a>(_: &mut Request, res: Response<'a>) -> MiddlewareResult<'a> {
    ///     Ok(Halt(try!(res.send("hello world"))))
    /// }
    /// ```
    pub fn send<T: BytesContainer> (self, text: T) -> io::Result<Response<'a, Streaming>> {
        let mut stream = try!(self.start());
        try!(stream.write_all(text.container_as_bytes()));
        Ok(stream)
    }

    /// Writes a file to the output.
    ///
    /// # Examples
    /// ```{rust}
    /// use nickel::{Request, Response, MiddlewareResult, Halt};
    /// use nickel::status::StatusCode;
    /// use std::path::Path;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     let favicon = Path::new("/assets/favicon.ico");
    ///     Ok(Halt(try!(res.send_file(favicon))))
    /// }
    /// ```
    pub fn send_file(mut self, path: &Path) -> io::Result<Response<'a, Streaming>> {
        // Chunk the response
        self.origin.headers_mut().remove::<ContentLength>();
        // Determine content type by file extension or default to binary
        self.content_type(path.extension()
                              .and_then(|os| os.to_str())
                              .and_then(|s| s.parse().ok())
                              .unwrap_or(MediaType::Bin));

        match File::open(path) {
            Ok(mut file) => {
                let mut stream = try!(self.start());
                try!(copy(&mut file, &mut stream));
                Ok(stream)
            }
            Err(e) => {
                warn!("Failed to send_file '{:?}': {}", path, e);
                self.status_code(StatusCode::InternalServerError);
                let _ = self.send("").map(|s| s.end());
                Err(e)
            }
        }
    }

    // TODO: This needs to be more sophisticated to return the correct headers
    // not just "some headers" :)
    //
    // Also, it should only set them if not already set.
    fn set_fallback_headers(&mut self) {
        self.set_header_fallback(|| Date(time::now_utc()));
        self.set_header_fallback(|| Server("Nickel".to_string()));
        self.set_header_fallback(|| ContentType(get_media_type(MediaType::Html)));
    }

    /// Sets the header if not already set.
    ///
    /// If the header is not set then `f` will be called.
    /// Renders the given template bound with the given data.
    ///
    /// # Examples
    /// ```{rust}
    /// # extern crate nickel;
    /// # extern crate hyper;
    ///
    /// # fn main() {
    /// use nickel::{Request, Response, MiddlewareResult, Halt, MediaType, get_media_type};
    /// use hyper::header::ContentType;
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     res.content_type(MediaType::Html);
    ///     res.set_header_fallback(|| {
    ///         panic!("Should not get called");
    ///         ContentType(get_media_type(MediaType::Txt))
    ///     });
    ///     let stream = try!(res.send("<h1>Hello World</h1>"));
    ///     Ok(Halt(stream))
    /// }
    /// # }
    /// ```
    pub fn set_header_fallback<F, H>(&mut self, f: F)
            where H: Header + HeaderFormat, F: FnOnce() -> H {
        let headers = self.origin.headers_mut();
        if !headers.has::<H>() { headers.set(f()) }
    }

    /// Renders the given template bound with the given data.
    ///
    /// # Examples
    /// ```{rust}
    /// use std::collections::HashMap;
    /// use nickel::{Request, Response, MiddlewareResult, Halt};
    ///
    /// fn handler<'a>(_: &mut Request, mut res: Response<'a>) -> MiddlewareResult<'a> {
    ///     let mut data = HashMap::new();
    ///     data.insert("name", "user");
    ///     let stream = try!(res.render("examples/assets/template.tpl", &data));
    ///     Ok(Halt(stream))
    /// }
    /// ```
    pub fn render<T>(self, path: &'static str, data: &T)
            -> io::Result<Response<'a, Streaming>>
            where T: Encodable {
        fn to_ioerr<U: Debug>(r: Result<(), U>) -> io::Result<()> {
            r.map_err(|e| io::Error::new(ErrorKind::Other,
                                         "Problem rendering template",
                                         Some(format!("{:?}", e))))
        }

        // Fast path doesn't need writer lock
        if let Some(t) = self.templates.read().unwrap().get(&path) {
            let mut stream = try!(self.start());
            try!(to_ioerr(t.render(&mut stream, data)));
            return Ok(stream);
        }

        // We didn't find the template, get writers lock
        let mut templates = self.templates.write().unwrap();
        // Search again incase there was a race to compile the template
        let template = match templates.entry(path) {
            Vacant(entry) => {
                let mut file = File::open(&Path::new(path))
                                     .ok().expect(&*format!("Couldn't open the template file: {}", path));
                let mut raw_template = String::new();

                file.read_to_string(&mut raw_template)
                    .ok().expect(&*format!("Couldn't open the template file: {}", path));

                entry.insert(mustache::compile_str(&*raw_template))
            },
            Occupied(entry) => entry.into_mut()
        };

        let mut stream = try!(self.start());
        try!(to_ioerr(template.render(&mut stream, data)));
        Ok(stream)
    }

    pub fn start(mut self) -> io::Result<Response<'a, Streaming>> {
        self.set_fallback_headers();

        let Response { origin, templates } = self;
        let origin = try!(origin.start());

        Ok(Response { origin: origin, templates: templates })
    }
}

impl<'a, 'b> Write for Response<'a, Streaming> {
    #[inline(always)]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.origin.write(buf)
    }
    #[inline(always)]
    fn flush(&mut self) -> io::Result<()> {
        self.origin.flush()
    }
}

impl<'a, 'b> Response<'a, Streaming> {
    /// Flushes all writing of a response to the client.
    pub fn end(self) -> io::Result<()> {
        self.origin.end()
    }
}

#[test]
fn matches_content_type () {
    use hyper::mime::{Mime, TopLevel, SubLevel};
    let path = &Path::new("test.txt");
    let content_type = path.extension()
                           .and_then(|os| os.to_str())
                           .and_then(|s| s.parse().ok());

    assert_eq!(content_type, Some(MediaType::Txt));
    let content_type = content_type.map(get_media_type).unwrap();

    match content_type {
        Mime(TopLevel::Text, SubLevel::Plain, _) => {}, // OK
        wrong => panic!("Wrong mime: {}", wrong)
    }
}
<|endoftext|>


<commit_before>use std::fs::OpenOptions;
use std::io::{Read, Write, BufReader, BufRead, Seek, SeekFrom};
use std::path::PathBuf;

use errors::DecodeError;
use crc32;
use constants::{NUL, CR, LF, ESCAPE, SPACE, DEFAULT_LINE_SIZE};

#[derive(Default,Debug)]
struct MetaData {
    name: Option<String>,
    line_length: Option<u16>,
    size: Option<usize>,
    crc32: Option<u32>,
    part: Option<u32>,
    begin: Option<usize>,
    end: Option<usize>,
}

/// Decodes the input file in a new output file.
///
/// If ok, returns the path of the decoded file.
///
/// # Example
/// ```rust,no_run
/// yenc::ydecode_file("test2.bin.yenc", "test2.bin");
/// ```
/// # Errors
/// - when the output file already exists
///
pub fn ydecode_file(input_filename: &str, output_path: &str) -> Result<String, DecodeError> {
    let mut input_file = OpenOptions::new().read(true).open(input_filename)?;
    ydecode_stream(&mut input_file, output_path)
}

/// Decodes the data from a stream in a new output file.
///
/// Writes the output to a file with the filename from the header line, and places it in the
/// output path. The path of the output file is returned.
pub fn ydecode_stream<R>(read_stream: &mut R, output_path: &str) -> Result<String, DecodeError>
    where R: Read
{
    let mut rdr = BufReader::new(read_stream);
    let mut output_pathbuf = PathBuf::new();
    output_pathbuf.push(output_path);

    let mut line_buf = Vec::<u8>::with_capacity(2 * DEFAULT_LINE_SIZE as usize);
    let mut checksum = crc32::Crc32::new();
    let mut yenc_block_found = false;

    while !yenc_block_found {
        line_buf.clear();
        let length = rdr.read_until(LF, &mut line_buf)?;
        if length == 0 {
            break;
        }
        if line_buf.starts_with(b"=ybegin ") {
            yenc_block_found = true;
        }
    }

    if yenc_block_found {
        // parse header line and determine output filename
        let mut metadata = parse_header_line(&line_buf, 8)?;
        output_pathbuf.push(metadata.name.unwrap().to_string().trim());
        let mut output_file =
            OpenOptions::new().create(true).write(true).open(output_pathbuf.as_path())?;

        let mut footer_found = false;
        while !footer_found {
            line_buf.clear();
            let length = rdr.read_until(LF, &mut line_buf)?;
            if length == 0 {
                break;
            }
            if line_buf.starts_with(b"=ypart ") {
                let part_metadata = parse_header_line(&line_buf, 7)?;
                metadata.part = part_metadata.part;
                metadata.begin = part_metadata.begin;
                metadata.end = part_metadata.end;
                if let Some(begin) = metadata.begin {
                    output_file.seek(SeekFrom::Start((begin - 1) as u64))?;
                }
            } else if line_buf.starts_with(b"=yend ") {
                footer_found = true;
            } else {
                let decoded = ydecode_buffer(&line_buf[0..length])?;
                checksum.update_with_slice(decoded.as_slice());
                output_file.write_all(decoded.as_slice())?;
            }
        }
        if footer_found {
            let metadata = parse_header_line(&line_buf, 6)?;
            if let Some(expected_size) = metadata.size {
                if expected_size != checksum.num_bytes {
                    return Err(DecodeError::IncompleteData {
                        expected_size: expected_size,
                        actual_size: checksum.num_bytes,
                    });
                }
            }
            if let Some(expected_crc) = metadata.crc32 {
                if expected_crc != checksum.crc {
                    return Err(DecodeError::InvalidChecksum);
                }
            }
        } else if let Some(expected_size) = metadata.size {
            if expected_size != checksum.num_bytes {
                return Err(DecodeError::IncompleteData {
                    expected_size: expected_size,
                    actual_size: checksum.num_bytes,
                });
            }
        }

    }
    Ok(output_pathbuf.to_str().unwrap().to_string())
}

/// Decode the yEncoded byte slice into a vector of bytes.
///
/// Carriage Return (CR) and Line Feed (LF) are ignored.
pub fn ydecode_buffer(input: &[u8]) -> Result<Vec<u8>, DecodeError> {
    // TODO remove heap allocation
    let mut output = Vec::<u8>::with_capacity((input.len() as f64 * 1.02) as usize);
    let mut iter = input.iter();
    while let Some(byte) = iter.next() {
        let mut byte = *byte;
        match byte {
            NUL | CR | LF => {
                // for now, just continue
                continue;
            }
            ESCAPE => {
                match iter.next() {
                    Some(b) => {
                        byte = b.overflowing_sub(64).0;
                    }
                    None => {
                        // for now, just continue, the only
                        continue;
                    }
                }
            }
            _ => {}
        }
        output.push(byte.overflowing_sub(42).0);
    }
    Ok(output)
}

fn parse_header_line(line_buf: &[u8], offset: usize) -> Result<MetaData, DecodeError> {
    #[derive(Debug)]
    enum State {
        Keyword,
        Value,
        End,
    };

    let mut metadata: MetaData = Default::default();
    let mut state = State::Keyword;
    let mut keyword = Vec::<u8>::with_capacity(6);
    let mut value = Vec::<u8>::with_capacity(96);

    let header_line = String::from_utf8_lossy(line_buf).to_string();
    for (i, &c) in line_buf[offset..].iter().enumerate() {
        let position = i + offset;
        match state {
            State::End => unreachable!(),
            State::Keyword => {
                match c {
                    b'a'...b'z' | b'0'...b'9' => keyword.push(c),
                    b'=' => {
                        if keyword.is_empty() || !is_known_keyword(&keyword) {
                            return Err(DecodeError::InvalidHeader {
                                line: header_line,
                                position: position,
                            });
                        } else {
                            state = State::Value;
                        }
                    }
                    CR | LF => {}
                    _ => {
                        return Err(DecodeError::InvalidHeader {
                            line: header_line,
                            position: position,
                        });
                    }
                }
            }
            State::Value => {
                match keyword.as_slice() {
                    b"name" => {
                        match c {
                            CR => {}
                            LF => {
                                state = State::End;
                                metadata.name = Some(String::from_utf8_lossy(&value).to_string());
                            }
                            _ => value.push(c),
                        }
                    }
                    b"size" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.size =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"begin" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.begin =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"end" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE | LF | CR => {
                                metadata.end =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"line" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.line_length =
                                    Some(u16::from_str_radix(&String::from_utf8_lossy(&value), 10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"part" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.part =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"crc32" | b"pcrc32" => {
                        match c {
                            b'0'...b'9' | b'A'...b'F' | b'a'...b'f' => value.push(c),
                            SPACE => {
                                state = State::Keyword;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            LF => {
                                state = State::End;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            CR => {}
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    _ => unreachable!(),
                }
            }
        };
    }
    Ok(metadata)
}

fn is_known_keyword(keyword_slice: &[u8]) -> bool {
    keyword_slice == b"name" || keyword_slice == b"line" || keyword_slice == b"size" ||
    keyword_slice == b"part" || keyword_slice == b"begin" ||
    keyword_slice == b"end" || keyword_slice == b"pcrc32" ||
    keyword_slice == b"crc32"
}

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

    #[test]
    fn decode_invalid() {
        assert!(ydecode_buffer(&[b'=']).unwrap().is_empty());
    }

    #[test]
    fn decode_valid_ff() {
        assert_eq!(&vec![0xff - 0x2A], &ydecode_buffer(&[0xff]).unwrap());
    }

    #[test]
    fn decode_valid_01() {
        assert_eq!(&vec![0xff - 0x28], &ydecode_buffer(&[0x01]).unwrap());
    }

    #[test]
    fn decode_valid_esc_ff() {
        assert_eq!(&vec![0xff - 0x40 - 0x2A],
                   &ydecode_buffer(&[b'=', 0xff]).unwrap());
    }

    #[test]
    fn decode_valid_esc_01() {
        assert_eq!(&vec![0xff - 0x40 - 0x2A + 2],
                   &ydecode_buffer(&[b'=', 0x01]).unwrap());
    }
}
<commit_msg>Slightly improve decode performance<commit_after>use std::fs::OpenOptions;
use std::io::{Read, Write, BufReader, BufRead, Seek, SeekFrom};
use std::path::PathBuf;

use errors::DecodeError;
use crc32;
use constants::{NUL, CR, LF, ESCAPE, SPACE, DEFAULT_LINE_SIZE};

#[derive(Default,Debug)]
struct MetaData {
    name: Option<String>,
    line_length: Option<u16>,
    size: Option<usize>,
    crc32: Option<u32>,
    part: Option<u32>,
    begin: Option<usize>,
    end: Option<usize>,
}

/// Decodes the input file in a new output file.
///
/// If ok, returns the path of the decoded file.
///
/// # Example
/// ```rust,no_run
/// yenc::ydecode_file("test2.bin.yenc", "test2.bin");
/// ```
/// # Errors
/// - when the output file already exists
///
pub fn ydecode_file(input_filename: &str, output_path: &str) -> Result<String, DecodeError> {
    let mut input_file = OpenOptions::new().read(true).open(input_filename)?;
    ydecode_stream(&mut input_file, output_path)
}

/// Decodes the data from a stream in a new output file.
///
/// Writes the output to a file with the filename from the header line, and places it in the
/// output path. The path of the output file is returned.
pub fn ydecode_stream<R>(read_stream: &mut R, output_path: &str) -> Result<String, DecodeError>
    where R: Read
{
    let mut rdr = BufReader::new(read_stream);
    let mut output_pathbuf = PathBuf::new();
    output_pathbuf.push(output_path);

    let mut checksum = crc32::Crc32::new();
    let mut yenc_block_found = false;
    let mut metadata: MetaData = Default::default();

    while !yenc_block_found {
        let mut line_buf = Vec::<u8>::with_capacity(2 * DEFAULT_LINE_SIZE as usize);
        let length = rdr.read_until(LF, &mut line_buf)?;
        if length == 0 {
            break;
        }
        if line_buf.starts_with(b"=ybegin ") {
            yenc_block_found = true;
            // parse header line and determine output filename
            metadata = parse_header_line(&line_buf, 8)?;
            output_pathbuf.push(metadata.name.unwrap().to_string().trim());
        }
    }

    if yenc_block_found {
        let mut output_file =
            OpenOptions::new().create(true).write(true).open(output_pathbuf.as_path())?;

        let mut footer_found = false;
        while !footer_found {
            let mut line_buf = Vec::<u8>::with_capacity(2 * DEFAULT_LINE_SIZE as usize);
            let length = rdr.read_until(LF, &mut line_buf)?;
            if length == 0 {
                break;
            }
            if line_buf.starts_with(b"=ypart ") {
                let part_metadata = parse_header_line(&line_buf, 7)?;
                metadata.part = part_metadata.part;
                metadata.begin = part_metadata.begin;
                metadata.end = part_metadata.end;
                if let Some(begin) = metadata.begin {
                    output_file.seek(SeekFrom::Start((begin - 1) as u64))?;
                }
            } else if line_buf.starts_with(b"=yend ") {
                footer_found = true;
                let mm = parse_header_line(&line_buf, 6)?;
                metadata.size = mm.size;
                metadata.crc32 = mm.crc32;
            } else {
                let decoded = ydecode_buffer(&line_buf[0..length])?;
                checksum.update_with_slice(decoded.as_slice());
                output_file.write_all(decoded.as_slice())?;
            }
        }
        if footer_found {
            if let Some(expected_size) = metadata.size {
                if expected_size != checksum.num_bytes {
                    return Err(DecodeError::IncompleteData {
                        expected_size: expected_size,
                        actual_size: checksum.num_bytes,
                    });
                }
            }
            if let Some(expected_crc) = metadata.crc32 {
                if expected_crc != checksum.crc {
                    return Err(DecodeError::InvalidChecksum);
                }
            }
        } else if let Some(expected_size) = metadata.size {
            if expected_size != checksum.num_bytes {
                return Err(DecodeError::IncompleteData {
                    expected_size: expected_size,
                    actual_size: checksum.num_bytes,
                });
            }
        }

    }
    Ok(output_pathbuf.to_str().unwrap().to_string())
}

/// Decode the yEncoded byte slice into a vector of bytes.
///
/// Carriage Return (CR) and Line Feed (LF) are ignored.
pub fn ydecode_buffer(input: &[u8]) -> Result<Vec<u8>, DecodeError> {
    // TODO remove heap allocation
    let mut output = Vec::<u8>::with_capacity((input.len() as f64 * 1.02) as usize);
    let mut iter = input.iter();
    while let Some(byte) = iter.next() {
        let mut byte = *byte;
        match byte {
            NUL | CR | LF => {
                // for now, just continue
                continue;
            }
            ESCAPE => {
                match iter.next() {
                    Some(b) => {
                        byte = b.overflowing_sub(64).0;
                    }
                    None => {
                        // for now, just continue, the only
                        continue;
                    }
                }
            }
            _ => {}
        }
        output.push(byte.overflowing_sub(42).0);
    }
    Ok(output)
}

fn parse_header_line(line_buf: &[u8], offset: usize) -> Result<MetaData, DecodeError> {
    #[derive(Debug)]
    enum State {
        Keyword,
        Value,
        End,
    };

    let mut metadata: MetaData = Default::default();
    let mut state = State::Keyword;
    let mut keyword = Vec::<u8>::with_capacity(6);
    let mut value = Vec::<u8>::with_capacity(96);

    let header_line = String::from_utf8_lossy(line_buf).to_string();
    for (i, &c) in line_buf[offset..].iter().enumerate() {
        let position = i + offset;
        match state {
            State::End => unreachable!(),
            State::Keyword => {
                match c {
                    b'a'...b'z' | b'0'...b'9' => keyword.push(c),
                    b'=' => {
                        if keyword.is_empty() || !is_known_keyword(&keyword) {
                            return Err(DecodeError::InvalidHeader {
                                line: header_line,
                                position: position,
                            });
                        } else {
                            state = State::Value;
                        }
                    }
                    CR | LF => {}
                    _ => {
                        return Err(DecodeError::InvalidHeader {
                            line: header_line,
                            position: position,
                        });
                    }
                }
            }
            State::Value => {
                match keyword.as_slice() {
                    b"name" => {
                        match c {
                            CR => {}
                            LF => {
                                state = State::End;
                                metadata.name = Some(String::from_utf8_lossy(&value).to_string());
                            }
                            _ => value.push(c),
                        }
                    }
                    b"size" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.size =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"begin" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.begin =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"end" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE | LF | CR => {
                                metadata.end =
                                    Some(usize::from_str_radix(&String::from_utf8_lossy(&value),
                                                               10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"line" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.line_length =
                                    Some(u16::from_str_radix(&String::from_utf8_lossy(&value), 10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"part" => {
                        match c {
                            b'0'...b'9' => value.push(c),
                            SPACE => {
                                metadata.part =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 10)
                                        .unwrap());
                                state = State::Keyword;
                                keyword.clear();
                                value.clear();
                            }
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    b"crc32" | b"pcrc32" => {
                        match c {
                            b'0'...b'9' | b'A'...b'F' | b'a'...b'f' => value.push(c),
                            SPACE => {
                                state = State::Keyword;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            LF => {
                                state = State::End;
                                metadata.crc32 =
                                    Some(u32::from_str_radix(&String::from_utf8_lossy(&value), 16)
                                        .unwrap());
                                keyword.clear();
                                value.clear();
                            }
                            CR => {}
                            _ => {
                                return Err(DecodeError::InvalidHeader {
                                    line: header_line,
                                    position: position,
                                });
                            }
                        }
                    }
                    _ => unreachable!(),
                }
            }
        };
    }
    Ok(metadata)
}

fn is_known_keyword(keyword_slice: &[u8]) -> bool {
    keyword_slice == b"name" || keyword_slice == b"line" || keyword_slice == b"size" ||
    keyword_slice == b"part" || keyword_slice == b"begin" ||
    keyword_slice == b"end" || keyword_slice == b"pcrc32" ||
    keyword_slice == b"crc32"
}

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

    #[test]
    fn decode_invalid() {
        assert!(ydecode_buffer(&[b'=']).unwrap().is_empty());
    }

    #[test]
    fn decode_valid_ff() {
        assert_eq!(&vec![0xff - 0x2A], &ydecode_buffer(&[0xff]).unwrap());
    }

    #[test]
    fn decode_valid_01() {
        assert_eq!(&vec![0xff - 0x28], &ydecode_buffer(&[0x01]).unwrap());
    }

    #[test]
    fn decode_valid_esc_ff() {
        assert_eq!(&vec![0xff - 0x40 - 0x2A],
                   &ydecode_buffer(&[b'=', 0xff]).unwrap());
    }

    #[test]
    fn decode_valid_esc_01() {
        assert_eq!(&vec![0xff - 0x40 - 0x2A + 2],
                   &ydecode_buffer(&[b'=', 0x01]).unwrap());
    }
}
<|endoftext|>


<commit_before>fn main() {
    // `print!` is like `println!` but it doesn't add a newline at the end
    print!("January has ");

    // `{}` are placeholders for arguments that will be stringified
    println!("{} days", 31);
    // The `i` suffix indicates the compiler that this literal has type: signed
    // pointer size integer, see next chapter for more details

    // The positional arguments can be reused along the template
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

    // Named arguments can also be used
    println!("{subject} {verb} {predicate}",
             predicate="over the lazy dog",
             subject="the quick brown fox",
             verb="jumps");

    // Special formatting can be specified in the placeholder after a `:`
    println!("{} of {:b} people know binary, the other half don't", 1i, 2i);

    // Error! You are missing an argument
    println!("My name is {0}, {1} {0}", "Bond");
    // FIXME ^ Add the missing argument: "James"
}
<commit_msg>Updated print.rs to remove the "i" suffix.<commit_after>fn main() {
    // `print!` is like `println!` but it doesn't add a newline at the end
    print!("January has ");

    // `{}` are placeholders for arguments that will be stringified
    println!("{} days", 31);
    // The `i` suffix indicates the compiler that this literal has type: signed
    // pointer size integer, see next chapter for more details

    // The positional arguments can be reused along the template
    println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");

    // Named arguments can also be used
    println!("{subject} {verb} {predicate}",
             predicate="over the lazy dog",
             subject="the quick brown fox",
             verb="jumps");

    // Special formatting can be specified in the placeholder after a `:`
    println!("{} of {:b} people know binary, the other half don't", 1, 2);

    // Error! You are missing an argument
    println!("My name is {0}, {1} {0}", "Bond");
    // FIXME ^ Add the missing argument: "James"
}
<|endoftext|>


<commit_before>use serde::{Deserialize, Serialize};

pub type Node = clang_ast::Node<Clang>;

#[derive(Deserialize, Serialize)]
pub enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    ImplicitCastExpr,
    ConstantExpr(ConstantExpr),
    Unknown,
}

#[derive(Deserialize, Serialize)]
pub struct NamespaceDecl {
    pub name: Option<Box<str>>,
}

#[derive(Deserialize, Serialize)]
pub struct EnumDecl {
    pub name: Option<Box<str>>,
    #[serde(rename = "fixedUnderlyingType")]
    pub fixed_underlying_type: Option<Type>,
}

#[derive(Deserialize, Serialize)]
pub struct EnumConstantDecl {
    pub name: Box<str>,
}

#[derive(Deserialize, Serialize)]
pub struct ConstantExpr {
    pub value: Box<str>,
}

#[derive(Deserialize, Serialize)]
pub struct Type {
    #[serde(rename = "qualType")]
    pub qual_type: Box<str>,
    #[serde(rename = "desugaredQualType")]
    pub desugared_qual_type: Option<Box<str>>,
}

#[cfg(all(test, target_pointer_width = "64"))]
const _: [(); std::mem::size_of::<Node>()] = [(); 88];
<commit_msg>Prune null fields from serialized clang tree<commit_after>use serde::{Deserialize, Serialize};

pub type Node = clang_ast::Node<Clang>;

#[derive(Deserialize, Serialize)]
pub enum Clang {
    NamespaceDecl(NamespaceDecl),
    EnumDecl(EnumDecl),
    EnumConstantDecl(EnumConstantDecl),
    ImplicitCastExpr,
    ConstantExpr(ConstantExpr),
    Unknown,
}

#[derive(Deserialize, Serialize)]
pub struct NamespaceDecl {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<Box<str>>,
}

#[derive(Deserialize, Serialize)]
pub struct EnumDecl {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<Box<str>>,
    #[serde(
        rename = "fixedUnderlyingType",
        skip_serializing_if = "Option::is_none"
    )]
    pub fixed_underlying_type: Option<Type>,
}

#[derive(Deserialize, Serialize)]
pub struct EnumConstantDecl {
    pub name: Box<str>,
}

#[derive(Deserialize, Serialize)]
pub struct ConstantExpr {
    pub value: Box<str>,
}

#[derive(Deserialize, Serialize)]
pub struct Type {
    #[serde(rename = "qualType")]
    pub qual_type: Box<str>,
    #[serde(rename = "desugaredQualType", skip_serializing_if = "Option::is_none")]
    pub desugared_qual_type: Option<Box<str>>,
}

#[cfg(all(test, target_pointer_width = "64"))]
const _: [(); std::mem::size_of::<Node>()] = [(); 88];
<|endoftext|>


<commit_before>use env;
use std::io::{LineWriter,stdout};
use std::path::Path;
use std::fs::OpenOptions;
use rand::{Rng,thread_rng};
use mio_uds::UnixDatagram;
use std::net::{TcpStream,UdpSocket,ToSocketAddrs};
use sozu_command::logging::{Logger,LoggerBackend};
use rand::distributions::Alphanumeric;

pub fn setup(tag: String, level: &str, target: &str, access_target: Option<&str>) {
  let backend = target_to_backend(target);
  let access_backend = access_target.map(target_to_backend);

  if let Ok(log_level) = env::var("RUST_LOG") {
    Logger::init(tag, &log_level, backend, access_backend);
  } else {
    // We set the env variable so every worker can access it
    env::set_var("RUST_LOG", level);
    Logger::init(tag, level, backend, access_backend);
  }
}

pub fn target_to_backend(target: &str) -> LoggerBackend {
  if target == "stdout" {
    LoggerBackend::Stdout(stdout())
  } else if target.starts_with("udp://") {
    let addr_res = (&target[6..]).to_socket_addrs();
    match addr_res {
      Err(e) => {
        println!("invalid log target configuration ({:?}): {}", e, target);
        LoggerBackend::Stdout(stdout())
      },
      Ok(mut addrs) => {
        let socket = UdpSocket::bind(("0.0.0.0", 0)).unwrap();
        LoggerBackend::Udp(socket, addrs.next().unwrap())
      }
    }
  } else if target.starts_with("tcp://") {
    let addr_res = (&target[6..]).to_socket_addrs();
    match addr_res {
      Err(e) => {
        println!("invalid log target configuration ({:?}): {}", e, target);
        LoggerBackend::Stdout(stdout())
      },
      Ok(mut addrs) => {
        LoggerBackend::Tcp(TcpStream::connect(addrs.next().unwrap()).unwrap())
      }
    }
  } else if target.starts_with("unix://") {
    let path = Path::new(&target[7..]);
    if !path.is_file() {
      println!("invalid log target configuration: {} is not a file", &target[7..]);
      LoggerBackend::Stdout(stdout())
    } else {
      let mut dir = env::temp_dir();
      let s: String = thread_rng().sample_iter(&Alphanumeric).take(12).collect();
      dir.push(s);
      let socket = UnixDatagram::bind(dir).unwrap();
      socket.connect(path).unwrap();
      LoggerBackend::Unix(socket)
    }
  } else if target.starts_with("file://") {
    let path = Path::new(&target[7..]);
    match OpenOptions::new().create(true).append(true).open(path) {
      Ok(file) => LoggerBackend::File(LineWriter::new(file)),
      Err(e)   => {
        println!("invalid log target configuration: could not open file at {} (error: {:?})", &target[7..], e);
        LoggerBackend::Stdout(stdout())
      }
    }
  } else {
    println!("invalid log target configuration: {}", target);
    LoggerBackend::Stdout(stdout())
  }
}

<commit_msg>check that the logging unix socket exists<commit_after>use env;
use std::io::{LineWriter,stdout};
use std::path::Path;
use std::fs::OpenOptions;
use rand::{Rng,thread_rng};
use mio_uds::UnixDatagram;
use std::net::{TcpStream,UdpSocket,ToSocketAddrs};
use sozu_command::logging::{Logger,LoggerBackend};
use rand::distributions::Alphanumeric;

pub fn setup(tag: String, level: &str, target: &str, access_target: Option<&str>) {
  let backend = target_to_backend(target);
  let access_backend = access_target.map(target_to_backend);

  if let Ok(log_level) = env::var("RUST_LOG") {
    Logger::init(tag, &log_level, backend, access_backend);
  } else {
    // We set the env variable so every worker can access it
    env::set_var("RUST_LOG", level);
    Logger::init(tag, level, backend, access_backend);
  }
}

pub fn target_to_backend(target: &str) -> LoggerBackend {
  if target == "stdout" {
    LoggerBackend::Stdout(stdout())
  } else if target.starts_with("udp://") {
    let addr_res = (&target[6..]).to_socket_addrs();
    match addr_res {
      Err(e) => {
        println!("invalid log target configuration ({:?}): {}", e, target);
        LoggerBackend::Stdout(stdout())
      },
      Ok(mut addrs) => {
        let socket = UdpSocket::bind(("0.0.0.0", 0)).unwrap();
        LoggerBackend::Udp(socket, addrs.next().unwrap())
      }
    }
  } else if target.starts_with("tcp://") {
    let addr_res = (&target[6..]).to_socket_addrs();
    match addr_res {
      Err(e) => {
        println!("invalid log target configuration ({:?}): {}", e, target);
        LoggerBackend::Stdout(stdout())
      },
      Ok(mut addrs) => {
        LoggerBackend::Tcp(TcpStream::connect(addrs.next().unwrap()).unwrap())
      }
    }
  } else if target.starts_with("unix://") {
    let path = Path::new(&target[7..]);
    if !path.exists() {
      println!("invalid log target configuration: {} is not a file", &target[7..]);
      LoggerBackend::Stdout(stdout())
    } else {
      let mut dir = env::temp_dir();
      let s: String = thread_rng().sample_iter(&Alphanumeric).take(12).collect();
      dir.push(s);
      let socket = UnixDatagram::bind(dir).unwrap();
      socket.connect(path).unwrap();
      LoggerBackend::Unix(socket)
    }
  } else if target.starts_with("file://") {
    let path = Path::new(&target[7..]);
    match OpenOptions::new().create(true).append(true).open(path) {
      Ok(file) => LoggerBackend::File(LineWriter::new(file)),
      Err(e)   => {
        println!("invalid log target configuration: could not open file at {} (error: {:?})", &target[7..], e);
        LoggerBackend::Stdout(stdout())
      }
    }
  } else {
    println!("invalid log target configuration: {}", target);
    LoggerBackend::Stdout(stdout())
  }
}

<|endoftext|>


<commit_before>use super::scheduler::*;

use std::sync::Arc;
use futures::sync::oneshot;
use futures::future::Future;

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
#[derive(Debug)]
pub struct Desync<T: Send> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    data:   Box<T>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Box::new(data)
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        unsafe {
            // As drop() is the last thing called, we know that this object will still exist at the point where
            let data = DataRef(&*self.data);

            async(&self.queue, move || {
                let data = data.0 as *mut T;
                job(&mut *data);
            })
        }
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = unsafe {
            // As drop() is the last thing called, we know that this object will still exist at the point where
            let data = DataRef(&*self.data);

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(&mut *data)
            })
        };

        result
    }

    ///
    /// Performs an operation asynchronously on this thread, returning the result via a future.
    ///
    pub fn future<TFn, Item: 'static+Send>(&self, job: TFn) -> Box<Future<Item=Item, Error=oneshot::Canceled>>
    where TFn: 'static+Send+FnOnce(&mut T) -> Item {
        let (send, receive) = oneshot::channel();

        self.async(|data| {
            let result = job(data);

            if let Err(e) = send.send(result) {
                panic!(e);
            }
        });

        Box::new(receive)
    }
}

impl<T: Send> Drop for Desync<T> {
    fn drop(&mut self) {
        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        sync(&self.queue, || {});
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::thread::*;
    use std::time::*;
    use super::super::scheduler::test::timeout;

    #[derive(Debug)]
    struct TestData {
        val: u32
    }

    #[test]
    fn can_retrieve_data_synchronously() {
        let desynced = Desync::new(TestData { val: 0 });

        assert!(desynced.sync(|data| data.val) == 0);
    }

    #[test]
    fn can_update_data_asynchronously() {
        let desynced = Desync::new(TestData { val: 0 });

        desynced.async(|data| {
            sleep(Duration::from_millis(100));
            data.val = 42;
        });

        assert!(desynced.sync(|data| data.val) == 42);
    }

    #[test]
    fn can_update_data_asynchronously_1000_times() {
        for _i in 0..1000 {
            timeout(|| {
                let desynced = Desync::new(TestData { val: 0 });

                desynced.async(|data| {
                    data.val = 42;
                });
                desynced.async(|data| {
                    data.val = 43;
                });

                assert!(desynced.sync(|data| data.val) == 43);
            }, 500);
        }
    }

    #[test]
    fn can_update_data_with_future() {
        timeout(|| {
            use futures::executor;

            let desynced = Desync::new(TestData { val: 0 });

            desynced.async(|data| {
                sleep(Duration::from_millis(100));
                data.val = 42;
            });

            let mut future = executor::spawn(desynced.future(|data| {
                data.val
            }));

            assert!(future.wait_future().unwrap() == 42);
        }, 500);
    }

    #[test]
    fn can_update_data_with_future_1000_times() {
        // Seems to timeout fairly reliably after signalling the future
        use futures::executor;

        for _i in 0..1000 {
            timeout(|| {
                let desynced = Desync::new(TestData { val: 0 });

                desynced.async(|data| {
                    data.val = 42;
                });
                desynced.async(|data| {
                    data.val = 43;
                });

                let mut future = executor::spawn(desynced.future(|data| data.val));

                assert!(future.wait_future().unwrap() == 43);
            }, 500);
        }
    }

    #[test]
    fn dropping_while_running_isnt_obviously_bad() {
        let desynced = Desync::new(TestData { val: 0 });

        desynced.async(|data| {
            sleep(Duration::from_millis(100));
            data.val = 42;
        });
        desynced.async(|data| {
            sleep(Duration::from_millis(100));
            data.val = 42;
        });
    }
}
<commit_msg>Not actually safe for this to be Debug<commit_after>use super::scheduler::*;

use std::sync::Arc;
use futures::sync::oneshot;
use futures::future::Future;

///
/// A data storage structure used to govern synchronous and asynchronous access to an underlying object.
///
pub struct Desync<T: Send> {
    /// Queue used for scheduling runtime for this object
    queue:  Arc<JobQueue>,

    /// Data for this object. Boxed so the pointer remains the same through the lifetime of the object.
    data:   Box<T>
}

// Rust actually derives this anyway at the moment
unsafe impl<T: Send> Send for Desync<T> {}

// True iff queue: Sync
unsafe impl<T: Send> Sync for Desync<T> {}

///
/// Used for passing the data pointer through to the queue
///
/// 'Safe' because the queue is synchronised during drop, so we can never use the pointer
/// if the object does not exist.
///
struct DataRef<T: Send>(*const T);
unsafe impl<T: Send> Send for DataRef<T> {}

// TODO: we can change DataRef to Shared (https://doc.rust-lang.org/std/ptr/struct.Shared.html in the future)

// TODO: T does not need to be static as we know that its lifetime is at least the lifetime of Desync<T> and hence the queue
impl<T: 'static+Send> Desync<T> {
    ///
    /// Creates a new Desync object
    ///
    pub fn new(data: T) -> Desync<T> {
        let queue = queue();

        Desync {
            queue:  queue,
            data:   Box::new(data)
        }
    }

    ///
    /// Performs an operation asynchronously on this item. This function will return
    /// immediately and the job will happen on a separate thread at some time in the
    /// future (generally fairly soon).
    ///
    /// Jobs are always performed in the order that they are queued and are always
    /// performed synchronously with respect to this object.
    ///
    pub fn async<TFn>(&self, job: TFn)
    where TFn: 'static+Send+FnOnce(&mut T) -> () {
        unsafe {
            // As drop() is the last thing called, we know that this object will still exist at the point where
            let data = DataRef(&*self.data);

            async(&self.queue, move || {
                let data = data.0 as *mut T;
                job(&mut *data);
            })
        }
    }

    ///
    /// Performs an operation synchronously on this item. This will be queued with any other
    /// jobs that this item may be performing, and this function will not return until the
    /// job is complete and the result is available.
    ///
    pub fn sync<TFn, Result>(&self, job: TFn) -> Result
    where TFn: Send+FnOnce(&mut T) -> Result, Result: Send {
        let result = unsafe {
            // As drop() is the last thing called, we know that this object will still exist at the point where
            let data = DataRef(&*self.data);

            sync(&self.queue, move || {
                let data = data.0 as *mut T;
                job(&mut *data)
            })
        };

        result
    }

    ///
    /// Performs an operation asynchronously on this thread, returning the result via a future.
    ///
    pub fn future<TFn, Item: 'static+Send>(&self, job: TFn) -> Box<Future<Item=Item, Error=oneshot::Canceled>>
    where TFn: 'static+Send+FnOnce(&mut T) -> Item {
        let (send, receive) = oneshot::channel();

        self.async(|data| {
            let result = job(data);

            if let Err(e) = send.send(result) {
                panic!(e);
            }
        });

        Box::new(receive)
    }
}

impl<T: Send> Drop for Desync<T> {
    fn drop(&mut self) {
        // Ensure that everything on the queue has committed by queueing a last synchronous event
        // (Not synchronising the queue would make this unsafe as we would hold on to a pointer to
        // the internal data structure)
        sync(&self.queue, || {});
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::thread::*;
    use std::time::*;
    use super::super::scheduler::test::timeout;

    #[derive(Debug)]
    struct TestData {
        val: u32
    }

    #[test]
    fn can_retrieve_data_synchronously() {
        let desynced = Desync::new(TestData { val: 0 });

        assert!(desynced.sync(|data| data.val) == 0);
    }

    #[test]
    fn can_update_data_asynchronously() {
        let desynced = Desync::new(TestData { val: 0 });

        desynced.async(|data| {
            sleep(Duration::from_millis(100));
            data.val = 42;
        });

        assert!(desynced.sync(|data| data.val) == 42);
    }

    #[test]
    fn can_update_data_asynchronously_1000_times() {
        for _i in 0..1000 {
            timeout(|| {
                let desynced = Desync::new(TestData { val: 0 });

                desynced.async(|data| {
                    data.val = 42;
                });
                desynced.async(|data| {
                    data.val = 43;
                });

                assert!(desynced.sync(|data| data.val) == 43);
            }, 500);
        }
    }

    #[test]
    fn can_update_data_with_future() {
        timeout(|| {
            use futures::executor;

            let desynced = Desync::new(TestData { val: 0 });

            desynced.async(|data| {
                sleep(Duration::from_millis(100));
                data.val = 42;
            });

            let mut future = executor::spawn(desynced.future(|data| {
                data.val
            }));

            assert!(future.wait_future().unwrap() == 42);
        }, 500);
    }

    #[test]
    fn can_update_data_with_future_1000_times() {
        // Seems to timeout fairly reliably after signalling the future
        use futures::executor;

        for _i in 0..1000 {
            timeout(|| {
                let desynced = Desync::new(TestData { val: 0 });

                desynced.async(|data| {
                    data.val = 42;
                });
                desynced.async(|data| {
                    data.val = 43;
                });

                let mut future = executor::spawn(desynced.future(|data| data.val));

                assert!(future.wait_future().unwrap() == 43);
            }, 500);
        }
    }

    #[test]
    fn dropping_while_running_isnt_obviously_bad() {
        let desynced = Desync::new(TestData { val: 0 });

        desynced.async(|data| {
            sleep(Duration::from_millis(100));
            data.val = 42;
        });
        desynced.async(|data| {
            sleep(Duration::from_millis(100));
            data.val = 42;
        });
    }
}
<|endoftext|>