<commit_before>use serde::{Serialize, Serializer};
use std::collections::HashMap;

#[derive(Serialize, PartialEq, Debug)]
pub struct Replay {
    pub header_size: u32,
    pub header_crc: u32,
    pub major_version: u32,
    pub minor_version: u32,
    pub game_type: String,

    #[serde(serialize_with = "pair_vec")]
    pub properties: Vec<(String, HeaderProp)>,
    pub content_size: u32,
    pub content_crc: u32,
    pub levels: Vec<String>,
    pub keyframes: Vec<KeyFrame>,
    pub debug_info: Vec<DebugInfo>,
    pub tick_marks: Vec<TickMark>,
    pub packages: Vec<String>,
    pub objects: Vec<String>,
    pub names: Vec<String>,
    pub class_indices: Vec<ClassIndex>,
    pub net_cache: Vec<ClassNetCache>,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct TickMark {
    pub description: String,
    pub frame: u32,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct KeyFrame {
    pub time: f32,
    pub frame: u32,
    pub position: u32,
}

#[derive(PartialEq, Debug)]
pub enum HeaderProp {
    Array(Vec<Vec<(String, HeaderProp)>>),
    Bool(bool),
    Byte,
    Float(f32),
    Int(u32),
    Name(String),
    QWord(u64),
    Str(String),
}

#[derive(Serialize, PartialEq, Debug)]
pub struct DebugInfo {
    pub frame: u32,
    pub user: String,
    pub text: String,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct ClassIndex {
    pub class: String,
    pub index: u32,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct CacheProp {
    pub index: u32,
    pub id: u32,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct ClassNetCache {
    pub index: u32,
    pub parent_id: u32,
    pub id: u32,
    pub properties: Vec<CacheProp>,
}

/// Serialize a vector of key value tuples into a map. This is useful when the data we're ingesting
/// (rocket league replay data) doesn't have a defined spec, so it may be assuming too much to
/// store it into an associative array, so it's stored as a normal sequence. Here we serialize as a
/// map structure because most replay parser do this, so we should be compliant and the data format
/// doesn't dictate that the keys in a sequence of key value pairs must be distinct. It's true,
/// JSON doesn't need the keys to be unique: http://stackoverflow.com/q/21832701/433785
fn pair_vec<K, V, S>(inp: &[(K, V)], serializer: &mut S) -> Result<(), S::Error>
    where K: Serialize,
          V: Serialize,
          S: Serializer
{
    let mut state = try!(serializer.serialize_map(Some(inp.len())));
    for &(ref key, ref val) in inp.iter() {
        try!(serializer.serialize_map_key(&mut state, key));
        try!(serializer.serialize_map_value(&mut state, val));
    }
    return serializer.serialize_map_end(state);
}

/// By default serde will generate a serialization method that writes out the enum as well as the
/// enum value. Since header values are self describing in JSON, we do not need to serialize the
/// enum type. This is slightly lossy as in the serialized format it will be ambiguous if a value
/// is a `Name` or `Str`, as well as `Byte`, `Float`, `Int`, or `QWord`.
impl Serialize for HeaderProp {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        match *self {
            HeaderProp::Array(ref x) => {
                let mut state = try!(serializer.serialize_seq(Some(x.len())));
                for inner in x {
                    // Look for a better way to do this instead of allocating the intermediate map
                    let mut els = HashMap::new();
                    for &(ref key, ref val) in inner.iter() {
                        els.insert(key.clone(), val.clone());
                    }
                    try!(serializer.serialize_seq_elt(&mut state, els));
                }
                return serializer.serialize_seq_end(state);
            }
            HeaderProp::Bool(ref x) => serializer.serialize_bool(*x),
            HeaderProp::Byte => serializer.serialize_u8(0),
            HeaderProp::Float(ref x) => serializer.serialize_f32(*x),
            HeaderProp::Int(ref x) => serializer.serialize_u32(*x),
            HeaderProp::Name(ref x) => serializer.serialize_str(&x),
            HeaderProp::QWord(ref x) => serializer.serialize_u64(*x),
            HeaderProp::Str(ref x) => serializer.serialize_str(&x),
        }
    }
}

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

    fn to_json<T: serde::Serialize>(input: &T) -> std::string::String {
        return serde_json::to_string(input).unwrap();
    }

    #[test]
    fn serialize_header_array() {
        let data = vec![
            vec![
                ("frame".to_string(), HeaderProp::Int(441)),
                ("PlayerName".to_string(), HeaderProp::Str("rust is awesome".to_string()))
            ], vec![
                ("frame".to_string(), HeaderProp::Int(1738)),
                ("PlayerName".to_string(), HeaderProp::Str("rusty".to_string()))
            ]
          ];
        let actual = HeaderProp::Array(data);
        assert_eq!(to_json(&actual), "[{\"PlayerName\":\"rust is awesome\",\"frame\":441},{\"PlayerName\":\"rusty\",\"frame\":1738}]");
    }

    #[test]
    fn serialize_header_bool() {
        assert_eq!(to_json(&HeaderProp::Bool(false)), "false");
        assert_eq!(to_json(&HeaderProp::Bool(true)), "true");
    }

    #[test]
    fn serialize_header_numbers() {
        assert_eq!(to_json(&HeaderProp::Byte), "0");
        assert_eq!(to_json(&HeaderProp::QWord(10)), "10");
        assert_eq!(to_json(&HeaderProp::Float(10.2)), "10.2");
        assert_eq!(to_json(&HeaderProp::Int(11)), "11");
    }

    #[test]
    fn serialize_header_str() {
        let val = "hello world";
        assert_eq!(to_json(&HeaderProp::Str(val.to_string())), "\"hello world\"");
        assert_eq!(to_json(&HeaderProp::Name(val.to_string())), "\"hello world\"");
    }
}
<commit_msg>Collapse duplicate match arm<commit_after>use serde::{Serialize, Serializer};
use std::collections::HashMap;

#[derive(Serialize, PartialEq, Debug)]
pub struct Replay {
    pub header_size: u32,
    pub header_crc: u32,
    pub major_version: u32,
    pub minor_version: u32,
    pub game_type: String,

    #[serde(serialize_with = "pair_vec")]
    pub properties: Vec<(String, HeaderProp)>,
    pub content_size: u32,
    pub content_crc: u32,
    pub levels: Vec<String>,
    pub keyframes: Vec<KeyFrame>,
    pub debug_info: Vec<DebugInfo>,
    pub tick_marks: Vec<TickMark>,
    pub packages: Vec<String>,
    pub objects: Vec<String>,
    pub names: Vec<String>,
    pub class_indices: Vec<ClassIndex>,
    pub net_cache: Vec<ClassNetCache>,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct TickMark {
    pub description: String,
    pub frame: u32,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct KeyFrame {
    pub time: f32,
    pub frame: u32,
    pub position: u32,
}

#[derive(PartialEq, Debug)]
pub enum HeaderProp {
    Array(Vec<Vec<(String, HeaderProp)>>),
    Bool(bool),
    Byte,
    Float(f32),
    Int(u32),
    Name(String),
    QWord(u64),
    Str(String),
}

#[derive(Serialize, PartialEq, Debug)]
pub struct DebugInfo {
    pub frame: u32,
    pub user: String,
    pub text: String,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct ClassIndex {
    pub class: String,
    pub index: u32,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct CacheProp {
    pub index: u32,
    pub id: u32,
}

#[derive(Serialize, PartialEq, Debug)]
pub struct ClassNetCache {
    pub index: u32,
    pub parent_id: u32,
    pub id: u32,
    pub properties: Vec<CacheProp>,
}

/// Serialize a vector of key value tuples into a map. This is useful when the data we're ingesting
/// (rocket league replay data) doesn't have a defined spec, so it may be assuming too much to
/// store it into an associative array, so it's stored as a normal sequence. Here we serialize as a
/// map structure because most replay parser do this, so we should be compliant and the data format
/// doesn't dictate that the keys in a sequence of key value pairs must be distinct. It's true,
/// JSON doesn't need the keys to be unique: http://stackoverflow.com/q/21832701/433785
fn pair_vec<K, V, S>(inp: &[(K, V)], serializer: &mut S) -> Result<(), S::Error>
    where K: Serialize,
          V: Serialize,
          S: Serializer
{
    let mut state = try!(serializer.serialize_map(Some(inp.len())));
    for &(ref key, ref val) in inp.iter() {
        try!(serializer.serialize_map_key(&mut state, key));
        try!(serializer.serialize_map_value(&mut state, val));
    }
    return serializer.serialize_map_end(state);
}

/// By default serde will generate a serialization method that writes out the enum as well as the
/// enum value. Since header values are self describing in JSON, we do not need to serialize the
/// enum type. This is slightly lossy as in the serialized format it will be ambiguous if a value
/// is a `Name` or `Str`, as well as `Byte`, `Float`, `Int`, or `QWord`.
impl Serialize for HeaderProp {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        match *self {
            HeaderProp::Array(ref x) => {
                let mut state = try!(serializer.serialize_seq(Some(x.len())));
                for inner in x {
                    // Look for a better way to do this instead of allocating the intermediate map
                    let mut els = HashMap::new();
                    for &(ref key, ref val) in inner.iter() {
                        els.insert(key.clone(), val.clone());
                    }
                    try!(serializer.serialize_seq_elt(&mut state, els));
                }
                return serializer.serialize_seq_end(state);
            }
            HeaderProp::Bool(ref x) => serializer.serialize_bool(*x),
            HeaderProp::Byte => serializer.serialize_u8(0),
            HeaderProp::Float(ref x) => serializer.serialize_f32(*x),
            HeaderProp::Int(ref x) => serializer.serialize_u32(*x),
            HeaderProp::QWord(ref x) => serializer.serialize_u64(*x),
            HeaderProp::Name(ref x) | HeaderProp::Str(ref x) => serializer.serialize_str(&x)
        }
    }
}

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

    fn to_json<T: serde::Serialize>(input: &T) -> std::string::String {
        return serde_json::to_string(input).unwrap();
    }

    #[test]
    fn serialize_header_array() {
        let data = vec![
            vec![
                ("frame".to_string(), HeaderProp::Int(441)),
                ("PlayerName".to_string(), HeaderProp::Str("rust is awesome".to_string()))
            ], vec![
                ("frame".to_string(), HeaderProp::Int(1738)),
                ("PlayerName".to_string(), HeaderProp::Str("rusty".to_string()))
            ]
          ];
        let actual = HeaderProp::Array(data);
        assert_eq!(to_json(&actual), "[{\"PlayerName\":\"rust is awesome\",\"frame\":441},{\"PlayerName\":\"rusty\",\"frame\":1738}]");
    }

    #[test]
    fn serialize_header_bool() {
        assert_eq!(to_json(&HeaderProp::Bool(false)), "false");
        assert_eq!(to_json(&HeaderProp::Bool(true)), "true");
    }

    #[test]
    fn serialize_header_numbers() {
        assert_eq!(to_json(&HeaderProp::Byte), "0");
        assert_eq!(to_json(&HeaderProp::QWord(10)), "10");
        assert_eq!(to_json(&HeaderProp::Float(10.2)), "10.2");
        assert_eq!(to_json(&HeaderProp::Int(11)), "11");
    }

    #[test]
    fn serialize_header_str() {
        let val = "hello world";
        assert_eq!(to_json(&HeaderProp::Str(val.to_string())), "\"hello world\"");
        assert_eq!(to_json(&HeaderProp::Name(val.to_string())), "\"hello world\"");
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
    assert!(false);
}<commit_msg>Test fix<commit_after>#[test]
fn it_works() {
    assert!(true);
}<|endoftext|>


<commit_before>#![deny(warnings)]
extern crate inflector;

use inflector::Inflector;

macro_rules! str_tests {
    ( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
        $(
            #[test]
            fn $test_name() {
                assert_eq!($to_cast.$imp_trait(), $casted)
            }
        )*
    }
}

macro_rules! string_tests {
    ( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
        $(
            #[test]
            fn $test_name() {
                assert_eq!($to_cast.to_string().$imp_trait(), $casted)
            }
        )*
    }
}

macro_rules! number_tests {
    ( $($test_name:ident => $imp_trait:ident => $typ:ident => $to_cast:expr => $casted:expr), *) => {
        $(
            #[test]
            fn $test_name() {
                let to_cast: $typ = $to_cast;
                assert_eq!(to_cast.to_string().$imp_trait(), $casted)
            }
        )*
    }
}

str_tests![
    str_to_class => to_class_case => "foo" => "Foo".to_string(),
    str_is_class => is_class_case => "Foo" => true,
    str_to_camel => to_camel_case => "foo_bar" => "fooBar".to_string(),
    str_is_camel => is_camel_case => "fooBar" => true,
    str_to_table => to_table_case => "fooBar" => "foo_bars".to_string(),
    str_is_table => is_table_case => "foo_bars" => true,
    str_to_screaming_snake => to_screaming_snake_case => "fooBar" => "FOO_BAR".to_string(),
    str_is_screaming_snake => is_screaming_snake_case => "FOO_BAR" => true,
    str_to_snake => to_snake_case => "fooBar" => "foo_bar".to_string(),
    str_is_snake => is_snake_case => "foo_bar" => true,
    str_to_kebab => to_kebab_case => "fooBar" => "foo-bar".to_string(),
    str_is_kebab => is_kebab_case => "foo-bar" => true,
    str_to_train => to_train_case => "fooBar" => "Foo-Bar".to_string(),
    str_is_train => is_train_case => "Foo-Bar" => true,
    str_to_sentence => to_sentence_case => "fooBar" => "Foo bar".to_string(),
    str_is_sentence => is_sentence_case => "Foo bar" => true,
    str_to_title => to_title_case => "fooBar" => "Foo Bar".to_string(),
    str_is_title => is_title_case => "Foo Bar" => true,
    str_ordinalize  => ordinalize => "1" => "1st".to_string(),
    str_deordinalize  => deordinalize => "1st" => "1".to_string(),
    str_to_foreign_key => to_foreign_key => "Foo::Bar" => "bar_id".to_string(),
    str_is_foreign_key => is_foreign_key => "bar_id" => true,
    str_demodulize => demodulize => "Foo::Bar" => "Bar".to_string(),
    str_deconstantize => deconstantize => "Foo::Bar" => "Foo".to_string(),
    str_pluralize => to_plural => "crate" => "crates".to_string(),
    str_singular => to_singular => "crates" => "crate".to_string()
];

string_tests![
    string_to_class => to_class_case => "foo" => "Foo".to_string(),
    string_is_class => is_class_case => "Foo" => true,
    string_to_camel => to_camel_case => "foo_bar" => "fooBar".to_string(),
    string_is_camel => is_camel_case => "fooBar" => true,
    string_to_table => to_table_case => "fooBar" => "foo_bars".to_string(),
    string_is_table => is_table_case => "foo_bars" => true,
    string_to_screaming_snake => to_screaming_snake_case => "fooBar" => "FOO_BAR".to_string(),
    string_is_screaming_snake => is_screaming_snake_case => "FOO_BAR" => true,
    string_to_snake => to_snake_case => "fooBar" => "foo_bar".to_string(),
    string_is_snake => is_snake_case => "foo_bar" => true,
    string_to_kebab => to_kebab_case => "fooBar" => "foo-bar".to_string(),
    string_is_kebab => is_kebab_case => "foo-bar" => true,
    string_to_train => to_train_case => "fooBar" => "Foo-Bar".to_string(),
    string_is_train => is_train_case => "Foo-Bar" => true,
    string_to_sentence => to_sentence_case => "fooBar" => "Foo bar".to_string(),
    string_is_sentence => is_sentence_case => "Foo bar" => true,
    string_to_title => to_title_case => "fooBar" => "Foo Bar".to_string(),
    string_is_title => is_title_case => "Foo Bar" => true,
    string_ordinalize  => ordinalize => "1" => "1st".to_string(),
    string_deordinalize  => deordinalize => "1st" => "1".to_string(),
    string_to_foreign_key => to_foreign_key => "Foo::Bar" => "bar_id".to_string(),
    string_is_foreign_key => is_foreign_key => "bar_id" => true,
    string_demodulize => demodulize => "Foo::Bar" => "Bar".to_string(),
    string_deconstantize => deconstantize => "Foo::Bar" => "Foo".to_string(),
    string_pluralize => to_plural => "crate" => "crates".to_string(),
    string_singular => to_singular => "crates" => "crate".to_string()
];

number_tests![
    i8_ordinalize   => ordinalize => i8  => 1 => "1st".to_string(),
    i16_ordinalize  => ordinalize => i16 => 1 => "1st".to_string(),
    i32_ordinalize  => ordinalize => i32 => 1 => "1st".to_string(),
    i64_ordinalize  => ordinalize => i64 => 1 => "1st".to_string(),
    u8_ordinalize   => ordinalize => u8  => 1 => "1st".to_string(),
    u16_ordinalize  => ordinalize => u16 => 1 => "1st".to_string(),
    u32_ordinalize  => ordinalize => u32 => 1 => "1st".to_string(),
    u64_ordinalize  => ordinalize => u64 => 1 => "1st".to_string(),
    isize_ordinalize  => ordinalize => isize => 1 => "1st".to_string(),
    usize_ordinalize  => ordinalize => usize => 1 => "1st".to_string(),
    f32_ordinalize  => ordinalize => f32 => 1.0 => "1st".to_string(),
    f64_ordinalize  => ordinalize => f64 => 1.0 => "1st".to_string()
];
<commit_msg>Fixed tests that were incorrectly passing due to copied marco<commit_after>#![deny(warnings)]
extern crate inflector;

use inflector::Inflector;
use inflector::InflectorNumbers;

macro_rules! str_tests {
    ( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
        $(
            #[test]
            fn $test_name() {
                assert_eq!($to_cast.$imp_trait(), $casted)
            }
        )*
    }
}

macro_rules! string_tests {
    ( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
        $(
            #[test]
            fn $test_name() {
                assert_eq!($to_cast.to_string().$imp_trait(), $casted)
            }
        )*
    }
}

macro_rules! number_tests {
    ( $($test_name:ident => $imp_trait:ident => $typ:ident => $to_cast:expr => $casted:expr), *) => {
        $(
            #[test]
            fn $test_name() {
                let to_cast: $typ = $to_cast;
                assert_eq!(to_cast.$imp_trait(), $casted)
            }
        )*
    }
}

str_tests![
    str_to_class => to_class_case => "foo" => "Foo".to_string(),
    str_is_class => is_class_case => "Foo" => true,
    str_to_camel => to_camel_case => "foo_bar" => "fooBar".to_string(),
    str_is_camel => is_camel_case => "fooBar" => true,
    str_to_table => to_table_case => "fooBar" => "foo_bars".to_string(),
    str_is_table => is_table_case => "foo_bars" => true,
    str_to_screaming_snake => to_screaming_snake_case => "fooBar" => "FOO_BAR".to_string(),
    str_is_screaming_snake => is_screaming_snake_case => "FOO_BAR" => true,
    str_to_snake => to_snake_case => "fooBar" => "foo_bar".to_string(),
    str_is_snake => is_snake_case => "foo_bar" => true,
    str_to_kebab => to_kebab_case => "fooBar" => "foo-bar".to_string(),
    str_is_kebab => is_kebab_case => "foo-bar" => true,
    str_to_train => to_train_case => "fooBar" => "Foo-Bar".to_string(),
    str_is_train => is_train_case => "Foo-Bar" => true,
    str_to_sentence => to_sentence_case => "fooBar" => "Foo bar".to_string(),
    str_is_sentence => is_sentence_case => "Foo bar" => true,
    str_to_title => to_title_case => "fooBar" => "Foo Bar".to_string(),
    str_is_title => is_title_case => "Foo Bar" => true,
    str_ordinalize  => ordinalize => "1" => "1st".to_string(),
    str_deordinalize  => deordinalize => "1st" => "1".to_string(),
    str_to_foreign_key => to_foreign_key => "Foo::Bar" => "bar_id".to_string(),
    str_is_foreign_key => is_foreign_key => "bar_id" => true,
    str_demodulize => demodulize => "Foo::Bar" => "Bar".to_string(),
    str_deconstantize => deconstantize => "Foo::Bar" => "Foo".to_string(),
    str_pluralize => to_plural => "crate" => "crates".to_string(),
    str_singular => to_singular => "crates" => "crate".to_string()
];

string_tests![
    string_to_class => to_class_case => "foo" => "Foo".to_string(),
    string_is_class => is_class_case => "Foo" => true,
    string_to_camel => to_camel_case => "foo_bar" => "fooBar".to_string(),
    string_is_camel => is_camel_case => "fooBar" => true,
    string_to_table => to_table_case => "fooBar" => "foo_bars".to_string(),
    string_is_table => is_table_case => "foo_bars" => true,
    string_to_screaming_snake => to_screaming_snake_case => "fooBar" => "FOO_BAR".to_string(),
    string_is_screaming_snake => is_screaming_snake_case => "FOO_BAR" => true,
    string_to_snake => to_snake_case => "fooBar" => "foo_bar".to_string(),
    string_is_snake => is_snake_case => "foo_bar" => true,
    string_to_kebab => to_kebab_case => "fooBar" => "foo-bar".to_string(),
    string_is_kebab => is_kebab_case => "foo-bar" => true,
    string_to_train => to_train_case => "fooBar" => "Foo-Bar".to_string(),
    string_is_train => is_train_case => "Foo-Bar" => true,
    string_to_sentence => to_sentence_case => "fooBar" => "Foo bar".to_string(),
    string_is_sentence => is_sentence_case => "Foo bar" => true,
    string_to_title => to_title_case => "fooBar" => "Foo Bar".to_string(),
    string_is_title => is_title_case => "Foo Bar" => true,
    string_ordinalize  => ordinalize => "1" => "1st".to_string(),
    string_deordinalize  => deordinalize => "1st" => "1".to_string(),
    string_to_foreign_key => to_foreign_key => "Foo::Bar" => "bar_id".to_string(),
    string_is_foreign_key => is_foreign_key => "bar_id" => true,
    string_demodulize => demodulize => "Foo::Bar" => "Bar".to_string(),
    string_deconstantize => deconstantize => "Foo::Bar" => "Foo".to_string(),
    string_pluralize => to_plural => "crate" => "crates".to_string(),
    string_singular => to_singular => "crates" => "crate".to_string()
];

number_tests![
    i8_ordinalize   => ordinalize => i8  => 1 => "1st".to_string(),
    i16_ordinalize  => ordinalize => i16 => 1 => "1st".to_string(),
    i32_ordinalize  => ordinalize => i32 => 1 => "1st".to_string(),
    i64_ordinalize  => ordinalize => i64 => 1 => "1st".to_string(),
    u8_ordinalize   => ordinalize => u8  => 1 => "1st".to_string(),
    u16_ordinalize  => ordinalize => u16 => 1 => "1st".to_string(),
    u32_ordinalize  => ordinalize => u32 => 1 => "1st".to_string(),
    u64_ordinalize  => ordinalize => u64 => 1 => "1st".to_string(),
    isize_ordinalize  => ordinalize => isize => 1 => "1st".to_string(),
    usize_ordinalize  => ordinalize => usize => 1 => "1st".to_string(),
    f32_ordinalize  => ordinalize => f32 => 1.0 => "1st".to_string(),
    f64_ordinalize  => ordinalize => f64 => 1.0 => "1st".to_string()
];
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

use jagged_array::Jagged2;
use ndarray::Array2;

use render::Renderer;
use routing::{Edge, GraphWatcher, NodeData, NodeHandle};
use routing::effect::{PrimitiveEffect, EffectData};
use streaming_iterator::StreamingIterator;
use util::unpack_f32;

#[derive(Debug, Default)]
struct NodeMap {
    nodes: HashMap<NodeHandle, Node>,
    output_edges: Vec<Option<Edge>>,
}

#[derive(Default, Debug)]
pub struct RefRenderer {
    nodes: NodeMap,
    /// inputs[slot][sample] represents the value of the external input at
    /// the given slot and time. On OOB, it is assumed to be 0.
    inputs: Vec<Vec<f32>>,
    /// Next expected sample to be queried.
    /// This is tracked because if we do a seek, the inputs need to be zero'd.
    head: u64,
}

#[derive(Debug)]
struct Node {
    data: MyNodeData,
    /// Inbound edges, indexed by slot idx.
    inbound: Vec<Option<Edge>>,
}

#[derive(Debug)]
enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// Primitive effect (delay, constant, etc).
    Primitive(PrimitiveEffect),
}

impl Renderer for RefRenderer {
    fn fill_buffer(&mut self, buff: &mut Array2<f32>, idx: u64, inputs: Jagged2<f32>) {
        let (n_slots, n_times) = buff.dim().into();
        // Store inputs for future use
        {
            // If this is a seek operation, forget historical input values.
            if idx != self.head {
                for slot in &mut self.inputs {
                    *slot = Vec::new();
                    // NB: Improper indexing on 32-bit OS, but will OOM first.
                    slot.resize(idx as usize, 0f32);
                }
            }
            // Make sure we have storage space for all inputs.
            while self.inputs.len() < buff.len() {
                // NB: Improper indexing on 32-bit OS, but will OOM first.
                let mut v = Vec::with_capacity(idx as usize);
                v.resize(idx as usize, 0f32);
                self.inputs.push(v);
            }
            let mut stream = inputs.stream();
            let mut self_it = self.inputs.iter_mut();
            while let (Some(row), Some(vec_dest)) = (stream.next(), self_it.next()) {
                assert_eq!(vec_dest.len(), idx as usize);
                vec_dest.extend(row);
                assert!(vec_dest.len() <= idx as usize + n_times); // cannot send inputs ahead of outputs.
                let pad_val = vec_dest.last().cloned().unwrap_or(0f32);
                vec_dest.resize(idx as usize +n_times, pad_val);
            }
        }

        // Calculate outputs
        for slot in 0..n_slots as u32 {
            for time in idx..idx+n_times as u64 {
                buff[[slot as usize, (time - idx) as usize]] = self.get_sample(time, slot);
            }
        }
        // Keep track of the playhead
        self.head = idx + n_times as u64;
    }
}

impl RefRenderer {
    /// Get the output at a particular time and to a particular output slot.
    fn get_sample(&mut self, time: u64, slot: u32) -> f32 {
        self.nodes.get_output(time, slot, |time2, slot2| {
            *self.inputs.get(slot2 as usize)
                .and_then(|v| v.get(time2 as usize))
                .unwrap_or(&0f32)
        })
    }
    /// Allocate renderer data based on data from a RouteGraph node.
    fn make_node(&self, effect: &NodeData) -> MyNodeData {
        match *effect.data() {
            EffectData::Primitive(e) => MyNodeData::Primitive(e),
            EffectData::RouteGraph(ref graph) => {
                let mut nodes: NodeMap = Default::default();

                for (node, data) in graph.iter_nodes() {
                    nodes.insert(*node, Node::new(self.make_node(data)));
                }
                for edge in graph.iter_edges() {
                    nodes.add_edge(edge);
                }
                MyNodeData::UserNode(nodes)
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(*handle, Node::new(my_node_data));
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.add_edge(edge);
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.nodes.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).expect("Attempt to delete edge, but it was never created!").inbound
        };
        if let Some(stored_edge) = inbound.get_mut(edge.to_slot() as usize) {
            *stored_edge = None
        }
    }
}

impl NodeMap {
    /// Add an edge that connects two nodes within this graph.
    fn add_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).unwrap().inbound
        };
        let slot: u32 = edge.to_slot().into();
        let slot = slot as usize;
        // allocate space to store the edge.
        if inbound.len() <= slot { inbound.resize(slot+1, None) }

        inbound[slot] = Some(edge.clone());
    }
    /// Get the value of a particular output slot at a given time.
    /// This will recurse down, all the way to the input to this node itself.
    /// `get_input(time, slot)` will be called (multiple times, with different args)
    /// in order to query whatever is input to this node.
    fn get_output<F: Fn(u64, u32) -> f32>(&self, time: u64, slot: u32, get_input: F) -> f32 {
        let out_edge = self.output_edges.get(slot as usize);
        self.get_maybe_edge_value(time, out_edge, &get_input)
    }
    /// Wrapper around `get_edge_value` that will return 0f32 if maybe_edge is not
    /// `Some(&Some(edge))`.
    fn get_maybe_edge_value(&self, time: u64,
        maybe_edge: Option<&Option<Edge>>, get_input: &Fn(u64, u32) -> f32) -> f32
    {
        if let Some(&Some(ref edge)) = maybe_edge {
            self.get_edge_value(time, &edge, get_input)
        } else {
            // Edge doesn't exist; value is zero.
            0f32
        }
    }
    /// Get the value on an edge at a specific time.
    /// This will recurse down, all the way to the input to this node itself.
    /// `get_input(time, slot)` will be called (multiple times, with different args)
    /// in order to query whatever is input to this node.
    fn get_edge_value(&self, time: u64, edge: &Edge, get_input: &Fn(u64, u32) -> f32) -> f32 {
        let from = edge.from_full();
        let from_slot = edge.from_slot();
        if *from.node_handle() == None {
            // reading from an input
            get_input(time, from_slot)
        } else {
            // Reading from another node within the DAG
            let node = &self.nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref new_nodes) => {
                    new_nodes.get_output(time, from_slot, |time2, slot2| {
                        // get the input to this node.
                        let in_edge = node.inbound.get(slot2 as usize);
                        self.get_maybe_edge_value(time2, in_edge, get_input)
                    })
                },
                MyNodeData::Primitive(prim) => match prim {
                    // Output = sum of all inputs to slot 0.
                    PrimitiveEffect::Delay => {
                        // The only nonzero output is slot=0.
                        if from_slot != 0 {
                            warn!("Attempt to read from Delay slot != 0");
                            0f32
                        } else {
                            let delay_frames = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                            // Clamp delay value to [0, u64::max]
                            if delay_frames >= 18446744073709551616f32 {
                                // delay is >= than 2^64; must be indexing from negative time.
                                return 0f32;
                            }
                            let delay_int = if delay_frames < 0f32 {
                                0u64
                            } else {
                                // Note: this conversion is flooring.
                                delay_frames as u64
                            };
                            // t<0 -> value is 0.
                            time.checked_sub(delay_int).map_or(0f32, |origin_time| {
                                self.get_maybe_edge_value(origin_time, node.inbound.get(0), get_input)
                            })
                        }
                    },
                    PrimitiveEffect::F32Constant => {
                        // Float value is encoded via the slot.
                        unpack_f32(from_slot)
                    },
                    PrimitiveEffect::Multiply => {
                        // The only nonzero output is slot=0.
                        if from_slot != 0 {
                            warn!("Attempt to read from Multiply slot != 0");
                            0f32
                        } else {
                            let input_left = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                            let input_right = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                            input_left * input_right
                        }
                    },
                    PrimitiveEffect::Sum2 => {
                        // The only nonzero output is slot=0.
                        if from_slot != 0 {
                            warn!("Attempt to read from Sum2 slot != 0");
                            0f32
                        } else {
                            let input_left = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                            let input_right = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                            input_left + input_right
                        }
                    },
                    PrimitiveEffect::Divide => {
                        // The only nonzero output is slot=0.
                        if from_slot != 0 {
                            warn!("Attempt to read from Divide slot != 0");
                            0f32
                        } else {
                            let dividend = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                            let divisor = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                            dividend / divisor
                        }
                    },
                    PrimitiveEffect::Minimum => {
                        // The only nonzero output is slot=0.
                        if from_slot != 0 {
                            warn!("Attempt to read from Minimum slot != 0");
                            0f32
                        } else {
                            let input_left = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                            let input_right = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                            input_left.min(input_right)
                        }
                    }
                    PrimitiveEffect::Modulo => {
                        // The only nonzero output is slot=0.
                        if from_slot != 0 {
                            warn!("Attempt to read from Modulo slot != 0");
                            0f32
                        } else {
                            let dividend = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                            let divisor = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                            let rem = dividend % divisor;
                            if rem < 0f32 {
                                // TODO: We may be losing precision here, if rem is small.
                                // We should find a way to do true modulus.
                                rem + divisor
                            } else {
                                rem
                            }
                        }
                    },
                }
            }
        }
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: Vec::new(),
        }
    }
}

impl Deref for NodeMap {
    type Target = HashMap<NodeHandle, Node>;
    fn deref(&self) -> &Self::Target {
        &self.nodes
    }
}

impl DerefMut for NodeMap {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.nodes
    }
}
<commit_msg>Make use of new RouteGraph restrictions<commit_after>use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

use jagged_array::Jagged2;
use ndarray::Array2;

use render::Renderer;
use routing::{Edge, GraphWatcher, NodeData, NodeHandle};
use routing::effect::{PrimitiveEffect, EffectData};
use streaming_iterator::StreamingIterator;
use util::unpack_f32;

#[derive(Debug, Default)]
struct NodeMap {
    nodes: HashMap<NodeHandle, Node>,
    output_edges: Vec<Option<Edge>>,
}

#[derive(Default, Debug)]
pub struct RefRenderer {
    nodes: NodeMap,
    /// inputs[slot][sample] represents the value of the external input at
    /// the given slot and time. On OOB, it is assumed to be 0.
    inputs: Vec<Vec<f32>>,
    /// Next expected sample to be queried.
    /// This is tracked because if we do a seek, the inputs need to be zero'd.
    head: u64,
}

#[derive(Debug)]
struct Node {
    data: MyNodeData,
    /// Inbound edges, indexed by slot idx.
    inbound: Vec<Option<Edge>>,
}

#[derive(Debug)]
enum MyNodeData {
    /// This node is a non-primitive effect.
    UserNode(NodeMap),
    /// Primitive effect (delay, constant, etc).
    Primitive(PrimitiveEffect),
}

impl Renderer for RefRenderer {
    fn fill_buffer(&mut self, buff: &mut Array2<f32>, idx: u64, inputs: Jagged2<f32>) {
        let (n_slots, n_times) = buff.dim().into();
        // Store inputs for future use
        {
            // If this is a seek operation, forget historical input values.
            if idx != self.head {
                for slot in &mut self.inputs {
                    *slot = Vec::new();
                    // NB: Improper indexing on 32-bit OS, but will OOM first.
                    slot.resize(idx as usize, 0f32);
                }
            }
            // Make sure we have storage space for all inputs.
            while self.inputs.len() < buff.len() {
                // NB: Improper indexing on 32-bit OS, but will OOM first.
                let mut v = Vec::with_capacity(idx as usize);
                v.resize(idx as usize, 0f32);
                self.inputs.push(v);
            }
            let mut stream = inputs.stream();
            let mut self_it = self.inputs.iter_mut();
            while let (Some(row), Some(vec_dest)) = (stream.next(), self_it.next()) {
                assert_eq!(vec_dest.len(), idx as usize);
                vec_dest.extend(row);
                assert!(vec_dest.len() <= idx as usize + n_times); // cannot send inputs ahead of outputs.
                let pad_val = vec_dest.last().cloned().unwrap_or(0f32);
                vec_dest.resize(idx as usize +n_times, pad_val);
            }
        }

        // Calculate outputs
        for slot in 0..n_slots as u32 {
            for time in idx..idx+n_times as u64 {
                buff[[slot as usize, (time - idx) as usize]] = self.get_sample(time, slot);
            }
        }
        // Keep track of the playhead
        self.head = idx + n_times as u64;
    }
}

impl RefRenderer {
    /// Get the output at a particular time and to a particular output slot.
    fn get_sample(&mut self, time: u64, slot: u32) -> f32 {
        self.nodes.get_output(time, slot, |time2, slot2| {
            *self.inputs.get(slot2 as usize)
                .and_then(|v| v.get(time2 as usize))
                .unwrap_or(&0f32)
        })
    }
    /// Allocate renderer data based on data from a RouteGraph node.
    fn make_node(&self, effect: &NodeData) -> MyNodeData {
        match *effect.data() {
            EffectData::Primitive(e) => MyNodeData::Primitive(e),
            EffectData::RouteGraph(ref graph) => {
                let mut nodes: NodeMap = Default::default();

                for (node, data) in graph.iter_nodes() {
                    nodes.insert(*node, Node::new(self.make_node(data)));
                }
                for edge in graph.iter_edges() {
                    nodes.add_edge(edge);
                }
                MyNodeData::UserNode(nodes)
            }
        }
    }
}

impl GraphWatcher for RefRenderer {
    fn on_add_node(&mut self, handle: &NodeHandle, data: &NodeData) {
        let my_node_data = self.make_node(data);
        self.nodes.insert(*handle, Node::new(my_node_data));
    }
    fn on_del_node(&mut self, handle: &NodeHandle) {
        self.nodes.remove(handle);
    }
    fn on_add_edge(&mut self, edge: &Edge) {
        self.nodes.add_edge(edge);
    }
    fn on_del_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.nodes.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).expect("Attempt to delete edge, but it was never created!").inbound
        };
        if let Some(stored_edge) = inbound.get_mut(edge.to_slot() as usize) {
            *stored_edge = None
        }
    }
}

impl NodeMap {
    /// Add an edge that connects two nodes within this graph.
    fn add_edge(&mut self, edge: &Edge) {
        let inbound = if edge.to_full().is_toplevel() {
            &mut self.output_edges
        } else {
            &mut self.nodes.get_mut(&edge.to_full()).unwrap().inbound
        };
        let slot: u32 = edge.to_slot().into();
        let slot = slot as usize;
        // allocate space to store the edge.
        if inbound.len() <= slot { inbound.resize(slot+1, None) }

        inbound[slot] = Some(edge.clone());
    }
    /// Get the value of a particular output slot at a given time.
    /// This will recurse down, all the way to the input to this node itself.
    /// `get_input(time, slot)` will be called (multiple times, with different args)
    /// in order to query whatever is input to this node.
    fn get_output<F: Fn(u64, u32) -> f32>(&self, time: u64, slot: u32, get_input: F) -> f32 {
        let out_edge = self.output_edges.get(slot as usize);
        self.get_maybe_edge_value(time, out_edge, &get_input)
    }
    /// Wrapper around `get_edge_value` that will return 0f32 if maybe_edge is not
    /// `Some(&Some(edge))`.
    fn get_maybe_edge_value(&self, time: u64,
        maybe_edge: Option<&Option<Edge>>, get_input: &Fn(u64, u32) -> f32) -> f32
    {
        if let Some(&Some(ref edge)) = maybe_edge {
            self.get_edge_value(time, &edge, get_input)
        } else {
            // Edge doesn't exist; value is zero.
            0f32
        }
    }
    /// Get the value on an edge at a specific time.
    /// This will recurse down, all the way to the input to this node itself.
    /// `get_input(time, slot)` will be called (multiple times, with different args)
    /// in order to query whatever is input to this node.
    fn get_edge_value(&self, time: u64, edge: &Edge, get_input: &Fn(u64, u32) -> f32) -> f32 {
        let from = edge.from_full();
        let from_slot = edge.from_slot();
        if *from.node_handle() == None {
            // reading from an input
            get_input(time, from_slot)
        } else {
            // Reading from another node within the DAG
            let node = &self.nodes[&from];
            match node.data {
                MyNodeData::UserNode(ref new_nodes) => {
                    new_nodes.get_output(time, from_slot, |time2, slot2| {
                        // get the input to this node.
                        let in_edge = node.inbound.get(slot2 as usize);
                        self.get_maybe_edge_value(time2, in_edge, get_input)
                    })
                },
                MyNodeData::Primitive(prim) => match prim {
                    // Output = sum of all inputs to slot 0.
                    PrimitiveEffect::Delay => {
                        // The only nonzero output is slot=0.
                        assert!(from_slot == 0);
                        let delay_frames = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                        // Clamp delay value to [0, u64::max]
                        if delay_frames >= 18446744073709551616f32 {
                            // delay is >= than 2^64; must be indexing from negative time.
                            return 0f32;
                        }
                        let delay_int = if delay_frames < 0f32 {
                            0u64
                        } else {
                            // Note: this conversion is flooring.
                            delay_frames as u64
                        };
                        // t<0 -> value is 0.
                        time.checked_sub(delay_int).map_or(0f32, |origin_time| {
                            self.get_maybe_edge_value(origin_time, node.inbound.get(0), get_input)
                        })
                    },
                    PrimitiveEffect::F32Constant => {
                        // Float value is encoded via the slot.
                        unpack_f32(from_slot)
                    },
                    PrimitiveEffect::Multiply => {
                        // The only nonzero output is slot=0.
                        assert!(from_slot == 0);
                        let input_left = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                        let input_right = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                        input_left * input_right
                    },
                    PrimitiveEffect::Sum2 => {
                        // The only nonzero output is slot=0.
                        assert!(from_slot == 0);
                        let input_left = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                        let input_right = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                        input_left + input_right
                    },
                    PrimitiveEffect::Divide => {
                        // The only nonzero output is slot=0.
                        assert!(from_slot == 0);
                        let dividend = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                        let divisor = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                        dividend / divisor
                    },
                    PrimitiveEffect::Minimum => {
                        // The only nonzero output is slot=0.
                        assert!(from_slot == 0);
                        let input_left = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                        let input_right = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                        input_left.min(input_right)
                    },
                    PrimitiveEffect::Modulo => {
                        // The only nonzero output is slot=0.
                        assert!(from_slot == 0);
                        let dividend = self.get_maybe_edge_value(time, node.inbound.get(0), get_input);
                        let divisor = self.get_maybe_edge_value(time, node.inbound.get(1), get_input);
                        let rem = dividend % divisor;
                        if rem < 0f32 {
                            // TODO: We may be losing precision here, if rem is small.
                            // We should find a way to do true modulus.
                            rem + divisor
                        } else {
                            rem
                        }
                    },
                }
            }
        }
    }
}

impl Node {
    fn new(data: MyNodeData) -> Self {
        Node {
            data: data,
            inbound: Vec::new(),
        }
    }
}

impl Deref for NodeMap {
    type Target = HashMap<NodeHandle, Node>;
    fn deref(&self) -> &Self::Target {
        &self.nodes
    }
}

impl DerefMut for NodeMap {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.nodes
    }
}
<|endoftext|>


<commit_before>use std::str;
use std::io::prelude::*;
use std::fs::File;

use nom::*;

use error::*;

named!(string_between_quotes, delimited!(char!('\"'), is_not!("\""), char!('\"')));
named!(get_cell, take_while!(is_not_cell_end));
named!(get_line, take_until!("\n"));
named!(consume_useless_chars, take_while!(is_whitespace));

fn is_whitespace(c: u8) -> bool {
    c as char == ' ' || c as char == '\t'
}

fn is_not_cell_end(c: u8) -> bool {
    c as char != ',' && c as char != '\n'
}

fn get_column_value(input: &[u8], pos: Position) -> IResult<&[u8], &[u8], CsvError> {
    let (i, cell) = try_parse!(input,
        fix_error!(CsvError,
            preceded!(
            opt!(consume_useless_chars),
                alt!(
                    string_between_quotes
                  | get_cell
                )
            )
        )
    );

    if i.len() == 0 {
        IResult::Incomplete(Needed::Unknown)
    } else if is_not_cell_end(i[0]) {
      let p = Position { line: pos.line, column: pos.column + input.offset(i) };
        IResult::Error(Err::Code(ErrorKind::Custom(
            CsvError::InvalidCharacter(CharError::new(',', i[0] as char, &p))
        )))
    } else {
      IResult::Done(i, cell)
    }
}

fn get_string_column_value(input: &[u8], pos: Position) -> IResult<&[u8], String, CsvError> {
    map_res!(input,
        map_res!(
            dbg_dmp!(
                apply!(get_column_value, Position::new(pos.line, pos.column))
            ),
            from_utf8
        ),
        |d| {
            str::FromStr::from_str(d)
        }
    )
}

fn comma_then_column(input: &[u8], pos: Position) -> IResult<&[u8], String, CsvError> {
    preceded!(input,
        fix_error!(CsvError, char!(',')),
        apply!(get_string_column_value, Position::new(pos.line, pos.column))
    )
}

use std::str::from_utf8;
fn get_line_values<'a>(entry: &'a[u8],ret: &mut Vec<String>, line: usize) -> IResult<&'a[u8], &'a[u8], CsvError> {
    if entry.len() == 0 {
        IResult::Done(b"", b"")
    } else {
        let (i, col) = try_parse!(entry, apply!(get_string_column_value, Position::new(line, ret.len())));
        ret.push(col);

        match fix_error!(i, CsvError, terminated!(
            many0!(
                apply!(comma_then_column, Position::new(line, ret.len()))
            ),
            char!('\n')
        )) {
            IResult::Done(i, v) => {
              ret.extend(v);
              IResult::Done(i, &entry[..entry.offset(i)])
            },
            IResult::Incomplete(i) => IResult::Incomplete(i),
            IResult::Error(e)      => IResult::Error(e)
        }
    }
}

fn get_lines_values(mut ret: Vec<Vec<String>>, entry: &[u8]) -> Result<Vec<Vec<String>>, CsvError> {
    let mut input = entry;
    let mut line  = 0;
    loop {
        let mut v: Vec<String> = Vec::new();
        match get_line_values(input, &mut v, line) {
            IResult::Error(Err::Code(ErrorKind::Custom(e))) => return Err(e),
            IResult::Error(_)                               => return Err(CsvError::GenericError),
            IResult::Incomplete(_)                          => {
              // we reached the end of file?
              break
            }
            IResult::Done(i,_)                              => {
                input = i;
                line += 1;
                ret.push(v);
                if input.len() == 0 {
                    break;
                }
            },
        }
    }

    Ok(ret)
}

pub fn parse_csv_from_slice(entry: &[u8]) -> Result<Vec<Vec<String>>, CsvError> {
    get_lines_values(vec!(), entry)
}

pub fn parse_csv_from_file(filename: &str) -> Result<Vec<Vec<String>>, CsvError> {
    let mut f = File::open(filename).unwrap();
    let mut buffer = vec!();

    f.read_to_end(&mut buffer).unwrap();
    parse_csv_from_slice(&buffer)
}

pub fn parse_csv(entry: &str) -> Result<Vec<Vec<String>>, CsvError> {
    parse_csv_from_slice(entry.as_bytes())
}

#[test]
fn check_string_between_quotes() {
    let f = b"\"nom\",age\ncarles,30\nlaure,28\n";

    match string_between_quotes(f) {
        IResult::Done(in_, out) => {
            assert_eq!(out, b"nom");
            assert_eq!(in_, b",age\ncarles,30\nlaure,28\n");
        },
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
}

#[test]
fn check_get_cell() {
    let f = b"age\ncarles,30\n";
    let g = b"age2,carles,30\n";

    match get_cell(f) {
        IResult::Done(_, out) => assert_eq!(out, b"age"),
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
    match get_cell(g) {
        IResult::Done(_, out) => assert_eq!(out, b"age2"),
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
}

#[test]
fn check_get_line() {
    let f = b"\"nom\",age\ncarles,30\nlaure,28\n";

    match get_line(f) {
        IResult::Done(_, out) => assert_eq!(out, b"\"nom\",age"),
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
}

#[test]
fn check_get_line_values() {
    // no terminator, this is not a line
    //let mut cells = vec!();
    //get_line_values(&mut cells, b"\"nom\",,age", 0);
    //assert_eq!(cells, vec!("nom".to_owned(), "".to_owned(), "age".to_owned()));

    let mut cells = vec!();
    let res = get_line_values(b"\"nom\",,age\n", &mut cells, 0);
    println!("res: {:?}", res);
    assert_eq!(cells, vec!("nom".to_owned(), "".to_owned(), "age".to_owned()));

    let mut cells = vec!();
    get_line_values(b"\"nom\",age,\n", &mut cells, 0);
    assert_eq!(cells, vec!("nom".to_owned(), "age".to_owned(), "".to_owned()));

    let mut cells = vec!();
    get_line_values(b"\"nom\",age,,\"hoho\",,end\n", &mut cells, 0);
    assert_eq!(cells, vec!("nom".to_owned(), "age".to_owned(), "".to_owned(), "hoho".to_owned(), "".to_owned(), "end".to_owned()));

    let mut cells = vec!();
    let e = get_line_values(b"\"nom\" ,age,\"hoho\"", &mut cells, 0);
    assert_eq!(e,
        IResult::Error(Err::Code(ErrorKind::Custom(
            CsvError::InvalidCharacter(CharError::new(',', ' ', &Position::new(0, 5)))
        )))
    );

}

#[test]
fn check_get_lines_values() {
    let f = b"\"nom\",age\ncarles,30\nlaure,28\n";

    assert_eq!(get_lines_values(vec!(), f),
               Ok(vec!(
                       vec!("nom".to_owned(), "age".to_owned()),
                       vec!("carles".to_owned(), "30".to_owned()),
                       vec!("laure".to_owned(), "28".to_owned()))));
}

#[test]
fn check_parse_csv() {
    let f = "\"nom\",age\ncarles,30\nlaure,28\n";

    assert_eq!(parse_csv(f),
               Ok(vec!(
                       vec!("nom".to_owned(), "age".to_owned()),
                       vec!("carles".to_owned(), "30".to_owned()),
                       vec!("laure".to_owned(), "28".to_owned()))));
}
<commit_msg>Some code nits fix<commit_after>use std::str;
use std::io::prelude::*;
use std::fs::File;
use std::str::from_utf8;

use nom::*;

use error::*;

named!(string_between_quotes, delimited!(char!('\"'), is_not!("\""), char!('\"')));
named!(get_cell, take_while!(is_not_cell_end));
named!(consume_useless_chars, take_while!(is_whitespace));

fn is_whitespace(c: u8) -> bool {
    c as char == ' ' || c as char == '\t'
}

fn is_not_cell_end(c: u8) -> bool {
    c as char != ',' && c as char != '\n'
}

fn get_column_value(input: &[u8], pos: Position) -> IResult<&[u8], &[u8], CsvError> {
    let (i, cell) = try_parse!(input,
        fix_error!(CsvError,
            preceded!(
            opt!(consume_useless_chars),
                alt!(
                    string_between_quotes
                  | get_cell
                )
            )
        )
    );

    if i.len() == 0 {
        IResult::Incomplete(Needed::Unknown)
    } else if is_not_cell_end(i[0]) {
        let p = Position { line: pos.line, column: pos.column + input.offset(i) };
        IResult::Error(Err::Code(ErrorKind::Custom(
            CsvError::InvalidCharacter(CharError::new(',', i[0] as char, &p))
        )))
    } else {
        IResult::Done(i, cell)
    }
}

fn get_string_column_value(input: &[u8], pos: Position) -> IResult<&[u8], String, CsvError> {
    map_res!(input,
        map_res!(
            dbg_dmp!(
                apply!(get_column_value, Position::new(pos.line, pos.column))
            ),
            from_utf8
        ),
        |d| {
            str::FromStr::from_str(d)
        }
    )
}

fn comma_then_column(input: &[u8], pos: Position) -> IResult<&[u8], String, CsvError> {
    preceded!(input,
        fix_error!(CsvError, char!(',')),
        apply!(get_string_column_value, Position::new(pos.line, pos.column))
    )
}

fn get_line_values<'a>(entry: &'a[u8],ret: &mut Vec<String>, line: usize) -> IResult<&'a[u8], &'a[u8], CsvError> {
    if entry.len() == 0 {
        IResult::Done(b"", b"")
    } else {
        let (i, col) = try_parse!(entry, apply!(get_string_column_value, Position::new(line, ret.len())));
        ret.push(col);

        match fix_error!(i, CsvError, terminated!(
            many0!(
                apply!(comma_then_column, Position::new(line, ret.len()))
            ),
            char!('\n')
        )) {
            IResult::Done(i, v)    => {
                ret.extend(v);
                IResult::Done(i, &entry[..entry.offset(i)])
            },
            IResult::Incomplete(i) => IResult::Incomplete(i),
            IResult::Error(e)      => IResult::Error(e)
        }
    }
}

fn get_lines_values(mut ret: Vec<Vec<String>>, entry: &[u8]) -> Result<Vec<Vec<String>>, CsvError> {
    let mut input = entry;
    let mut line  = 0;
    loop {
        let mut v: Vec<String> = Vec::new();
        match get_line_values(input, &mut v, line) {
            IResult::Error(Err::Code(ErrorKind::Custom(e))) => return Err(e),
            IResult::Error(_)                               => return Err(CsvError::GenericError),
            IResult::Incomplete(_)                          => {
                // did we reach the end of file?
                break
            }
            IResult::Done(i,_)                              => {
                input = i;
                line += 1;
                ret.push(v);
                if input.len() == 0 {
                    break;
                }
            },
        }
    }

    Ok(ret)
}

pub fn parse_csv_from_slice(entry: &[u8]) -> Result<Vec<Vec<String>>, CsvError> {
    get_lines_values(vec!(), entry)
}

pub fn parse_csv_from_file(filename: &str) -> Result<Vec<Vec<String>>, CsvError> {
    let mut f = File::open(filename).unwrap();
    let mut buffer = vec!();

    f.read_to_end(&mut buffer).unwrap();
    parse_csv_from_slice(&buffer)
}

pub fn parse_csv(entry: &str) -> Result<Vec<Vec<String>>, CsvError> {
    parse_csv_from_slice(entry.as_bytes())
}

#[test]
fn check_string_between_quotes() {
    let f = b"\"nom\",age\ncarles,30\nlaure,28\n";

    match string_between_quotes(f) {
        IResult::Done(in_, out) => {
            assert_eq!(out, b"nom");
            assert_eq!(in_, b",age\ncarles,30\nlaure,28\n");
        },
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
}

#[test]
fn check_get_cell() {
    let f = b"age\ncarles,30\n";
    let g = b"age2,carles,30\n";

    match get_cell(f) {
        IResult::Done(_, out) => assert_eq!(out, b"age"),
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
    match get_cell(g) {
        IResult::Done(_, out) => assert_eq!(out, b"age2"),
        IResult::Incomplete(x) => panic!("incomplete: {:?}", x),
        IResult::Error(e) => panic!("error: {:?}", e),
    }
}

#[test]
fn check_get_line_values() {
    // no terminator, this is not a line
    //let mut cells = vec!();
    //get_line_values(&mut cells, b"\"nom\",,age", 0);
    //assert_eq!(cells, vec!("nom".to_owned(), "".to_owned(), "age".to_owned()));

    let mut cells = vec!();
    let res = get_line_values(b"\"nom\",,age\n", &mut cells, 0);
    println!("res: {:?}", res);
    assert_eq!(cells, vec!("nom".to_owned(), "".to_owned(), "age".to_owned()));

    let mut cells = vec!();
    get_line_values(b"\"nom\",age,\n", &mut cells, 0);
    assert_eq!(cells, vec!("nom".to_owned(), "age".to_owned(), "".to_owned()));

    let mut cells = vec!();
    get_line_values(b"\"nom\",age,,\"hoho\",,end\n", &mut cells, 0);
    assert_eq!(cells, vec!("nom".to_owned(), "age".to_owned(), "".to_owned(), "hoho".to_owned(), "".to_owned(), "end".to_owned()));

    let mut cells = vec!();
    let e = get_line_values(b"\"nom\" ,age,\"hoho\"", &mut cells, 0);
    assert_eq!(e,
        IResult::Error(Err::Code(ErrorKind::Custom(
            CsvError::InvalidCharacter(CharError::new(',', ' ', &Position::new(0, 5)))
        )))
    );
}

#[test]
fn check_get_lines_values() {
    let f = b"\"nom\",age\ncarles,30\nlaure,28\n";

    assert_eq!(get_lines_values(vec!(), f),
               Ok(vec!(
                       vec!("nom".to_owned(), "age".to_owned()),
                       vec!("carles".to_owned(), "30".to_owned()),
                       vec!("laure".to_owned(), "28".to_owned()))));
}

#[test]
fn check_parse_csv() {
    let f = "\"nom\",age\ncarles,30\nlaure,28\n";

    assert_eq!(parse_csv(f),
               Ok(vec!(
                       vec!("nom".to_owned(), "age".to_owned()),
                       vec!("carles".to_owned(), "30".to_owned()),
                       vec!("laure".to_owned(), "28".to_owned()))));
}
<|endoftext|>


<commit_before>use nom::IResult;
use std::str;

use common::Operator;
use select::*;
use insert::*;

#[derive(Clone, Debug, PartialEq)]
pub enum SqlQuery {
    Insert(InsertStatement),
    Select(SelectStatement),
}

#[derive(Clone, Debug, PartialEq)]
pub enum ConditionBase {
    Field(String),
    Literal(String),
    Placeholder,
}

#[derive(Clone, Debug, PartialEq)]
pub struct ConditionTree {
    pub operator: Operator,
    pub left: Option<Box<ConditionExpression>>,
    pub right: Option<Box<ConditionExpression>>,
}

#[derive(Clone, Debug, PartialEq)]
pub enum ConditionExpression {
    ComparisonOp(ConditionTree),
    LogicalOp(ConditionTree),
    Base(ConditionBase),
}

/// Parse sequence of SQL statements, divided by semicolons or newlines
// named!(pub query_list<&[u8], Vec<SqlQuery> >,
//    many1!(map_res!(selection, |s| { SqlQuery::Select(s) }))
// );

pub fn parse_query(input: &str) -> Result<SqlQuery, &str> {
    // we process all queries in lowercase to avoid having to deal with capitalization in the
    // parser.
    let q_bytes = String::from(input).into_bytes();

    // TODO(malte): appropriately pass through errors from nom
    match insertion(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::Insert(o)),
        _ => (),
    };

    match selection(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::Select(o)),
        _ => (),
    };

    Err("failed to parse query")
}
<commit_msg>Re-export Operator publicly<commit_after>use nom::IResult;
use std::str;

pub use common::Operator;
use select::*;
use insert::*;

#[derive(Clone, Debug, PartialEq)]
pub enum SqlQuery {
    Insert(InsertStatement),
    Select(SelectStatement),
}

#[derive(Clone, Debug, PartialEq)]
pub enum ConditionBase {
    Field(String),
    Literal(String),
    Placeholder,
}

#[derive(Clone, Debug, PartialEq)]
pub struct ConditionTree {
    pub operator: Operator,
    pub left: Option<Box<ConditionExpression>>,
    pub right: Option<Box<ConditionExpression>>,
}

#[derive(Clone, Debug, PartialEq)]
pub enum ConditionExpression {
    ComparisonOp(ConditionTree),
    LogicalOp(ConditionTree),
    Base(ConditionBase),
}

/// Parse sequence of SQL statements, divided by semicolons or newlines
// named!(pub query_list<&[u8], Vec<SqlQuery> >,
//    many1!(map_res!(selection, |s| { SqlQuery::Select(s) }))
// );

pub fn parse_query(input: &str) -> Result<SqlQuery, &str> {
    // we process all queries in lowercase to avoid having to deal with capitalization in the
    // parser.
    let q_bytes = String::from(input).into_bytes();

    // TODO(malte): appropriately pass through errors from nom
    match insertion(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::Insert(o)),
        _ => (),
    };

    match selection(&q_bytes) {
        IResult::Done(_, o) => return Ok(SqlQuery::Select(o)),
        _ => (),
    };

    Err("failed to parse query")
}
<|endoftext|>


<commit_before>//! The parser functionality

use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::*;

use std::convert::AsRef;
use std::ffi::c_void;
use std::ffi::{CStr, CString};
use std::fmt;
use std::fs;
use std::io;
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::slice;
use std::str;

enum XmlParserOption {
  Recover = 1, // Relaxed parsing
  // XML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // XML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // XML_PARSE_NOBLANKS = 256, // remove blank nodes
                // XML_PARSE_NONET = 2048, // Forbid network access
                // XML_PARSE_NOIMPLIED = 8192, // Do not add implied Xml/body... elements
                // XML_PARSE_COMPACT = 65536, // compact small text nodes
                // XML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

enum HtmlParserOption {
  Recover = 1, // Relaxed parsing
  // HTML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // HTML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // HTML_PARSE_NOBLANKS = 256, // remove blank nodes
                // HTML_PARSE_NONET = 2048, // Forbid network access
                // HTML_PARSE_NOIMPLIED = 8192, // Do not add implied html/body... elements
                // HTML_PARSE_COMPACT = 65536, // compact small text nodes
                // HTML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

///Parser Errors
pub enum XmlParseError {
  ///Parsing returned a null pointer as document pointer
  GotNullPointer,
  ///Could not open file error.
  FileOpenError,
  ///Document too large for libxml2.
  DocumentTooLarge,
}

impl fmt::Debug for XmlParseError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      XmlParseError::GotNullPointer => write!(f, "Got a Null pointer"),
      XmlParseError::FileOpenError => write!(f, "Unable to open path to file."),
      XmlParseError::DocumentTooLarge => write!(f, "Document too large for i32."),
    }
  }
}

/// Default encoding when not provided.
const DEFAULT_ENCODING: *const c_char = ptr::null();

/// Default URL when not provided.
const DEFAULT_URL: *const c_char = ptr::null();

/// Open file function.
fn xml_open(filename: &str) -> io::Result<*mut c_void> {
  let ptr = Box::into_raw(Box::new(fs::File::open(filename)?));
  Ok(ptr as *mut c_void)
}

/// Read callback for an FS file.
unsafe extern "C" fn xml_read(context: *mut c_void, buffer: *mut c_char, len: c_int) -> c_int {
  // Len is always positive, typically 40-4000 bytes.
  let file = context as *mut fs::File;
  let buf = slice::from_raw_parts_mut(buffer as *mut u8, len as usize);
  match io::Read::read(&mut *file, buf) {
    Ok(v) => v as c_int,
    Err(_) => -1,
  }
}

type XmlReadCallback = unsafe extern "C" fn(*mut c_void, *mut c_char, c_int) -> c_int;

/// Close callback for an FS file.
unsafe extern "C" fn xml_close(context: *mut c_void) -> c_int {
  // Take rust ownership of the context and then drop it.
  let file = context as *mut fs::File;
  let _ = Box::from_raw(file);
  0
}

type XmlCloseCallback = unsafe extern "C" fn(*mut c_void) -> c_int;

///Convert usize to i32 safely.
fn try_usize_to_i32(value: usize) -> Result<i32, XmlParseError> {
  if cfg!(target_pointer_width = "16") {
    // Cannot safely use our value comparison, but the conversion if always safe.
    Ok(value as i32)
  } else {
    if value < i32::max_value() as usize {
      // If the value can be safely represented as a 32-bit signed integer.
      Ok(value as i32)
    } else {
      // Document too large, cannot parse using libxml2.
      Err(XmlParseError::DocumentTooLarge)
    }
  }
}

#[derive(PartialEq)]
/// Enum for the parse formats supported by libxml2
pub enum ParseFormat {
  /// Strict parsing for XML
  XML,
  /// Relaxed parsing for HTML
  HTML,
}
/// Parsing API wrapper for libxml2
pub struct Parser {
  /// The `ParseFormat` for this parser
  pub format: ParseFormat,
}
impl Default for Parser {
  /// Create a parser for XML documents
  fn default() -> Self {
    Parser {
      format: ParseFormat::XML,
    }
  }
}
impl Parser {
  /// Create a parser for HTML documents
  pub fn default_html() -> Self {
    Parser {
      format: ParseFormat::HTML,
    }
  }

  /// Parses the XML/HTML file `filename` to generate a new `Document`
  pub fn parse_file(&self, filename: &str) -> Result<Document, XmlParseError> {
    self.parse_file_with_encoding(filename, None)
  }

  /// Parses the XML/HTML file `filename` with a manually-specified encoding
  /// to generate a new `Document`
  pub fn parse_file_with_encoding(
    &self,
    filename: &str,
    encoding: Option<&str>,
  ) -> Result<Document, XmlParseError> {
    // Create extern C callbacks for to read and close a Rust file through
    // a void pointer.
    let ioread: Option<XmlReadCallback> = Some(xml_read);
    let ioclose: Option<XmlCloseCallback> = Some(xml_close);
    let ioctx = match xml_open(filename) {
      Ok(v) => v,
      Err(_) => return Err(XmlParseError::FileOpenError),
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;

    unsafe {
      xmlKeepBlanksDefault(1);
    }
    match self.format {
      ParseFormat::XML => {
        let options: i32 = XmlParserOption::Recover as i32
          + XmlParserOption::Noerror as i32
          + XmlParserOption::Nowarning as i32;
        unsafe {
          let doc_ptr = xmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
      ParseFormat::HTML => {
        // TODO: Allow user-specified options later on
        let options: i32 = HtmlParserOption::Recover as i32
          + HtmlParserOption::Noerror as i32
          + HtmlParserOption::Nowarning as i32;
        unsafe {
          let doc_ptr = htmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
    }
  }

  ///Parses the XML/HTML bytes `input` to generate a new `Document`
  pub fn parse_string<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> Result<Document, XmlParseError> {
    self.parse_string_with_encoding(input, None)
  }

  ///Parses the XML/HTML bytes `input` with a manually-specified
  ///encoding to generate a new `Document`
  pub fn parse_string_with_encoding<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    encoding: Option<&str>,
  ) -> Result<Document, XmlParseError> {
    // Process input bytes.
    let input_bytes = input.as_ref();
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = try_usize_to_i32(input_bytes.len())?;

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;
    match self.format {
      ParseFormat::XML => unsafe {
        let options: i32 = XmlParserOption::Recover as i32
          + XmlParserOption::Noerror as i32
          + XmlParserOption::Nowarning as i32;
        let docptr = xmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => unsafe {
        let options: i32 = HtmlParserOption::Recover as i32
          + HtmlParserOption::Noerror as i32
          + HtmlParserOption::Nowarning as i32;
        let docptr = htmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
    }
  }

  /// Checks a string for well-formedness.
  pub fn is_well_formed_html<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> bool {
    self.is_well_formed_html_with_encoding(input, None)
  }

  /// Checks a string for well-formedness with manually-specified encoding.
  /// IMPORTANT: This function is currently implemented in a HACKY way, to ignore invalid errors for HTML5 elements (such as <math>)
  ///            this means you should NEVER USE IT WHILE THREADING, it is CERTAIN TO BREAK
  ///
  /// Help is welcome in implementing it correctly.
  pub fn is_well_formed_html_with_encoding<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    encoding: Option<&str>,
  ) -> bool {
    // Process input string.
    let input_bytes = input.as_ref();
    if input_bytes.is_empty() {
      return false;
    }
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = match try_usize_to_i32(input_bytes.len()) {
      Ok(v) => v,
      Err(_) => return false,
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;
    // disable generic error lines from libxml2
    match self.format {
      ParseFormat::XML => false, // TODO: Add support for XML at some point
      ParseFormat::HTML => unsafe {
        let ctxt = htmlNewParserCtxt();
        setWellFormednessHandler(ctxt);
        let docptr = htmlCtxtReadMemory(ctxt, input_ptr, input_len, url_ptr, encoding_ptr, 10_596); // htmlParserOption = 4+32+64+256+2048+8192
        let well_formed_final = if htmlWellFormed(ctxt) {
          // Basic well-formedness passes, let's check if we have an <html> element as root too
          if !docptr.is_null() {
            let node_ptr = xmlDocGetRootElement(docptr);
            let name_ptr = xmlNodeGetName(node_ptr);
            if name_ptr.is_null() {
              false
            }
            //empty string
            else {
              let c_root_name = CStr::from_ptr(name_ptr);
              let root_name = str::from_utf8(c_root_name.to_bytes()).unwrap().to_owned();
              root_name == "html"
            }
          } else {
            false
          }
        } else {
          false
        };

        if !ctxt.is_null() {
          htmlFreeParserCtxt(ctxt);
        }
        if !docptr.is_null() {
          xmlFreeDoc(docptr);
        }
        well_formed_final
      },
    }
  }
}
<commit_msg>minor clippy lint<commit_after>//! The parser functionality

use crate::bindings::*;
use crate::c_helpers::*;
use crate::tree::*;

use std::convert::AsRef;
use std::ffi::c_void;
use std::ffi::{CStr, CString};
use std::fmt;
use std::fs;
use std::io;
use std::os::raw::{c_char, c_int};
use std::ptr;
use std::slice;
use std::str;

enum XmlParserOption {
  Recover = 1, // Relaxed parsing
  // XML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // XML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // XML_PARSE_NOBLANKS = 256, // remove blank nodes
                // XML_PARSE_NONET = 2048, // Forbid network access
                // XML_PARSE_NOIMPLIED = 8192, // Do not add implied Xml/body... elements
                // XML_PARSE_COMPACT = 65536, // compact small text nodes
                // XML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

enum HtmlParserOption {
  Recover = 1, // Relaxed parsing
  // HTML_PARSE_NODEFDTD = 4, // do not default a doctype if not found
  Noerror = 32, // suppress error reports
  Nowarning = 64, // suppress warning reports
                // HTML_PARSE_PEDANTIC = 128, // pedantic error reporting
                // HTML_PARSE_NOBLANKS = 256, // remove blank nodes
                // HTML_PARSE_NONET = 2048, // Forbid network access
                // HTML_PARSE_NOIMPLIED = 8192, // Do not add implied html/body... elements
                // HTML_PARSE_COMPACT = 65536, // compact small text nodes
                // HTML_PARSE_IGNORE_ENC = 2097152, // ignore internal document encoding hint
}

///Parser Errors
pub enum XmlParseError {
  ///Parsing returned a null pointer as document pointer
  GotNullPointer,
  ///Could not open file error.
  FileOpenError,
  ///Document too large for libxml2.
  DocumentTooLarge,
}

impl fmt::Debug for XmlParseError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      XmlParseError::GotNullPointer => write!(f, "Got a Null pointer"),
      XmlParseError::FileOpenError => write!(f, "Unable to open path to file."),
      XmlParseError::DocumentTooLarge => write!(f, "Document too large for i32."),
    }
  }
}

/// Default encoding when not provided.
const DEFAULT_ENCODING: *const c_char = ptr::null();

/// Default URL when not provided.
const DEFAULT_URL: *const c_char = ptr::null();

/// Open file function.
fn xml_open(filename: &str) -> io::Result<*mut c_void> {
  let ptr = Box::into_raw(Box::new(fs::File::open(filename)?));
  Ok(ptr as *mut c_void)
}

/// Read callback for an FS file.
unsafe extern "C" fn xml_read(context: *mut c_void, buffer: *mut c_char, len: c_int) -> c_int {
  // Len is always positive, typically 40-4000 bytes.
  let file = context as *mut fs::File;
  let buf = slice::from_raw_parts_mut(buffer as *mut u8, len as usize);
  match io::Read::read(&mut *file, buf) {
    Ok(v) => v as c_int,
    Err(_) => -1,
  }
}

type XmlReadCallback = unsafe extern "C" fn(*mut c_void, *mut c_char, c_int) -> c_int;

/// Close callback for an FS file.
unsafe extern "C" fn xml_close(context: *mut c_void) -> c_int {
  // Take rust ownership of the context and then drop it.
  let file = context as *mut fs::File;
  let _ = Box::from_raw(file);
  0
}

type XmlCloseCallback = unsafe extern "C" fn(*mut c_void) -> c_int;

///Convert usize to i32 safely.
fn try_usize_to_i32(value: usize) -> Result<i32, XmlParseError> {
  if cfg!(target_pointer_width = "16") || (value < i32::max_value() as usize) {
    // Cannot safely use our value comparison, but the conversion if always safe.
    // Or, if the value can be safely represented as a 32-bit signed integer.
    Ok(value as i32)
  } else {
    // Document too large, cannot parse using libxml2.
    Err(XmlParseError::DocumentTooLarge)
  }
}

#[derive(PartialEq)]
/// Enum for the parse formats supported by libxml2
pub enum ParseFormat {
  /// Strict parsing for XML
  XML,
  /// Relaxed parsing for HTML
  HTML,
}
/// Parsing API wrapper for libxml2
pub struct Parser {
  /// The `ParseFormat` for this parser
  pub format: ParseFormat,
}
impl Default for Parser {
  /// Create a parser for XML documents
  fn default() -> Self {
    Parser {
      format: ParseFormat::XML,
    }
  }
}
impl Parser {
  /// Create a parser for HTML documents
  pub fn default_html() -> Self {
    Parser {
      format: ParseFormat::HTML,
    }
  }

  /// Parses the XML/HTML file `filename` to generate a new `Document`
  pub fn parse_file(&self, filename: &str) -> Result<Document, XmlParseError> {
    self.parse_file_with_encoding(filename, None)
  }

  /// Parses the XML/HTML file `filename` with a manually-specified encoding
  /// to generate a new `Document`
  pub fn parse_file_with_encoding(
    &self,
    filename: &str,
    encoding: Option<&str>,
  ) -> Result<Document, XmlParseError> {
    // Create extern C callbacks for to read and close a Rust file through
    // a void pointer.
    let ioread: Option<XmlReadCallback> = Some(xml_read);
    let ioclose: Option<XmlCloseCallback> = Some(xml_close);
    let ioctx = match xml_open(filename) {
      Ok(v) => v,
      Err(_) => return Err(XmlParseError::FileOpenError),
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;

    unsafe {
      xmlKeepBlanksDefault(1);
    }
    match self.format {
      ParseFormat::XML => {
        let options: i32 = XmlParserOption::Recover as i32
          + XmlParserOption::Noerror as i32
          + XmlParserOption::Nowarning as i32;
        unsafe {
          let doc_ptr = xmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
      ParseFormat::HTML => {
        // TODO: Allow user-specified options later on
        let options: i32 = HtmlParserOption::Recover as i32
          + HtmlParserOption::Noerror as i32
          + HtmlParserOption::Nowarning as i32;
        unsafe {
          let doc_ptr = htmlReadIO(ioread, ioclose, ioctx, url_ptr, encoding_ptr, options);
          if doc_ptr.is_null() {
            Err(XmlParseError::GotNullPointer)
          } else {
            Ok(Document::new_ptr(doc_ptr))
          }
        }
      }
    }
  }

  ///Parses the XML/HTML bytes `input` to generate a new `Document`
  pub fn parse_string<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> Result<Document, XmlParseError> {
    self.parse_string_with_encoding(input, None)
  }

  ///Parses the XML/HTML bytes `input` with a manually-specified
  ///encoding to generate a new `Document`
  pub fn parse_string_with_encoding<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    encoding: Option<&str>,
  ) -> Result<Document, XmlParseError> {
    // Process input bytes.
    let input_bytes = input.as_ref();
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = try_usize_to_i32(input_bytes.len())?;

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;
    match self.format {
      ParseFormat::XML => unsafe {
        let options: i32 = XmlParserOption::Recover as i32
          + XmlParserOption::Noerror as i32
          + XmlParserOption::Nowarning as i32;
        let docptr = xmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
      ParseFormat::HTML => unsafe {
        let options: i32 = HtmlParserOption::Recover as i32
          + HtmlParserOption::Noerror as i32
          + HtmlParserOption::Nowarning as i32;
        let docptr = htmlReadMemory(input_ptr, input_len, url_ptr, encoding_ptr, options);
        if docptr.is_null() {
          Err(XmlParseError::GotNullPointer)
        } else {
          Ok(Document::new_ptr(docptr))
        }
      },
    }
  }

  /// Checks a string for well-formedness.
  pub fn is_well_formed_html<Bytes: AsRef<[u8]>>(&self, input: Bytes) -> bool {
    self.is_well_formed_html_with_encoding(input, None)
  }

  /// Checks a string for well-formedness with manually-specified encoding.
  /// IMPORTANT: This function is currently implemented in a HACKY way, to ignore invalid errors for HTML5 elements (such as <math>)
  ///            this means you should NEVER USE IT WHILE THREADING, it is CERTAIN TO BREAK
  ///
  /// Help is welcome in implementing it correctly.
  pub fn is_well_formed_html_with_encoding<Bytes: AsRef<[u8]>>(
    &self,
    input: Bytes,
    encoding: Option<&str>,
  ) -> bool {
    // Process input string.
    let input_bytes = input.as_ref();
    if input_bytes.is_empty() {
      return false;
    }
    let input_ptr = input_bytes.as_ptr() as *const c_char;
    let input_len = match try_usize_to_i32(input_bytes.len()) {
      Ok(v) => v,
      Err(_) => return false,
    };

    // Process encoding.
    let encoding_cstring: Option<CString> = encoding.map(|v| CString::new(v).unwrap());
    let encoding_ptr = match encoding_cstring {
      Some(v) => v.as_ptr(),
      None => DEFAULT_ENCODING,
    };

    // Process url.
    let url_ptr = DEFAULT_URL;
    // disable generic error lines from libxml2
    match self.format {
      ParseFormat::XML => false, // TODO: Add support for XML at some point
      ParseFormat::HTML => unsafe {
        let ctxt = htmlNewParserCtxt();
        setWellFormednessHandler(ctxt);
        let docptr = htmlCtxtReadMemory(ctxt, input_ptr, input_len, url_ptr, encoding_ptr, 10_596); // htmlParserOption = 4+32+64+256+2048+8192
        let well_formed_final = if htmlWellFormed(ctxt) {
          // Basic well-formedness passes, let's check if we have an <html> element as root too
          if !docptr.is_null() {
            let node_ptr = xmlDocGetRootElement(docptr);
            let name_ptr = xmlNodeGetName(node_ptr);
            if name_ptr.is_null() {
              false
            }
            //empty string
            else {
              let c_root_name = CStr::from_ptr(name_ptr);
              let root_name = str::from_utf8(c_root_name.to_bytes()).unwrap().to_owned();
              root_name == "html"
            }
          } else {
            false
          }
        } else {
          false
        };

        if !ctxt.is_null() {
          htmlFreeParserCtxt(ctxt);
        }
        if !docptr.is_null() {
          xmlFreeDoc(docptr);
        }
        well_formed_final
      },
    }
  }
}
<|endoftext|>