<commit_before>use std::path::{PathBuf, Path};
use std::collections::{HashSet, HashMap};
use std::fs::File;
use std::io::Read;
use std::io::{BufReader, BufRead};
use cargo::core::{Workspace, Package};
use cargo::sources::PathSource;
use cargo::util::Config as CargoConfig;
use syn::{parse_file, Item, ItemMod, ItemFn, Ident, Meta, NestedMeta, Stmt};
use proc_macro2::Span;
use regex::Regex;
use config::Config;

/// Represents the results of analysis of a single file. Does not store the file
/// in question as this is expected to be maintained by the user.
#[derive(Clone, Debug)]
pub struct LineAnalysis {
    /// This represents lines that should be ignored in coverage
    /// but may be identifed as coverable in the DWARF tables
    pub ignore: HashSet<usize>,
    /// This represents lines that should be included in coverage
    /// But may be ignored.
    pub cover: HashSet<usize>,
}

/// When the LineAnalysis results are mapped to their files there needs to be
/// an easy way to get the information back. For the container used implement
/// this trait
pub trait SourceAnalysisQuery {
    fn should_ignore(&self, path: &Path, l:&usize) -> bool;
}

impl SourceAnalysisQuery for HashMap<PathBuf, LineAnalysis> {

    fn should_ignore(&self, path: &Path, l:&usize) -> bool {
        if self.contains_key(path) {
            self.get(path).unwrap().ignore.contains(l)
        } else {
            false
        }
    }

}

impl LineAnalysis {
    fn new() -> LineAnalysis {
        LineAnalysis {
            ignore: HashSet::new(),
            cover: HashSet::new()
        }
    }

    /// Adds the lines of the provided span to the ignore set
    pub fn ignore_span(&mut self, span: &Span) {
        for i in span.start().line..(span.end().line+1) {
            self.ignore.insert(i);
            if self.cover.contains(&i) {
                self.cover.remove(&i);
            }
        }
    }

    /// Adds the lines of the provided span to the cover set
    pub fn cover_span(&mut self, span: &Span) {
        for i in span.start().line..(span.end().line +1) {
            if !self.ignore.contains(&i) {
                self.cover.insert(i);
            }
        }
    }

    pub fn should_ignore(&self, line: &usize) -> bool {
        self.ignore.contains(line)
    }

    fn add_to_ignore(&mut self, lines: &[usize]) {
        for l in lines {
            self.ignore.insert(*l);
            if self.cover.contains(l) {
                self.cover.remove(l);
            }
        }
    }

    fn add_to_cover(&mut self, lines: &[usize]) {
        for l in lines {
            if !self.ignore.contains(l) {
                self.cover.insert(*l);
            }
        }
    }
}

/// Returns a list of files and line numbers to ignore (not indexes!)
pub fn get_line_analysis(project: &Workspace, config: &Config) -> HashMap<PathBuf, LineAnalysis> {
    let mut result: HashMap<PathBuf, LineAnalysis> = HashMap::new();
    // Members iterates over all non-virtual packages in the workspace
    for pkg in project.members() {
        if config.packages.is_empty() || config.packages.contains(&pkg.name().to_string()) {
            analyse_package(pkg, &config, project.config(), &mut result);
        }
    }
    result
}

fn analyse_lib_rs(file: &Path, result: &mut HashMap<PathBuf, LineAnalysis>) {
    if let Ok(f) = File::open(file) {
        let mut read_file = BufReader::new(f);
        if let Some(Ok(first)) = read_file.lines().nth(0) {
            if !(first.starts_with("pub") || first.starts_with("fn")) {
                let file = file.to_path_buf();
                if result.contains_key(&file) {
                    let l = result.get_mut(&file).unwrap();
                    l.add_to_ignore(&[1]);
                } else {
                    let mut l = LineAnalysis::new();
                    l.add_to_ignore(&[1]);
                    result.insert(file, l);
                }
            }
        }
    }
}

fn analyse_package(pkg: &Package,
                   config:&Config,
                   cargo_conf: &CargoConfig,
                   result: &mut HashMap<PathBuf, LineAnalysis>) {

    let mut src = PathSource::new(pkg.root(), pkg.package_id().source_id(), cargo_conf);
    if let Ok(package) = src.root_package() {
        for target in package.targets() {
            let path = target.src_path();
            let file = match path.to_str() {
                Some(s) => s,
                _ => continue
            };
            let skip_cause_test = config.ignore_tests &&
                                  path.starts_with(pkg.root().join("tests"));
            let skip_cause_example = path.starts_with(pkg.root().join("examples"));
            if !(skip_cause_test || skip_cause_example)  {
                let file = File::open(file);
                let mut file = match file {
                    Ok(f) => f,
                    _ => continue,
                };
                let mut content = String::new();
                let _ = file.read_to_string(&mut content);
                let file = parse_file(&content);
                if let Ok(file) = file {
                    let mut analysis = LineAnalysis::new();
                    find_ignorable_lines(&content, &mut analysis);
                    process_items(&file.items, config, &mut analysis);
                    // Check there's no conflict!
                    result.insert(path.to_path_buf(), analysis);
                }
            }
            // This could probably be done with the DWARF if I could find a discriminating factor
            // to why lib.rs:1 shows up as a real line!
            if file.ends_with("src/lib.rs") {
                analyse_lib_rs(path, result);
            }
        }
    }
}

fn find_ignorable_lines(content: &str, analysis: &mut LineAnalysis) {
    let lines = content.lines()
                       .enumerate()
                       .filter(|(_, x)| !x.chars().any(|x| !"(){}[]?;\t ,".contains(x)))
                       .map(|(i, _)| i+1)
                       .collect::<Vec<usize>>();
    analysis.add_to_ignore(&lines);
}

fn process_items(items: &[Item], config: &Config, analysis: &mut LineAnalysis) {
    for item in items {
        match item {
            Item::ExternCrate(i) => analysis.ignore_span(&i.extern_token.0),
            Item::Use(i) => analysis.ignore_span(&i.use_token.0),
            Item::Mod(i) => visit_mod(i, analysis, config),
            Item::Fn(i) => visit_fn(i, analysis, config),
            _ =>{}
        }
    }
}

fn process_statements(stmts: &[Stmt], config: &Config, analysis: &mut LineAnalysis) {
    for stmt in stmts {
        match stmt {
            Stmt::Item(i) => process_items(&[i.clone()], config, analysis),
            _ => {},
        }
    }
}

fn visit_mod(module: &ItemMod, analysis: &mut LineAnalysis, config: &Config) {
    analysis.ignore_span(&module.mod_token.0);
    let mut check_insides = true;
    for attr in &module.attrs {
        if let Some(Meta::List(ref ml)) = attr.interpret_meta() {
            if ml.ident != Ident::from("cfg") {
                continue;
            }
            for nested in &ml.nested {
                if let NestedMeta::Meta(Meta::Word(i)) = nested {
                    if i == &Ident::from("test") {
                        check_insides = false;
                        analysis.ignore_span(&module.mod_token.0);
                        if let Some((ref braces, _)) = module.content {
                            analysis.ignore_span(&braces.0);
                        }
                    }
                }
            }
        }
    }
    if check_insides {
        if let Some((_, ref items)) = module.content {
            process_items(items, config, analysis);
        }
    }
}

fn visit_fn(func: &ItemFn, analysis: &mut LineAnalysis, config: &Config) {
    // Need to read the nested meta.. But this should work for fns
    let mut ignore_func = false;
    for attr in &func.attrs {
        if let Some(x) = attr.interpret_meta() {
            let id = x.name();
            if id == Ident::from("test") {
                ignore_func = true;
            } else if id == Ident::from("derive") {
                analysis.ignore_span(&attr.bracket_token.0);
            }
        }
    }
    if ignore_func && config.ignore_tests {
        analysis.ignore_span(&func.decl.fn_token.0);
        analysis.ignore_span(&func.block.brace_token.0);
    } else {
        process_statements(&func.block.stmts, config, analysis);
    }
}

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

    #[test]
    fn filter_str_literals() {
        let mut lines = LineAnalysis::new();
        let config = Config::default();
        let parser = parse_file("fn test() {\nwriteln!(#\"test\n\ttest\n\ttest\"#);\n}\n").unwrap();
        process_items(&parser.items, &config, &mut lines);
        assert!(lines.ignore.len() > 1);
        assert!(lines.ignore.contains(&3));
        assert!(lines.ignore.contains(&4));

        let parser = parse_file("fn test() {\nwrite(\"test\ntest\ntest\");\n}\nfn write(s:&str){}").unwrap();
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &config, &mut lines);
        assert!(lines.ignore.len() > 1);
        assert!(lines.ignore.contains(&3));
        assert!(lines.ignore.contains(&4));

        let mut lines = LineAnalysis::new();
        let parser = parse_file("\n\nfn test() {\nwriteln!(\n#\"test\"#\n);\n}\n").unwrap();
        process_items(&parser.items, &config, &mut lines);
        assert!(lines.ignore.contains(&5));
    }

    #[test]
    fn filter_struct_members() {
        let config = Config::default();
        let mut lines = LineAnalysis::new();
        let parser = parse_file("#[derive(Debug)]\npub struct Struct {\npub i: i32,\nj:String,\n}").unwrap();
        process_items(&parser.items, &config, &mut lines);

        assert_eq!(lines.ignore.len(), 3);
        assert!(lines.ignore.contains(&1));
        assert!(lines.ignore.contains(&3));
        assert!(lines.ignore.contains(&4));
    }

    #[test]
    fn filter_struct_consts() {
        let config = Config::default();
        let mut lines = LineAnalysis::new();
        let parser = parse_file("struct T{x:String, y:i32}\nfn test()-> T {\nT{\nx:\"hello\".to_string(),\ny:4,\n}\n}").unwrap();
        process_items(&parser.items, &config, &mut lines);

        assert!(lines.ignore.contains(&5));
    }

    #[test]
    fn filter_mods() {
        let config = Config::default();
        let mut lines = LineAnalysis::new();
        let parser = parse_file("mod foo {\nfn double(x:i32)->i32 {\n x*2\n}\n}").unwrap();
        process_items(&parser.items, &config, &mut lines);
        assert!(!lines.ignore.contains(&3));

        let mut lines = LineAnalysis::new();
        let parser = parse_file("mod foo{}").unwrap();
        process_items(&parser.items, &config, &mut lines);
        assert!(lines.ignore.contains(&1));
    }

    #[test]
    fn filter_macros() {
        let config = Config::default();
        let mut lines = LineAnalysis::new();
        let parser = parse_file("\n\nfn unused() {\nunimplemented!();\n}").unwrap();
        process_items(&parser.items, &config, &mut lines);

        // Braces should be ignored so number could be higher
        assert!(lines.ignore.len() >= 1);
        assert!(lines.ignore.contains(&4));

        let mut lines = LineAnalysis::new();
        let parser = parse_file("fn unused() {\nunreachable!();\n}").unwrap();
        process_items(&parser.items, &config, &mut lines);
        assert!(lines.ignore.len() >= 1);
        assert!(lines.ignore.contains(&2));

        let mut lines = LineAnalysis::new();
        let parser = parse_file("fn unused() {\nprintln!(\"text\");\n}").unwrap();
        process_items(&parser.items, &config, &mut lines);
        assert!(!lines.ignore.contains(&2));
    }

    #[test]
    fn filter_tests() {
        let mut config = Config::default();
        let mut lines = LineAnalysis::new();
        let src_string = "#[cfg(test)]\nmod tests {\n fn boo(){\nassert!(true);\n}\n}";
        let parser = parse_file(src_string).unwrap();
        process_items(&parser.items, &config, &mut lines);
        assert!(!lines.ignore.contains(&4));

        config.ignore_tests = true;
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &config, &mut lines);
        assert!(lines.ignore.contains(&4));

        let src_string = "#[test]\nfn mytest() { \n assert!(true);\n}";
        let parser = parse_file(src_string).unwrap();
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &config, &mut lines);
        assert!(!lines.ignore.contains(&2));
        assert!(!lines.ignore.contains(&3));

        config.ignore_tests = true;
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &config, &mut lines);
        assert!(lines.ignore.contains(&2));
        assert!(lines.ignore.contains(&3));

    }
}
<commit_msg>added whole file context<commit_after>use std::path::{PathBuf, Path};
use std::collections::{HashSet, HashMap};
use std::fs::File;
use std::io::Read;
use std::io::{BufReader, BufRead};
use cargo::core::{Workspace, Package};
use cargo::sources::PathSource;
use cargo::util::Config as CargoConfig;
use syn::{parse_file, Item, ItemMod, ItemFn, Ident, Meta, NestedMeta, Stmt};
use proc_macro2::Span;
use regex::Regex;
use config::Config;

/// Represents the results of analysis of a single file. Does not store the file
/// in question as this is expected to be maintained by the user.
#[derive(Clone, Debug)]
pub struct LineAnalysis {
    /// This represents lines that should be ignored in coverage
    /// but may be identifed as coverable in the DWARF tables
    pub ignore: HashSet<usize>,
    /// This represents lines that should be included in coverage
    /// But may be ignored.
    pub cover: HashSet<usize>,
}

/// When the LineAnalysis results are mapped to their files there needs to be
/// an easy way to get the information back. For the container used implement
/// this trait
pub trait SourceAnalysisQuery {
    fn should_ignore(&self, path: &Path, l:&usize) -> bool;
}

impl SourceAnalysisQuery for HashMap<PathBuf, LineAnalysis> {

    fn should_ignore(&self, path: &Path, l:&usize) -> bool {
        if self.contains_key(path) {
            self.get(path).unwrap().ignore.contains(l)
        } else {
            false
        }
    }

}

impl LineAnalysis {
    /// Creates a new LineAnalysis object
    fn new() -> LineAnalysis {
        LineAnalysis {
            ignore: HashSet::new(),
            cover: HashSet::new()
        }
    }

    /// Adds the lines of the provided span to the ignore set
    pub fn ignore_span(&mut self, span: &Span) {
        for i in span.start().line..(span.end().line+1) {
            self.ignore.insert(i);
            if self.cover.contains(&i) {
                self.cover.remove(&i);
            }
        }
    }

    /// Adds the lines of the provided span to the cover set
    pub fn cover_span(&mut self, span: &Span) {
        for i in span.start().line..(span.end().line +1) {
            if !self.ignore.contains(&i) {
                self.cover.insert(i);
            }
        }
    }

    /// Shows whether the line should be ignored by tarpaulin
    pub fn should_ignore(&self, line: &usize) -> bool {
        self.ignore.contains(line)
    }

    /// Adds a line to the list of lines to ignore
    fn add_to_ignore(&mut self, lines: &[usize]) {
        for l in lines {
            self.ignore.insert(*l);
            if self.cover.contains(l) {
                self.cover.remove(l);
            }
        }
    }

    /// Adds a line to the list of lines to cover
    fn add_to_cover(&mut self, lines: &[usize]) {
        for l in lines {
            if !self.ignore.contains(l) {
                self.cover.insert(*l);
            }
        }
    }
}

/// Returns a list of files and line numbers to ignore (not indexes!)
pub fn get_line_analysis(project: &Workspace, config: &Config) -> HashMap<PathBuf, LineAnalysis> {
    let mut result: HashMap<PathBuf, LineAnalysis> = HashMap::new();
    // Members iterates over all non-virtual packages in the workspace
    for pkg in project.members() {
        if config.packages.is_empty() || config.packages.contains(&pkg.name().to_string()) {
            analyse_package(pkg, &config, project.config(), &mut result);
        }
    }
    result
}

/// Analyse the crates lib.rs for some common false positives
fn analyse_lib_rs(file: &Path, result: &mut HashMap<PathBuf, LineAnalysis>) {
    if let Ok(f) = File::open(file) {
        let mut read_file = BufReader::new(f);
        if let Some(Ok(first)) = read_file.lines().nth(0) {
            if !(first.starts_with("pub") || first.starts_with("fn")) {
                let file = file.to_path_buf();
                if result.contains_key(&file) {
                    let l = result.get_mut(&file).unwrap();
                    l.add_to_ignore(&[1]);
                } else {
                    let mut l = LineAnalysis::new();
                    l.add_to_ignore(&[1]);
                    result.insert(file, l);
                }
            }
        }
    }
}

/// Provides context to the source analysis stage including the tarpaulin
/// config and the source code being analysed.
struct Context<'a> {
    config: &'a Config,
    file_contents: &'a str
}

/// Analyses a package of the target crate.
fn analyse_package(pkg: &Package,
                   config:&Config,
                   cargo_conf: &CargoConfig,
                   result: &mut HashMap<PathBuf, LineAnalysis>) {

    let mut src = PathSource::new(pkg.root(), pkg.package_id().source_id(), cargo_conf);
    if let Ok(package) = src.root_package() {
        for target in package.targets() {
            let path = target.src_path();
            let file = match path.to_str() {
                Some(s) => s,
                _ => continue
            };
            let skip_cause_test = config.ignore_tests &&
                                  path.starts_with(pkg.root().join("tests"));
            let skip_cause_example = path.starts_with(pkg.root().join("examples"));
            if !(skip_cause_test || skip_cause_example)  {
                let file = File::open(file);
                let mut file = match file {
                    Ok(f) => f,
                    _ => continue,
                };
                let mut content = String::new();
                let _ = file.read_to_string(&mut content);
                let file = parse_file(&content);
                if let Ok(file) = file {
                    let mut analysis = LineAnalysis::new();
                    let ctx = Context {
                        config: config,
                        file_contents: &content,
                    };

                    find_ignorable_lines(&content, &mut analysis);
                    process_items(&file.items, &ctx, &mut analysis);
                    // Check there's no conflict!
                    result.insert(path.to_path_buf(), analysis);
                }
            }
            // This could probably be done with the DWARF if I could find a discriminating factor
            // to why lib.rs:1 shows up as a real line!
            if file.ends_with("src/lib.rs") {
                analyse_lib_rs(path, result);
            }
        }
    }
}

fn find_ignorable_lines(content: &str, analysis: &mut LineAnalysis) {
    let lines = content.lines()
                       .enumerate()
                       .filter(|(_, x)| !x.chars().any(|x| !"(){}[]?;\t ,".contains(x)))
                       .map(|(i, _)| i+1)
                       .collect::<Vec<usize>>();
    analysis.add_to_ignore(&lines);
}

fn process_items(items: &[Item], ctx: &Context, analysis: &mut LineAnalysis) {
    for item in items {
        match item {
            Item::ExternCrate(i) => analysis.ignore_span(&i.extern_token.0),
            Item::Use(i) => analysis.ignore_span(&i.use_token.0),
            Item::Mod(i) => visit_mod(i, analysis, ctx),
            Item::Fn(i) => visit_fn(i, analysis, ctx),
            _ =>{}
        }
    }
}

fn process_statements(stmts: &[Stmt], ctx: &Context, analysis: &mut LineAnalysis) {
    for stmt in stmts {
        match stmt {
            Stmt::Item(i) => process_items(&[i.clone()], ctx, analysis),
            _ => {},
        }
    }
}

fn visit_mod(module: &ItemMod, analysis: &mut LineAnalysis, ctx: &Context) {
    analysis.ignore_span(&module.mod_token.0);
    let mut check_insides = true;
    if ctx.config.ignore_tests {
        for attr in &module.attrs {
            if let Some(Meta::List(ref ml)) = attr.interpret_meta() {
                if ml.ident != Ident::from("cfg") {
                    continue;
                }
                for nested in &ml.nested {
                    if let NestedMeta::Meta(Meta::Word(i)) = nested {
                        if i == &Ident::from("test") {
                            check_insides = false;
                            analysis.ignore_span(&module.mod_token.0);
                            if let Some((ref braces, _)) = module.content {
                                analysis.ignore_span(&braces.0);
                            }
                        }
                    }
                }
            }
        }
    }
    if check_insides {
        if let Some((_, ref items)) = module.content {
            process_items(items, ctx, analysis);
        }
    }
}

fn visit_fn(func: &ItemFn, analysis: &mut LineAnalysis, ctx: &Context) {
    // Need to read the nested meta.. But this should work for fns
    let mut ignore_func = false;
    for attr in &func.attrs {
        if let Some(x) = attr.interpret_meta() {
            let id = x.name();
            if id == Ident::from("test") {
                ignore_func = true;
            } else if id == Ident::from("derive") {
                analysis.ignore_span(&attr.bracket_token.0);
            }
        }
    }
    if ignore_func && ctx.config.ignore_tests {
        analysis.ignore_span(&func.decl.fn_token.0);
        analysis.ignore_span(&func.block.brace_token.0);
    } else {
        process_statements(&func.block.stmts, ctx, analysis);
    }
}

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

    #[test]
    fn filter_str_literals() {
        let mut lines = LineAnalysis::new();
        let config = Config::default();
        let ctx = Context {
            config: &config,
            file_contents: "fn test() {\nwriteln!(#\"test\n\ttest\n\ttest\"#);\n}\n",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.len() > 1);
        assert!(lines.ignore.contains(&3));
        assert!(lines.ignore.contains(&4));

        let ctx = Context {
            config: &config,
            file_contents: "fn test() {\nwrite(\"test\ntest\ntest\");\n}\nfn write(s:&str){}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.len() > 1);
        assert!(lines.ignore.contains(&3));
        assert!(lines.ignore.contains(&4));

        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "\n\nfn test() {\nwriteln!(\n#\"test\"#\n);\n}\n",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.contains(&5));
    }

    #[test]
    fn filter_struct_members() {
        let config = Config::default();
        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "#[derive(Debug)]\npub struct Struct {\npub i: i32,\nj:String,\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);

        assert_eq!(lines.ignore.len(), 3);
        assert!(lines.ignore.contains(&1));
        assert!(lines.ignore.contains(&3));
        assert!(lines.ignore.contains(&4));
    }

    #[test]
    fn filter_struct_consts() {
        let config = Config::default();
        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "struct T{x:String, y:i32}\nfn test()-> T {\nT{\nx:\"hello\".to_string(),\ny:4,\n}\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.contains(&5));
    }

    #[test]
    fn filter_mods() {
        let config = Config::default();
        let ctx = Context {
            config: &config,
            file_contents: "mod foo {\nfn double(x:i32)->i32 {\n x*2\n}\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(!lines.ignore.contains(&3));

        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "mod foo;",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.contains(&1));

        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "mod foo{}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.contains(&1));
    }

    #[test]
    fn filter_macros() {
        let config = Config::default();
        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "\n\nfn unused() {\nunimplemented!();\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);

        // Braces should be ignored so number could be higher
        assert!(lines.ignore.len() >= 1);
        assert!(lines.ignore.contains(&4));

        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "\n\nfn unused() {\nunreachable!();\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.len() >= 1);
        assert!(lines.ignore.contains(&2));

        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "fn unused() {\nprintln!(\"text\");\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(!lines.ignore.contains(&2));
    }

    #[test]
    fn filter_tests() {
        let config = Config::default();
        let mut igconfig = Config::default();
        igconfig.ignore_tests = true;

        let mut lines = LineAnalysis::new();
        let ctx = Context {
            config: &config,
            file_contents: "#[cfg(test)]\nmod tests {\n fn boo(){\nassert!(true);\n}\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(!lines.ignore.contains(&4));

        let ctx = Context {
            config: &igconfig,
            file_contents: "#[cfg(test)]\nmod tests {\n fn boo(){\nassert!(true);\n}\n}",
        };

        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.contains(&4));

        let ctx = Context {
            config: &config,
            file_contents: "#[test]\nfn mytest() { \n assert!(true);\n}",
        };
        let parser = parse_file(ctx.file_contents).unwrap();
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(!lines.ignore.contains(&2));
        assert!(!lines.ignore.contains(&3));

        let ctx = Context {
            config: &igconfig,
            file_contents: "#[test]\nfn mytest() { \n assert!(true);\n}",
        };
        let mut lines = LineAnalysis::new();
        process_items(&parser.items, &ctx, &mut lines);
        assert!(lines.ignore.contains(&2));
        assert!(lines.ignore.contains(&3));

    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;
use std::io::Write;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    pub fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        // TODO: Don't use "/store" but value from configuration
        debug!("Building StorageBackend for {}", path);
        StorageBackend::new(path)
    }

    fn get_file_ids(&self) -> Option<Vec<FileID>> {
        debug!("Getting files from {}", self.basepath);
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<'a, HP>(&self, f: File, p: &Parser<HP>) ->
        Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let written = p.write(f.contents());
        if let Ok(string) = written {
            let path = self.build_filepath(&f);
            debug!("Writing file: {}", path);
            debug!("    contents: {}", string);
            Ok(Ok(()))
        } else {
            debug!("Error parsing : {:?}", f.contents());
            Err(written.err().unwrap())
        }
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<'a, HP>(&self, f: File, p: &Parser<HP>)
        -> Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let contents = p.write(f.contents());

        if contents.is_err() {
            debug!("Error parsing contents: {:?}", f.contents());
            return Err(contents.err().unwrap());
        }

        let content = contents.unwrap();
        debug!("Success parsing content : {}", content);

        let path = self.build_filepath(&f);
        debug!("Trying to write to file at {}", path);
        if let Err(_) = FSFile::open(&path) {
            debug!("Error opening {}", path);
            return Ok(Err(StorageBackendError::new(
                String::from("File::open()"),
                format!("Tried to open '{}'", path),
                String::from("Tried to update contents of this file, though file doesn't exist"),
                None)))
        }

        if let Ok(mut file) = FSFile::create(&path) {
            if let Err(writeerr) = file.write_all(&content.clone().into_bytes()) {
                debug!("Error writing to {}", path);
                return Ok(Err(StorageBackendError::new(
                    String::from("File::write()"),
                    format!("Tried to write '{}'", path),
                    String::from("Tried to write contents of this file, though operation did not succeed"),
                    Some(content))))
            }
        }

        debug!("Successfully written to file.");
        Ok(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, id: FileID, p: &Parser<HP>) -> Option<File>
        where HP: FileHeaderParser<'a>
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.id())
    }

    fn build_filepath_with_id(&self, id: FileID) -> String {
        debug!("Building filepath for id '{}'", id);
        self.basepath.clone() + &id[..]
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub explanation: String,        // A long, user friendly description
    pub data_dump: Option<String>   // Data dump, if any
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           explan: String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            explanation:    explan,
            data_dump:      data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}\n\n{}",
               self.action, self.desc, self.explanation)
    }
}

<commit_msg>Remove StorageBackendError::explanation member<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;
use std::io::Write;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use storage::parser::{FileHeaderParser, Parser, ParserError};

use module::Module;
use runtime::Runtime;

pub type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    pub fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
        let path = rt.get_rtp() + m.name() + "/store";
        // TODO: Don't use "/store" but value from configuration
        debug!("Building StorageBackend for {}", path);
        StorageBackend::new(path)
    }

    fn get_file_ids(&self) -> Option<Vec<FileID>> {
        debug!("Getting files from {}", self.basepath);
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    debug!(" - File: {:?}", path);
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    /*
     * Write a file to disk.
     *
     * The file is moved to this function as the file won't be edited afterwards
     */
    pub fn put_file<'a, HP>(&self, f: File, p: &Parser<HP>) ->
        Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let written = p.write(f.contents());
        if let Ok(string) = written {
            let path = self.build_filepath(&f);
            debug!("Writing file: {}", path);
            debug!("    contents: {}", string);
            Ok(Ok(()))
        } else {
            debug!("Error parsing : {:?}", f.contents());
            Err(written.err().unwrap())
        }
    }

    /*
     * Update a file. We have the UUID and can find the file on FS with it and
     * then replace its contents with the contents of the passed file object
     */
    pub fn update_file<'a, HP>(&self, f: File, p: &Parser<HP>)
        -> Result<BackendOperationResult, ParserError>
        where HP: FileHeaderParser<'a>
    {
        let contents = p.write(f.contents());

        if contents.is_err() {
            debug!("Error parsing contents: {:?}", f.contents());
            return Err(contents.err().unwrap());
        }

        let content = contents.unwrap();
        debug!("Success parsing content : {}", content);

        let path = self.build_filepath(&f);
        debug!("Trying to write to file at {}", path);
        if let Err(_) = FSFile::open(&path) {
            debug!("Error opening {}", path);
            return Ok(Err(StorageBackendError::new(
                String::from("File::open()"),
                format!("Tried to open '{}'", path),
                String::from("Tried to update contents of this file, though file doesn't exist"),
                None)))
        }

        if let Ok(mut file) = FSFile::create(&path) {
            if let Err(writeerr) = file.write_all(&content.clone().into_bytes()) {
                debug!("Error writing to {}", path);
                return Ok(Err(StorageBackendError::new(
                    String::from("File::write()"),
                    format!("Tried to write '{}'", path),
                    String::from("Tried to write contents of this file, though operation did not succeed"),
                    Some(content))))
            }
        }

        debug!("Successfully written to file.");
        Ok(Ok(()))
    }

    /*
     * Find a file by its ID and return it if found. Return nothing if not
     * found, of course.
     *
     * TODO: Needs refactoring, as there might be an error when reading from
     * disk OR the id just does not exist.
     */
    pub fn get_file_by_id<'a, HP>(&self, id: FileID, p: &Parser<HP>) -> Option<File>
        where HP: FileHeaderParser<'a>
    {
        debug!("Searching for file with id '{}'", id);
        if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
            let mut s = String::new();
            fs.read_to_string(&mut s);
            debug!("Success reading file with id '{}'", id);
            debug!("Parsing to internal structure now");
            p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
        } else {
            debug!("No file with id '{}'", id);
            None
        }
    }

    fn build_filepath(&self, f: &File) -> String {
        self.build_filepath_with_id(f.id())
    }

    fn build_filepath_with_id(&self, id: FileID) -> String {
        debug!("Building filepath for id '{}'", id);
        self.basepath.clone() + &id[..]
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub data_dump: Option<String>   // Data dump, if any
}

impl StorageBackendError {
    fn new(action: String,
           desc  : String,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         action,
            desc:           desc,
            data_dump:      data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}",
               self.action, self.desc)
    }
}

<|endoftext|>


<commit_before>use super::*;

#[test]
fn test_open_close() {
    unsafe {
        let mrb = mrb_open();

        mrb_close(mrb);
    }
}

#[test]
fn test_exec_context() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        mrbc_filename(mrb, context, "script.rb\0".as_ptr());

        let code = "'' + 0\0".as_ptr();

        mrb_load_string_cxt(mrb, code, context);

        assert_eq!(mrb_ext_get_exc(mrb).to_str(mrb).unwrap(), "script.rb:1: expected String (TypeError)");

        mrb_close(mrb);
    }
}

#[test]
fn test_create_run_proc() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let code = "1 + 1\0".as_ptr();
        let parser = mrb_parse_string(mrb, code, context);
        let prc = mrb_generate_code(mrb, parser);

        let result = mrb_run(mrb, prc, mrb_top_self(mrb));

        assert_eq!(result.to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_class_defined() {
    unsafe {
        let mrb = mrb_open();

        let obj_class = "Object\0".as_ptr();

        assert_eq!(mrb_class_defined(mrb, obj_class), 1);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_class() {
    unsafe {
        let mrb = mrb_open();

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        assert_eq!(mrb_class_defined(mrb, "Mine\0".as_ptr()), 1);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_module() {
    unsafe {
        let mrb = mrb_open();

        mrb_define_module(mrb, "Mine\0".as_ptr());
        mrb_module_get(mrb, "Mine\0".as_ptr());

        mrb_close(mrb);
    }
}

#[test]
fn test_include_module() {
    unsafe {
        let mrb = mrb_open();

        let new_module = mrb_define_module(mrb, "Mine\0".as_ptr());
        let kernel = mrb_module_get(mrb, "Kernel\0".as_ptr());

        mrb_include_module(mrb, kernel, new_module);

        mrb_close(mrb);
    }
}

#[test]
fn test_prepend_module() {
    unsafe {
        let mrb = mrb_open();

        let new_module = mrb_define_module(mrb, "Mine\0".as_ptr());
        let kernel = mrb_module_get(mrb, "Kernel\0".as_ptr());

        mrb_prepend_module(mrb, kernel, new_module);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_method() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        let new_class = mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        mrb_define_method(mrb, new_class, "job\0".as_ptr(), job, 0);

        let code = "Mine.new.job\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_class_method() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        let new_class = mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        mrb_define_class_method(mrb, new_class, "job\0".as_ptr(), job, 0);

        let code = "Mine.job\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_module_function() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let new_module = mrb_define_module(mrb, "Mine\0".as_ptr());

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        mrb_define_module_function(mrb, new_module, "job\0".as_ptr(), job, 0);

        let code = "Mine.job\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_obj_new() {
    use std::ptr;

    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());

        mrb_obj_new(mrb, obj_class, 0, ptr::null() as *const MRValue);

        mrb_close(mrb);
    }
}

#[test]
fn test_proc_new_cfunc() {
    use std::ptr;

    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        let prc = MRValue::prc(mrb, mrb_proc_new_cfunc(mrb, job));

        mrb_funcall_with_block(mrb, MRValue::fixnum(5), mrb_intern_cstr(mrb, "times\0".as_ptr()), 0, ptr::null() as *const MRValue, prc);

        mrb_close(mrb);
    }
}

#[test]
pub fn test_args() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        extern "C" fn add(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                let mut a = MRValue::empty();
                let mut b = MRValue::empty();

                mrb_get_args(mrb, "oo\0".as_ptr(), &a as *const MRValue, &b as *const MRValue);

                mrb_funcall(mrb, a, "+\0".as_ptr(), 1, b)
            }
        }

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        let new_class = mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        mrb_define_method(mrb, new_class, "add\0".as_ptr(), add, (2 & 0x1f) << 18);

        let code = "Mine.new.add 1, 1\0".as_ptr();mrb_load_string_cxt(mrb, code, context);

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_nil() {
    unsafe {
        let mrb = mrb_open();

        let nil = MRValue::nil();
        let result = mrb_funcall(mrb, nil, "to_s\0".as_ptr(), 0);

        assert_eq!(result.to_str(mrb).unwrap(), "");

        mrb_close(mrb);
    }
}

#[test]
fn test_bool_true() {
    unsafe {
        let bool_true = MRValue::bool(true);
        assert_eq!(bool_true.to_bool().unwrap(), true);
    }
}

#[test]
fn test_bool_false() {
    unsafe {
        let bool_false = MRValue::bool(false);
        assert_eq!(bool_false.to_bool().unwrap(), false);
    }
}

#[test]
fn test_fixnum() {
    unsafe {
        let number = MRValue::fixnum(-1291657);
        assert_eq!(number.to_i32().unwrap(), -1291657);
    }
}

#[test]
fn test_float() {
    unsafe {
        let mrb = mrb_open();

        let number = MRValue::float(mrb, -1291657.37);
        assert_eq!(number.to_f64().unwrap(), -1291657.37);

        mrb_close(mrb);
    }
}

#[test]
fn test_string() {
    unsafe {
        let mrb = mrb_open();

        let string_value = MRValue::str(mrb, "qwerty\0");
        assert_eq!(string_value.to_str(mrb).unwrap(), "qwerty");

        mrb_close(mrb);
    }
}

#[test]
fn test_proc() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let code = "1 + 1\0".as_ptr();
        let parser = mrb_parse_string(mrb, code, context);
        let prc = mrb_generate_code(mrb, parser);

        let result = mrb_run(mrb, MRValue::prc(mrb, prc).to_prc().unwrap(), mrb_top_self(mrb));

        assert_eq!(result.to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_object() {
    unsafe {
        let mrb = mrb_open();

        #[derive(Clone, Copy, Debug, PartialEq)]
        struct Container {
            value: i32
        }

        let container = Container { value: 42 };
        let obj = MRValue::obj::<Container>(mrb, &container);

        assert_eq!(obj.to_obj::<Container>().unwrap(), container);

        mrb_close(mrb);
    }
}
<commit_msg>Added yield testing.<commit_after>use super::*;

#[test]
fn test_open_close() {
    unsafe {
        let mrb = mrb_open();

        mrb_close(mrb);
    }
}

#[test]
fn test_exec_context() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        mrbc_filename(mrb, context, "script.rb\0".as_ptr());

        let code = "'' + 0\0".as_ptr();

        mrb_load_string_cxt(mrb, code, context);

        assert_eq!(mrb_ext_get_exc(mrb).to_str(mrb).unwrap(), "script.rb:1: expected String (TypeError)");

        mrb_close(mrb);
    }
}

#[test]
fn test_create_run_proc() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let code = "1 + 1\0".as_ptr();
        let parser = mrb_parse_string(mrb, code, context);
        let prc = mrb_generate_code(mrb, parser);

        let result = mrb_run(mrb, prc, mrb_top_self(mrb));

        assert_eq!(result.to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_class_defined() {
    unsafe {
        let mrb = mrb_open();

        let obj_class = "Object\0".as_ptr();

        assert_eq!(mrb_class_defined(mrb, obj_class), 1);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_class() {
    unsafe {
        let mrb = mrb_open();

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        assert_eq!(mrb_class_defined(mrb, "Mine\0".as_ptr()), 1);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_module() {
    unsafe {
        let mrb = mrb_open();

        mrb_define_module(mrb, "Mine\0".as_ptr());
        mrb_module_get(mrb, "Mine\0".as_ptr());

        mrb_close(mrb);
    }
}

#[test]
fn test_include_module() {
    unsafe {
        let mrb = mrb_open();

        let new_module = mrb_define_module(mrb, "Mine\0".as_ptr());
        let kernel = mrb_module_get(mrb, "Kernel\0".as_ptr());

        mrb_include_module(mrb, kernel, new_module);

        mrb_close(mrb);
    }
}

#[test]
fn test_prepend_module() {
    unsafe {
        let mrb = mrb_open();

        let new_module = mrb_define_module(mrb, "Mine\0".as_ptr());
        let kernel = mrb_module_get(mrb, "Kernel\0".as_ptr());

        mrb_prepend_module(mrb, kernel, new_module);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_method() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        let new_class = mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        mrb_define_method(mrb, new_class, "job\0".as_ptr(), job, 0);

        let code = "Mine.new.job\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_class_method() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        let new_class = mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        mrb_define_class_method(mrb, new_class, "job\0".as_ptr(), job, 0);

        let code = "Mine.job\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_define_module_function() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let new_module = mrb_define_module(mrb, "Mine\0".as_ptr());

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        mrb_define_module_function(mrb, new_module, "job\0".as_ptr(), job, 0);

        let code = "Mine.job\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_obj_new() {
    use std::ptr;

    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());

        mrb_obj_new(mrb, obj_class, 0, ptr::null() as *const MRValue);

        mrb_close(mrb);
    }
}

#[test]
fn test_proc_new_cfunc() {
    use std::ptr;

    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        extern "C" fn job(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                MRValue::fixnum(2)
            }
        }

        let prc = MRValue::prc(mrb, mrb_proc_new_cfunc(mrb, job));

        mrb_funcall_with_block(mrb, MRValue::fixnum(5), mrb_intern_cstr(mrb, "times\0".as_ptr()), 0, ptr::null() as *const MRValue, prc);

        mrb_close(mrb);
    }
}

#[test]
pub fn test_args() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        extern "C" fn add(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                let mut a = MRValue::empty();
                let mut b = MRValue::empty();

                mrb_get_args(mrb, "oo\0".as_ptr(), &a as *const MRValue, &b as *const MRValue);

                mrb_funcall(mrb, a, "+\0".as_ptr(), 1, b)
            }
        }

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        let new_class = mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        mrb_define_method(mrb, new_class, "add\0".as_ptr(), add, (2 & 0x1f) << 18);

        let code = "Mine.new.add 1, 1\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_yield() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        extern "C" fn add(mrb: *mut MRState, slf: MRValue) -> MRValue {
            unsafe {
                let mut a = MRValue::empty();
                let mut b = MRValue::fixnum(1);

                let mut prc = MRValue::empty();

                mrb_get_args(mrb, "o&\0".as_ptr(), &a as *const MRValue, &prc as *const MRValue);
                let mut b = mrb_yield_argv(mrb, prc, 1, [b].as_ptr());

                mrb_funcall(mrb, a, "+\0".as_ptr(), 1, b)
            }
        }

        let obj_class = mrb_class_get(mrb, "Object\0".as_ptr());
        let new_class = mrb_define_class(mrb, "Mine\0".as_ptr(), obj_class);

        mrb_define_method(mrb, new_class, "add\0".as_ptr(), add, (2 & 0x1f) << 18);

        let code = "Mine.new.add(1) { |n| n + 1 }\0".as_ptr();

        assert_eq!(mrb_load_string_cxt(mrb, code, context).to_i32().unwrap(), 3);

        mrb_close(mrb);
    }
}

#[test]
fn test_nil() {
    unsafe {
        let mrb = mrb_open();

        let nil = MRValue::nil();
        let result = mrb_funcall(mrb, nil, "to_s\0".as_ptr(), 0);

        assert_eq!(result.to_str(mrb).unwrap(), "");

        mrb_close(mrb);
    }
}

#[test]
fn test_bool_true() {
    unsafe {
        let bool_true = MRValue::bool(true);
        assert_eq!(bool_true.to_bool().unwrap(), true);
    }
}

#[test]
fn test_bool_false() {
    unsafe {
        let bool_false = MRValue::bool(false);
        assert_eq!(bool_false.to_bool().unwrap(), false);
    }
}

#[test]
fn test_fixnum() {
    unsafe {
        let number = MRValue::fixnum(-1291657);
        assert_eq!(number.to_i32().unwrap(), -1291657);
    }
}

#[test]
fn test_float() {
    unsafe {
        let mrb = mrb_open();

        let number = MRValue::float(mrb, -1291657.37);
        assert_eq!(number.to_f64().unwrap(), -1291657.37);

        mrb_close(mrb);
    }
}

#[test]
fn test_string() {
    unsafe {
        let mrb = mrb_open();

        let string_value = MRValue::str(mrb, "qwerty\0");
        assert_eq!(string_value.to_str(mrb).unwrap(), "qwerty");

        mrb_close(mrb);
    }
}

#[test]
fn test_proc() {
    unsafe {
        let mrb = mrb_open();
        let context = mrbc_context_new(mrb);

        let code = "1 + 1\0".as_ptr();
        let parser = mrb_parse_string(mrb, code, context);
        let prc = mrb_generate_code(mrb, parser);

        let result = mrb_run(mrb, MRValue::prc(mrb, prc).to_prc().unwrap(), mrb_top_self(mrb));

        assert_eq!(result.to_i32().unwrap(), 2);

        mrb_close(mrb);
    }
}

#[test]
fn test_object() {
    unsafe {
        let mrb = mrb_open();

        #[derive(Clone, Copy, Debug, PartialEq)]
        struct Container {
            value: i32
        }

        let container = Container { value: 42 };
        let obj = MRValue::obj::<Container>(mrb, &container);

        assert_eq!(obj.to_obj::<Container>().unwrap(), container);

        mrb_close(mrb);
    }
}
<|endoftext|>


<commit_before>extern crate piston;
extern crate opengl_graphics;
extern crate graphics;
extern crate shader_version;
#[cfg(feature = "include_sdl2")]
extern crate sdl2_window;
#[cfg(feature = "include_glfw")]
extern crate glfw_window;
#[cfg(feature = "include_glutin")]
extern crate glutin_window;

use opengl_graphics::GlGraphics;
use graphics::{ Graphics };
use std::cell::RefCell;
use piston::quack::Set;
use piston::window::{ WindowSettings, CaptureCursor };
use piston::input::Button;
use piston::input::keyboard::Key;
use piston::event::{
    PressEvent,
    ReleaseEvent,
    MouseCursorEvent,
    MouseScrollEvent,
    MouseRelativeEvent,
    TextEvent,
    ResizeEvent,
    FocusEvent,
    RenderEvent,
    UpdateEvent
};
use shader_version::OpenGL;
#[cfg(feature = "include_sdl2")]
use sdl2_window::Sdl2Window as Window;
#[cfg(feature = "include_glfw")]
use glfw_window::GlfwWindow as Window;
#[cfg(feature = "include_glutin")]
use glutin_window::GlutinWindow as Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Window::new(
        opengl,
        WindowSettings {
            title: "piston-examples/user_input".to_string(),
            size: [600, 600],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    println!("Press C to turn capture cursor on/off");

    let mut capture_cursor = false;
    let ref window = RefCell::new(window);
    let ref mut gl = GlGraphics::new(opengl);
    let mut cursor = [0.0, 0.0];
    for e in piston::events(window) {
        if let Some(Button::Mouse(button)) = e.press_args() {
            println!("Pressed mouse button '{:?}'", button);
        }
        if let Some(Button::Keyboard(key)) = e.press_args() {
            if key == Key::C {
                println!("Turned capture cursor on");
                capture_cursor = !capture_cursor;
                // window.set(CaptureCursor(capture_cursor));
            }

            println!("Pressed keyboard key '{:?}'", key);
        };
        if let Some(button) = e.release_args() {
            match button {
                Button::Keyboard(key) => println!("Released keyboard key '{:?}'", key),
                Button::Mouse(button) => println!("Released mouse button '{:?}'", button),
            }
        };
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });
        e.mouse_scroll(|dx, dy| println!("Scrolled mouse '{}, {}'", dx, dy));
        e.mouse_relative(|dx, dy| println!("Relative mouse moved '{} {}'", dx, dy));
        e.text(|text| println!("Typed '{}'", text));
        e.resize(|w, h| println!("Resized '{}, {}'", w, h));
        if let Some(focused) = e.focus_args() {
            if focused { println!("Gained focus"); }
            else { println!("Lost focus"); }
        };
        if let Some(args) = e.render_args() {
            gl.draw(
                [0, 0, args.width as i32, args.height as i32],
                |_, g| {
                    graphics::clear([1.0; 4], g);
                    draw_rectangles(cursor, &window, g);
                }
            );
        }
        e.update(|_| {});
    }
}

fn draw_rectangles<G: Graphics>(
    cursor: [f64; 2],
    window: &RefCell<Window>,
    g: &mut G,
) {
    use piston::window::{ Size, DrawSize };
    use piston::quack::Get;

    let Size([w, h]) = window.get();
    let DrawSize([dw, dh]) = window.get();
    let zoom = 0.2;

    let draw_state = graphics::default_draw_state();
    let transform = graphics::abs_transform(w as f64, h as f64);
    let rect_border = graphics::Rectangle::border([1.0, 0.0, 0.0, 1.0], 1.0);

    // Cursor.
    let cursor_color = [0.0, 0.0, 0.0, 1.0];
    let zoomed_cursor = [cursor[0] * zoom, cursor[1] * zoom];
    graphics::ellipse(
        cursor_color,
        graphics::ellipse::circle(zoomed_cursor[0], zoomed_cursor[1], 4.0),
        transform,
        g
    );

    // User coordinates.
    rect_border.draw([0.0, 0.0, w as f64 * zoom, h as f64 * zoom],
        draw_state, transform, g);
    let rect_border = graphics::Rectangle::border([0.0, 0.0, 1.0, 1.0], 1.0);
    rect_border.draw([w as f64 * zoom, 0.0, dw as f64 * zoom, dh as f64 * zoom],
        draw_state, transform, g);
}
<commit_msg>Added offset to detect bugs in rendering coordinates<commit_after>extern crate piston;
extern crate opengl_graphics;
extern crate graphics;
extern crate shader_version;
#[cfg(feature = "include_sdl2")]
extern crate sdl2_window;
#[cfg(feature = "include_glfw")]
extern crate glfw_window;
#[cfg(feature = "include_glutin")]
extern crate glutin_window;

use opengl_graphics::GlGraphics;
use graphics::{ Graphics };
use std::cell::RefCell;
use piston::quack::Set;
use piston::window::{ WindowSettings, CaptureCursor };
use piston::input::Button;
use piston::input::keyboard::Key;
use piston::event::{
    PressEvent,
    ReleaseEvent,
    MouseCursorEvent,
    MouseScrollEvent,
    MouseRelativeEvent,
    TextEvent,
    ResizeEvent,
    FocusEvent,
    RenderEvent,
    UpdateEvent
};
use shader_version::OpenGL;
#[cfg(feature = "include_sdl2")]
use sdl2_window::Sdl2Window as Window;
#[cfg(feature = "include_glfw")]
use glfw_window::GlfwWindow as Window;
#[cfg(feature = "include_glutin")]
use glutin_window::GlutinWindow as Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Window::new(
        opengl,
        WindowSettings {
            title: "piston-examples/user_input".to_string(),
            size: [600, 600],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    println!("Press C to turn capture cursor on/off");

    let mut capture_cursor = false;
    let ref window = RefCell::new(window);
    let ref mut gl = GlGraphics::new(opengl);
    let mut cursor = [0.0, 0.0];
    for e in piston::events(window) {
        if let Some(Button::Mouse(button)) = e.press_args() {
            println!("Pressed mouse button '{:?}'", button);
        }
        if let Some(Button::Keyboard(key)) = e.press_args() {
            if key == Key::C {
                println!("Turned capture cursor on");
                capture_cursor = !capture_cursor;
                // window.set(CaptureCursor(capture_cursor));
            }

            println!("Pressed keyboard key '{:?}'", key);
        };
        if let Some(button) = e.release_args() {
            match button {
                Button::Keyboard(key) => println!("Released keyboard key '{:?}'", key),
                Button::Mouse(button) => println!("Released mouse button '{:?}'", button),
            }
        };
        e.mouse_cursor(|x, y| {
            cursor = [x, y];
            println!("Mouse moved '{} {}'", x, y);
        });
        e.mouse_scroll(|dx, dy| println!("Scrolled mouse '{}, {}'", dx, dy));
        e.mouse_relative(|dx, dy| println!("Relative mouse moved '{} {}'", dx, dy));
        e.text(|text| println!("Typed '{}'", text));
        e.resize(|w, h| println!("Resized '{}, {}'", w, h));
        if let Some(focused) = e.focus_args() {
            if focused { println!("Gained focus"); }
            else { println!("Lost focus"); }
        };
        if let Some(args) = e.render_args() {
            gl.draw(
                [0, 0, args.width as i32, args.height as i32],
                |_, g| {
                    graphics::clear([1.0; 4], g);
                    draw_rectangles(cursor, &window, g);
                }
            );
        }
        e.update(|_| {});
    }
}

fn draw_rectangles<G: Graphics>(
    cursor: [f64; 2],
    window: &RefCell<Window>,
    g: &mut G,
) {
    use piston::window::{ Size, DrawSize };
    use piston::quack::Get;

    let Size([w, h]) = window.get();
    let DrawSize([dw, dh]) = window.get();
    let zoom = 0.2;
    let offset = 30.0;

    let draw_state = graphics::default_draw_state();
    let transform = graphics::abs_transform(w as f64, h as f64);
    let rect_border = graphics::Rectangle::border([1.0, 0.0, 0.0, 1.0], 1.0);

    // Cursor.
    let cursor_color = [0.0, 0.0, 0.0, 1.0];
    let zoomed_cursor = [offset + cursor[0] * zoom, offset + cursor[1] * zoom];
    graphics::ellipse(
        cursor_color,
        graphics::ellipse::circle(zoomed_cursor[0], zoomed_cursor[1], 4.0),
        transform,
        g
    );

    // User coordinates.
    rect_border.draw([offset, offset, w as f64 * zoom, h as f64 * zoom],
        draw_state, transform, g);
    let rect_border = graphics::Rectangle::border([0.0, 0.0, 1.0, 1.0], 1.0);
    rect_border.draw([offset + w as f64 * zoom, offset, dw as f64 * zoom, dh as f64 * zoom],
        draw_state, transform, g);
}
<|endoftext|>


<commit_before>//! Treiber stacks.

use std::sync::atomic;
use std::{mem, ptr};
use {Atomic, Guard};

/// A Treiber stack.
pub struct Treiber<T> {
    head: Atomic<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *const Node<T>,
}

impl<T> Treiber<T> {
    /// Create a new, empty Treiber stack.
    pub fn new() -> Treiber<T> {
        Treiber {
            head: Atomic::default(),
        }
    }

    /// Pop an item from the stack.
    pub fn pop(&self) -> Option<Guard<T>> {
        // TODO: Use `catch {}` here when it lands.
        // Read the current head.
        let mut current = self.head.load(atomic::Ordering::Acquire);

        // Unless the current head snapshot is `None`, try to replace it with the tail.
        while let Some(node) = current {
            // Attempt to replace the head with the tail of the head.
            match unsafe {
                self.current.compare_and_swap_raw(
                    &*node,
                    node.next as *mut Node<T>,
                    atomic::Ordering::Release,
                )
            } {
                // It succeeded; return the item.
                Ok(_) => return Some(node.map(|x| &x.data)),
                // It failed, update the current head and continue.
                Err(new) => current = new,
            }
        }

        // As the head was empty, there is nothing to pop.
        None
    }

    /// Push an item to the stack.
    pub fn push(&self, item: T)
    where T: 'static {
        // Load the current head.
        let mut current = self.head.load(atomic::Ordering::Acquire);
        let mut current_ptr: Option<*const Node<T>>;

        // TODO: Use `catch {}` here when it lands.
        // Construct a node, which will be the new head.
        let mut node = Box::new(Node {
            data: item,
            // Placeholder; we will replace it with an actual value in the loop.
            next: ptr::null(),
        });

        loop {
            // Derive the nullable current pointer from the current head.
            current_ptr = current.as_ref().map(Guard::as_raw);
            // Construct the next-pointer of the new node from the current head.
            node.next = current_ptr.unwrap_or(ptr::null());

            // TODO: This should be something that ignores the guard creation when the CAS
            //       succeeds, because it's expensive to do and not used anyway. It should be easy
            //       enough to implement, but I am struggling to come up with a good name for the
            //       method.

            // CAS from the read pointer to the new head.
            match self.head.compare_and_swap(current_ptr, Some(node), atomic::Ordering::Release) {
                // If it succeeds, the item has been pushed.
                Ok(_) => break,
                // If it fails, we will retry the CAS with updated values.
                Err((new_head, Some(node2))) => {
                    // Update the current head.
                    current = new_head;
                    // Put the box we gave back to the variable where it belongs.
                    node = node2;
                },
                // This should never be reached as we gave an argument which was unconditionally
                // `Some`.
                _ => unreachable!(),
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::sync::Arc;

    #[test]
    fn single_thread() {
        let stack = Treiber::new();

        for i in 0..10000 {
            stack.push(i);
        }

        for i in (0..10000).rev() {
            assert_eq!(*stack.pop().unwrap(), i);
        }

        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
    }

    #[test]
    fn push_pop() {
        let stack = Arc::new(Treiber::new());
        let mut j = Vec::new();
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for _ in 0..10_000_000 {
                    s.push(23);
                    assert_eq!(*s.pop().unwrap(), 23);
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }
    }
}
<commit_msg>Rename `current` to `snapshot` for more clear naming.<commit_after>//! Treiber stacks.

use std::sync::atomic;
use std::{mem, ptr};
use {Atomic, Guard};

/// A Treiber stack.
pub struct Treiber<T> {
    head: Atomic<Node<T>>,
}

struct Node<T> {
    data: T,
    next: *const Node<T>,
}

impl<T> Treiber<T> {
    /// Create a new, empty Treiber stack.
    pub fn new() -> Treiber<T> {
        Treiber {
            head: Atomic::default(),
        }
    }

    /// Pop an item from the stack.
    pub fn pop(&self) -> Option<Guard<T>> {
        // TODO: Use `catch {}` here when it lands.
        // Read the head snapshot.
        let mut snapshot = self.head.load(atomic::Ordering::Acquire);

        // Unless the head snapshot is `None`, try to replace it with the tail.
        while let Some(node) = snapshot {
            // Attempt to replace the head with the tail of the head.
            match unsafe {
                self.head.compare_and_swap_raw(
                    &*node,
                    node.next as *mut Node<T>,
                    atomic::Ordering::Release,
                )
            } {
                // It succeeded; return the item.
                Ok(_) => return Some(node.map(|x| &x.data)),
                // It failed, update the head snapshot and continue.
                Err(new) => snapshot = new,
            }
        }

        // As the head was empty, there is nothing to pop.
        None
    }

    /// Push an item to the stack.
    pub fn push(&self, item: T)
    where T: 'static {
        // Load the head snapshot.
        let mut snapshot = self.head.load(atomic::Ordering::Acquire);
        let mut snapshot_ptr: Option<*const Node<T>>;

        // TODO: Use `catch {}` here when it lands.
        // Construct a node, which will be the new head.
        let mut node = Box::new(Node {
            data: item,
            // Placeholder; we will replace it with an actual value in the loop.
            next: ptr::null(),
        });

        loop {
            // Derive the nullable snapshot pointer from the head snapshot.
            snapshot_ptr = snapshot.as_ref().map(Guard::as_raw);
            // Construct the next-pointer of the new node from the head snapshot.
            node.next = snapshot_ptr.unwrap_or(ptr::null());

            // TODO: This should be something that ignores the guard creation when the CAS
            //       succeeds, because it's expensive to do and not used anyway. It should be easy
            //       enough to implement, but I am struggling to come up with a good name for the
            //       method.

            // CAS from the read pointer to the new head.
            match self.head.compare_and_swap(snapshot_ptr, Some(node), atomic::Ordering::Release) {
                // If it succeeds, the item has been pushed.
                Ok(_) => break,
                // If it fails, we will retry the CAS with updated values.
                Err((new_head, Some(node2))) => {
                    // Update the head snapshot.
                    snapshot = new_head;
                    // Put the box we gave back to the variable where it belongs.
                    node = node2;
                },
                // This should never be reached as we gave an argument which was unconditionally
                // `Some`.
                _ => unreachable!(),
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::sync::Arc;

    #[test]
    fn single_thread() {
        let stack = Treiber::new();

        for i in 0..10000 {
            stack.push(i);
        }

        for i in (0..10000).rev() {
            assert_eq!(*stack.pop().unwrap(), i);
        }

        for i in 0..10000 {
            stack.push(i);
        }

        for i in (0..10000).rev() {
            assert_eq!(*stack.pop().unwrap(), i);
        }

        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
        assert!(stack.pop().is_none());
    }

    #[test]
    fn push_pop() {
        let stack = Arc::new(Treiber::new());
        let mut j = Vec::new();
        for _ in 0..16 {
            let s = stack.clone();
            j.push(thread::spawn(move || {
                for _ in 0..10_000_000 {
                    s.push(23);
                    assert_eq!(*s.pop().unwrap(), 23);
                }
            }));
        }

        for i in j {
            i.join().unwrap();
        }
    }
}
<|endoftext|>


<commit_before>//! Strategies are containers for all the logic required to generate
//! trading signals and manage positions for a portfolio.  Only one strategy is
//! meant to run at a time on a single portfolio over which that strategy holds
//! complete control.
//!
//! Strategies are "black boxes" inasmuch as all of their mechanisms for generating
//! trading signals are entirely self-contained.  However, they are configurable
//! manually through the use of `Command`s sent from the MMI or some other source.
//! The main methods through which strategies interact with the world are listed:
//! 1. Commands sent through the optimizer's `CommandServer` to the Tick Processors
//! 2. Direction interaction with the database
//! 3. Live data brodacast over redis channels to which the strategy manually subscribes
//! 4. Ticks inserted into the strategy by the optimizer as they are received live
//!
//! TODO: Update this descrition

use std::collections::HashMap;

use futures;

// use trading::broker::Broker;
use transport::command_server::CommandServer;
use transport::query_server::QueryServer;
use trading::broker::Broker;

/// Wrapper for a user-defined strategy providing a variety of helper methods and utility functions for the
/// individual strategies.
pub struct StrategyContainer {
    pub cs: CommandServer,
    pub qs: QueryServer,
    pub strategy: Box<Strategy>,
}

pub trait Strategy {
    /// Instruct the strategy to initialize itself, subscribing to data streams and communicating with the
    /// the rest of the platform as necessary
    fn init(&mut self, broker: Box<Broker>);

    // /// Indicates that the strategy should save a copy of its internal state of its internal state to
    // /// the database.  The supplied future should be resolved when the dump is complete.
            // Disabled for now until a time when the platform supports that kind of functionality.
    // fn dump_state(&mut self, done: futures::Complete<()>);

    /// Indicates that the platform is going into an inoperable state and that
    /// the strategy should do whatever necessary to exit as soon as possible.
    /// Provides a oneshot that should be resolved when the strategy is ready to exit.
    fn exit_now(&mut self, ready: futures::Complete<()>);
}

pub trait ContingencyHandler {
    fn
}
<commit_msg>Fleshed out some prelimenary strategy internals  - Built up more strategy internals involving contingency handlers and handler containers  - Introduced FSM-based contingency handlers<commit_after>//! Strategies are containers for all the logic required to generate
//! trading signals and manage positions for a portfolio.  Only one strategy is
//! meant to run at a time on a single portfolio over which that strategy holds
//! complete control.
//!
//! Strategies are "black boxes" inasmuch as all of their mechanisms for generating
//! trading signals are entirely self-contained.  However, they are configurable
//! manually through the use of `Command`s sent from the MMI or some other source.
//! The main methods through which strategies interact with the world are listed:
//! 1. Commands sent through the optimizer's `CommandServer` to the Tick Processors
//! 2. Direction interaction with the database
//! 3. Live data brodacast over redis channels to which the strategy manually subscribes
//! 4. Ticks inserted into the strategy by the optimizer as they are received live
//!
//! TODO: Update this descrition

use std::collections::HashMap;

use futures::Complete;
use futures::stream::BoxStream;
use uuid::Uuid;

// use trading::broker::Broker;
use transport::command_server::CommandServer;
use transport::query_server::QueryServer;
use trading::objects::BrokerAction;
use trading::broker::Broker;
use trading::tick::Tick;

/// A stream of input ticks from some source (real or artificial) used to drive progress of the strategy.
pub struct Tickstream {
    pub inner: BoxStream<Tick, ()>,
}

/// Wrapper for the interior of a tick processor providing a variety of helper methods and utility functions
/// for management of the handled contingencies and communication with the rest of the platform.
pub struct ContingencyContainer<T> {
    pub cs: CommandServer,
    /// hold which contained contingency handlers are subscribed to which tickstreams
    pub tickstreams: Vec<Tickstream>,
    pub subscriptions: Vec<Vec<usize>>,
    /// The threadsafe part of the container shared by all of the contingency handlers
    pub threadsafe_state: T,
    /// holds the actual contained handlers
    pub handlers: HashMap<Uuid, Box<ContingencyHandler<T>>>,
}

impl<T: Clone + Send> ContingencyContainer<T> {
    pub fn new(state: T, name: &str) -> ContingencyContainer<T> {
        ContingencyContainer {
            cs: CommandServer::new(Uuid::new_v4(), name),
            tickstreams: Vec::new(),
            subscriptions: Vec::new(),
            threadsafe_state: state,
            handlers: HashMap::new(),
        }
    }

    pub fn add_handler(&mut self, handler: Box<ContingencyHandler<T>>, subscriptions: Vec<usize>) -> Uuid {
        self.subscriptions.push(subscriptions);
        let uuid = Uuid::new_v4();
        self.handlers.insert(uuid, handler);
        debug_assert_eq!(self.handlers.len(), self.subscriptions.len());
        uuid
    }

    pub fn get_mut_handler(&mut self, uuid: Uuid) -> Option<&mut Box<ContingencyHandler<T>>> {
        self.handlers.get_mut(&uuid)
    }

    pub fn remove_handler(&mut self, uuid: Uuid) -> Option<Box<ContingencyHandler<T>>> {
        self.handlers.remove(&uuid)
    }
}

/// Wrapper for a user-defined strategy providing a variety of helper methods and utility functions for the
/// individual strategies.  This is passed off to a strategy executor that handles the act of actually ticking
/// the user-defined strategy and driving the process forward.
pub struct StrategyManager<S> {
    pub tickstreams: Vec<Tickstream>,
    pub cs: CommandServer,
    pub qs: QueryServer,
    /// The user-defined portion of the strategy container
    pub strategy: Box<Strategy>,
    pub broker: Box<Broker>,
    /// The user-defined part of the container where persistant state can be stored.
    pub state: S,
}

/// A user-defined piece of logic for controlling the creation, modification, and deletion of
/// `ContingencyHandler`s on the tick parser.
pub trait Strategy {
    /// Instruct the strategy to initialize itself, subscribing to data streams and communicating with the
    /// the rest of the platform as necessary.
    fn init(&mut self);

    // /// Indicates that the strategy should save a copy of its internal state of its internal state to
    // /// the database.  The supplied future should be resolved when the dump is complete.
            // Disabled for now until a time when the platform supports that kind of functionality.
    // fn dump_state(&mut self, done: futures::Complete<()>);

    /// Indicates that the platform is going into an inoperable state and that
    /// the strategy should do whatever necessary to exit as soon as possible.
    /// Provides a oneshot that should be resolved when the strategy is ready to exit.
    fn exit_now(&mut self, ready: Complete<()>);
}

/// Often representing the status of an order or a block of closely related orders, each handler contained in a
/// `ContingencyContainer` is `tick()`ed every time new data is available and any returned actions processed.
pub trait ContingencyHandler<T> {
    /// Called every time new data is available.
    fn tick(&mut self, data_ix: usize, t: &Tick) -> Option<BrokerAction>;

    /// If this is called, it means that we have a situation where the platform can no longer manage this
    /// contingency and we should not expect any further interaction.  Depending on the specifics of the handler,
    /// this action may pull an underlying pending order, simply stop responding to ticks, or take some other action
    /// to deal with the situation.
    fn abort(&mut self);
}

pub trait FSMInnerHandler<S> {
    fn tick(&mut self, old_state: &S, data_ix: usize, t: &Tick) -> (S, Option<BrokerAction>);

    fn abort(&mut self);
}

/// A contingency handler implemented using a Finite State Machine.  Every tick, the handler either keeps its currrent
/// state or transitions to another state.  This is an easy way to define contingency handlers that can deal with
/// multiple different phases of a single task.
///
/// For example, a handler that simply opens an order has to take into account an order not being filled, of being denied
/// for a reason such as insufficient balance, or receiving a partial fill.  With a FSM, it is possible to maintain this
/// full set of actions/responses without having to create new `ContingencyHandler` for each of the possible alternatives.
pub struct FSMContingencyHandler<S> {
    state: S,
    handler: Box<FSMInnerHandler<S>>,
}

impl<S> ContingencyHandler<S> for FSMContingencyHandler<S> {
    fn tick(&mut self, data_ix: usize, t: &Tick) -> Option<BrokerAction> {
        let (state, action_opt) = self.handler.tick(&self.state, data_ix, t);
        self.state = state;
        action_opt
    }

    fn abort(&mut self) {
        self.handler.abort()
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::iter::Iterator;

pub struct Pids {
    iter: Box<Iterator<Item=i32>>,
}

impl Iterator for Pids {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        self.iter.next()
    }
}

fn pids_from_path(proc_path: &str) -> Pids {
    let iter = fs::read_dir(proc_path).unwrap()
        .filter_map(|entry| entry.ok())
        .filter_map(|entry| entry.file_name().into_string().ok())
        .filter_map(|entry| entry.parse::<i32>().ok());
    Pids{iter: Box::new(iter)}
}

pub fn pids() -> Pids {
    pids_from_path("/proc")
}

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

    #[test]
    fn test_pids_from_path() {
        let mut pids: Vec<i32> = super::pids_from_path("testdata/proc").collect();
        pids.sort();
        assert_eq!(pids, vec![1, 33, 68]);
    }
}
<commit_msg>Add some comments<commit_after>use std::fs;
use std::iter::Iterator;

pub struct Pids {
    iter: Box<Iterator<Item=i32>>,
}

impl Iterator for Pids {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        self.iter.next()
    }
}

fn pids_from_path(proc_path: &str) -> Pids {
    let iter = fs::read_dir(proc_path).unwrap()
        // Process directories might have gone away since
        // the directory was read. It's fine to ignore those.
        .filter_map(|entry| entry.ok())
        // Map entry to a string, removing it if it fails to
        // parse as unicode.
        .filter_map(|entry| entry.file_name().into_string().ok())
        // Remove any entries that can't be converted to an integer.
        .filter_map(|entry| entry.parse::<i32>().ok());
    Pids{iter: Box::new(iter)}
}

pub fn pids() -> Pids {
    pids_from_path("/proc")
}

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

    #[test]
    fn test_pids_from_path() {
        let mut pids: Vec<i32> = super::pids_from_path("testdata/proc").collect();
        pids.sort();
        assert_eq!(pids, vec![1, 33, 68]);
    }
}
<|endoftext|>


<commit_before>use libc::c_double;
use std::ptr::{null, null_mut};
use raster::{Dataset};
use gdal_sys::{self, CPLErr, GDALRIOResampleAlg};
use utils::_last_cpl_err;

use errors::*;

pub fn reproject(src: &Dataset, dst: &Dataset) -> Result<()> {
    let rv = unsafe {
        gdal_sys::GDALReprojectImage(
                src._c_ptr(),
                null(),
                dst._c_ptr(),
                null(),
                GDALRIOResampleAlg::GRIORA_Bilinear,
                0.0,
                0.0 as c_double,
                None,
                null_mut(),
                null_mut()
            )
    };
    if rv != CPLErr::CE_None {
        return Err(_last_cpl_err(rv).into());
    }
    Ok(())
}
<commit_msg>Fix resample alg type in GDALReprojectImage<commit_after>use libc::c_double;
use std::ptr::{null, null_mut};
use raster::{Dataset};
use gdal_sys::{self, CPLErr, GDALResampleAlg};
use utils::_last_cpl_err;

use errors::*;

pub fn reproject(src: &Dataset, dst: &Dataset) -> Result<()> {
    let rv = unsafe {
        gdal_sys::GDALReprojectImage(
                src._c_ptr(),
                null(),
                dst._c_ptr(),
                null(),
                GDALResampleAlg::GRA_Bilinear,
                0.0,
                0.0 as c_double,
                None,
                null_mut(),
                null_mut()
            )
    };
    if rv != CPLErr::CE_None {
        return Err(_last_cpl_err(rv).into());
    }
    Ok(())
}
<|endoftext|>