<commit_before>extern crate pb_rs;
extern crate prost_build;
extern crate protobuf_codegen_pure;

use pb_rs::types::{RpcService, Config, FileDescriptor};
use std::path::{Path, PathBuf};
use std::{env, fs};
use std::io::Write;

fn generate_rpc_add_1_test<W: Write + ?Sized>(rpc: &RpcService, w: &mut W) -> Result<(), pb_rs::errors::Error> {
    /* Example:
        trait <service> {
            fn <func>(&self, arg: &<arg>) -> Result<<ret>, failure::Error>;
        }
    */

    writeln!(w, "\npub trait {SERVICE} {{", SERVICE = rpc.service_name)?;
    for func in rpc.functions.iter() {
        writeln!(w, "   fn {FUNC}(&self, arg: &{ARG}) -> std::result::Result<{RET}, failure::Error>;",
            FUNC = func.name, ARG = func.arg, RET = func.ret)?;
    }
    writeln!(w, "}}\n")?;

    Ok(())
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();

    // protobuf
    protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
        out_dir: "src",
        input: &["src/perftest_data.proto"],
        includes: &["src"],
        customize: protobuf_codegen_pure::Customize {
            ..Default::default()
        },
    })
    .expect("protoc");

    // quick-protobuf
    let quick_dest = Path::new(&out_dir).join("perftest_data_quick.rs");
    let config = Config {
        in_file: PathBuf::from("src/perftest_data.proto"),
        out_file: quick_dest,
        single_module: true,
        import_search_path: vec![PathBuf::from("src")],
        no_output: false,
        error_cycle: false,
        headers: false,
        dont_use_cow: false,
        custom_struct_derive: vec![],
        custom_rpc_generator: Box::new(|rpc, writer| generate_rpc_add_1_test(rpc, writer))
    };
    FileDescriptor::write_proto(&config).unwrap();

    // prost
    let old = ::std::env::var("OUT_DIR");
    env::set_var("OUT_DIR", ".");
    prost_build::compile_protos(&["src/perftest_data.proto"], &["src"]).unwrap();
    let _ = old.map(|val| env::set_var("OUT_DIR", &val));
    fs::rename("perftest_data.rs", "src/perftest_data_prost.rs").unwrap();
}
<commit_msg>Added custom_includes to Config generation<commit_after>extern crate pb_rs;
extern crate prost_build;
extern crate protobuf_codegen_pure;

use pb_rs::types::{RpcService, Config, FileDescriptor};
use std::path::{Path, PathBuf};
use std::{env, fs};
use std::io::Write;

fn generate_rpc_test<W: Write + ?Sized>(rpc: &RpcService, w: &mut W) -> Result<(), pb_rs::errors::Error> {
    /* Example:
        trait <service> {
            fn <func>(&self, arg: &<arg>) -> Result<<ret>, failure::Error>;
        }
    */

    writeln!(w, "\npub trait {SERVICE} {{", SERVICE = rpc.service_name)?;
    for func in rpc.functions.iter() {
        writeln!(w, "   fn {FUNC}(&self, arg: &{ARG}) -> std::result::Result<{RET}, failure::Error>;",
            FUNC = func.name, ARG = func.arg, RET = func.ret)?;
    }
    writeln!(w, "}}\n")?;

    Ok(())
}

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();

    // protobuf
    protobuf_codegen_pure::run(protobuf_codegen_pure::Args {
        out_dir: "src",
        input: &["src/perftest_data.proto"],
        includes: &["src"],
        customize: protobuf_codegen_pure::Customize {
            ..Default::default()
        },
    })
    .expect("protoc");

    // quick-protobuf
    let quick_dest = Path::new(&out_dir).join("perftest_data_quick.rs");
    let config = Config {
        in_file: PathBuf::from("src/perftest_data.proto"),
        out_file: quick_dest,
        single_module: true,
        import_search_path: vec![PathBuf::from("src")],
        no_output: false,
        error_cycle: false,
        headers: false,
        dont_use_cow: false,
        custom_struct_derive: vec![],
        custom_rpc_generator: Box::new(|rpc, writer| generate_rpc_test(rpc, writer)),
        custom_includes: Vec::new()
    };
    FileDescriptor::write_proto(&config).unwrap();

    // prost
    let old = ::std::env::var("OUT_DIR");
    env::set_var("OUT_DIR", ".");
    prost_build::compile_protos(&["src/perftest_data.proto"], &["src"]).unwrap();
    let _ = old.map(|val| env::set_var("OUT_DIR", &val));
    fs::rename("perftest_data.rs", "src/perftest_data_prost.rs").unwrap();
}
<|endoftext|>


<commit_before>#![feature(phase, macro_rules)]

#[phase(plugin)]
extern crate phf_mac;
extern crate phf;

mod map {
    use std::collections::{HashMap, HashSet};
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::Map<&'static str, int> = phf_map!(
        "foo" => 10,
    );

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::Map<&'static str, int> = phf_map!(
        "foo" => 10
    );

    #[test]
    fn test_two() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        assert!(Some(&10) == MAP.find(&("foo")));
        assert!(Some(&11) == MAP.find(&("bar")));
        assert_eq!(None, MAP.find(&("asdf")));
        assert_eq!(2, MAP.len());
    }

    #[test]
    fn test_entries() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        let hash = MAP.entries().map(|&e| e).collect::<HashMap<_, int>>();
        assert!(Some(&10) == hash.find(&("foo")));
        assert!(Some(&11) == hash.find(&("bar")));
        assert_eq!(2, hash.len());
    }

    #[test]
    fn test_keys() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        let hash = MAP.keys().map(|&e| e).collect::<HashSet<_>>();
        assert!(hash.contains(&("foo")));
        assert!(hash.contains(&("bar")));
        assert_eq!(2, hash.len());
    }

    #[test]
    fn test_values() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        let hash = MAP.values().map(|&e| e).collect::<HashSet<int>>();
        assert!(hash.contains(&10));
        assert!(hash.contains(&11));
        assert_eq!(2, hash.len());
    }

    #[test]
    fn test_large() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
            "b" => 1,
            "c" => 2,
            "d" => 3,
            "e" => 4,
            "f" => 5,
            "g" => 6,
            "h" => 7,
            "i" => 8,
            "j" => 9,
            "k" => 10,
            "l" => 11,
            "m" => 12,
            "n" => 13,
            "o" => 14,
            "p" => 15,
            "q" => 16,
            "r" => 17,
            "s" => 18,
            "t" => 19,
            "u" => 20,
            "v" => 21,
            "w" => 22,
            "x" => 23,
            "y" => 24,
            "z" => 25,
        );
        assert!(MAP.find(&("a")) == Some(&0));
    }

    #[test]
    fn test_macro_key() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            concat!("foo", "bar") => 1
        );
        assert!(Some(&1) == MAP.find(&("foobar")));
    }

    #[test]
    fn test_non_static_str_key() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
        );
        assert_eq!(Some(&0), MAP.find_equiv("a".to_string()[]));
    }

    #[test]
    fn test_index_ok() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
        );
        assert_eq!(0, MAP["a"]);
    }

    #[test]
    #[should_fail]
    fn test_index_fail() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
        );
        MAP["b"];
    }

    macro_rules! test_key_type(
        ($t:ty, $($k:expr => $v:expr),+) => ({
            static MAP: phf::Map<$t, int> = phf_map! {
                $($k => $v),+
            };
            $(
                assert_eq!(Some(&$v), MAP.find(&$k));
            )+
        })
    )

    #[test]
    fn test_binary_keys() {
        test_key_type!(&'static [u8], b"hello" => 0, b"world" => 1);
    }

    #[test]
    fn test_byte_keys() {
        test_key_type!(u8, b'a' => 0, b'b' => 1);
    }

    #[test]
    fn test_char_keys() {
        test_key_type!(char, 'a' => 0, 'b' => 1);
    }

    #[test]
    fn test_i8_keys() {
        test_key_type!(i8, 0i8 => 0, 1i8 => 1);
    }

    #[test]
    fn test_i16_keys() {
        test_key_type!(i16, 0i16 => 0, 1i16 => 1);
    }

    #[test]
    fn test_i32_keys() {
        test_key_type!(i32, 0i32 => 0, 1i32 => 1);
    }

    #[test]
    fn test_i64_keys() {
        test_key_type!(i64, 0i64 => 0, 1i64 => 1);
    }

    #[test]
    fn test_u8_keys() {
        test_key_type!(u8, 0u8 => 0, 1u8 => 1);
    }

    #[test]
    fn test_u16_keys() {
        test_key_type!(u16, 0u16 => 0, 1u16 => 1);
    }

    #[test]
    fn test_u32_keys() {
        test_key_type!(u32, 0u32 => 0, 1u32 => 1);
    }

    #[test]
    fn test_u64_keys() {
        test_key_type!(u64, 0u64 => 0, 1u64 => 1);
    }

    #[test]
    fn test_bool_keys() {
        test_key_type!(bool, false => 0, true => 1);
    }
}

mod set {
    use std::collections::HashSet;
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::Set<&'static str> = phf_set! {
        "foo",
    };

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::Set<&'static str> = phf_set! {
        "foo"
    };

    #[test]
    fn test_two() {
        static SET: phf::Set<&'static str> = phf_set! {
            "hello",
            "world",
        };
        assert!(SET.contains(&"hello"));
        assert!(SET.contains(&"world"));
        assert!(!SET.contains(&"foo"));
        assert_eq!(2, SET.len());
    }

    #[test]
    fn test_iter() {
        static SET: phf::Set<&'static str> = phf_set! {
            "hello",
            "world",
        };
        let set = SET.iter().map(|e| *e).collect::<HashSet<_>>();
        assert!(set.contains(&"hello"));
        assert!(set.contains(&"world"));
        assert_eq!(2, set.len());
    }

    #[test]
    fn test_non_static_str_contains() {
        static SET: phf::Set<&'static str> = phf_set! {
            "hello",
            "world",
        };
        assert!(SET.contains_equiv("hello".to_string()[]));
    }
}

mod ordered_map {
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
        "foo" => 10,
    );

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
        "foo" => 10
    );

    #[test]
    fn test_two() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
        );
        assert!(Some(&10) == MAP.find(&"foo"));
        assert!(Some(&11) == MAP.find(&"bar"));
        assert_eq!(None, MAP.find(&"asdf"));
        assert_eq!(2, MAP.len());
    }

    #[test]
    fn test_find_index() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 5,
            "bar" => 5,
            "baz" => 5,
        );
        assert_eq!(Some(0), MAP.find_index(&"foo"));
        assert_eq!(Some(2), MAP.find_index(&"baz"));
        assert_eq!(None, MAP.find_index(&"xyz"));
        assert_eq!(&"baz", MAP.keys().idx(MAP.find_index(&"baz").unwrap()).unwrap());

        assert_eq!(Some(0), MAP.find_index_equiv("foo".to_string()[]));
        assert_eq!(Some(2), MAP.find_index_equiv("baz".to_string()[]));
        assert_eq!(None, MAP.find_index_equiv("xyz".to_string()[]));
    }

    #[test]
    fn test_entries() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
            "baz" => 12,
        );
        let vec = MAP.entries().map(|&(k, v)| (k, v)).collect::<Vec<_>>();
        assert_eq!(vec, vec!(("foo", 10i), ("bar", 11), ("baz", 12)));
    }

    #[test]
    fn test_keys() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
            "baz" => 12,
        );
        let vec = MAP.keys().map(|&e| e).collect::<Vec<_>>();
        assert_eq!(vec, vec!("foo", "bar", "baz"));
    }

    #[test]
    fn test_values() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
            "baz" => 12,
        );
        let vec = MAP.values().map(|&v| v).collect::<Vec<_>>();
        assert_eq!(vec, vec!(10i, 11, 12));
    }

    #[test]
    fn test_index_ok() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "a" => 0,
        );
        assert_eq!(0, MAP["a"]);
    }

    #[test]
    #[should_fail]
    fn test_index_fail() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "a" => 0,
        );
        MAP["b"];
    }

    #[test]
    fn test_non_static_str_key() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "a" => 0,
        );
        assert_eq!(Some(&0), MAP.find_equiv("a".to_string()[]));
    }
}

mod ordered_set {
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::OrderedSet<&'static str> = phf_ordered_set! {
        "foo",
    };

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::OrderedSet<&'static str> = phf_ordered_set! {
        "foo"
    };

    #[test]
    fn test_two() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "hello",
            "there",
            "world",
        };
        assert!(SET.contains(&"hello"));
        assert!(SET.contains(&"there"));
        assert!(SET.contains(&"world"));
        assert!(!SET.contains(&"foo"));
        assert_eq!(3, SET.len());
    }

    #[test]
    fn test_find_index() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "foo",
            "bar",
            "baz",
        };
        assert_eq!(Some(0), SET.find_index(&"foo"));
        assert_eq!(Some(2), SET.find_index(&"baz"));
        assert_eq!(None, SET.find_index(&"xyz"));
        assert_eq!(&"baz", SET.iter().idx(SET.find_index(&"baz").unwrap()).unwrap());

        assert_eq!(Some(0), SET.find_index_equiv("foo".to_string()[]));
        assert_eq!(Some(2), SET.find_index_equiv("baz".to_string()[]));
        assert_eq!(None, SET.find_index_equiv("xyz".to_string()[]));
    }

    #[test]
    fn test_iter() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "hello",
            "there",
            "world",
        };
        let vec = SET.iter().map(|&e| e).collect::<Vec<_>>();
        assert_eq!(vec, vec!("hello", "there", "world"));
    }

    #[test]
    fn test_non_static_str_contains() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "hello",
            "world",
        };
        assert!(SET.contains_equiv("hello".to_string()[]));
    }
}
<commit_msg>Fix some deprecation warnings<commit_after>#![feature(phase, macro_rules)]

#[phase(plugin)]
extern crate phf_mac;
extern crate phf;

mod map {
    use std::collections::{HashMap, HashSet};
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::Map<&'static str, int> = phf_map!(
        "foo" => 10,
    );

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::Map<&'static str, int> = phf_map!(
        "foo" => 10
    );

    #[test]
    fn test_two() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        assert!(Some(&10) == MAP.find(&("foo")));
        assert!(Some(&11) == MAP.find(&("bar")));
        assert_eq!(None, MAP.find(&("asdf")));
        assert_eq!(2, MAP.len());
    }

    #[test]
    fn test_entries() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        let hash = MAP.entries().map(|&e| e).collect::<HashMap<_, int>>();
        assert!(Some(&10) == hash.get(&("foo")));
        assert!(Some(&11) == hash.get(&("bar")));
        assert_eq!(2, hash.len());
    }

    #[test]
    fn test_keys() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        let hash = MAP.keys().map(|&e| e).collect::<HashSet<_>>();
        assert!(hash.contains(&("foo")));
        assert!(hash.contains(&("bar")));
        assert_eq!(2, hash.len());
    }

    #[test]
    fn test_values() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "foo" => 10,
            "bar" => 11,
        );
        let hash = MAP.values().map(|&e| e).collect::<HashSet<int>>();
        assert!(hash.contains(&10));
        assert!(hash.contains(&11));
        assert_eq!(2, hash.len());
    }

    #[test]
    fn test_large() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
            "b" => 1,
            "c" => 2,
            "d" => 3,
            "e" => 4,
            "f" => 5,
            "g" => 6,
            "h" => 7,
            "i" => 8,
            "j" => 9,
            "k" => 10,
            "l" => 11,
            "m" => 12,
            "n" => 13,
            "o" => 14,
            "p" => 15,
            "q" => 16,
            "r" => 17,
            "s" => 18,
            "t" => 19,
            "u" => 20,
            "v" => 21,
            "w" => 22,
            "x" => 23,
            "y" => 24,
            "z" => 25,
        );
        assert!(MAP.find(&("a")) == Some(&0));
    }

    #[test]
    fn test_macro_key() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            concat!("foo", "bar") => 1
        );
        assert!(Some(&1) == MAP.find(&("foobar")));
    }

    #[test]
    fn test_non_static_str_key() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
        );
        assert_eq!(Some(&0), MAP.find_equiv("a".to_string()[]));
    }

    #[test]
    fn test_index_ok() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
        );
        assert_eq!(0, MAP["a"]);
    }

    #[test]
    #[should_fail]
    fn test_index_fail() {
        static MAP: phf::Map<&'static str, int> = phf_map!(
            "a" => 0,
        );
        MAP["b"];
    }

    macro_rules! test_key_type(
        ($t:ty, $($k:expr => $v:expr),+) => ({
            static MAP: phf::Map<$t, int> = phf_map! {
                $($k => $v),+
            };
            $(
                assert_eq!(Some(&$v), MAP.find(&$k));
            )+
        })
    )

    #[test]
    fn test_binary_keys() {
        test_key_type!(&'static [u8], b"hello" => 0, b"world" => 1);
    }

    #[test]
    fn test_byte_keys() {
        test_key_type!(u8, b'a' => 0, b'b' => 1);
    }

    #[test]
    fn test_char_keys() {
        test_key_type!(char, 'a' => 0, 'b' => 1);
    }

    #[test]
    fn test_i8_keys() {
        test_key_type!(i8, 0i8 => 0, 1i8 => 1);
    }

    #[test]
    fn test_i16_keys() {
        test_key_type!(i16, 0i16 => 0, 1i16 => 1);
    }

    #[test]
    fn test_i32_keys() {
        test_key_type!(i32, 0i32 => 0, 1i32 => 1);
    }

    #[test]
    fn test_i64_keys() {
        test_key_type!(i64, 0i64 => 0, 1i64 => 1);
    }

    #[test]
    fn test_u8_keys() {
        test_key_type!(u8, 0u8 => 0, 1u8 => 1);
    }

    #[test]
    fn test_u16_keys() {
        test_key_type!(u16, 0u16 => 0, 1u16 => 1);
    }

    #[test]
    fn test_u32_keys() {
        test_key_type!(u32, 0u32 => 0, 1u32 => 1);
    }

    #[test]
    fn test_u64_keys() {
        test_key_type!(u64, 0u64 => 0, 1u64 => 1);
    }

    #[test]
    fn test_bool_keys() {
        test_key_type!(bool, false => 0, true => 1);
    }
}

mod set {
    use std::collections::HashSet;
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::Set<&'static str> = phf_set! {
        "foo",
    };

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::Set<&'static str> = phf_set! {
        "foo"
    };

    #[test]
    fn test_two() {
        static SET: phf::Set<&'static str> = phf_set! {
            "hello",
            "world",
        };
        assert!(SET.contains(&"hello"));
        assert!(SET.contains(&"world"));
        assert!(!SET.contains(&"foo"));
        assert_eq!(2, SET.len());
    }

    #[test]
    fn test_iter() {
        static SET: phf::Set<&'static str> = phf_set! {
            "hello",
            "world",
        };
        let set = SET.iter().map(|e| *e).collect::<HashSet<_>>();
        assert!(set.contains(&"hello"));
        assert!(set.contains(&"world"));
        assert_eq!(2, set.len());
    }

    #[test]
    fn test_non_static_str_contains() {
        static SET: phf::Set<&'static str> = phf_set! {
            "hello",
            "world",
        };
        assert!(SET.contains_equiv("hello".to_string()[]));
    }
}

mod ordered_map {
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
        "foo" => 10,
    );

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
        "foo" => 10
    );

    #[test]
    fn test_two() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
        );
        assert!(Some(&10) == MAP.find(&"foo"));
        assert!(Some(&11) == MAP.find(&"bar"));
        assert_eq!(None, MAP.find(&"asdf"));
        assert_eq!(2, MAP.len());
    }

    #[test]
    fn test_find_index() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 5,
            "bar" => 5,
            "baz" => 5,
        );
        assert_eq!(Some(0), MAP.find_index(&"foo"));
        assert_eq!(Some(2), MAP.find_index(&"baz"));
        assert_eq!(None, MAP.find_index(&"xyz"));
        assert_eq!(&"baz", MAP.keys().idx(MAP.find_index(&"baz").unwrap()).unwrap());

        assert_eq!(Some(0), MAP.find_index_equiv("foo".to_string()[]));
        assert_eq!(Some(2), MAP.find_index_equiv("baz".to_string()[]));
        assert_eq!(None, MAP.find_index_equiv("xyz".to_string()[]));
    }

    #[test]
    fn test_entries() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
            "baz" => 12,
        );
        let vec = MAP.entries().map(|&(k, v)| (k, v)).collect::<Vec<_>>();
        assert_eq!(vec, vec!(("foo", 10i), ("bar", 11), ("baz", 12)));
    }

    #[test]
    fn test_keys() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
            "baz" => 12,
        );
        let vec = MAP.keys().map(|&e| e).collect::<Vec<_>>();
        assert_eq!(vec, vec!("foo", "bar", "baz"));
    }

    #[test]
    fn test_values() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "foo" => 10,
            "bar" => 11,
            "baz" => 12,
        );
        let vec = MAP.values().map(|&v| v).collect::<Vec<_>>();
        assert_eq!(vec, vec!(10i, 11, 12));
    }

    #[test]
    fn test_index_ok() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "a" => 0,
        );
        assert_eq!(0, MAP["a"]);
    }

    #[test]
    #[should_fail]
    fn test_index_fail() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "a" => 0,
        );
        MAP["b"];
    }

    #[test]
    fn test_non_static_str_key() {
        static MAP: phf::OrderedMap<&'static str, int> = phf_ordered_map!(
            "a" => 0,
        );
        assert_eq!(Some(&0), MAP.find_equiv("a".to_string()[]));
    }
}

mod ordered_set {
    use phf;

    #[allow(dead_code)]
    static TRAILING_COMMA: phf::OrderedSet<&'static str> = phf_ordered_set! {
        "foo",
    };

    #[allow(dead_code)]
    static NO_TRAILING_COMMA: phf::OrderedSet<&'static str> = phf_ordered_set! {
        "foo"
    };

    #[test]
    fn test_two() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "hello",
            "there",
            "world",
        };
        assert!(SET.contains(&"hello"));
        assert!(SET.contains(&"there"));
        assert!(SET.contains(&"world"));
        assert!(!SET.contains(&"foo"));
        assert_eq!(3, SET.len());
    }

    #[test]
    fn test_find_index() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "foo",
            "bar",
            "baz",
        };
        assert_eq!(Some(0), SET.find_index(&"foo"));
        assert_eq!(Some(2), SET.find_index(&"baz"));
        assert_eq!(None, SET.find_index(&"xyz"));
        assert_eq!(&"baz", SET.iter().idx(SET.find_index(&"baz").unwrap()).unwrap());

        assert_eq!(Some(0), SET.find_index_equiv("foo".to_string()[]));
        assert_eq!(Some(2), SET.find_index_equiv("baz".to_string()[]));
        assert_eq!(None, SET.find_index_equiv("xyz".to_string()[]));
    }

    #[test]
    fn test_iter() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "hello",
            "there",
            "world",
        };
        let vec = SET.iter().map(|&e| e).collect::<Vec<_>>();
        assert_eq!(vec, vec!("hello", "there", "world"));
    }

    #[test]
    fn test_non_static_str_contains() {
        static SET: phf::OrderedSet<&'static str> = phf_ordered_set! {
            "hello",
            "world",
        };
        assert!(SET.contains_equiv("hello".to_string()[]));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use rand::SeedableRng;
use std::fmt;

/// Whether a test is expected to fail or not
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ShouldFail {
    No,
    Yes,
    YesWithMessage(&'static str),
}

/// Represents a Test in Forge
///
/// This is meant to be a super trait of the other test interfaces.
pub trait Test: Send + Sync + fmt::Debug {
    /// Returns the name of the Test
    fn name(&self) -> &'static str;

    /// Indicates if the Test should be ignored
    fn ignored(&self) -> bool {
        false
    }

    /// Indicates if the Test should fail
    fn should_fail(&self) -> ShouldFail {
        ShouldFail::No
    }
}

impl<T: Test + ?Sized> Test for &T {
    fn name(&self) -> &'static str {
        (**self).name()
    }

    fn ignored(&self) -> bool {
        (**self).ignored()
    }

    fn should_fail(&self) -> ShouldFail {
        (**self).should_fail()
    }
}

#[derive(Debug)]
pub struct CoreContext {
    rng: ::rand::rngs::StdRng,
}

impl CoreContext {
    pub fn new(rng: ::rand::rngs::StdRng) -> Self {
        Self { rng }
    }

    pub fn from_rng<R: ::rand::RngCore + ::rand::CryptoRng>(rng: R) -> Self {
        Self {
            rng: ::rand::rngs::StdRng::from_rng(rng).unwrap(),
        }
    }

    pub fn rng(&mut self) -> &mut ::rand::rngs::StdRng {
        &mut self.rng
    }
}
<commit_msg>forge: remove requirement for Test's to impl Debug<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use rand::SeedableRng;

/// Whether a test is expected to fail or not
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum ShouldFail {
    No,
    Yes,
    YesWithMessage(&'static str),
}

/// Represents a Test in Forge
///
/// This is meant to be a super trait of the other test interfaces.
pub trait Test: Send + Sync {
    /// Returns the name of the Test
    fn name(&self) -> &'static str;

    /// Indicates if the Test should be ignored
    fn ignored(&self) -> bool {
        false
    }

    /// Indicates if the Test should fail
    fn should_fail(&self) -> ShouldFail {
        ShouldFail::No
    }
}

impl<T: Test + ?Sized> Test for &T {
    fn name(&self) -> &'static str {
        (**self).name()
    }

    fn ignored(&self) -> bool {
        (**self).ignored()
    }

    fn should_fail(&self) -> ShouldFail {
        (**self).should_fail()
    }
}

#[derive(Debug)]
pub struct CoreContext {
    rng: ::rand::rngs::StdRng,
}

impl CoreContext {
    pub fn new(rng: ::rand::rngs::StdRng) -> Self {
        Self { rng }
    }

    pub fn from_rng<R: ::rand::RngCore + ::rand::CryptoRng>(rng: R) -> Self {
        Self {
            rng: ::rand::rngs::StdRng::from_rng(rng).unwrap(),
        }
    }

    pub fn rng(&mut self) -> &mut ::rand::rngs::StdRng {
        &mut self.rng
    }
}
<|endoftext|>


<commit_before>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::hash::Hash;

use scored::MinScored;
use super::visit::{
    EdgeRef,
    GraphBase,
    IntoEdges,
    VisitMap,
    Visitable,
};

use algo::Measure;

/// [Generic] A* shortest path algorithm.
///
/// Compute the length of the shortest path from `start` to `finish`.
///
/// `finish` is implicitly given via the `is_goal` callback, which should return `true` if the
/// given node is the finish node.
///
/// The function `edge_cost` should return the cost for a particular edge, which is used to compute
/// path costs.
///
/// The function `estimate_cost` should return the estimated cost to the finish for a particular
/// node, also used to compute path costs.
///
/// ```
/// use petgraph::Graph;
/// use petgraph::algo::astar;
///
/// let mut g = Graph::new();
/// let a = g.add_node((0., 0.));
/// let b = g.add_node((2., 0.));
/// let c = g.add_node((1., 1.));
/// let d = g.add_node((0., 2.));
/// let e = g.add_node((3., 3.));
/// let f = g.add_node((4., 2.));
/// let ab = g.add_edge(a, b, 2);
/// let ad = g.add_edge(a, d, 4);
/// let bc = g.add_edge(b, c, 1);
/// let bf = g.add_edge(b, f, 7);
/// let ce = g.add_edge(c, e, 5);
/// let ef = g.add_edge(e, f, 1);
/// let de = g.add_edge(d, e, 1);
///
/// let path = astar(&g, a, |finish| finish == f, |e| *e.weight(), |_| 0);
/// assert_eq!(path, Some(vec![a, d, e, f]));
/// ```
///
/// The graph should be `Visitable` and implement `IntoEdges`. Edge costs must be non-negative.
///
/// Returns a Path of subsequent `NodeId` from start to finish, if one was found.
pub fn astar<G, F, H, K, IsGoal>(graph: G, start: G::NodeId, is_goal: IsGoal,
                                     mut edge_cost: F, mut estimate_cost: H)
    -> Option<Path<G>>
    where G: IntoEdges + Visitable,
          IsGoal: Fn(G::NodeId) -> bool,
          G::NodeId: Eq + Hash,
          F: FnMut(G::EdgeRef) -> K,
          H: FnMut(G::NodeId) -> K,
          K: Measure + Copy,
{
    let mut visited = graph.visit_map();
    let mut visit_next = BinaryHeap::new();
    let mut scores = HashMap::new();
    let mut path_tracker = PathTracker::<G>::new();

    let zero_score = K::default();
    scores.insert(start, zero_score);
    visit_next.push(MinScored(estimate_cost(start), start));

    while let Some(MinScored(_, node)) = visit_next.pop() {
        if is_goal(node) {
            return Some(path_tracker.reconstruct_path_to(node));
        }

        // Don't visit the same node several times, as the first time it was visited it was using
        // the shortest available path.
        if !visited.visit(node) {
            continue
        }

        // This lookup can be unwrapped without fear of panic since the node was necessarily scored
        // before adding him to `visit_next`.
        let node_score = *scores.get(&node).unwrap();

        for edge in graph.edges(node) {
            let next = edge.target();
            if visited.is_visited(&next) {
                continue
            }

            let mut next_score = node_score + edge_cost(edge);

            match scores.entry(next) {
                Occupied(ent) => {
                    let old_score = *ent.get();
                    if next_score < old_score {
                        *ent.into_mut() = next_score;
                        path_tracker.set_predecessor(next, node);
                    } else {
                        next_score = old_score;
                    }
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    path_tracker.set_predecessor(next, node);
                }
            }

            let next_estimate_score = next_score + estimate_cost(next);
            visit_next.push(MinScored(next_estimate_score, next));
        }
    }

    None
}

pub type Path<G: GraphBase> = Vec<G::NodeId>;

struct PathTracker<G>
    where G: GraphBase,
          G::NodeId: Eq + Hash,
{
    came_from: HashMap<G::NodeId, G::NodeId>,
}

impl<G> PathTracker<G>
    where G: GraphBase,
          G::NodeId: Eq + Hash,
{
    fn new() -> PathTracker<G> {
        PathTracker {
            came_from: HashMap::new(),
        }
    }

    fn set_predecessor(&mut self, node: G::NodeId, previous: G::NodeId) {
        self.came_from.insert(node, previous);
    }

    fn reconstruct_path_to(&self, last: G::NodeId) -> Path<G> {
        let mut path = vec![last];

        let mut current = last;
        loop {
            if let Some(&previous) = self.came_from.get(&current) {
                path.push(previous);
                current = previous;
            } else {
                break
            }
        }

        path.reverse();

        path
    }
}
<commit_msg>A*: Minor fixes<commit_after>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::hash::Hash;

use scored::MinScored;
use super::visit::{
    EdgeRef,
    GraphBase,
    IntoEdges,
    VisitMap,
    Visitable,
};

use algo::Measure;

/// [Generic] A* shortest path algorithm.
///
/// Compute the length of the shortest path from `start` to `finish`.
///
/// `finish` is implicitly given via the `is_goal` callback, which should return `true` if the
/// given node is the finish node.
///
/// The function `edge_cost` should return the cost for a particular edge, which is used to compute
/// path costs.
///
/// The function `estimate_cost` should return the estimated cost to the finish for a particular
/// node, also used to compute path costs.
///
/// ```
/// use petgraph::Graph;
/// use petgraph::algo::astar;
///
/// let mut g = Graph::new();
/// let a = g.add_node((0., 0.));
/// let b = g.add_node((2., 0.));
/// let c = g.add_node((1., 1.));
/// let d = g.add_node((0., 2.));
/// let e = g.add_node((3., 3.));
/// let f = g.add_node((4., 2.));
/// g.extend_with_edges(&[
///     (a, b, 2),
///     (a, d, 4),
///     (b, c, 1),
///     (b, f, 7),
///     (c, e, 5),
///     (e, f, 1),
///     (d, e, 1),
/// ]);
///
/// let path = astar(&g, a, |finish| finish == f, |e| *e.weight(), |_| 0);
/// assert_eq!(path, Some(vec![a, d, e, f]));
/// ```
///
/// The graph should be `Visitable` and implement `IntoEdges`. Edge costs must be non-negative.
///
/// Returns a Path of subsequent `NodeId` from start to finish, if one was found.
pub fn astar<G, F, H, K, IsGoal>(graph: G, start: G::NodeId, mut is_goal: IsGoal,
                                     mut edge_cost: F, mut estimate_cost: H)
    -> Option<Vec<G::NodeId>>
    where G: IntoEdges + Visitable,
          IsGoal: FnMut(G::NodeId) -> bool,
          G::NodeId: Eq + Hash,
          F: FnMut(G::EdgeRef) -> K,
          H: FnMut(G::NodeId) -> K,
          K: Measure + Copy,
{
    let mut visited = graph.visit_map();
    let mut visit_next = BinaryHeap::new();
    let mut scores = HashMap::new();
    let mut path_tracker = PathTracker::<G>::new();

    let zero_score = K::default();
    scores.insert(start, zero_score);
    visit_next.push(MinScored(estimate_cost(start), start));

    while let Some(MinScored(_, node)) = visit_next.pop() {
        if is_goal(node) {
            return Some(path_tracker.reconstruct_path_to(node));
        }

        // Don't visit the same node several times, as the first time it was visited it was using
        // the shortest available path.
        if !visited.visit(node) {
            continue
        }

        // This lookup can be unwrapped without fear of panic since the node was necessarily scored
        // before adding him to `visit_next`.
        let node_score = *scores.get(&node).unwrap();

        for edge in graph.edges(node) {
            let next = edge.target();
            if visited.is_visited(&next) {
                continue
            }

            let mut next_score = node_score + edge_cost(edge);

            match scores.entry(next) {
                Occupied(ent) => {
                    let old_score = *ent.get();
                    if next_score < old_score {
                        *ent.into_mut() = next_score;
                        path_tracker.set_predecessor(next, node);
                    } else {
                        next_score = old_score;
                    }
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    path_tracker.set_predecessor(next, node);
                }
            }

            let next_estimate_score = next_score + estimate_cost(next);
            visit_next.push(MinScored(next_estimate_score, next));
        }
    }

    None
}

struct PathTracker<G>
    where G: GraphBase,
          G::NodeId: Eq + Hash,
{
    came_from: HashMap<G::NodeId, G::NodeId>,
}

impl<G> PathTracker<G>
    where G: GraphBase,
          G::NodeId: Eq + Hash,
{
    fn new() -> PathTracker<G> {
        PathTracker {
            came_from: HashMap::new(),
        }
    }

    fn set_predecessor(&mut self, node: G::NodeId, previous: G::NodeId) {
        self.came_from.insert(node, previous);
    }

    fn reconstruct_path_to(&self, last: G::NodeId) -> Vec<G::NodeId> {
        let mut path = vec![last];

        let mut current = last;
        loop {
            if let Some(&previous) = self.came_from.get(&current) {
                path.push(previous);
                current = previous;
            } else {
                break
            }
        }

        path.reverse();

        path
    }
}
<|endoftext|>


<commit_before>use std::{
    env,
    path::{Path, PathBuf},
};

#[cfg(feature = "snappy")]
const SNAPPY_VERSION: &'static str = "1.1.7";
const LEVELDB_VERSION: &'static str = "1.22";

#[cfg(feature = "snappy")]
fn build_snappy() -> PathBuf {
    println!("[snappy] Building");

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let dest_prefix =
        cmake::Config::new(Path::new("deps").join(format!("snappy-{}", SNAPPY_VERSION)))
            .define("BUILD_SHARED_LIBS", "OFF")
            .define("SNAPPY_BUILD_TESTS", "OFF")
            .define("HAVE_LIBZ", "OFF")
            .build();

    println!(
        "cargo:rustc-link-search=native={}",
        dest_prefix.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=snappy");

    dest_prefix
}

fn build_leveldb(snappy_prefix: Option<PathBuf>) {
    println!("[leveldb] Building");

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let mut config =
        cmake::Config::new(Path::new("deps").join(format!("leveldb-{}", LEVELDB_VERSION)));
    config
        .define("LEVELDB_BUILD_TESTS", "OFF")
        .define("LEVELDB_BUILD_BENCHMARKS", "OFF");
    if let Some(snappy_prefix) = snappy_prefix {
        env::set_var(
            "LDFLAGS",
            format!("-L{}", snappy_prefix.join("lib").display()),
        );
        config
            .define("HAVE_SNAPPY", "ON")
            .cflag(format!("-I{}", snappy_prefix.join("include").display()))
            .cxxflag(format!("-I{}", snappy_prefix.join("include").display()));
    } else {
        config.define("HAVE_SNAPPY", "OFF");
    }
    let dest_prefix = config.build();

    println!(
        "cargo:rustc-link-search=native={}",
        dest_prefix.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=leveldb");
}

fn main() {
    println!("[build] Started");

    // If we have the appropriate feature, then we build snappy.
    #[cfg(feature = "snappy")]
    let snappy_prefix = Some(build_snappy());
    #[cfg(not(feature = "snappy"))]
    let snappy_prefix: Option<PathBuf> = None;

    // Build LevelDB
    build_leveldb(snappy_prefix);

    // Link to the standard C++ library
    let target = env::var("TARGET").unwrap();
    if target.contains("apple") || target.contains("freebsd") {
        println!("cargo:rustc-link-lib=c++");
    } else if target.contains("gnu") || target.contains("netbsd") || target.contains("openbsd") {
        println!("cargo:rustc-link-lib=stdc++");
    }

    println!("[build] Finished");
}
<commit_msg>Use predictable CMake LIBDIR<commit_after>use std::{
    env,
    path::{Path, PathBuf},
};

#[cfg(feature = "snappy")]
const SNAPPY_VERSION: &'static str = "1.1.7";
const LEVELDB_VERSION: &'static str = "1.22";
/// Directory name within `$OUT_DIR` where the static libraries should be built.
const LIBDIR: &'static str = "lib";

#[cfg(feature = "snappy")]
fn build_snappy() -> PathBuf {
    println!("[snappy] Building");

    let outdir = env::var("OUT_DIR").unwrap();
    let libdir = Path::new(&outdir).join(LIBDIR);

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let dest_prefix =
        cmake::Config::new(Path::new("deps").join(format!("snappy-{}", SNAPPY_VERSION)))
            .define("BUILD_SHARED_LIBS", "OFF")
            .define("SNAPPY_BUILD_TESTS", "OFF")
            .define("HAVE_LIBZ", "OFF")
            .define("CMAKE_INSTALL_LIBDIR", &libdir)
            .build();

    assert_eq!(
        dest_prefix.join(LIBDIR),
        libdir,
        "CMake should build Snappy in provided LIBDIR"
    );
    println!("cargo:rustc-link-search=native={}", libdir.display());
    println!("cargo:rustc-link-lib=static=snappy");

    dest_prefix
}

fn build_leveldb(snappy_prefix: Option<PathBuf>) {
    println!("[leveldb] Building");

    let outdir = env::var("OUT_DIR").unwrap();
    let libdir = Path::new(&outdir).join(LIBDIR);

    env::set_var("NUM_JOBS", num_cpus::get().to_string());
    let mut config =
        cmake::Config::new(Path::new("deps").join(format!("leveldb-{}", LEVELDB_VERSION)));
    config
        .define("LEVELDB_BUILD_TESTS", "OFF")
        .define("LEVELDB_BUILD_BENCHMARKS", "OFF")
        .define("CMAKE_INSTALL_LIBDIR", &libdir);
    if let Some(snappy_prefix) = snappy_prefix {
        env::set_var(
            "LDFLAGS",
            format!("-L{}", snappy_prefix.join(LIBDIR).display()),
        );
        config
            .define("HAVE_SNAPPY", "ON")
            .cflag(format!("-I{}", snappy_prefix.join("include").display()))
            .cxxflag(format!("-I{}", snappy_prefix.join("include").display()));
    } else {
        config.define("HAVE_SNAPPY", "OFF");
    }
    let dest_prefix = config.build();

    assert_eq!(
        dest_prefix.join(LIBDIR),
        libdir,
        "CMake should build LevelDB in provided LIBDIR"
    );
    println!("cargo:rustc-link-search=native={}", libdir.display());
    println!("cargo:rustc-link-lib=static=leveldb");
}

fn main() {
    println!("[build] Started");

    // If we have the appropriate feature, then we build snappy.
    #[cfg(feature = "snappy")]
    let snappy_prefix = Some(build_snappy());
    #[cfg(not(feature = "snappy"))]
    let snappy_prefix: Option<PathBuf> = None;

    // Build LevelDB
    build_leveldb(snappy_prefix);

    // Link to the standard C++ library
    let target = env::var("TARGET").unwrap();
    if target.contains("apple") || target.contains("freebsd") {
        println!("cargo:rustc-link-lib=c++");
    } else if target.contains("gnu") || target.contains("netbsd") || target.contains("openbsd") {
        println!("cargo:rustc-link-lib=stdc++");
    }

    println!("[build] Finished");
}
<|endoftext|>


<commit_before>use std::fmt;

// this is an excerise to override a to_s or toString()

struct MinMax(i64, i64);

struct Point2D {
    x: f64,
    y: f64
}

impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.0, self.1)
    }
}

impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "X: {} and Y: {}", self.x, self.y)
    }
}

fn main() {

    let minmax = MinMax(10,5);

    println!("Comparing structures");

    println!("Display: {}", minmax);

    let points = Point2D{ x: 4.0, y: 6.0};

    println!("points: {}", points);

}<commit_msg>added activity<commit_after>use std::fmt;

// this is an excerise to override a to_s or toString()

struct MinMax(i64, i64);

#[derive(Debug)]
struct Point2D {
    x: f64,
    y: f64

}

#[derive(Debug)]
struct Complex {
    real: f64,
    img: f64
}

impl fmt::Display for MinMax {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.0, self.1)
    }
}

impl fmt::Display for Point2D {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//        write!(f, "X: {} and Y: {}", self.x, self.y)
        write!(f, "{} + {}i", self.x, self.y) // activity

    }
}

fn main() {

    let minmax = MinMax(10,5);

    println!("Comparing structures");

    println!("Display: {}", minmax);

    let points = Point2D{ x: 4.0, y: 6.0};

    println!("Display points: {}", points);

    println!("Debug points: {:?}", points);

    let complex = Complex {real: 3.3, img: 7.2};
    println!("Debug: {:?}", complex);

}<|endoftext|>


<commit_before><commit_msg>Fn new() for error type.<commit_after><|endoftext|>


<commit_before><commit_msg>Connect to next server when backend connection fails<commit_after><|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A collection of invalidations due to changes in which stylesheets affect a
//! document.

#![deny(unsafe_code)]

use Atom;
use data::StoredRestyleHint;
use dom::{TElement, TNode};
use fnv::FnvHashSet;
use selector_parser::SelectorImpl;
use selectors::attr::CaseSensitivity;
use selectors::parser::{Component, Selector};
use shared_lock::SharedRwLockReadGuard;
use stylesheets::{CssRule, Stylesheet};
use stylist::Stylist;

/// An invalidation scope represents a kind of subtree that may need to be
/// restyled.
#[derive(Debug, Hash, Eq, PartialEq)]
enum InvalidationScope {
    /// All the descendants of an element with a given id.
    ID(Atom),
    /// All the descendants of an element with a given class name.
    Class(Atom),
}

impl InvalidationScope {
    fn is_id(&self) -> bool {
        matches!(*self, InvalidationScope::ID(..))
    }

    fn matches<E>(&self, element: E) -> bool
        where E: TElement,
    {
        match *self {
            InvalidationScope::Class(ref class) => {
                element.has_class(class, CaseSensitivity::CaseSensitive)
            }
            InvalidationScope::ID(ref id) => {
                match element.get_id() {
                    Some(element_id) => element_id == *id,
                    None => false,
                }
            }
        }
    }
}

/// A set of invalidations due to stylesheet additions.
///
/// TODO(emilio): We might be able to do the same analysis for removals and
/// media query changes too?
pub struct StylesheetInvalidationSet {
    /// The style scopes we know we have to restyle so far.
    invalid_scopes: FnvHashSet<InvalidationScope>,
    /// Whether the whole document should be invalid.
    fully_invalid: bool,
}

impl StylesheetInvalidationSet {
    /// Create an empty `StylesheetInvalidationSet`.
    pub fn new() -> Self {
        Self {
            invalid_scopes: FnvHashSet::default(),
            fully_invalid: false,
        }
    }

    /// Mark the DOM tree styles' as fully invalid.
    pub fn invalidate_fully(&mut self) {
        debug!("StylesheetInvalidationSet::invalidate_fully");
        self.invalid_scopes.clear();
        self.fully_invalid = true;
    }

    /// Analyze the given stylesheet, and collect invalidations from their
    /// rules, in order to avoid doing a full restyle when we style the document
    /// next time.
    pub fn collect_invalidations_for(
        &mut self,
        stylist: &Stylist,
        stylesheet: &Stylesheet,
        guard: &SharedRwLockReadGuard)
    {
        debug!("StylesheetInvalidationSet::collect_invalidations_for");
        if self.fully_invalid {
            debug!(" > Fully invalid already");
            return;
        }

        if stylesheet.disabled() ||
           !stylesheet.is_effective_for_device(stylist.device(), guard) {
            debug!(" > Stylesheet was not effective");
            return; // Nothing to do here.
        }

        for rule in stylesheet.effective_rules(stylist.device(), guard) {
            self.collect_invalidations_for_rule(rule, guard);
            if self.fully_invalid {
                self.invalid_scopes.clear();
                break;
            }
        }

        debug!(" > resulting invalidations: {:?}", self.invalid_scopes);
        debug!(" > fully_invalid: {}", self.fully_invalid);
    }

    /// Clears the invalidation set, invalidating elements as needed if
    /// `document_element` is provided.
    pub fn flush<E>(&mut self, document_element: Option<E>)
        where E: TElement,
    {
        if let Some(e) = document_element {
            self.process_invalidations_in_subtree(e);
        }
        self.invalid_scopes.clear();
        self.fully_invalid = false;
    }

    /// Process style invalidations in a given subtree, that is, look for all
    /// the relevant scopes in the subtree, and mark as dirty only the relevant
    /// ones.
    ///
    /// Returns whether it invalidated at least one element's style.
    #[allow(unsafe_code)]
    fn process_invalidations_in_subtree<E>(&self, element: E) -> bool
        where E: TElement,
    {
        let mut data = match element.mutate_data() {
            Some(data) => data,
            None => return false,
        };

        if !data.has_styles() {
            return false;
        }

        if let Some(ref r) = data.get_restyle() {
            if r.hint.contains_subtree() {
                debug!("process_invalidations_in_subtree: {:?} was already invalid",
                       element);
                return false;
            }
        }

        if self.fully_invalid {
            debug!("process_invalidations_in_subtree: fully_invalid({:?})",
                   element);
            data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
            return true;
        }

        if self.invalid_scopes.is_empty() {
            debug!("process_invalidations_in_subtree: empty invalidation set");
            return false;
        }

        for scope in &self.invalid_scopes {
            if scope.matches(element) {
                debug!("process_invalidations_in_subtree: {:?} matched {:?}",
                       element, scope);
                data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
                return true;
            }
        }

        let mut any_children_invalid = false;

        for child in element.as_node().traversal_children() {
            let child = match child.as_element() {
                Some(e) => e,
                None => continue,
            };

            any_children_invalid |= self.process_invalidations_in_subtree(child);
        }

        if any_children_invalid {
            debug!("Children of {:?} changed, setting dirty descendants",
                   element);
            unsafe { element.set_dirty_descendants() }
        }

        return any_children_invalid
    }

    fn scan_component(
        component: &Component<SelectorImpl>,
        scope: &mut Option<InvalidationScope>)
    {
        match *component {
            Component::Class(ref class) => {
                if scope.as_ref().map_or(true, |s| !s.is_id()) {
                    *scope = Some(InvalidationScope::Class(class.clone()));
                }
            }
            Component::ID(ref id) => {
                if scope.is_none() {
                    *scope = Some(InvalidationScope::ID(id.clone()));
                }
            }
            _ => {
                // Ignore everything else, at least for now.
            }
        }
    }

    /// Collect a style scopes for a given selector.
    ///
    /// We look at the outermost class or id selector to the left of an ancestor
    /// combinator, in order to restyle only a given subtree.
    ///
    /// We prefer id scopes to class scopes, and outermost scopes to innermost
    /// scopes (to reduce the amount of traversal we need to do).
    fn collect_scopes(&mut self, selector: &Selector<SelectorImpl>) {
        debug!("StylesheetInvalidationSet::collect_scopes({:?})", selector);

        let mut scope: Option<InvalidationScope> = None;

        let mut scan = true;
        let mut iter = selector.iter();

        loop {
            for component in &mut iter {
                if scan {
                    Self::scan_component(component, &mut scope);
                }
            }
            match iter.next_sequence() {
                None => break,
                Some(combinator) => {
                    scan = combinator.is_ancestor();
                }
            }
        }

        match scope {
            Some(s) => {
                debug!(" > Found scope: {:?}", s);
                self.invalid_scopes.insert(s);
            }
            None => {
                debug!(" > Scope not found");

                // If we didn't find a scope, any element could match this, so
                // let's just bail out.
                self.fully_invalid = true;
            }
        }
    }

    /// Collects invalidations for a given CSS rule.
    fn collect_invalidations_for_rule(
        &mut self,
        rule: &CssRule,
        guard: &SharedRwLockReadGuard)
    {
        use stylesheets::CssRule::*;
        debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
        debug_assert!(!self.fully_invalid, "Not worth to be here!");

        match *rule {
            Style(ref lock) => {
                let style_rule = lock.read_with(guard);
                for selector_and_hashes in &style_rule.selectors.0 {
                    self.collect_scopes(&selector_and_hashes.selector);
                    if self.fully_invalid {
                        return;
                    }
                }
            }
            Document(..) |
            Namespace(..) |
            Import(..) |
            Media(..) |
            Supports(..) => {
                // Do nothing, relevant nested rules are visited as part of the
                // iteration.
            }
            FontFace(..) |
            CounterStyle(..) |
            Keyframes(..) |
            Page(..) |
            Viewport(..) => {
                debug!(" > Found unsupported rule, marking the whole subtree \
                       invalid.");

                // TODO(emilio): Can we do better here?
                //
                // At least in `@page`, we could check the relevant media, I
                // guess.
                self.fully_invalid = true;
            }
        }
    }
}
<commit_msg>style: Move one-off checks out of the recursive invalidation function.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A collection of invalidations due to changes in which stylesheets affect a
//! document.

#![deny(unsafe_code)]

use Atom;
use data::StoredRestyleHint;
use dom::{TElement, TNode};
use fnv::FnvHashSet;
use selector_parser::SelectorImpl;
use selectors::attr::CaseSensitivity;
use selectors::parser::{Component, Selector};
use shared_lock::SharedRwLockReadGuard;
use stylesheets::{CssRule, Stylesheet};
use stylist::Stylist;

/// An invalidation scope represents a kind of subtree that may need to be
/// restyled.
#[derive(Debug, Hash, Eq, PartialEq)]
enum InvalidationScope {
    /// All the descendants of an element with a given id.
    ID(Atom),
    /// All the descendants of an element with a given class name.
    Class(Atom),
}

impl InvalidationScope {
    fn is_id(&self) -> bool {
        matches!(*self, InvalidationScope::ID(..))
    }

    fn matches<E>(&self, element: E) -> bool
        where E: TElement,
    {
        match *self {
            InvalidationScope::Class(ref class) => {
                element.has_class(class, CaseSensitivity::CaseSensitive)
            }
            InvalidationScope::ID(ref id) => {
                match element.get_id() {
                    Some(element_id) => element_id == *id,
                    None => false,
                }
            }
        }
    }
}

/// A set of invalidations due to stylesheet additions.
///
/// TODO(emilio): We might be able to do the same analysis for removals and
/// media query changes too?
pub struct StylesheetInvalidationSet {
    /// The style scopes we know we have to restyle so far.
    invalid_scopes: FnvHashSet<InvalidationScope>,
    /// Whether the whole document should be invalid.
    fully_invalid: bool,
}

impl StylesheetInvalidationSet {
    /// Create an empty `StylesheetInvalidationSet`.
    pub fn new() -> Self {
        Self {
            invalid_scopes: FnvHashSet::default(),
            fully_invalid: false,
        }
    }

    /// Mark the DOM tree styles' as fully invalid.
    pub fn invalidate_fully(&mut self) {
        debug!("StylesheetInvalidationSet::invalidate_fully");
        self.invalid_scopes.clear();
        self.fully_invalid = true;
    }

    /// Analyze the given stylesheet, and collect invalidations from their
    /// rules, in order to avoid doing a full restyle when we style the document
    /// next time.
    pub fn collect_invalidations_for(
        &mut self,
        stylist: &Stylist,
        stylesheet: &Stylesheet,
        guard: &SharedRwLockReadGuard)
    {
        debug!("StylesheetInvalidationSet::collect_invalidations_for");
        if self.fully_invalid {
            debug!(" > Fully invalid already");
            return;
        }

        if stylesheet.disabled() ||
           !stylesheet.is_effective_for_device(stylist.device(), guard) {
            debug!(" > Stylesheet was not effective");
            return; // Nothing to do here.
        }

        for rule in stylesheet.effective_rules(stylist.device(), guard) {
            self.collect_invalidations_for_rule(rule, guard);
            if self.fully_invalid {
                self.invalid_scopes.clear();
                break;
            }
        }

        debug!(" > resulting invalidations: {:?}", self.invalid_scopes);
        debug!(" > fully_invalid: {}", self.fully_invalid);
    }

    /// Clears the invalidation set, invalidating elements as needed if
    /// `document_element` is provided.
    pub fn flush<E>(&mut self, document_element: Option<E>)
        where E: TElement,
    {
        if let Some(e) = document_element {
            self.process_invalidations(e);
        }
        self.invalid_scopes.clear();
        self.fully_invalid = false;
    }

    fn process_invalidations<E>(&self, element: E) -> bool
        where E: TElement,
    {
        {
            let mut data = match element.mutate_data() {
                Some(data) => data,
                None => return false,
            };

            if self.fully_invalid {
                debug!("process_invalidations: fully_invalid({:?})",
                       element);
                data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
                return true;
            }
        }

        if self.invalid_scopes.is_empty() {
            debug!("process_invalidations: empty invalidation set");
            return false;
        }

        self.process_invalidations_in_subtree(element)
    }

    /// Process style invalidations in a given subtree, that is, look for all
    /// the relevant scopes in the subtree, and mark as dirty only the relevant
    /// ones.
    ///
    /// Returns whether it invalidated at least one element's style.
    #[allow(unsafe_code)]
    fn process_invalidations_in_subtree<E>(&self, element: E) -> bool
        where E: TElement,
    {
        let mut data = match element.mutate_data() {
            Some(data) => data,
            None => return false,
        };

        if !data.has_styles() {
            return false;
        }

        if let Some(ref r) = data.get_restyle() {
            if r.hint.contains_subtree() {
                debug!("process_invalidations_in_subtree: {:?} was already invalid",
                       element);
                return false;
            }
        }

        for scope in &self.invalid_scopes {
            if scope.matches(element) {
                debug!("process_invalidations_in_subtree: {:?} matched {:?}",
                       element, scope);
                data.ensure_restyle().hint.insert(StoredRestyleHint::subtree());
                return true;
            }
        }

        let mut any_children_invalid = false;

        for child in element.as_node().traversal_children() {
            let child = match child.as_element() {
                Some(e) => e,
                None => continue,
            };

            any_children_invalid |= self.process_invalidations_in_subtree(child);
        }

        if any_children_invalid {
            debug!("Children of {:?} changed, setting dirty descendants",
                   element);
            unsafe { element.set_dirty_descendants() }
        }

        return any_children_invalid
    }

    fn scan_component(
        component: &Component<SelectorImpl>,
        scope: &mut Option<InvalidationScope>)
    {
        match *component {
            Component::Class(ref class) => {
                if scope.as_ref().map_or(true, |s| !s.is_id()) {
                    *scope = Some(InvalidationScope::Class(class.clone()));
                }
            }
            Component::ID(ref id) => {
                if scope.is_none() {
                    *scope = Some(InvalidationScope::ID(id.clone()));
                }
            }
            _ => {
                // Ignore everything else, at least for now.
            }
        }
    }

    /// Collect a style scopes for a given selector.
    ///
    /// We look at the outermost class or id selector to the left of an ancestor
    /// combinator, in order to restyle only a given subtree.
    ///
    /// We prefer id scopes to class scopes, and outermost scopes to innermost
    /// scopes (to reduce the amount of traversal we need to do).
    fn collect_scopes(&mut self, selector: &Selector<SelectorImpl>) {
        debug!("StylesheetInvalidationSet::collect_scopes({:?})", selector);

        let mut scope: Option<InvalidationScope> = None;

        let mut scan = true;
        let mut iter = selector.iter();

        loop {
            for component in &mut iter {
                if scan {
                    Self::scan_component(component, &mut scope);
                }
            }
            match iter.next_sequence() {
                None => break,
                Some(combinator) => {
                    scan = combinator.is_ancestor();
                }
            }
        }

        match scope {
            Some(s) => {
                debug!(" > Found scope: {:?}", s);
                self.invalid_scopes.insert(s);
            }
            None => {
                debug!(" > Scope not found");

                // If we didn't find a scope, any element could match this, so
                // let's just bail out.
                self.fully_invalid = true;
            }
        }
    }

    /// Collects invalidations for a given CSS rule.
    fn collect_invalidations_for_rule(
        &mut self,
        rule: &CssRule,
        guard: &SharedRwLockReadGuard)
    {
        use stylesheets::CssRule::*;
        debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
        debug_assert!(!self.fully_invalid, "Not worth to be here!");

        match *rule {
            Style(ref lock) => {
                let style_rule = lock.read_with(guard);
                for selector_and_hashes in &style_rule.selectors.0 {
                    self.collect_scopes(&selector_and_hashes.selector);
                    if self.fully_invalid {
                        return;
                    }
                }
            }
            Document(..) |
            Namespace(..) |
            Import(..) |
            Media(..) |
            Supports(..) => {
                // Do nothing, relevant nested rules are visited as part of the
                // iteration.
            }
            FontFace(..) |
            CounterStyle(..) |
            Keyframes(..) |
            Page(..) |
            Viewport(..) => {
                debug!(" > Found unsupported rule, marking the whole subtree \
                       invalid.");

                // TODO(emilio): Can we do better here?
                //
                // At least in `@page`, we could check the relevant media, I
                // guess.
                self.fully_invalid = true;
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Result as IoResult;
use std::result::Result;
use std::iter::{Iterator, IntoIterator};
use std::collections::HashMap;
use std::collections::hash_map;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

type Pixels = usize;
type FileName = String;
type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    x_pixels: Pixels,
    y_pixels: Pixels,
}

impl ImageDimensions {
    fn new(x: Pixels, y: Pixels) -> ImageDimensions {
        ImageDimensions {
            x_pixels: x,
            y_pixels: y,
        }
    }
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
enum PageOps {
    Identify(FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

trait ElementaryPageOperations {
    fn identify(path: FilePath)                -> IoResult<String>;
    fn rescale(amount: Pixels, dir: Direction) -> IoResult<String>;
    fn expand_left_edge(amount: Pixels)        -> IoResult<String>;
    fn expand_right_edge(amount: Pixels)       -> IoResult<String>;
    fn expand_top_edge(amount: Pixels)         -> IoResult<String>;
    fn expand_bottom_edge(amount: Pixels)      -> IoResult<String>;
    fn trim_left_edge(amount: Pixels)          -> IoResult<String>;
    fn trim_right_edge(amount: Pixels)         -> IoResult<String>;
    fn trim_top_edge(amount: Pixels)           -> IoResult<String>;
    fn trim_bottom_edge(amount: Pixels)        -> IoResult<String>;
    fn set_resolution(res: ImageResolution)    -> IoResult<String>;
}

trait RunOperation<OpTrait, OtherOp> {
    fn run_operation(op: OtherOp) -> IoResult<String>;
}

impl<Op> RunOperation<Op, PageOps> for Op where Op: ElementaryPageOperations {
    fn run_operation(page_op: PageOps) -> IoResult<String> {
        match page_op {
            PageOps::Identify(path)           => Op::identify(path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }
}

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Option<Vec<PageOps>>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {

        if ops.is_empty() {
            return CompoundPageOperation {
                page_name: page_name,
                page_path: page_path,
                ops: None,
            };
        }

        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: Some(vec),
        }
    }

    fn make_noop() -> CompoundPageOperation {
        CompoundPageOperation {
            page_name: String::from(""),
            page_path: String::from(""),
            ops: None,
        }
    }

    fn is_noop(&self) -> bool {
        match self.ops {
            Some(ref vec) => vec.is_empty(),
            None      => true,
        }
    }

    fn run_operation<Op>(&self) -> IoResult<String>
        where Op: ElementaryPageOperations {

        match self.ops {

            None      => {
                Ok(String::from("No Operation"))
            }
            Some(ref vec) => {
                if self.is_noop() {
                    // Should not happen.
                    unreachable!();
                    //return Ok(String::from("No Operation"));
                }

                let mut result = Ok(String::from(""));
                for op in vec.iter() {
                    let res = Op::run_operation(op.clone());
                    match res {
                        Ok(s) => {
                            continue;
                        }
                        Err(e) => {
                            result = Err(e);
                            break;
                        }
                    }
                }
                result
            }
        }
    }

}

#[derive(Clone)]
enum CompoundPageOps {
    AnOp(CompoundPageOperation),
    NoOp(CompoundPageOperation),
}

impl CompoundPageOps {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOps {
        if ops.is_empty() {
            return CompoundPageOps::NoOp(CompoundPageOperation::new(page_name, page_path, ops));
        }

        CompoundPageOps::AnOp(CompoundPageOperation::new(page_name, page_path, ops))
    }
}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
            file_name: FileName,
            file_extension: ImageFileFormat,
            file_path: FilePath,
            dimensions: ImageDimensions,
            resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H)
        where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

struct OperationResults {
    results: Vec<IoResult<String>>,
}

impl OperationResults {
    fn new(vec: Vec<IoResult<String>>) -> Self {
        OperationResults {
            results: vec,
        }
    }
}

#[derive(Clone, Eq, PartialEq)]
enum OperationScheduleError {
    LengthMismatch,
}

impl OperationSchedule {
    fn new() -> Self {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_operation(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, OperationScheduleError> {
        if pages.len() == ops.len() {

            let mut schedule = OperationSchedule::new();

            for page_number in 0..pages.len() {
                schedule.add_operation(pages[page_number].clone(), ops[page_number].clone());
            }

            Ok(schedule)

        } else {
            Err(OperationScheduleError::LengthMismatch)
        }

    }

    fn iter(&self) -> OpSchedIter {
        OpSchedIter {
            inner: self.schedule.iter()
        }
    }

    fn run_schedule(&self) -> OperationResults {
/*
        let mut results = Vec::new();

        for (page, op) in (*schedule).schedule {
            let result = op.run_operation();
            results.push(result);
        }

        OperationResults::new(results)
*/
        unimplemented!();
    }

}

struct OpSchedIter<'a> {
    inner:  hash_map::Iter<'a, Page, CompoundPageOperation>
}

impl<'a> Iterator for OpSchedIter<'a> {
    type Item = (&'a Page, &'a CompoundPageOperation);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

}

impl<'a> IntoIterator for &'a OperationSchedule {
    type Item = (&'a Page, &'a CompoundPageOperation);
    type IntoIter = OpSchedIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        OpSchedIter {
            inner: self.schedule.iter(),
        }
    }
}
<commit_msg>Implement run_operation for OperationSchedule.<commit_after>use std::io::Result as IoResult;
use std::result::Result;
use std::iter::{Iterator, IntoIterator};
use std::collections::HashMap;
use std::collections::hash_map;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

type Pixels = usize;
type FileName = String;
type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    x_pixels: Pixels,
    y_pixels: Pixels,
}

impl ImageDimensions {
    fn new(x: Pixels, y: Pixels) -> ImageDimensions {
        ImageDimensions {
            x_pixels: x,
            y_pixels: y,
        }
    }
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
enum PageOps {
    Identify(FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

trait ElementaryPageOperations {
    fn identify(path: FilePath)                -> IoResult<String>;
    fn rescale(amount: Pixels, dir: Direction) -> IoResult<String>;
    fn expand_left_edge(amount: Pixels)        -> IoResult<String>;
    fn expand_right_edge(amount: Pixels)       -> IoResult<String>;
    fn expand_top_edge(amount: Pixels)         -> IoResult<String>;
    fn expand_bottom_edge(amount: Pixels)      -> IoResult<String>;
    fn trim_left_edge(amount: Pixels)          -> IoResult<String>;
    fn trim_right_edge(amount: Pixels)         -> IoResult<String>;
    fn trim_top_edge(amount: Pixels)           -> IoResult<String>;
    fn trim_bottom_edge(amount: Pixels)        -> IoResult<String>;
    fn set_resolution(res: ImageResolution)    -> IoResult<String>;
}

trait RunOperation<OpTrait, OtherOp> {
    fn run_operation(op: OtherOp) -> IoResult<String>;
}

impl<Op> RunOperation<Op, PageOps> for Op where Op: ElementaryPageOperations {
    fn run_operation(page_op: PageOps) -> IoResult<String> {
        match page_op {
            PageOps::Identify(path)           => Op::identify(path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }
}

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Option<Vec<PageOps>>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {

        if ops.is_empty() {
            return CompoundPageOperation {
                page_name: page_name,
                page_path: page_path,
                ops: None,
            };
        }

        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: Some(vec),
        }
    }

    fn make_noop() -> CompoundPageOperation {
        CompoundPageOperation {
            page_name: String::from(""),
            page_path: String::from(""),
            ops: None,
        }
    }

    fn is_noop(&self) -> bool {
        match self.ops {
            Some(ref vec) => vec.is_empty(),
            None          => true,
        }
    }

    fn run_operation<Op>(&self) -> IoResult<String>
        where Op: ElementaryPageOperations {

        match self.ops {

            None      => {
                Ok(String::from("No Operation"))
            }
            Some(ref vec) => {
                if self.is_noop() {
                    // Should not happen.
                    unreachable!();
                    //return Ok(String::from("No Operation"));
                }

                let mut result = Ok(String::from(""));
                for op in vec.iter() {
                    let res = Op::run_operation(op.clone());
                    match res {
                        Ok(s) => {
                            continue;
                        }
                        Err(e) => {
                            result = Err(e);
                            break;
                        }
                    }
                }
                result
            }
        }
    }

}

#[derive(Clone)]
enum CompoundPageOps {
    AnOp(CompoundPageOperation),
    NoOp(CompoundPageOperation),
}

impl CompoundPageOps {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOps {
        if ops.is_empty() {
            return CompoundPageOps::NoOp(CompoundPageOperation::new(page_name, page_path, ops));
        }

        CompoundPageOps::AnOp(CompoundPageOperation::new(page_name, page_path, ops))
    }
}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
            file_name: FileName,
            file_extension: ImageFileFormat,
            file_path: FilePath,
            dimensions: ImageDimensions,
            resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H)
        where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

struct OperationResults {
    results: Vec<IoResult<String>>,
}

impl OperationResults {
    fn new(vec: Vec<IoResult<String>>) -> Self {
        OperationResults {
            results: vec,
        }
    }
}

#[derive(Clone, Eq, PartialEq)]
enum OperationScheduleError {
    LengthMismatch,
}

impl OperationSchedule {
    fn new() -> Self {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_operation(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, OperationScheduleError> {
        if pages.len() == ops.len() {

            let mut schedule = OperationSchedule::new();

            for page_number in 0..pages.len() {
                schedule.add_operation(pages[page_number].clone(), ops[page_number].clone());
            }

            Ok(schedule)

        } else {
            Err(OperationScheduleError::LengthMismatch)
        }

    }

    fn iter(&self) -> OpSchedIter {
        OpSchedIter {
            inner: self.schedule.iter()
        }
    }

    fn run_operation<Op>(&self) -> OperationResults
        where Op: ElementaryPageOperations {

        let mut results = Vec::new();

        for (page, op) in self {
            let result = op.run_operation::<Op>();
            results.push(result);
        }

        OperationResults::new(results)
    }

}

struct OpSchedIter<'a> {
    inner:  hash_map::Iter<'a, Page, CompoundPageOperation>
}

impl<'a> Iterator for OpSchedIter<'a> {
    type Item = (&'a Page, &'a CompoundPageOperation);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

}

impl<'a> IntoIterator for &'a OperationSchedule {
    type Item = (&'a Page, &'a CompoundPageOperation);
    type IntoIter = OpSchedIter<'a>;

    fn into_iter(self) -> OpSchedIter<'a> {
        self.iter()
    }
}

struct OpSchedIntoIter {
    inner: hash_map::IntoIter<Page, CompoundPageOperation>,
}

impl IntoIterator for OperationSchedule {
    type Item = (Page, CompoundPageOperation);
    type IntoIter = OpSchedIntoIter;

    fn into_iter(self) -> OpSchedIntoIter {
        OpSchedIntoIter {
            inner: self.schedule.into_iter()
        }
    }
}

impl Iterator for OpSchedIntoIter {
    type Item = (Page, CompoundPageOperation);

    fn next(&mut self) -> Option<(Page, CompoundPageOperation)> {
        self.inner.next()
    }
}
<|endoftext|>