<commit_before>#![allow(clippy::blacklisted_name)]

use askama::{SizedTemplate, Template};

use std::collections::HashMap;

#[derive(Template)]
#[template(path = "simple.html")]
struct VariablesTemplate<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables() {
    let s = VariablesTemplate {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
    assert_eq!(
        <VariablesTemplate as SizedTemplate>::extension(),
        Some("html")
    );
}

#[derive(Template)]
#[template(path = "hello.html")]
struct EscapeTemplate<'a> {
    name: &'a str,
}

#[test]
fn test_escape() {
    let s = EscapeTemplate { name: "<>&\"'/" };

    assert_eq!(
        s.render().unwrap(),
        "Hello, <>&"'/!"
    );
}

#[derive(Template)]
#[template(path = "simple-no-escape.txt")]
struct VariablesTemplateNoEscape<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables_no_escape() {
    let s = VariablesTemplateNoEscape {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
}

#[derive(Template)]
#[template(
    source = "{{ foo }} {{ foo_bar }} {{ FOO }} {{ FOO_BAR }} {{ self::FOO }} {{ self::FOO_BAR }} {{ Self::BAR }} {{ Self::BAR_BAZ }}",
    ext = "txt"
)]
struct ConstTemplate {
    foo: &'static str,
    foo_bar: &'static str,
}

impl ConstTemplate {
    const BAR: &'static str = "BAR";
    const BAR_BAZ: &'static str = "BAR BAZ";
}

#[test]
fn test_constants() {
    let t = ConstTemplate {
        foo: "foo",
        foo_bar: "foo bar",
    };
    assert_eq!(
        t.render().unwrap(),
        "foo foo bar FOO FOO BAR FOO FOO BAR BAR BAR BAZ"
    );
}

const FOO: &str = "FOO";
const FOO_BAR: &str = "FOO BAR";

#[derive(Template)]
#[template(path = "if.html")]
struct IfTemplate {
    cond: bool,
}

#[test]
fn test_if() {
    let s = IfTemplate { cond: true };
    assert_eq!(s.render().unwrap(), "true");
}

#[derive(Template)]
#[template(path = "else.html")]
struct ElseTemplate {
    cond: bool,
}

#[test]
fn test_else() {
    let s = ElseTemplate { cond: false };
    assert_eq!(s.render().unwrap(), "false");
}

#[derive(Template)]
#[template(path = "else-if.html")]
struct ElseIfTemplate {
    cond: bool,
    check: bool,
}

#[test]
fn test_else_if() {
    let s = ElseIfTemplate {
        cond: false,
        check: true,
    };
    assert_eq!(s.render().unwrap(), "checked");
}

#[derive(Template)]
#[template(path = "literals.html")]
struct LiteralsTemplate {}

#[test]
fn test_literals() {
    let s = LiteralsTemplate {};
    assert_eq!(s.render().unwrap(), "a\na\ntrue\nfalse");
}

#[derive(Template)]
#[template(path = "literals-escape.html")]
struct LiteralsEscapeTemplate {}

#[test]
fn test_literals_escape() {
    let s = LiteralsEscapeTemplate {};
    assert_eq!(
        s.render().unwrap(),
        "A\n\r\t\\\0♥'""\nA\n\r\t\\\0♥'"'"
    );
}

struct Holder {
    a: usize,
}

#[derive(Template)]
#[template(path = "attr.html")]
struct AttrTemplate {
    inner: Holder,
}

#[test]
fn test_attr() {
    let t = AttrTemplate {
        inner: Holder { a: 5 },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "tuple-attr.html")]
struct TupleAttrTemplate<'a> {
    tuple: (&'a str, &'a str),
}

#[test]
fn test_tuple_attr() {
    let t = TupleAttrTemplate {
        tuple: ("foo", "bar"),
    };
    assert_eq!(t.render().unwrap(), "foobar");
}

struct NestedHolder {
    holder: Holder,
}

#[derive(Template)]
#[template(path = "nested-attr.html")]
struct NestedAttrTemplate {
    inner: NestedHolder,
}

#[test]
fn test_nested_attr() {
    let t = NestedAttrTemplate {
        inner: NestedHolder {
            holder: Holder { a: 5 },
        },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "option.html")]
struct OptionTemplate<'a> {
    var: Option<&'a str>,
}

#[test]
fn test_option() {
    let some = OptionTemplate { var: Some("foo") };
    assert_eq!(some.render().unwrap(), "some: foo");
    let none = OptionTemplate { var: None };
    assert_eq!(none.render().unwrap(), "none");
}

#[derive(Template)]
#[template(path = "generics.html")]
struct GenericsTemplate<T, U = u8>
where
    T: std::fmt::Display,
    U: std::fmt::Display,
{
    t: T,
    u: U,
}

#[test]
fn test_generics() {
    let t = GenericsTemplate { t: "a", u: 42 };
    assert_eq!(t.render().unwrap(), "a42");
}

#[derive(Template)]
#[template(path = "composition.html")]
struct CompositionTemplate {
    foo: IfTemplate,
}

#[test]
fn test_composition() {
    let t = CompositionTemplate {
        foo: IfTemplate { cond: true },
    };
    assert_eq!(t.render().unwrap(), "composed: true");
}

#[derive(PartialEq, Eq)]
enum Alphabet {
    Alpha,
}

#[derive(Template)]
#[template(source = "{% if x == Alphabet::Alpha %}true{% endif %}", ext = "txt")]
struct PathCompareTemplate {
    x: Alphabet,
}

#[test]
fn test_path_compare() {
    let t = PathCompareTemplate { x: Alphabet::Alpha };
    assert_eq!(t.render().unwrap(), "true");
}

#[derive(Template)]
#[template(
    source = "{% for i in [\"a\", \"\"] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ArrayTemplate {}

#[test]
fn test_slice_literal() {
    let t = ArrayTemplate {};
    assert_eq!(t.render().unwrap(), "a");
}

#[derive(Template)]
#[template(source = "Hello, {{ world(\"123\", 4) }}!", ext = "txt")]
struct FunctionRefTemplate {
    world: fn(s: &str, v: u8) -> String,
}

#[test]
fn test_func_ref_call() {
    let t = FunctionRefTemplate {
        world: |s, r| format!("world({}, {})", s, r),
    };
    assert_eq!(t.render().unwrap(), "Hello, world(123, 4)!");
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn world2(s: &str, v: u8) -> String {
    format!("world{}{}", v, s)
}

#[derive(Template)]
#[template(source = "Hello, {{ self::world2(\"123\", 4) }}!", ext = "txt")]
struct PathFunctionTemplate;

#[test]
fn test_path_func_call() {
    assert_eq!(PathFunctionTemplate.render().unwrap(), "Hello, world4123!");
}

#[derive(Template)]
#[template(source = "{{ ::std::string::String::from(\"123\") }}", ext = "txt")]
struct RootPathFunctionTemplate;

#[test]
fn test_root_path_func_call() {
    assert_eq!(RootPathFunctionTemplate.render().unwrap(), "123");
}

#[derive(Template)]
#[template(source = "Hello, {{ Self::world3(self, \"123\", 4) }}!", ext = "txt")]
struct FunctionTemplate;

impl FunctionTemplate {
    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn world3(&self, s: &str, v: u8) -> String {
        format!("world{}{}", s, v)
    }
}

#[derive(Template)]
#[template(source = "  {# foo -#} ", ext = "txt")]
struct CommentTemplate {}

#[test]
fn test_comment() {
    let t = CommentTemplate {};
    assert_eq!(t.render().unwrap(), "  ");
}

#[derive(Template)]
#[template(source = "{% if !foo %}Hello{% endif %}", ext = "txt")]
struct NegationTemplate {
    foo: bool,
}

#[test]
fn test_negation() {
    let t = NegationTemplate { foo: false };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{% if foo > -2 %}Hello{% endif %}", ext = "txt")]
struct MinusTemplate {
    foo: i8,
}

#[test]
fn test_minus() {
    let t = MinusTemplate { foo: 1 };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{{ foo[\"bar\"] }}", ext = "txt")]
struct IndexTemplate {
    foo: HashMap<String, String>,
}

#[test]
fn test_index() {
    let mut foo = HashMap::new();
    foo.insert("bar".into(), "baz".into());
    let t = IndexTemplate { foo };
    assert_eq!(t.render().unwrap(), "baz");
}

#[derive(Template)]
#[template(source = "foo", ext = "txt")]
struct Empty;

#[test]
fn test_empty() {
    assert_eq!(Empty.render().unwrap(), "foo");
}

#[derive(Template)]
#[template(path = "raw-simple.html")]
struct RawTemplate;

#[test]
fn test_raw_simple() {
    let template = RawTemplate;
    assert_eq!(template.render().unwrap(), "\n{{ name }}\n");
}

#[derive(Template)]
#[template(path = "raw-complex.html")]
struct RawTemplateComplex;

#[test]
fn test_raw_complex() {
    let template = RawTemplateComplex;
    assert_eq!(
        template.render().unwrap(),
        "\n{% block name %}\n  {{ name }}\n{% endblock %}\n"
    );
}

mod without_import_on_derive {
    #[derive(askama::Template)]
    #[template(source = "foo", ext = "txt")]
    struct WithoutImport;

    #[test]
    fn test_without_import() {
        use askama::Template;
        assert_eq!(WithoutImport.render().unwrap(), "foo");
    }
}

#[derive(askama::Template)]
#[template(source = "{% let s = String::new() %}{{ s }}", ext = "txt")]
struct DefineStringVar;

#[test]
fn test_define_string_var() {
    let template = DefineStringVar;
    assert_eq!(template.render().unwrap(), "");
}
<commit_msg>Added option test<commit_after>#![allow(clippy::blacklisted_name)]

use askama::{SizedTemplate, Template};

use std::collections::HashMap;

#[derive(Template)]
#[template(path = "simple.html")]
struct VariablesTemplate<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables() {
    let s = VariablesTemplate {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
    assert_eq!(
        <VariablesTemplate as SizedTemplate>::extension(),
        Some("html")
    );
}

#[derive(Template)]
#[template(path = "hello.html")]
struct EscapeTemplate<'a> {
    name: &'a str,
}

#[test]
fn test_escape() {
    let s = EscapeTemplate { name: "<>&\"'/" };

    assert_eq!(
        s.render().unwrap(),
        "Hello, <>&"'/!"
    );
}

#[derive(Template)]
#[template(path = "simple-no-escape.txt")]
struct VariablesTemplateNoEscape<'a> {
    strvar: &'a str,
    num: i64,
    i18n: String,
}

#[test]
fn test_variables_no_escape() {
    let s = VariablesTemplateNoEscape {
        strvar: "foo",
        num: 42,
        i18n: "Iñtërnâtiônàlizætiøn".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "\nhello world, foo\n\
         with number: 42\n\
         Iñtërnâtiônàlizætiøn is important\n\
         in vars too: Iñtërnâtiônàlizætiøn"
    );
}

#[derive(Template)]
#[template(
    source = "{{ foo }} {{ foo_bar }} {{ FOO }} {{ FOO_BAR }} {{ self::FOO }} {{ self::FOO_BAR }} {{ Self::BAR }} {{ Self::BAR_BAZ }}",
    ext = "txt"
)]
struct ConstTemplate {
    foo: &'static str,
    foo_bar: &'static str,
}

impl ConstTemplate {
    const BAR: &'static str = "BAR";
    const BAR_BAZ: &'static str = "BAR BAZ";
}

#[test]
fn test_constants() {
    let t = ConstTemplate {
        foo: "foo",
        foo_bar: "foo bar",
    };
    assert_eq!(
        t.render().unwrap(),
        "foo foo bar FOO FOO BAR FOO FOO BAR BAR BAR BAZ"
    );
}

const FOO: &str = "FOO";
const FOO_BAR: &str = "FOO BAR";

#[derive(Template)]
#[template(path = "if.html")]
struct IfTemplate {
    cond: bool,
}

#[test]
fn test_if() {
    let s = IfTemplate { cond: true };
    assert_eq!(s.render().unwrap(), "true");
}

#[derive(Template)]
#[template(path = "else.html")]
struct ElseTemplate {
    cond: bool,
}

#[test]
fn test_else() {
    let s = ElseTemplate { cond: false };
    assert_eq!(s.render().unwrap(), "false");
}

#[derive(Template)]
#[template(path = "else-if.html")]
struct ElseIfTemplate {
    cond: bool,
    check: bool,
}

#[test]
fn test_else_if() {
    let s = ElseIfTemplate {
        cond: false,
        check: true,
    };
    assert_eq!(s.render().unwrap(), "checked");
}

#[derive(Template)]
#[template(path = "literals.html")]
struct LiteralsTemplate {}

#[test]
fn test_literals() {
    let s = LiteralsTemplate {};
    assert_eq!(s.render().unwrap(), "a\na\ntrue\nfalse");
}

#[derive(Template)]
#[template(path = "literals-escape.html")]
struct LiteralsEscapeTemplate {}

#[test]
fn test_literals_escape() {
    let s = LiteralsEscapeTemplate {};
    assert_eq!(
        s.render().unwrap(),
        "A\n\r\t\\\0♥'""\nA\n\r\t\\\0♥'"'"
    );
}

struct Holder {
    a: usize,
}

#[derive(Template)]
#[template(path = "attr.html")]
struct AttrTemplate {
    inner: Holder,
}

#[test]
fn test_attr() {
    let t = AttrTemplate {
        inner: Holder { a: 5 },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "tuple-attr.html")]
struct TupleAttrTemplate<'a> {
    tuple: (&'a str, &'a str),
}

#[test]
fn test_tuple_attr() {
    let t = TupleAttrTemplate {
        tuple: ("foo", "bar"),
    };
    assert_eq!(t.render().unwrap(), "foobar");
}

struct NestedHolder {
    holder: Holder,
}

#[derive(Template)]
#[template(path = "nested-attr.html")]
struct NestedAttrTemplate {
    inner: NestedHolder,
}

#[test]
fn test_nested_attr() {
    let t = NestedAttrTemplate {
        inner: NestedHolder {
            holder: Holder { a: 5 },
        },
    };
    assert_eq!(t.render().unwrap(), "5");
}

#[derive(Template)]
#[template(path = "option.html")]
struct OptionTemplate<'a> {
    var: Option<&'a str>,
}

#[test]
fn test_option() {
    let some = OptionTemplate { var: Some("foo") };
    assert_eq!(some.render().unwrap(), "some: foo");
    let none = OptionTemplate { var: None };
    assert_eq!(none.render().unwrap(), "none");
}

#[derive(Template)]
#[template(source = "{{ Self::foo(None) }} {{ Self::foo(Some(1)) }}", ext = "txt")]
struct OptionNoneSomeTemplate;

impl OptionNoneSomeTemplate {
    fn foo(x: Option<i32>) -> i32 {
        x.unwrap_or_default()
    }
}

#[test]
fn test_option_none_some() {
    let t = OptionNoneSomeTemplate;
    assert_eq!(t.render().unwrap(), "0 1");
}

#[derive(Template)]
#[template(path = "generics.html")]
struct GenericsTemplate<T, U = u8>
where
    T: std::fmt::Display,
    U: std::fmt::Display,
{
    t: T,
    u: U,
}

#[test]
fn test_generics() {
    let t = GenericsTemplate { t: "a", u: 42 };
    assert_eq!(t.render().unwrap(), "a42");
}

#[derive(Template)]
#[template(path = "composition.html")]
struct CompositionTemplate {
    foo: IfTemplate,
}

#[test]
fn test_composition() {
    let t = CompositionTemplate {
        foo: IfTemplate { cond: true },
    };
    assert_eq!(t.render().unwrap(), "composed: true");
}

#[derive(PartialEq, Eq)]
enum Alphabet {
    Alpha,
}

#[derive(Template)]
#[template(source = "{% if x == Alphabet::Alpha %}true{% endif %}", ext = "txt")]
struct PathCompareTemplate {
    x: Alphabet,
}

#[test]
fn test_path_compare() {
    let t = PathCompareTemplate { x: Alphabet::Alpha };
    assert_eq!(t.render().unwrap(), "true");
}

#[derive(Template)]
#[template(
    source = "{% for i in [\"a\", \"\"] %}{{ i }}{% endfor %}",
    ext = "txt"
)]
struct ArrayTemplate {}

#[test]
fn test_slice_literal() {
    let t = ArrayTemplate {};
    assert_eq!(t.render().unwrap(), "a");
}

#[derive(Template)]
#[template(source = "Hello, {{ world(\"123\", 4) }}!", ext = "txt")]
struct FunctionRefTemplate {
    world: fn(s: &str, v: u8) -> String,
}

#[test]
fn test_func_ref_call() {
    let t = FunctionRefTemplate {
        world: |s, r| format!("world({}, {})", s, r),
    };
    assert_eq!(t.render().unwrap(), "Hello, world(123, 4)!");
}

#[allow(clippy::trivially_copy_pass_by_ref)]
fn world2(s: &str, v: u8) -> String {
    format!("world{}{}", v, s)
}

#[derive(Template)]
#[template(source = "Hello, {{ self::world2(\"123\", 4) }}!", ext = "txt")]
struct PathFunctionTemplate;

#[test]
fn test_path_func_call() {
    assert_eq!(PathFunctionTemplate.render().unwrap(), "Hello, world4123!");
}

#[derive(Template)]
#[template(source = "{{ ::std::string::String::from(\"123\") }}", ext = "txt")]
struct RootPathFunctionTemplate;

#[test]
fn test_root_path_func_call() {
    assert_eq!(RootPathFunctionTemplate.render().unwrap(), "123");
}

#[derive(Template)]
#[template(source = "Hello, {{ Self::world3(self, \"123\", 4) }}!", ext = "txt")]
struct FunctionTemplate;

impl FunctionTemplate {
    #[allow(clippy::trivially_copy_pass_by_ref)]
    fn world3(&self, s: &str, v: u8) -> String {
        format!("world{}{}", s, v)
    }
}

#[derive(Template)]
#[template(source = "  {# foo -#} ", ext = "txt")]
struct CommentTemplate {}

#[test]
fn test_comment() {
    let t = CommentTemplate {};
    assert_eq!(t.render().unwrap(), "  ");
}

#[derive(Template)]
#[template(source = "{% if !foo %}Hello{% endif %}", ext = "txt")]
struct NegationTemplate {
    foo: bool,
}

#[test]
fn test_negation() {
    let t = NegationTemplate { foo: false };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{% if foo > -2 %}Hello{% endif %}", ext = "txt")]
struct MinusTemplate {
    foo: i8,
}

#[test]
fn test_minus() {
    let t = MinusTemplate { foo: 1 };
    assert_eq!(t.render().unwrap(), "Hello");
}

#[derive(Template)]
#[template(source = "{{ foo[\"bar\"] }}", ext = "txt")]
struct IndexTemplate {
    foo: HashMap<String, String>,
}

#[test]
fn test_index() {
    let mut foo = HashMap::new();
    foo.insert("bar".into(), "baz".into());
    let t = IndexTemplate { foo };
    assert_eq!(t.render().unwrap(), "baz");
}

#[derive(Template)]
#[template(source = "foo", ext = "txt")]
struct Empty;

#[test]
fn test_empty() {
    assert_eq!(Empty.render().unwrap(), "foo");
}

#[derive(Template)]
#[template(path = "raw-simple.html")]
struct RawTemplate;

#[test]
fn test_raw_simple() {
    let template = RawTemplate;
    assert_eq!(template.render().unwrap(), "\n{{ name }}\n");
}

#[derive(Template)]
#[template(path = "raw-complex.html")]
struct RawTemplateComplex;

#[test]
fn test_raw_complex() {
    let template = RawTemplateComplex;
    assert_eq!(
        template.render().unwrap(),
        "\n{% block name %}\n  {{ name }}\n{% endblock %}\n"
    );
}

mod without_import_on_derive {
    #[derive(askama::Template)]
    #[template(source = "foo", ext = "txt")]
    struct WithoutImport;

    #[test]
    fn test_without_import() {
        use askama::Template;
        assert_eq!(WithoutImport.render().unwrap(), "foo");
    }
}

#[derive(askama::Template)]
#[template(source = "{% let s = String::new() %}{{ s }}", ext = "txt")]
struct DefineStringVar;

#[test]
fn test_define_string_var() {
    let template = DefineStringVar;
    assert_eq!(template.render().unwrap(), "");
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::Split;

use clap::ArgMatches;
use toml::Value;

use libimagstore::store::EntryHeader;
use libimagrt::runtime::Runtime;
use libimagutil::key_value_split::IntoKeyValue;

pub fn build_entry_path(rt: &Runtime, path_elem: &str) -> PathBuf {
    debug!("Building path...");
    let mut path = rt.store().path().clone();

    if path_elem.chars().next() == Some('/') {
        path.push(&path_elem[1..path_elem.len()]);
    } else {
        path.push(path_elem);
    }

    path
}

pub fn build_toml_header(matches: &ArgMatches, mut header: EntryHeader) -> EntryHeader {
    debug!("Building header from cli spec");
    if let Some(headerspecs) = matches.values_of("header") {
        let mut main = header.toml_mut();
        for tpl in headerspecs.into_iter().filter_map(|hs| String::from(hs).into_kv()) {
            let (key, value) = tpl.into();
            debug!("Splitting: {:?}", key);
            let mut split = key.split(".");
            let current = split.next();
            if current.is_some() {
                insert_key_into(String::from(current.unwrap()), &mut split, value, &mut main);
            }
        }
    }
    header
}

fn insert_key_into(current: String,
                   rest_path: &mut Split<&str>,
                   value: String,
                   map: &mut BTreeMap<String, Value>) {
    let next = rest_path.next();

    if next.is_none() {
        map.insert(current, parse_value(value));
    } else {
        if map.contains_key(&current) {
            match map.get_mut(&current).unwrap() {
                &mut Value::Table(ref mut t) => {
                    insert_key_into(String::from(next.unwrap()), rest_path, value, t);
                },
                _ => unreachable!(),
            }
        } else {
            let mut submap = BTreeMap::new();
            insert_key_into(String::from(next.unwrap()), rest_path, value, &mut submap);
            map.insert(current, Value::Table(submap));
        }
    }
}

fn parse_value(value: String) -> Value {
    fn is_ary(v: &String) -> bool {
        v.chars().next() == Some('[') && v.chars().last() == Some(']') && v.len() >= 3
    }

    debug!("Building value out of: {:?}", value);
    if value == "true" {
        Value::Boolean(true)
    } else if value == "false" {
        Value::Boolean(false)
    } else if is_ary(&value) {
        let sub = &value[1..(value.len()-1)];
        Value::Array(sub.split(",").map(|v| parse_value(String::from(v))).collect())
    } else {
        Value::String(value)
    }
}

<commit_msg>Add extensive debugging output in utilities<commit_after>use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::Split;

use clap::ArgMatches;
use toml::Value;

use libimagstore::store::EntryHeader;
use libimagrt::runtime::Runtime;
use libimagutil::key_value_split::IntoKeyValue;

pub fn build_entry_path(rt: &Runtime, path_elem: &str) -> PathBuf {
    debug!("Building path...");
    let mut path = rt.store().path().clone();

    if path_elem.chars().next() == Some('/') {
        path.push(&path_elem[1..path_elem.len()]);
    } else {
        path.push(path_elem);
    }

    path
}

pub fn build_toml_header(matches: &ArgMatches, mut header: EntryHeader) -> EntryHeader {
    debug!("Building header from cli spec");
    if let Some(headerspecs) = matches.values_of("header") {
        let mut main = header.toml_mut();
        debug!("headerspec = {:?}", headerspecs);
        let kvs = headerspecs.into_iter()
                            .filter_map(|hs| {
                                debug!("- Processing: '{}'", hs);
                                let kv = String::from(hs).into_kv();
                                debug!("-        got: '{:?}'", kv);
                                kv
                            });
        for tpl in kvs {
            let (key, value) = tpl.into();
            debug!("Splitting: {:?}", key);
            let mut split = key.split(".");
            let current = split.next();
            if current.is_some() {
                insert_key_into(String::from(current.unwrap()), &mut split, value, &mut main);
            }
        }
    }
    debug!("Header = {:?}", header);
    header
}

fn insert_key_into(current: String,
                   rest_path: &mut Split<&str>,
                   value: String,
                   map: &mut BTreeMap<String, Value>) {
    let next = rest_path.next();

    if next.is_none() {
        debug!("Inserting into {:?} = {:?}", current, value);
        map.insert(current, parse_value(value));
    } else {
        debug!("Inserting into {:?} ... = {:?}", current, value);
        if map.contains_key(&current) {
            match map.get_mut(&current).unwrap() {
                &mut Value::Table(ref mut t) => {
                    insert_key_into(String::from(next.unwrap()), rest_path, value, t);
                },
                _ => unreachable!(),
            }
        } else {
            let mut submap = BTreeMap::new();
            insert_key_into(String::from(next.unwrap()), rest_path, value, &mut submap);
            debug!("Inserting submap = {:?}", submap);
            map.insert(current, Value::Table(submap));
        }
    }
}

fn parse_value(value: String) -> Value {
    fn is_ary(v: &String) -> bool {
        v.chars().next() == Some('[') && v.chars().last() == Some(']') && v.len() >= 3
    }

    if value == "true" {
        debug!("Building Boolean out of: {:?}...", value);
        Value::Boolean(true)
    } else if value == "false" {
        debug!("Building Boolean out of: {:?}...", value);
        Value::Boolean(false)
    } else if is_ary(&value) {
        debug!("Building Array out of: {:?}...", value);
        let sub = &value[1..(value.len()-1)];
        Value::Array(sub.split(",").map(|v| parse_value(String::from(v))).collect())
    } else {
        debug!("Building String out of: {:?}...", value);
        Value::String(value)
    }
}

<|endoftext|>


<commit_before>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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/.

use std::io;
use std::io::{BufRead, Write};
use std::str::FromStr;

use domain::DomainDispatcher;
use io::*;
use proto::*;
use types::*;

mod input;
mod output;

use self::input::RawInputMessage;
use self::output::RawOutputMessage;

pub struct Oacsp {
    dev: Device,
    domains: DomainDispatcher,
    client_id: Option<String>
}

impl Oacsp {

    pub fn new(dev: Device, domains: DomainDispatcher) -> Oacsp {
        Oacsp { dev: dev, domains: domains, client_id: None }
    }

    fn line_is_ready(&self) -> bool {
        self.dev.recv_bytes().contains(&b'\n')
    }

    fn process_input(&mut self) -> io::Result<usize> {
        assert!(self.line_is_ready());
        let dev_id = self.dev.id();
        let mut buf = io::BufReader::new(self.dev.recv_bytes());
        let mut line = String::new();
        let nbytes = try!(buf.read_line(&mut line)) + 1; // end-of-line byte counts
        let begin_received = self.client_id.is_some();
        match (try!(RawInputMessage::from_str(&line)), begin_received) {
            (RawInputMessage::Begin { version: _, client_id }, false) => {
                info!("received a begin message from client {}", client_id);
            	self.client_id = Some(client_id);
            	Ok(nbytes)
            },
            (RawInputMessage::Begin { version: _, client_id: _ }, true) => {
				Err(io::Error::new(io::ErrorKind::InvalidData, "begin message already received"))
            }
            (RawInputMessage::WriteLvar { lvar, value }, true) => {
                debug!("received a WRITE_LVAR message from client {}: {} <- {}",
                    self.client_id_str(), lvar, value);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.write(&Var::Named(lvar), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::WriteOffset { offset, value }, true) => {
                debug!("received a WRITE_OFFSET message from client {}: {} <- {}",
                    self.client_id_str(), offset, value);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.write(&Var::Offset(offset), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveLvar { lvar }, true) => {
                debug!("received a OBSERVE_LVAR message from client {}: {}",
                    self.client_id_str(), lvar);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.subscribe(dev_id, &Var::Named(lvar))
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveOffset { offset }, true) => {
                debug!("received a OBSERVE_OFFSET message from client {}: {}",
                    self.client_id_str(), offset);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.subscribe(dev_id, &Var::Offset(offset))
                }));
                Ok(nbytes)
            }
            (_, false) =>  {
                let error = io::Error::new(
                    io::ErrorKind::InvalidData,
                    "unexpected message while waiting for begin");
				Err(error)
            }
        }
    }

    fn client_id_str(&self) -> &str {
        self.client_id
        	.as_ref()
        	.map(|id| id.as_str())
        	.unwrap_or("none")
    }
}

impl DeviceHandler for Oacsp {
    fn device(&mut self) -> &mut Device { &mut self.dev }

    fn process_event(&mut self, event: Event) -> io::Result<()> {
        match event {
            Event::Ready => self.dev.request_read(),
            Event::BytesRead(_) => {
                if self.line_is_ready() {
                    let nread = try!(self.process_input());
                    self.dev.consume_recv_buffer(nread);
                }
                self.dev.request_read()
            }
            Event::BytesWritten(_) => Ok(()),
        }
    }
}

impl Protocol for Oacsp {

    fn send_update(&mut self, domain: &str, variable: Var, value: Value) -> io::Result<()> {
        let raw = try!(match variable {
            Var::Offset(offset) if domain == "fsuipc" =>
                Ok(RawOutputMessage::EventOffset { offset: offset, value: value }),
            Var::Named(ref lvar) if domain == "lvar" =>
                Ok(RawOutputMessage::EventLvar { lvar: lvar.clone(), value: value }),
            _ => {
                Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("cannot encode a message for domain '{}', var '{:?}'",
                        domain, variable)))
            }
        });
        let mut buf = Vec::new();
        try!(write!(&mut buf, "{}\n", raw));
        self.dev.request_write(&buf)
    }
}
<commit_msg>Bug fixing: consume of additional byte when message is decoded<commit_after>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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/.

use std::io;
use std::io::{BufRead, Write};
use std::str::FromStr;

use domain::DomainDispatcher;
use io::*;
use proto::*;
use types::*;

mod input;
mod output;

use self::input::RawInputMessage;
use self::output::RawOutputMessage;

pub struct Oacsp {
    dev: Device,
    domains: DomainDispatcher,
    client_id: Option<String>
}

impl Oacsp {

    pub fn new(dev: Device, domains: DomainDispatcher) -> Oacsp {
        Oacsp { dev: dev, domains: domains, client_id: None }
    }

    fn line_is_ready(&self) -> bool {
        self.dev.recv_bytes().contains(&b'\n')
    }

    fn process_input(&mut self) -> io::Result<usize> {
        assert!(self.line_is_ready());
        let dev_id = self.dev.id();
        let mut buf = io::BufReader::new(self.dev.recv_bytes());
        let mut line = String::new();
        let nbytes = try!(buf.read_line(&mut line));
        let begin_received = self.client_id.is_some();
        match (try!(RawInputMessage::from_str(&line)), begin_received) {
            (RawInputMessage::Begin { version: _, client_id }, false) => {
                info!("received a begin message from client {}", client_id);
            	self.client_id = Some(client_id);
            	Ok(nbytes)
            },
            (RawInputMessage::Begin { version: _, client_id: _ }, true) => {
				Err(io::Error::new(io::ErrorKind::InvalidData, "begin message already received"))
            }
            (RawInputMessage::WriteLvar { lvar, value }, true) => {
                debug!("received a WRITE_LVAR message from client {}: {} <- {}",
                    self.client_id_str(), lvar, value);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.write(&Var::Named(lvar), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::WriteOffset { offset, value }, true) => {
                debug!("received a WRITE_OFFSET message from client {}: {} <- {}",
                    self.client_id_str(), offset, value);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.write(&Var::Offset(offset), &value)
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveLvar { lvar }, true) => {
                debug!("received a OBSERVE_LVAR message from client {}: {}",
                    self.client_id_str(), lvar);
                try!(self.domains.with_domain("lvar", |dom| {
					dom.subscribe(dev_id, &Var::Named(lvar))
                }));
                Ok(nbytes)
            }
            (RawInputMessage::ObserveOffset { offset }, true) => {
                debug!("received a OBSERVE_OFFSET message from client {}: {}",
                    self.client_id_str(), offset);
                try!(self.domains.with_domain("fsuipc", |dom| {
					dom.subscribe(dev_id, &Var::Offset(offset))
                }));
                Ok(nbytes)
            }
            (_, false) =>  {
                let error = io::Error::new(
                    io::ErrorKind::InvalidData,
                    "unexpected message while waiting for begin");
				Err(error)
            }
        }
    }

    fn client_id_str(&self) -> &str {
        self.client_id
        	.as_ref()
        	.map(|id| id.as_str())
        	.unwrap_or("none")
    }
}

impl DeviceHandler for Oacsp {
    fn device(&mut self) -> &mut Device { &mut self.dev }

    fn process_event(&mut self, event: Event) -> io::Result<()> {
        match event {
            Event::Ready => self.dev.request_read(),
            Event::BytesRead(_) => {
                if self.line_is_ready() {
                    let nread = try!(self.process_input());
                    self.dev.consume_recv_buffer(nread);
                }
                self.dev.request_read()
            }
            Event::BytesWritten(_) => Ok(()),
        }
    }
}

impl Protocol for Oacsp {

    fn send_update(&mut self, domain: &str, variable: Var, value: Value) -> io::Result<()> {
        let raw = try!(match variable {
            Var::Offset(offset) if domain == "fsuipc" =>
                Ok(RawOutputMessage::EventOffset { offset: offset, value: value }),
            Var::Named(ref lvar) if domain == "lvar" =>
                Ok(RawOutputMessage::EventLvar { lvar: lvar.clone(), value: value }),
            _ => {
                Err(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("cannot encode a message for domain '{}', var '{:?}'",
                        domain, variable)))
            }
        });
        let mut buf = Vec::new();
        try!(write!(&mut buf, "{}\n", raw));
        self.dev.request_write(&buf)
    }
}
<|endoftext|>


<commit_before>// rust
// http://rosettacode.org/wiki/foo
// Accepts two command line arguments
// cargo run --name agm arg1 arg2

use std::os;
use std::num;

fn main () {
    let args = os::args();
    let args = args.as_slice();

    let x = from_str::<f32>(args[1].as_slice()).unwrap() ;
    let y = from_str::<f32>(args[2].as_slice()).unwrap() ;

    let result = agm(x,y);
    println!("The arithmetic-geometric mean is {}", result);
}

fn agm (x: f32, y: f32) -> (f32) {
    let e: f32 = 0.000001;
    let mut a = x;
    let mut g = y;
    let mut a1: f32;
    let mut g1: f32;

    loop {
            a1 = (a + g) / 2f32;
            g1 = (a * g).sqrt();
            a = a1;
            g = g1;
            if num::abs( a - g) < e {  return a; }
        }

}

#[test]
fn test_agm_12_6() {
    assert! ( agm(12,6) == 8.740746 );
}
<commit_msg>fixed test. Still no test for command line argument parsing.<commit_after>// rust
// http://rosettacode.org/wiki/foo
// Accepts two command line arguments
// cargo run --name agm arg1 arg2

use std::os;
use std::num;

fn main () {
    let args = os::args();
    let args = args.as_slice();

    let x = from_str::<f32>(args[1].as_slice()).unwrap() ;
    let y = from_str::<f32>(args[2].as_slice()).unwrap() ;

    let result = agm(x,y);
    println!("The arithmetic-geometric mean is {}", result);
}

fn agm (x: f32, y: f32) -> (f32) {
    let e: f32 = 0.000001;
    let mut a = x;
    let mut g = y;
    let mut a1: f32;
    let mut g1: f32;

    loop {
            a1 = (a + g) / 2f32;
            g1 = (a * g).sqrt();
            a = a1;
            g = g1;
            if num::abs( a - g) < e {  return a; }
        }

}

#[test]
fn test_agm_12_6() {
    assert! ( agm(12f32,6f32) == 8.740746f32 );
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(collections)]

#![feature(plugin)]
#![plugin(regex_macros)] extern crate regex;
extern crate git2;
extern crate core;
extern crate rustc_serialize;
extern crate docopt;

use git2::{Repository, Commit, Diff, DiffFormat, Oid};
use docopt::Docopt;
use std::collections::HashMap;
// use std::old_io::BufferedReader;
// use std::old_io::File;
use std::str;
// use regex::Regex;
use rustc_serialize::json;
use std::fmt::Display;
use std::path::Path;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit [--web | <repo> [<old> <new>] | --help]

Examples:
  transit --web             Spawn a web service.
  transit $REPO             Output the results of a revwalk through a repo.
  transit $REPO $ID1 $ID2   Output the data for a pair of commits.
  transit --help            Display this message.

Output is in JSON.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_web: bool,
    arg_repo: Option<String>,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    print!("{:?}", args);

    if args.flag_web {
        unimplemented!();
    } else if let Some(path_string) = args.arg_repo {
        // Pull up the revwalk.
        let path = Path::new(&path_string);
        let repo = Repository::discover(&path)
            .ok().expect("Unable to find repo.");
        // If we can destructures the two optional args into real things.
        if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
            // Compare a specific commit pair.
            // Fist, get the commits. (Error checked)
            let old_id = Oid::from_str(&old_string[..])
                .ok().expect("Couldn't parse old ID");
            let old = repo.find_commit(old_id);
            let new_id = Oid::from_str(&new_string[..])
                .ok().expect("Couldn't parse new ID");
            let new = repo.find_commit(new_id);
            if old.is_ok() && new.is_ok() {
                let (old_commit, new_commit) = (old.unwrap(), new.unwrap());
                let output = find_moves(&repo, &old_commit, &new_commit).unwrap();
                make_json(vec![OutputSet {
                    old: TransitOid(old_id),
                    old_time: old_commit.time().seconds(), // Seconds from Epoch
                    new: TransitOid(new_id),
                    new_time: new_commit.time().seconds(), // Seconds from Epoch
                    outputs: output,
                }]);
            } else {
                panic!("Commit ids were not valid.");
            }
        } else {
            // Revwalk.
            let mut revwalk = repo.revwalk()
                .ok().expect("Unable to get revwalk.");
            // Setup some options.
            revwalk.simplify_first_parent(); // TODO: Maybe remove?
            let mut flags = git2::Sort::empty();
            flags.insert(git2::SORT_TIME);
            flags.insert(git2::SORT_TOPOLOGICAL);
            revwalk.set_sorting(flags);
            // Push HEAD to the revwalk.
            revwalk.push_head()
                .ok().expect("Unable to push HEAD.");
            // We sadly must collect here to use `.windows()`
            let history = revwalk.collect::<Vec<Oid>>();
            let mut output = Vec::with_capacity(history.len());
            // Walk through each pair of commits.
            for pair in history.windows(2) {
                let (old_id, new_id) = (pair[0], pair[1]);
                if let (Ok(old), Ok(new)) = (repo.find_commit(old_id), repo.find_commit(new_id)) {
                    let detected = find_moves(&repo, &old, &new).unwrap();
                    output.push(OutputSet {
                        old: TransitOid(old_id),
                        old_time: old.time().seconds(), // Seconds from Epoch
                        new: TransitOid(new_id),
                        new_time: new.time().seconds(), // Seconds from Epoch
                        outputs: detected,
                    });
                } else {
                    continue;
                }
            }
            make_json(output);
        }
    } else {
        unreachable!();
    }
}

fn make_json(output: Vec<OutputSet>) {
    let out = json::as_pretty_json(&output).indent(4);
    println!("{}", out);
}

#[derive(Debug, PartialEq, Eq)]
enum FoundState {
    Added, Deleted
}

fn format_key(key: String) -> String {
    let remove_whitespace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    let trim = regex!(r"^[\s]+|[\s]+$");
    let result = remove_whitespace.replace_all(&key[..], "");
    trim.replace_all(&result[..], "")
}

#[derive(Debug)]
struct Found {
    filename: String,
    key: String,
    state: FoundState,
    start_position: u32,
    line_count: u32,
}

fn find_additions_and_deletions(diff: Diff) -> Vec<Found> {

    #[derive(Debug)]
    enum State {
        Other, Addition, Deletion
    }

    let mut founds: Vec<Found> = Vec::new();

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();
    let mut old_path = String::new();
    let mut new_path = String::new();

    let mut line_count: u32 = 0;
    let mut start_position: u32 = 0;

    // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
    // It's a bit weird, but I think it will provide the necessary information.
    diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        // if delta.nfiles() != 2 {
        //     // This is diff with only one side, and thus can't have a move.
        //     return true;
        // }
        // assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files. Found {} files.", delta.nfiles());

        // Thinking:
        //  * If is not a hunk, keep going.
        //  * If it's a hunk, do regex magic.
        //  * Stick regex output into a hashmap as a hash.
        //  * Later, we will iterate through and output pased on the values.
        // Filter out all the boring context lines.
        // If we're not interested in this line just return since it will iterate to the next.
        if maybe_hunk.is_none() { return true }; // Return early.

        //dump_diffline(&line);
        //dump_diffdelta(&delta);
        //dump_diffhunk(&maybe_hunk.unwrap());

        old_path = match delta.old_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };
        new_path = match delta.new_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };

        match line.origin() {
            // Additions
            '+' | '>' => {
                // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                // TODO: Is it?
                let line_str = str::from_utf8(line.content()).unwrap_or("");
                added.push_str(line_str);

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                        line_count = 0;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Addition => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Addition;
                true
            },
            // Deletions
            '-' | '<' => {
                // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                // TODO: Is it?
                let line_str = str::from_utf8(line.content()).unwrap_or("");
                deleted.push_str(line_str);

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                        line_count = 0;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Deletion => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Deletion;
                true
            },
            // Other
            _         => {
                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
        }
    }).ok(); // We don't care if we exit early. That's fine.

    // Grab last one.
    match state {
        State::Addition => {
            if added.len() > 0 {
                founds.push(Found {
                    filename: new_path.clone(),
                    key: format_key(added.clone()),
                    state: FoundState::Added,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        State::Deletion => {
            if deleted.len() > 0 {
                founds.push(Found {
                    filename: old_path.clone(),
                    key: format_key(deleted.clone()),
                    state: FoundState::Deleted,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        _ => (),
    }

    return founds;
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, git2::Error> {
    let old_tree = try!(old.tree());
    let new_tree = try!(new.tree());
    // Build up a diff of the two trees.
    let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let founds: Vec<Found> = find_additions_and_deletions(diff);

    let mut moves: Vec<Output> = Vec::new();
    let mut map: HashMap<String, Found> = HashMap::new();

    for f in founds {
        if f.key.len() == 0 { continue; }

        if map.contains_key(&f.key) {
            let q = map.get(&f.key).unwrap();

            // If both states are added, there are no moves.
            // assert!(f.state != q.state, format!("States {:?}, {:?}. Should be Addition/Deletion ", f.state, q.state));
            if f.state == q.state {
                return Ok(Vec::<Output>::new())
            }

            let output: Output;

            match f.state {
                FoundState::Added => {
                    output = Output {
                        old_filename: q.filename.clone(),
                        new_filename: f.filename.clone(),
                        origin_line: q.start_position,
                        destination_line: f.start_position,
                        num_lines: f.line_count,
                    };
                },
                FoundState::Deleted => {
                    output = Output {
                        old_filename: f.filename.clone(),
                        new_filename: q.filename.clone(),
                        origin_line: f.start_position,
                        destination_line: q.start_position,
                        num_lines: f.line_count,
                    };
                },
            }

            moves.push(output);
        } else {
            map.insert(f.key.clone(), f);
        }
    }

    return Ok(moves);
}

#[derive(Debug, RustcEncodable)]
struct OutputSet {
    old: TransitOid,
    old_time: i64, // Seconds from Epoch
    new: TransitOid,
    new_time: i64, // Seconds from Epoch
    outputs: Vec<Output>,
}

#[derive(Debug, RustcEncodable)]
struct Output {
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}

#[derive(Debug, Hash, PartialEq, Eq)]
struct TransitOid(Oid);
impl Display for TransitOid {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        Display::fmt(&self.0, f)
    }
}

impl rustc_serialize::Encodable for TransitOid {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_str(&format!("{}", self)[..])
    }
}
<commit_msg>We'll want state outside of find<commit_after>#![feature(core)]
#![feature(collections)]

#![feature(plugin)]
#![plugin(regex_macros)] extern crate regex;
extern crate git2;
extern crate core;
extern crate rustc_serialize;
extern crate docopt;

use git2::{Repository, Commit, Diff, DiffFormat, Oid};
use docopt::Docopt;
use std::collections::HashMap;
// use std::old_io::BufferedReader;
// use std::old_io::File;
use std::str;
// use regex::Regex;
use rustc_serialize::json;
use std::fmt::Display;
use std::path::Path;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit [--web | <repo> [<old> <new>] | --help]

Examples:
  transit --web             Spawn a web service.
  transit $REPO             Output the results of a revwalk through a repo.
  transit $REPO $ID1 $ID2   Output the data for a pair of commits.
  transit --help            Display this message.

Output is in JSON.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_web: bool,
    arg_repo: Option<String>,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    print!("{:?}", args);

    if args.flag_web {
        unimplemented!();
    } else if let Some(path_string) = args.arg_repo {
        // Pull up the revwalk.
        let path = Path::new(&path_string);
        let repo = Repository::discover(&path)
            .ok().expect("Unable to find repo.");
        // If we can destructures the two optional args into real things.
        if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
            // Compare a specific commit pair.
            // Fist, get the commits. (Error checked)
            let old_id = Oid::from_str(&old_string[..])
                .ok().expect("Couldn't parse old ID");
            let old = repo.find_commit(old_id);
            let new_id = Oid::from_str(&new_string[..])
                .ok().expect("Couldn't parse new ID");
            let new = repo.find_commit(new_id);
            if old.is_ok() && new.is_ok() {
                let (old_commit, new_commit) = (old.unwrap(), new.unwrap());
                let output = find_moves(&repo, &old_commit, &new_commit).unwrap();
                make_json(vec![OutputSet {
                    old: TransitOid(old_id),
                    old_time: old_commit.time().seconds(), // Seconds from Epoch
                    new: TransitOid(new_id),
                    new_time: new_commit.time().seconds(), // Seconds from Epoch
                    outputs: output,
                }]);
            } else {
                panic!("Commit ids were not valid.");
            }
        } else {
            // Revwalk.
            let mut revwalk = repo.revwalk()
                .ok().expect("Unable to get revwalk.");
            // Setup some options.
            revwalk.simplify_first_parent(); // TODO: Maybe remove?
            let mut flags = git2::Sort::empty();
            flags.insert(git2::SORT_TIME);
            flags.insert(git2::SORT_TOPOLOGICAL);
            revwalk.set_sorting(flags);
            // Push HEAD to the revwalk.
            revwalk.push_head()
                .ok().expect("Unable to push HEAD.");
            // We sadly must collect here to use `.windows()`
            let history = revwalk.collect::<Vec<Oid>>();
            let mut output = Vec::with_capacity(history.len());
            // Walk through each pair of commits.
            for pair in history.windows(2) {
                let (old_id, new_id) = (pair[0], pair[1]);
                if let (Ok(old), Ok(new)) = (repo.find_commit(old_id), repo.find_commit(new_id)) {
                    let detected = find_moves(&repo, &old, &new).unwrap();
                    output.push(OutputSet {
                        old: TransitOid(old_id),
                        old_time: old.time().seconds(), // Seconds from Epoch
                        new: TransitOid(new_id),
                        new_time: new.time().seconds(), // Seconds from Epoch
                        outputs: detected,
                    });
                } else {
                    continue;
                }
            }
            make_json(output);
        }
    } else {
        unreachable!();
    }
}

fn make_json(output: Vec<OutputSet>) {
    let out = json::as_pretty_json(&output).indent(4);
    println!("{}", out);
}

#[derive(Debug, PartialEq, Eq)]
enum FoundState {
    Added, Deleted
}

fn format_key(key: String) -> String {
    let remove_whitespace = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.
    let trim = regex!(r"^[\s]+|[\s]+$");
    let result = remove_whitespace.replace_all(&key[..], "");
    trim.replace_all(&result[..], "")
}

#[derive(Debug)]
struct Found {
    filename: String,
    key: String,
    state: FoundState,
    start_position: u32,
    line_count: u32,
}

#[derive(Debug)]
enum State {
    Other, Addition, Deletion
}

fn find_additions_and_deletions(diff: Diff) -> Vec<Found> {

    let mut founds: Vec<Found> = Vec::new();

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();
    let mut old_path = String::new();
    let mut new_path = String::new();

    let mut line_count: u32 = 0;
    let mut start_position: u32 = 0;

    // Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
    // It's a bit weird, but I think it will provide the necessary information.
    diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

        // if delta.nfiles() != 2 {
        //     // This is diff with only one side, and thus can't have a move.
        //     return true;
        // }
        // assert!(delta.nfiles() == 2, "This only works on diffs between exactly 2 files. Found {} files.", delta.nfiles());

        // Thinking:
        //  * If is not a hunk, keep going.
        //  * If it's a hunk, do regex magic.
        //  * Stick regex output into a hashmap as a hash.
        //  * Later, we will iterate through and output pased on the values.
        // Filter out all the boring context lines.
        // If we're not interested in this line just return since it will iterate to the next.
        if maybe_hunk.is_none() { return true }; // Return early.

        //dump_diffline(&line);
        //dump_diffdelta(&delta);
        //dump_diffhunk(&maybe_hunk.unwrap());

        old_path = match delta.old_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };
        new_path = match delta.new_file().path()
        .and_then(|x| x.to_str()) {
            Some(path) => String::from_str(path),
            None => return false,
        };

        match line.origin() {
            // Additions
            '+' | '>' => {
                // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                // TODO: Is it?
                let line_str = str::from_utf8(line.content()).unwrap_or("");
                added.push_str(line_str);

                match state {
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                        line_count = 0;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Addition => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.new_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Addition;
                true
            },
            // Deletions
            '-' | '<' => {
                // If we attempt to unwrap and get `InvalidBytes(_)` it's probably just junk.
                // TODO: Is it?
                let line_str = str::from_utf8(line.content()).unwrap_or("");
                deleted.push_str(line_str);

                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                        line_count = 0;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                    State::Deletion => {
                        line_count += 1;
                    },
                    State::Other => {
                        line_count = 1;
                        start_position = match line.old_lineno() {
                            Some(lineno) => lineno,
                            None => return false, // Can't be a move if deleted.
                        }
                    },
                }

                state = State::Deletion;
                true
            },
            // Other
            _         => {
                match state {
                    State::Addition => {
                        founds.push(Found {
                            filename: new_path.clone(),
                            key: format_key(added.clone()),
                            state: FoundState::Added,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        added = String::new();
                    },
                    State::Deletion => {
                        founds.push(Found {
                            filename: old_path.clone(),
                            key: format_key(deleted.clone()),
                            state: FoundState::Deleted,
                            start_position: start_position,
                            line_count: line_count,
                        });
                        deleted = String::new();
                    },
                    _ => (),
                }

                state = State::Other;
                true
            }
        }
    }).ok(); // We don't care if we exit early. That's fine.

    // Grab last one.
    match state {
        State::Addition => {
            if added.len() > 0 {
                founds.push(Found {
                    filename: new_path.clone(),
                    key: format_key(added.clone()),
                    state: FoundState::Added,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        State::Deletion => {
            if deleted.len() > 0 {
                founds.push(Found {
                    filename: old_path.clone(),
                    key: format_key(deleted.clone()),
                    state: FoundState::Deleted,
                    start_position: start_position,
                    line_count: line_count,
                });
            }
        },
        _ => (),
    }

    return founds;
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, git2::Error> {
    let old_tree = try!(old.tree());
    let new_tree = try!(new.tree());
    // Build up a diff of the two trees.
    let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    let founds: Vec<Found> = find_additions_and_deletions(diff);

    let mut moves: Vec<Output> = Vec::new();
    let mut map: HashMap<String, Found> = HashMap::new();

    for f in founds {
        if f.key.len() == 0 { continue; }

        if map.contains_key(&f.key) {
            let q = map.get(&f.key).unwrap();

            // If both states are added, there are no moves.
            // assert!(f.state != q.state, format!("States {:?}, {:?}. Should be Addition/Deletion ", f.state, q.state));
            if f.state == q.state {
                return Ok(Vec::<Output>::new())
            }

            let output: Output;

            match f.state {
                FoundState::Added => {
                    output = Output {
                        old_filename: q.filename.clone(),
                        new_filename: f.filename.clone(),
                        origin_line: q.start_position,
                        destination_line: f.start_position,
                        num_lines: f.line_count,
                    };
                },
                FoundState::Deleted => {
                    output = Output {
                        old_filename: f.filename.clone(),
                        new_filename: q.filename.clone(),
                        origin_line: f.start_position,
                        destination_line: q.start_position,
                        num_lines: f.line_count,
                    };
                },
            }

            moves.push(output);
        } else {
            map.insert(f.key.clone(), f);
        }
    }

    return Ok(moves);
}

#[derive(Debug, RustcEncodable)]
struct OutputSet {
    old: TransitOid,
    old_time: i64, // Seconds from Epoch
    new: TransitOid,
    new_time: i64, // Seconds from Epoch
    outputs: Vec<Output>,
}

#[derive(Debug, RustcEncodable)]
struct Output {
    old_filename: String,
    new_filename: String,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}

#[derive(Debug, Hash, PartialEq, Eq)]
struct TransitOid(Oid);
impl Display for TransitOid {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        Display::fmt(&self.0, f)
    }
}

impl rustc_serialize::Encodable for TransitOid {
    fn encode<S: rustc_serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_str(&format!("{}", self)[..])
    }
}
<|endoftext|>


<commit_before>extern crate sdl;
extern crate rand;
extern crate time;

use rand::Rng;

use time::PreciseTime;

use sdl::video::{SurfaceFlag, VideoFlag, Surface};
use sdl::event::{Event, Key};

struct Color {
    r: u8,
    g: u8,
    b: u8
}

struct Stars_3D {
    speed: i32,
    spread: i32,
    stars_x: Vec<f32>,
    stars_y: Vec<f32>,
    stars_z: Vec<f32>
}

impl Stars_3D {
    pub fn new(num_stars: usize, _spread: i32, _speed: i32) -> Stars_3D {
        let mut obj = Stars_3D {
            spread: _spread,
            speed: _speed,
            stars_x: vec![0.0; num_stars],
            stars_y: vec![0.0; num_stars],
            stars_z: vec![0.0; num_stars] };

        for idx in 0..num_stars {
            obj.init_star(idx);
        }

        obj
    }

    fn init_star(&mut self, idx: usize) {
        let mut rng = rand::thread_rng();
        self.stars_x[idx] = (2 as f32) * (rng.next_f32() - 0.5) * (self.spread as f32);
        self.stars_y[idx] = (2 as f32) * (rng.next_f32() - 0.5) * (self.spread as f32);
        self.stars_z[idx] = (rng.next_f32() + 0.00001) * (self.spread as f32);
    }

    pub fn update_and_render(&mut self, screen: &Surface, delta: f32) {
        clear(screen);

        let color = Color{r:233, g:233, b:233};
        let halfWidth  = screen.get_width() as f32 / 2.0f32;
        let halfHeight = screen.get_height()as f32 / 2.0f32;

        for i in 0..self.stars_x.len() {
            self.stars_z[i] -= delta * self.speed as f32;

            if(self.stars_z[i] <= 0f32) {
                self.init_star(i);
            }

            let x = (self.stars_x[i]/self.stars_z[i] * halfWidth + halfWidth);
            let y = (self.stars_y[i]/self.stars_z[i] * halfHeight + halfHeight);

            if x < 0f32 || x >= screen.get_width() as f32 || y < 0f32 || y >= screen.get_height() as f32 {
                self.init_star(i);
            } else {
                set_pixel(x as i32, y as i32, &color, &screen);
            }
        }
    }
}

fn clear(screen: &Surface) {
    screen.fill_rect(Some(sdl::Rect {
                x: 0,
                y: 0,
                w: screen.get_width(),
                h: screen.get_height()
            }), sdl::video::Color::RGB(0,0,0));
}

fn set_pixel(_x: i32, _y:i32, color: &Color, screen: &Surface) {

    // println!("{}",screen.get_width());
    let x: usize = _x as usize * 4;
    let y: usize = _y as usize * 4;
    let idx_blue: usize = x + y * screen.get_width() as usize;
    let idx_green: usize = (x + y * screen.get_width() as usize) + 1;
    let idx_red: usize = (x + y * screen.get_width() as usize) + 2;
    let idx_alpha: usize = (x + y * screen.get_width() as usize) + 3;

    screen.with_lock(|pixels| {
        pixels[idx_blue] = color.b;
        pixels[idx_green] = color.g;
        pixels[idx_red] = color.r;
        pixels[idx_alpha] = 0;

        true
    });
}

fn draw_line(x0: i32, y0: i32, x1: i32, y1: i32, color: &Color, screen: &Surface) {
    for x in x0..x1 {
        // println!("{}", x);
        let t: f32 = (x-x0) as f32 /(x1-x0) as f32;
        let y: i32 = (y0 as f32 *(1.0-t) + y1 as f32 *t) as i32;
        // image.set(x, y, color);
        set_pixel(x, y, color, screen);
    }
}

fn main() {
    sdl::init(&[sdl::InitFlag::Video]);
    sdl::wm::set_caption("rust-sdl demo - video", "rust-sdl");

    let mut rng = rand::thread_rng();
    let screen = match sdl::video::set_video_mode(1024, 768, 32,
                                                  &[SurfaceFlag::HWSurface],
                                                  &[VideoFlag::DoubleBuf]) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to set video mode: {}", err)
    };

    // let color = Color{r:233, g:233, b:233};

    // set_pixel(400, 300, &color, &screen);

    // draw_line(10, 10, 100, 100, &color, &screen);

    // draw_line(13, 20, 80, 40,  &color, &screen);
    // draw_line(20, 13, 40, 80,  &color, &screen);
    // draw_line(80, 40, 13, 20,  &color, &screen);

    let mut stars = Stars_3D::new(4000, 64, 20);

    let mut start = PreciseTime::now();

    'main : loop {
        'event : loop {
            match sdl::event::poll_event() {
                Event::Quit => break 'main,
                Event::None => break 'event,
                Event::Key(k, _, _, _)
                    if k == Key::Escape
                        => break 'main,
                _ => { }
            }
        }

        let end = PreciseTime::now();
        let delta = start.to(end);
        start = PreciseTime::now();

        println!("{} ms", delta.num_milliseconds());

        stars.update_and_render(&screen, delta.num_milliseconds() as f32 / 1000f32);
        screen.flip();

    }

    sdl::quit();
}
<commit_msg>format<commit_after>extern crate sdl;
extern crate rand;
extern crate time;

use rand::Rng;

use time::PreciseTime;

use sdl::video::{SurfaceFlag, VideoFlag, Surface};
use sdl::event::{Event, Key};

struct Color {
    r: u8,
    g: u8,
    b: u8
}

struct Stars_3D {
    speed: i32,
    spread: i32,
    stars_x: Vec<f32>, //TODO(dustin): do i need vec here? [f32] ..
    stars_y: Vec<f32>,
    stars_z: Vec<f32>
}

impl Stars_3D {
    pub fn new(num_stars: usize, _spread: i32, _speed: i32) -> Stars_3D {
        let mut obj = Stars_3D {
            spread: _spread,
            speed: _speed,
            stars_x: vec![0.0; num_stars],
            stars_y: vec![0.0; num_stars],
            stars_z: vec![0.0; num_stars] };

        for idx in 0..num_stars {
            obj.init_star(idx);
        }

        obj
    }

    fn init_star(&mut self, idx: usize) {
        let mut rng = rand::thread_rng();
        self.stars_x[idx] = (2 as f32) * (rng.next_f32() - 0.5) * (self.spread as f32);
        self.stars_y[idx] = (2 as f32) * (rng.next_f32() - 0.5) * (self.spread as f32);
        self.stars_z[idx] = (rng.next_f32() + 0.00001) * (self.spread as f32);
    }

    pub fn update_and_render(&mut self, screen: &Surface, delta: f32) {
        clear(screen);

        let color = Color{r:233, g:233, b:233};
        let halfWidth  = screen.get_width() as f32 / 2.0f32;
        let halfHeight = screen.get_height()as f32 / 2.0f32;

        //TODO(dustin):use idiomatic iterator
        for i in 0..self.stars_x.len() {
            self.stars_z[i] -= delta * self.speed as f32;

            if(self.stars_z[i] <= 0f32) {
                self.init_star(i);
            }

            let x = (self.stars_x[i]/self.stars_z[i] * halfWidth + halfWidth);
            let y = (self.stars_y[i]/self.stars_z[i] * halfHeight + halfHeight);

            if x < 0f32 || x >= screen.get_width() as f32 || y < 0f32 || y >= screen.get_height() as f32 {
                self.init_star(i);
            } else {
                set_pixel(x as i32, y as i32, &color, &screen);
            }
        }
    }
}

fn clear(screen: &Surface) {
    screen.fill_rect(Some(sdl::Rect {
                x: 0,
                y: 0,
                w: screen.get_width(),
                h: screen.get_height()
            }), sdl::video::Color::RGB(0,0,0));
}

fn set_pixel(_x: i32, _y:i32, color: &Color, screen: &Surface) {

    // println!("{}",screen.get_width());
    let x: usize = _x as usize * 4;
    let y: usize = _y as usize * 4;
    let idx_blue: usize = x + y * screen.get_width() as usize;
    let idx_green: usize = (x + y * screen.get_width() as usize) + 1;
    let idx_red: usize = (x + y * screen.get_width() as usize) + 2;
    let idx_alpha: usize = (x + y * screen.get_width() as usize) + 3;

    screen.with_lock(|pixels| {
        pixels[idx_blue] = color.b;
        pixels[idx_green] = color.g;
        pixels[idx_red] = color.r;
        pixels[idx_alpha] = 0;

        true
    });
}

//TODO(dustin): scan convert line - this is actually useless – prototyping
fn draw_line(x0: i32, y0: i32, x1: i32, y1: i32, color: &Color, screen: &Surface) {
    for x in x0..x1 {
        // println!("{}", x);
        let t: f32 = (x-x0) as f32 /(x1-x0) as f32;
        let y: i32 = (y0 as f32 *(1.0-t) + y1 as f32 *t) as i32;
        // image.set(x, y, color);
        set_pixel(x, y, color, screen);
    }
}

fn main() {
    sdl::init(&[sdl::InitFlag::Video]);
    sdl::wm::set_caption("pixelcannon", "rust-sdl");

    let mut rng = rand::thread_rng();
    let screen = match sdl::video::set_video_mode(1024, 768, 32,
                                                  &[SurfaceFlag::HWSurface],
                                                  &[VideoFlag::DoubleBuf]) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to set video mode: {}", err)
    };

    // let color = Color{r:233, g:233, b:233};

    // set_pixel(400, 300, &color, &screen);

    // draw_line(10, 10, 100, 100, &color, &screen);

    // draw_line(13, 20, 80, 40,  &color, &screen);
    // draw_line(20, 13, 40, 80,  &color, &screen);
    // draw_line(80, 40, 13, 20,  &color, &screen);

    let mut stars = Stars_3D::new(4000, 64, 20);
    let mut start = PreciseTime::now();

    'main : loop {
        'event : loop {
            match sdl::event::poll_event() {
                Event::Quit => break 'main,
                Event::None => break 'event,
                Event::Key(k, _, _, _)
                    if k == Key::Escape
                        => break 'main,
                _ => { }
            }
        }

        let end = PreciseTime::now();
        let delta = start.to(end);
        start = PreciseTime::now();

        println!("{} ms", delta.num_milliseconds());

        stars.update_and_render(&screen, delta.num_milliseconds() as f32 / 1000f32);
        screen.flip();
    }

    sdl::quit();
}
<|endoftext|>


<commit_before>use std::io::{BufferedReader, File, stdin};
use std::num::FromPrimitive;
use std::os;

static BUFFER_SIZE: uint = 100;

#[allow(non_camel_case_types)]
#[deriving(PartialEq, FromPrimitive, Show)]
enum Foptypes{
    GF_FOP_NULL=0,
    GF_FOP_MKNOD=3,
    GF_FOP_MKDIR=4,
    GF_FOP_UNLINK=5,
    GF_FOP_RMDIR=6,
    GF_FOP_SYMLINK=7,
    GF_FOP_RENAME=8,
    GF_FOP_LINK=9,
    GF_FOP_SETXATTR=17,
    GF_FOP_REMOVEXATTR=19,
    GF_FOP_CREATE=23,
    GF_FOP_SETATTR=38,
}

impl Foptypes{
    fn get(&self) -> &str{
        match *self{
            GF_FOP_NULL => "NULL",
            GF_FOP_MKNOD => "MKNOD",
            GF_FOP_MKDIR => "MKDIR",
            GF_FOP_UNLINK => "UNLINK",
            GF_FOP_RMDIR => "RMDIR",
            GF_FOP_SYMLINK => "SYMLINK",
            GF_FOP_RENAME => "RENAME",
            GF_FOP_LINK => "LINK",
            GF_FOP_SETXATTR => "SETXATTR",
            GF_FOP_REMOVEXATTR => "REMOVEXATTR",
            GF_FOP_CREATE => "CREATE",
            GF_FOP_SETATTR => "SETATTR"
        }
    }

    fn num_fields(&self) -> uint{
        match *self{
            GF_FOP_NULL => 3,
            GF_FOP_MKNOD => 7,
            GF_FOP_MKDIR => 7,
            GF_FOP_UNLINK => 4,
            GF_FOP_RMDIR => 4,
            GF_FOP_SYMLINK => 4,
            GF_FOP_RENAME => 5,
            GF_FOP_LINK => 4,
            GF_FOP_SETXATTR => 3,
            GF_FOP_REMOVEXATTR => 3,
            GF_FOP_CREATE => 7,
            GF_FOP_SETATTR => 3
        }
    }
}

#[deriving(Show)]
struct Parser{
    record_complete: bool,
    fop_type: char,
    token: String,
    tokens: Vec<String>,
    sep: char
}

impl Parser{
    fn new() -> Parser{
        Parser{record_complete: true,
               fop_type: '.',
               token: "".to_string(),
               tokens: Vec::new(),
               sep: '\x00'}
    }

    fn get_num_tokens(&self) -> uint {
        match self.fop_type{
            'E' => 7,
            'M' => 3,
            'D' => 2,
            _ => 0
        }
    }

    fn get_fop_enum(&self, v: String) -> Option<Foptypes>{
        let key:int = from_str::<int>(v.as_slice()).unwrap();
        let ty: Option<Foptypes> = FromPrimitive::from_int(key);
        return ty;
    }

    fn process_record(&self){
        let mut op: String = String::new();
        for (i, v) in self.tokens.iter().enumerate(){
            if (self.fop_type == 'E' || self.fop_type == 'M') && i == 2 {
                let ty: Option<Foptypes> = self.get_fop_enum(v.clone());
                match ty{
                    Some(aa) => {
                        op.push_str(aa.get());
                        op.push_str(" ");
                    },
                    None => {}
                }
            }
            else{
                op.push_str(v.as_slice());
                op.push_str(" ");
            }
        }
        println!("{}", op.as_slice().trim_chars(' '));
    }

    fn if_record_complete(&mut self){
        if self.token.as_slice() != ""{
            self.tokens.push(self.token.clone());
            self.token = "".to_string();
        }

        let mut num_tokens: uint = self.get_num_tokens();
        if (self.fop_type == 'E' || self.fop_type == 'M') && self.tokens.len() >= 3{
            let ty: Option<Foptypes> = self.get_fop_enum(self.tokens[2].clone());
            num_tokens = match ty{
                Some(aa) => aa.num_fields(),
                None => 0
            }
        }

        if num_tokens == self.tokens.len(){
            self.record_complete = true;
            self.process_record();
            self.tokens = Vec::new();
        }
    }

    fn parse_chunk(&mut self, chunk: &str){
        for c in chunk.chars(){
            if self.record_complete && (c == 'E' || c == 'M' || c == 'D'){
                self.fop_type = c;
                self.tokens.push(String::from_char(1, c));
                self.record_complete = false;
                continue;
            }

            if c == self.sep{
                self.if_record_complete();
            }
            else{
                self.token.push_str(String::from_char(1, c).as_slice());
            }
        }
    }

    fn parse<T:Reader>(&mut self, mut reader: BufferedReader<T>){
        let buf:&mut [u8] = [0, ..BUFFER_SIZE];
        // read and ignore header
        let _header = reader.read_line();

        loop{
            match reader.read(buf) {
                Ok(nread) => self.parse_chunk(std::str::from_utf8(buf.slice(0, nread)).unwrap()),
                Err(_e) => {
                    break;
                }
            }
        }
        // process last record
        self.if_record_complete();
    }
}

fn main() {
    let mut parser = Parser::new();
    let reader = stdin();
    if reader.get_ref().isatty(){
        let args = os::args();
        let file = File::open(&Path::new(args[1].clone()));
        let reader_file = BufferedReader::new(file);
        parser.parse(reader_file);
    }
    else{
        parser.parse(reader);
    }
}
<commit_msg>Added error handling while opening of file.<commit_after>use std::io::{BufferedReader, File, stdin, EndOfFile};
use std::num::FromPrimitive;
use std::os;

static BUFFER_SIZE: uint = 100;

#[allow(non_camel_case_types)]
#[deriving(PartialEq, FromPrimitive, Show)]
enum Foptypes{
    GF_FOP_NULL=0,
    GF_FOP_MKNOD=3,
    GF_FOP_MKDIR=4,
    GF_FOP_UNLINK=5,
    GF_FOP_RMDIR=6,
    GF_FOP_SYMLINK=7,
    GF_FOP_RENAME=8,
    GF_FOP_LINK=9,
    GF_FOP_SETXATTR=17,
    GF_FOP_REMOVEXATTR=19,
    GF_FOP_CREATE=23,
    GF_FOP_SETATTR=38,
}

impl Foptypes{
    fn get(&self) -> &str{
        match *self{
            GF_FOP_NULL => "NULL",
            GF_FOP_MKNOD => "MKNOD",
            GF_FOP_MKDIR => "MKDIR",
            GF_FOP_UNLINK => "UNLINK",
            GF_FOP_RMDIR => "RMDIR",
            GF_FOP_SYMLINK => "SYMLINK",
            GF_FOP_RENAME => "RENAME",
            GF_FOP_LINK => "LINK",
            GF_FOP_SETXATTR => "SETXATTR",
            GF_FOP_REMOVEXATTR => "REMOVEXATTR",
            GF_FOP_CREATE => "CREATE",
            GF_FOP_SETATTR => "SETATTR"
        }
    }

    fn num_fields(&self) -> uint{
        match *self{
            GF_FOP_NULL => 3,
            GF_FOP_MKNOD => 7,
            GF_FOP_MKDIR => 7,
            GF_FOP_UNLINK => 4,
            GF_FOP_RMDIR => 4,
            GF_FOP_SYMLINK => 4,
            GF_FOP_RENAME => 5,
            GF_FOP_LINK => 4,
            GF_FOP_SETXATTR => 3,
            GF_FOP_REMOVEXATTR => 3,
            GF_FOP_CREATE => 7,
            GF_FOP_SETATTR => 3
        }
    }
}

#[deriving(Show)]
struct Parser{
    record_complete: bool,
    fop_type: char,
    token: String,
    tokens: Vec<String>,
    sep: char
}

impl Parser{
    fn new() -> Parser{
        Parser{record_complete: true,
               fop_type: '.',
               token: "".to_string(),
               tokens: Vec::new(),
               sep: '\x00'}
    }

    fn get_num_tokens(&self) -> uint {
        match self.fop_type{
            'E' => 7,
            'M' => 3,
            'D' => 2,
            _ => 0
        }
    }

    fn get_fop_enum(&self, v: String) -> Option<Foptypes>{
        let key:int = from_str::<int>(v.as_slice()).unwrap();
        let ty: Option<Foptypes> = FromPrimitive::from_int(key);
        return ty;
    }

    fn process_record(&self){
        let mut op: String = String::new();
        for (i, v) in self.tokens.iter().enumerate(){
            if (self.fop_type == 'E' || self.fop_type == 'M') && i == 2 {
                let ty: Option<Foptypes> = self.get_fop_enum(v.clone());
                match ty{
                    Some(aa) => {
                        op.push_str(aa.get());
                        op.push_str(" ");
                    },
                    None => {}
                }
            }
            else{
                op.push_str(v.as_slice());
                op.push_str(" ");
            }
        }
        println!("{}", op.as_slice().trim_chars(' '));
    }

    fn if_record_complete(&mut self){
        if self.token.as_slice() != ""{
            self.tokens.push(self.token.clone());
            self.token = "".to_string();
        }

        let mut num_tokens: uint = self.get_num_tokens();
        if (self.fop_type == 'E' || self.fop_type == 'M') && self.tokens.len() >= 3{
            let ty: Option<Foptypes> = self.get_fop_enum(self.tokens[2].clone());
            num_tokens = match ty{
                Some(aa) => aa.num_fields(),
                None => 0
            }
        }

        if num_tokens == self.tokens.len(){
            self.record_complete = true;
            self.process_record();
            self.tokens = Vec::new();
        }
    }

    fn parse_chunk(&mut self, chunk: &str){
        for c in chunk.chars(){
            if self.record_complete && (c == 'E' || c == 'M' || c == 'D'){
                self.fop_type = c;
                self.tokens.push(String::from_char(1, c));
                self.record_complete = false;
                continue;
            }

            if c == self.sep{
                self.if_record_complete();
            }
            else{
                self.token.push_str(String::from_char(1, c).as_slice());
            }
        }
    }

    fn parse<T:Reader>(&mut self, mut reader: BufferedReader<T>){
        let buf:&mut [u8] = [0, ..BUFFER_SIZE];
        // read and ignore header
        let _header = reader.read_line();

        loop{
            match reader.read(buf) {
                Ok(nread) => self.parse_chunk(std::str::from_utf8(buf.slice(0, nread)).unwrap()),
                Err(e) => match e.kind{
                    EndOfFile => break,
                    _ => {
                        fail!("ERROR: {}", e);
                    }
                }
            }
        }
        // process last record
        self.if_record_complete();
    }
}

fn main() {
    let mut parser = Parser::new();
    let reader = stdin();
    if reader.get_ref().isatty(){
        let args = os::args();
        let file = File::open(&Path::new(args[1].clone()));
        let reader_file = BufferedReader::new(file);
        parser.parse(reader_file);
    }
    else{
        parser.parse(reader);
    }
}
<|endoftext|>


<commit_before>use crossbeam_channel::bounded;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::ExitCode;
use std::thread;
use std::vec::Vec;

#[derive(Debug)]
struct TranscodeJob {
    src: PathBuf,
    dest: PathBuf,
}

fn main() -> Result<(), ExitCode> {
    let (srcdir, destdir) = parse_args()?;

    println!(
        "videos src: {} dest: {}",
        srcdir.display(),
        destdir.display()
    );

    if srcdir == destdir {
        println!("src and dest dirs must differ");
        return Err(ExitCode::FAILURE);
    }

    thread::scope(|s| {
        const NTHREADS: i32 = 6;

        let (job_sender, job_recv) = bounded::<TranscodeJob>(0);

        for i in 0..NTHREADS {
            let job_recv = job_recv.clone();
            s.spawn(move || transcoder(i, job_recv));
        }

        jobscheduler(srcdir.as_path(), destdir.as_path(), job_sender);
    });

    println!("done");

    Ok(())
}

fn jobscheduler(srcdir: &Path, destdir: &Path, job_sender: Sender<TranscodeJob>) {
    println!("starting job scheduler");

    let entries = fs::read_dir(srcdir).unwrap();
    let mut srcs = Vec::new();

    for entry in entries {
        match entry {
            Ok(entry) => {
                let srcdir = entry.path();
                if let Some(extension) = srcdir.extension() {
                    let extension = extension
                        .to_str()
                        .expect("file extensions should be valid utf-8");

                    match extension {
                        "avi" => {
                            srcs.push(srcdir);
                        }
                        _ => {
                            println!("ignoring file with extension: {}", extension)
                        }
                    }
                }
            }
            Err(err) => {
                print!("error reading dir {}: {}", srcdir.display(), err);
            }
        }
    }

    for src in srcs {
        // We already validated srcs, so they always have a filename
        let dest_file = src.with_extension("mkv");
        let mut dest = destdir.to_path_buf();
        dest.push(dest_file.file_name().unwrap());

        let job = TranscodeJob {
            src: src,
            dest: dest,
        };
        job_sender.send(job).expect("failed to send job to workers");
    }

    drop(job_sender);
}

fn transcoder(id: i32, job_recv: Receiver<TranscodeJob>) {
    println!("starting transcoder {}", id);

    while let Ok(job) = job_recv.recv() {
        println!(
            "transcoder {} transcoding from {} to {}",
            id,
            job.src.display(),
            job.dest.display()
        );

        let pipeline = format!(
            r#"
                gst-launch-1.0 filesrc location="{}" ! decodebin name=decoder ! \
                queue ! videoconvert ! videoscale ! videorate ! x264enc ! queue ! \
                matroskamux name=muxer ! queue ! filesink location="{}" decoder. ! \
                queue ! audioconvert ! audioresample ! vorbisenc ! queue ! muxer.
            "#,
            job.src.display(),
            job.dest.display(),
        );

        println!("running: {}", pipeline);
        let mut cmd = Command::new("sh");
        cmd.arg("-c");
        cmd.arg(pipeline);

        match cmd.spawn() {
            Ok(mut child) => match child.wait() {
                Ok(res) => {
                    println!("transcoder result: {}", res);
                }
                Err(err) => {
                    println!("error running transcoder: {}", err);
                }
            },
            Err(err) => {
                println!("error starting transcoder: {}", err);
            }
        }
    }

    println!("finishing transcoder {}", id);
}

fn parse_args() -> Result<(PathBuf, PathBuf), ExitCode> {
    let args: Vec<String> = env::args().collect();

    return match args.len() {
        1 => {
            let src = env::current_dir().unwrap();
            let mut dest = src.clone();
            dest.push("transcoded");
            Ok((src, dest))
        }
        3 => Ok((PathBuf::from(&args[1]), PathBuf::from(&args[2]))),
        _ => {
            println!("usage using current dir: {}", args[0]);
            println!("usage explicit dirs: {} <src dir> <dest dir>", args[0]);
            Err(ExitCode::FAILURE)
        }
    };
}
<commit_msg>chore: use ffmpeg to increase framerate<commit_after>use crossbeam_channel::bounded;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::ExitCode;
use std::thread;
use std::vec::Vec;

#[derive(Debug)]
struct TranscodeJob {
    src: PathBuf,
    dest: PathBuf,
}

fn main() -> Result<(), ExitCode> {
    let (srcdir, destdir) = parse_args()?;

    println!(
        "videos src: {} dest: {}",
        srcdir.display(),
        destdir.display()
    );

    if srcdir == destdir {
        println!("src and dest dirs must differ");
        return Err(ExitCode::FAILURE);
    }

    thread::scope(|s| {
        const NTHREADS: i32 = 6;

        let (job_sender, job_recv) = bounded::<TranscodeJob>(0);

        for i in 0..NTHREADS {
            let job_recv = job_recv.clone();
            s.spawn(move || transcoder(i, job_recv));
        }

        jobscheduler(srcdir.as_path(), destdir.as_path(), job_sender);
    });

    println!("done");

    Ok(())
}

fn jobscheduler(srcdir: &Path, destdir: &Path, job_sender: Sender<TranscodeJob>) {
    println!("starting job scheduler");

    let entries = fs::read_dir(srcdir).unwrap();
    let mut srcs = Vec::new();

    for entry in entries {
        match entry {
            Ok(entry) => {
                let srcdir = entry.path();
                if let Some(extension) = srcdir.extension() {
                    let extension = extension
                        .to_str()
                        .expect("file extensions should be valid utf-8");

                    match extension {
                        "avi" => {
                            srcs.push(srcdir);
                        }
                        _ => {
                            println!("ignoring file with extension: {}", extension)
                        }
                    }
                }
            }
            Err(err) => {
                print!("error reading dir {}: {}", srcdir.display(), err);
            }
        }
    }

    for src in srcs {
        // We already validated srcs, so they always have a filename
        let dest_file = src.with_extension("mkv");
        let mut dest = destdir.to_path_buf();
        dest.push(dest_file.file_name().unwrap());

        let job = TranscodeJob {
            src: src,
            dest: dest,
        };
        job_sender.send(job).expect("failed to send job to workers");
    }

    drop(job_sender);
}

fn transcoder(id: i32, job_recv: Receiver<TranscodeJob>) {
    println!("starting transcoder {}", id);

    while let Ok(job) = job_recv.recv() {
        println!(
            "transcoder {} transcoding from {} to {}",
            id,
            job.src.display(),
            job.dest.display()
        );

        // I usually go with Gstreamer, but it does not have a proper
        // way to increase the framerate. So I used ffmpeg.
        //let pipeline = format!(
        //r#"
        //gst-launch-1.0 filesrc location="{}" ! decodebin name=decoder ! \
        //queue ! videoconvert ! videoscale ! videorate ! x264enc ! queue ! \
        //matroskamux name=muxer ! queue ! filesink location="{}" decoder. ! \
        //queue ! audioconvert ! audioresample ! vorbisenc ! queue ! muxer.
        //"#,
        //job.src.display(),
        //job.dest.display(),
        //);

        let mut cmd = Command::new("ffmpeg");
        cmd.arg("-i");
        cmd.arg(job.src);
        cmd.arg("-crf");
        cmd.arg("10");
        cmd.arg("-filter:v");
        cmd.arg("tblend");
        cmd.arg("-r");
        cmd.arg("60");
        cmd.arg(job.dest);

        match cmd.spawn() {
            Ok(mut child) => match child.wait() {
                Ok(res) => {
                    println!("transcoder result: {}", res);
                }
                Err(err) => {
                    println!("error running transcoder: {}", err);
                }
            },
            Err(err) => {
                println!("error starting transcoder: {}", err);
            }
        }
    }

    println!("finishing transcoder {}", id);
}

fn parse_args() -> Result<(PathBuf, PathBuf), ExitCode> {
    let args: Vec<String> = env::args().collect();

    return match args.len() {
        1 => {
            let src = env::current_dir().unwrap();
            let mut dest = src.clone();
            dest.push("transcoded");
            Ok((src, dest))
        }
        3 => Ok((PathBuf::from(&args[1]), PathBuf::from(&args[2]))),
        _ => {
            println!("usage using current dir: {}", args[0]);
            println!("usage explicit dirs: {} <src dir> <dest dir>", args[0]);
            Err(ExitCode::FAILURE)
        }
    };
}
<|endoftext|>