<commit_before>use syntax::{
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
    },
    AstNode, TextRange, T,
};

use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};

// Assist: unwrap_block
//
// This assist removes if...else, for, while and loop control statements to just keep the body.
//
// ```
// fn foo() {
//     if true {<|>
//         println!("foo");
//     }
// }
// ```
// ->
// ```
// fn foo() {
//     println!("foo");
// }
// ```
pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let assist_id = AssistId("unwrap_block", AssistKind::RefactorRewrite);
    let assist_label = "Unwrap block";

    let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
    let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
    let mut parent = block.syntax().parent()?;
    if ast::MatchArm::can_cast(parent.kind()) {
        parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
    }

    let parent = ast::Expr::cast(parent)?;

    match parent.clone() {
        ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
        ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
        ast::Expr::IfExpr(if_expr) => {
            let then_branch = if_expr.then_branch()?;
            if then_branch == block {
                if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
                    // For `else if` blocks
                    let ancestor_then_branch = ancestor.then_branch()?;

                    let target = then_branch.syntax().text_range();
                    return acc.add(assist_id, assist_label, target, |edit| {
                        let range_to_del_else_if = TextRange::new(
                            ancestor_then_branch.syntax().text_range().end(),
                            l_curly_token.text_range().start(),
                        );
                        let range_to_del_rest = TextRange::new(
                            then_branch.syntax().text_range().end(),
                            if_expr.syntax().text_range().end(),
                        );

                        edit.delete(range_to_del_rest);
                        edit.delete(range_to_del_else_if);
                        edit.replace(
                            target,
                            update_expr_string(then_branch.to_string(), &[' ', '{']),
                        );
                    });
                }
            } else {
                let target = block.syntax().text_range();
                return acc.add(assist_id, assist_label, target, |edit| {
                    let range_to_del = TextRange::new(
                        then_branch.syntax().text_range().end(),
                        l_curly_token.text_range().start(),
                    );

                    edit.delete(range_to_del);
                    edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
                });
            }
        }
        _ => return None,
    };

    let unwrapped = unwrap_trivial_block(block);
    let target = unwrapped.syntax().text_range();
    acc.add(assist_id, assist_label, target, |builder| {
        builder.replace(
            parent.syntax().text_range(),
            update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
        );
    })
}

fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
    let expr_string = expr_str.trim_start_matches(trim_start_pat);
    let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
    expr_string_lines.pop(); // Delete last line

    expr_string_lines
        .into_iter()
        .map(|line| line.replacen("    ", "", 1)) // Delete indentation
        .collect::<Vec<String>>()
        .join("\n")
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn simple_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {<|>
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                } else {<|>
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {<|>
                    println!("bar");
                } else {
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                } else {<|>
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                }
                println!("else");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_middle() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                } else {
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                bar();<|>
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {
                    if true {<|>
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                for i in 0..5 {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_loop() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                loop {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_while() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                while true {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn unwrap_match_arm() {
        check_assist(
            unwrap_block,
            r#"
fn main() {
    match rel_path {
        Ok(rel_path) => {<|>
            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
            Some((*id, rel_path))
        }
        Err(_) => None,
    }
}
"#,
            r#"
fn main() {
    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    Some((*id, rel_path))
}
"#,
        );
    }

    #[test]
    fn simple_if_in_while_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                while true {
                    if true {
                        foo();<|>

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
        );
    }
}
<commit_msg>Assist target should point at *existing* code<commit_after>use syntax::{
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
    },
    AstNode, TextRange, T,
};

use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};

// Assist: unwrap_block
//
// This assist removes if...else, for, while and loop control statements to just keep the body.
//
// ```
// fn foo() {
//     if true {<|>
//         println!("foo");
//     }
// }
// ```
// ->
// ```
// fn foo() {
//     println!("foo");
// }
// ```
pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let assist_id = AssistId("unwrap_block", AssistKind::RefactorRewrite);
    let assist_label = "Unwrap block";

    let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
    let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
    let mut parent = block.syntax().parent()?;
    if ast::MatchArm::can_cast(parent.kind()) {
        parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
    }

    let parent = ast::Expr::cast(parent)?;

    match parent.clone() {
        ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
        ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
        ast::Expr::IfExpr(if_expr) => {
            let then_branch = if_expr.then_branch()?;
            if then_branch == block {
                if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
                    // For `else if` blocks
                    let ancestor_then_branch = ancestor.then_branch()?;

                    let target = then_branch.syntax().text_range();
                    return acc.add(assist_id, assist_label, target, |edit| {
                        let range_to_del_else_if = TextRange::new(
                            ancestor_then_branch.syntax().text_range().end(),
                            l_curly_token.text_range().start(),
                        );
                        let range_to_del_rest = TextRange::new(
                            then_branch.syntax().text_range().end(),
                            if_expr.syntax().text_range().end(),
                        );

                        edit.delete(range_to_del_rest);
                        edit.delete(range_to_del_else_if);
                        edit.replace(
                            target,
                            update_expr_string(then_branch.to_string(), &[' ', '{']),
                        );
                    });
                }
            } else {
                let target = block.syntax().text_range();
                return acc.add(assist_id, assist_label, target, |edit| {
                    let range_to_del = TextRange::new(
                        then_branch.syntax().text_range().end(),
                        l_curly_token.text_range().start(),
                    );

                    edit.delete(range_to_del);
                    edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
                });
            }
        }
        _ => return None,
    };

    let target = block.syntax().text_range();
    let unwrapped = unwrap_trivial_block(block);
    acc.add(assist_id, assist_label, target, |builder| {
        builder.replace(
            parent.syntax().text_range(),
            update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
        );
    })
}

fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String {
    let expr_string = expr_str.trim_start_matches(trim_start_pat);
    let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
    expr_string_lines.pop(); // Delete last line

    expr_string_lines
        .into_iter()
        .map(|line| line.replacen("    ", "", 1)) // Delete indentation
        .collect::<Vec<String>>()
        .join("\n")
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn simple_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {<|>
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                } else {<|>
                    println!("bar");
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if true {
                    foo();

                    //comment
                    bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {<|>
                    println!("bar");
                } else {
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                }
                println!("bar");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_else() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                } else {<|>
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {
                    println!("foo");
                }
                println!("else");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_else_if_nested_middle() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                } else if true {<|>
                    println!("foo");
                } else {
                    println!("else");
                }
            }
            "#,
            r#"
            fn main() {
                //bar();
                if true {
                    println!("true");

                    //comment
                    //bar();
                } else if false {
                    println!("bar");
                }
                println!("foo");
            }
            "#,
        );
    }

    #[test]
    fn simple_if_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                bar();<|>
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_if_in_for() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                for i in 0..5 {
                    if true {<|>
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                for i in 0..5 {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_loop() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                loop {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn simple_while() {
        check_assist(
            unwrap_block,
            r#"
            fn main() {
                while true {<|>
                    if true {
                        foo();

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
            r#"
            fn main() {
                if true {
                    foo();

                    //comment
                    bar();
                } else {
                    println!("bar");
                }
            }
            "#,
        );
    }

    #[test]
    fn unwrap_match_arm() {
        check_assist(
            unwrap_block,
            r#"
fn main() {
    match rel_path {
        Ok(rel_path) => {<|>
            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
            Some((*id, rel_path))
        }
        Err(_) => None,
    }
}
"#,
            r#"
fn main() {
    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    Some((*id, rel_path))
}
"#,
        );
    }

    #[test]
    fn simple_if_in_while_bad_cursor_position() {
        check_assist_not_applicable(
            unwrap_block,
            r#"
            fn main() {
                while true {
                    if true {
                        foo();<|>

                        //comment
                        bar();
                    } else {
                        println!("bar");
                    }
                }
            }
            "#,
        );
    }
}
<|endoftext|>


<commit_before>use super::*;
use std::io::Write;
use std::mem;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use std::time;
use std::time::Duration;

#[test]
fn test_ram_directory() {
    let mut ram_directory = RAMDirectory::create();
    test_directory(&mut ram_directory);
}

#[test]
#[cfg(feature = "mmap")]
fn test_mmap_directory() {
    let mut mmap_directory = MmapDirectory::create_from_tempdir().unwrap();
    test_directory(&mut mmap_directory);
}

#[test]
#[should_panic]
fn ram_directory_panics_if_flush_forgotten() {
    let test_path: &'static Path = Path::new("some_path_for_test");
    let mut ram_directory = RAMDirectory::create();
    let mut write_file = ram_directory.open_write(test_path).unwrap();
    assert!(write_file.write_all(&[4]).is_ok());
}

fn test_simple(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    {
        let mut write_file = directory.open_write(test_path).unwrap();
        assert!(directory.exists(test_path));
        write_file.write_all(&[4]).unwrap();
        write_file.write_all(&[3]).unwrap();
        write_file.write_all(&[7, 3, 5]).unwrap();
        write_file.flush().unwrap();
    }
    {
        let read_file = directory.open_read(test_path).unwrap();
        let data: &[u8] = &*read_file;
        assert_eq!(data, &[4u8, 3u8, 7u8, 3u8, 5u8]);
    }
    assert!(directory.delete(test_path).is_ok());
    assert!(!directory.exists(test_path));
}

fn test_rewrite_forbidden(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    {
        directory.open_write(test_path).unwrap();
        assert!(directory.exists(test_path));
    }
    {
        assert!(directory.open_write(test_path).is_err());
    }
    assert!(directory.delete(test_path).is_ok());
}

fn test_write_create_the_file(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    {
        assert!(directory.open_read(test_path).is_err());
        let _w = directory.open_write(test_path).unwrap();
        assert!(directory.exists(test_path));
        assert!(directory.open_read(test_path).is_ok());
        assert!(directory.delete(test_path).is_ok());
    }
}

fn test_directory_delete(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    assert!(directory.open_read(test_path).is_err());
    let mut write_file = directory.open_write(&test_path).unwrap();
    write_file.write_all(&[1, 2, 3, 4]).unwrap();
    write_file.flush().unwrap();
    {
        let read_handle = directory.open_read(&test_path).unwrap();
        assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
        // Mapped files can't be deleted on Windows
        if !cfg!(windows) {
            assert!(directory.delete(&test_path).is_ok());
            assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
        }

        assert!(directory.delete(Path::new("SomeOtherPath")).is_err());
    }

    if cfg!(windows) {
        assert!(directory.delete(&test_path).is_ok());
    }

    assert!(directory.open_read(&test_path).is_err());
    assert!(directory.delete(&test_path).is_err());
}

fn test_directory(directory: &mut dyn Directory) {
    test_simple(directory);
    test_rewrite_forbidden(directory);
    test_write_create_the_file(directory);
    test_directory_delete(directory);
    test_lock_non_blocking(directory);
    test_lock_blocking(directory);
    test_watch(directory);
}

fn test_watch(directory: &mut dyn Directory) {
    let counter: Arc<AtomicUsize> = Default::default();
    let counter_clone = counter.clone();
    let watch_callback = Box::new(move || {
        counter_clone.fetch_add(1, Ordering::SeqCst);
    });
    assert!(directory
        .atomic_write(Path::new("meta.json"), b"random_test_data")
        .is_ok());
    thread::sleep(Duration::new(0, 10_000));
    assert_eq!(0, counter.load(Ordering::SeqCst));

    let watch_handle = directory.watch(watch_callback).unwrap();
    for i in 0..10 {
        assert_eq!(i, counter.load(Ordering::SeqCst));
        assert!(directory
            .atomic_write(Path::new("meta.json"), b"random_test_data_2")
            .is_ok());
        for _ in 0..100 {
            if counter.load(Ordering::SeqCst) > i {
                break;
            }
            thread::sleep(Duration::from_millis(10));
        }
        assert_eq!(i + 1, counter.load(Ordering::SeqCst));
    }
    mem::drop(watch_handle);
    assert!(directory
        .atomic_write(Path::new("meta.json"), b"random_test_data")
        .is_ok());
    thread::sleep(Duration::from_millis(200));
    assert_eq!(10, counter.load(Ordering::SeqCst));
}

fn test_lock_non_blocking(directory: &mut dyn Directory) {
    {
        let lock_a_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: false,
        });
        assert!(lock_a_res.is_ok());
        let lock_b_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("b.lock"),
            is_blocking: false,
        });
        assert!(lock_b_res.is_ok());
        let lock_a_res2 = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: false,
        });
        assert!(lock_a_res2.is_err());
    }
    let lock_a_res = directory.acquire_lock(&Lock {
        filepath: PathBuf::from("a.lock"),
        is_blocking: false,
    });
    assert!(lock_a_res.is_ok());
}

fn test_lock_blocking(directory: &mut dyn Directory) {
    let lock_a_res = directory.acquire_lock(&Lock {
        filepath: PathBuf::from("a.lock"),
        is_blocking: true,
    });
    assert!(lock_a_res.is_ok());
    std::thread::spawn(move || {
        //< lock_a_res is sent to the thread.
        std::thread::sleep(time::Duration::from_millis(10));
        // explicitely droping lock_a_res. It would have been sufficient to just force it
        // to be part of the move, but the intent seems clearer that way.
        drop(lock_a_res);
    });
    {
        // A non-blocking call should fail, as the thread is running and holding the lock.
        let lock_a_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: false,
        });
        assert!(lock_a_res.is_err());
    }
    {
        // the blocking call should wait for at least 10ms.
        let start = time::Instant::now();
        let lock_a_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: true,
        });
        assert!(lock_a_res.is_ok());
        assert!(start.elapsed().subsec_millis() >= 10);
    }
}
<commit_msg>Waiting potentially longer on watch<commit_after>use super::*;
use std::io::Write;
use std::mem;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use std::time;
use std::time::Duration;

#[test]
fn test_ram_directory() {
    let mut ram_directory = RAMDirectory::create();
    test_directory(&mut ram_directory);
}

#[test]
#[cfg(feature = "mmap")]
fn test_mmap_directory() {
    let mut mmap_directory = MmapDirectory::create_from_tempdir().unwrap();
    test_directory(&mut mmap_directory);
}

#[test]
#[should_panic]
fn ram_directory_panics_if_flush_forgotten() {
    let test_path: &'static Path = Path::new("some_path_for_test");
    let mut ram_directory = RAMDirectory::create();
    let mut write_file = ram_directory.open_write(test_path).unwrap();
    assert!(write_file.write_all(&[4]).is_ok());
}

fn test_simple(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    {
        let mut write_file = directory.open_write(test_path).unwrap();
        assert!(directory.exists(test_path));
        write_file.write_all(&[4]).unwrap();
        write_file.write_all(&[3]).unwrap();
        write_file.write_all(&[7, 3, 5]).unwrap();
        write_file.flush().unwrap();
    }
    {
        let read_file = directory.open_read(test_path).unwrap();
        let data: &[u8] = &*read_file;
        assert_eq!(data, &[4u8, 3u8, 7u8, 3u8, 5u8]);
    }
    assert!(directory.delete(test_path).is_ok());
    assert!(!directory.exists(test_path));
}

fn test_rewrite_forbidden(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    {
        directory.open_write(test_path).unwrap();
        assert!(directory.exists(test_path));
    }
    {
        assert!(directory.open_write(test_path).is_err());
    }
    assert!(directory.delete(test_path).is_ok());
}

fn test_write_create_the_file(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    {
        assert!(directory.open_read(test_path).is_err());
        let _w = directory.open_write(test_path).unwrap();
        assert!(directory.exists(test_path));
        assert!(directory.open_read(test_path).is_ok());
        assert!(directory.delete(test_path).is_ok());
    }
}

fn test_directory_delete(directory: &mut dyn Directory) {
    let test_path: &'static Path = Path::new("some_path_for_test");
    assert!(directory.open_read(test_path).is_err());
    let mut write_file = directory.open_write(&test_path).unwrap();
    write_file.write_all(&[1, 2, 3, 4]).unwrap();
    write_file.flush().unwrap();
    {
        let read_handle = directory.open_read(&test_path).unwrap();
        assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
        // Mapped files can't be deleted on Windows
        if !cfg!(windows) {
            assert!(directory.delete(&test_path).is_ok());
            assert_eq!(&*read_handle, &[1u8, 2u8, 3u8, 4u8]);
        }

        assert!(directory.delete(Path::new("SomeOtherPath")).is_err());
    }

    if cfg!(windows) {
        assert!(directory.delete(&test_path).is_ok());
    }

    assert!(directory.open_read(&test_path).is_err());
    assert!(directory.delete(&test_path).is_err());
}

fn test_directory(directory: &mut dyn Directory) {
    test_simple(directory);
    test_rewrite_forbidden(directory);
    test_write_create_the_file(directory);
    test_directory_delete(directory);
    test_lock_non_blocking(directory);
    test_lock_blocking(directory);
    test_watch(directory);
}

fn test_watch(directory: &mut dyn Directory) {
    let counter: Arc<AtomicUsize> = Default::default();
    let counter_clone = counter.clone();
    let watch_callback = Box::new(move || {
        counter_clone.fetch_add(1, Ordering::SeqCst);
    });
    assert!(directory
        .atomic_write(Path::new("meta.json"), b"random_test_data")
        .is_ok());
    thread::sleep(Duration::new(0, 10_000));
    assert_eq!(0, counter.load(Ordering::SeqCst));

    let watch_handle = directory.watch(watch_callback).unwrap();
    for i in 0..10 {
        assert_eq!(i, counter.load(Ordering::SeqCst));
        assert!(directory
            .atomic_write(Path::new("meta.json"), b"random_test_data_2")
            .is_ok());
        for _ in 0..1_000 {
            if counter.load(Ordering::SeqCst) > i {
                break;
            }
            thread::sleep(Duration::from_millis(10));
        }
        assert_eq!(i + 1, counter.load(Ordering::SeqCst));
    }
    mem::drop(watch_handle);
    assert!(directory
        .atomic_write(Path::new("meta.json"), b"random_test_data")
        .is_ok());
    thread::sleep(Duration::from_millis(200));
    assert_eq!(10, counter.load(Ordering::SeqCst));
}

fn test_lock_non_blocking(directory: &mut dyn Directory) {
    {
        let lock_a_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: false,
        });
        assert!(lock_a_res.is_ok());
        let lock_b_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("b.lock"),
            is_blocking: false,
        });
        assert!(lock_b_res.is_ok());
        let lock_a_res2 = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: false,
        });
        assert!(lock_a_res2.is_err());
    }
    let lock_a_res = directory.acquire_lock(&Lock {
        filepath: PathBuf::from("a.lock"),
        is_blocking: false,
    });
    assert!(lock_a_res.is_ok());
}

fn test_lock_blocking(directory: &mut dyn Directory) {
    let lock_a_res = directory.acquire_lock(&Lock {
        filepath: PathBuf::from("a.lock"),
        is_blocking: true,
    });
    assert!(lock_a_res.is_ok());
    std::thread::spawn(move || {
        //< lock_a_res is sent to the thread.
        std::thread::sleep(time::Duration::from_millis(10));
        // explicitely droping lock_a_res. It would have been sufficient to just force it
        // to be part of the move, but the intent seems clearer that way.
        drop(lock_a_res);
    });
    {
        // A non-blocking call should fail, as the thread is running and holding the lock.
        let lock_a_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: false,
        });
        assert!(lock_a_res.is_err());
    }
    {
        // the blocking call should wait for at least 10ms.
        let start = time::Instant::now();
        let lock_a_res = directory.acquire_lock(&Lock {
            filepath: PathBuf::from("a.lock"),
            is_blocking: true,
        });
        assert!(lock_a_res.is_ok());
        assert!(start.elapsed().subsec_millis() >= 10);
    }
}
<|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/.

pub mod ffi;

use std::ffi::CString;
use std::io;

use domain::*;
use types::*;

use self::ffi::*;

pub struct LVar {
    subscriptions: Vec<Subscription>,
}

impl LVar {
    pub fn new() -> LVar {
        LVar { subscriptions: Vec::new() }
    }
}

impl Domain for LVar {
    fn write(&mut self, variable: &Var, value: &Value) -> io::Result<()> {
        debug!("processing a write operation for {:?} <- {}", variable, value);
        match variable {
            &Var::Named(ref lvar) => {
                let id = try!(check_named_variable(lvar).ok_or(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("there is no such named variable '{}'", lvar))));
                set_named_variable_value(id, f64::from(value));
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn subscribe(&mut self, device: DeviceId, variable: &Var) -> io::Result<()> {
        info!("receiving a subscription from device {} for {:?}", device, variable);
        match variable {
            &Var::Named(ref lvar) => {
                let subs = Subscription {
                    device: device,
                    lvar: lvar.clone(),
                    retain: None,
                };
                self.subscriptions.push(subs);
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn unsubscribe_all(&mut self, device: DeviceId) -> io::Result<()> {
        debug!("removing all subscriptions for device ID {}", device);
        self.subscriptions.retain(|s| s.device != device);
        Ok(())
    }

    fn poll(&mut self, events: &mut Vec<Event>) -> io::Result<()> {
        events.clear();
        for sub in self.subscriptions.iter_mut() {
            sub.trigger_event(events);
        }
        Ok(())
    }
}

struct Subscription {
    device: DeviceId,
    lvar: String,
    retain: Option<Value>,
}

impl Subscription {
    fn trigger_event(&mut self, events: &mut Vec<Event>) {
        let id = check_named_variable(&self.lvar).unwrap();
        let val = Value::Number(get_named_variable_value(id) as isize);
        let must_trigger = self.retain.as_ref().map(|v| *v != val).unwrap_or(true);
        if must_trigger {
            let var = Var::Named(self.lvar.clone());
            let event = Event::new(self.device, "lvar", var, val);
            events.push(event);
        }
    }
}

fn check_named_variable(name: &str) -> Option<Id> {
    unsafe {
        let func = (*Panels).check_named_variable;
        let name = CString::new(name).unwrap();
        let id = (func)(name.as_ptr());
        if id != -1 { Some(id) } else { None }
    }
}

fn get_named_variable_value(id: Id) -> f64 {
    unsafe {
        let func = (*Panels).get_named_variable_value;
        (func)(id)
    }
}

fn set_named_variable_value(id: Id, value: f64) {
    unsafe {
        let func = (*Panels).set_named_variable_value;
        (func)(id, value)
    }
}
<commit_msg>Avoid LVAR event trigerring when var name is not found<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/.

pub mod ffi;

use std::ffi::CString;
use std::io;

use domain::*;
use types::*;

use self::ffi::*;

pub struct LVar {
    subscriptions: Vec<Subscription>,
}

impl LVar {
    pub fn new() -> LVar {
        LVar { subscriptions: Vec::new() }
    }
}

impl Domain for LVar {
    fn write(&mut self, variable: &Var, value: &Value) -> io::Result<()> {
        debug!("processing a write operation for {:?} <- {}", variable, value);
        match variable {
            &Var::Named(ref lvar) => {
                let id = try!(check_named_variable(lvar).ok_or(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("there is no such named variable '{}'", lvar))));
                set_named_variable_value(id, f64::from(value));
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn subscribe(&mut self, device: DeviceId, variable: &Var) -> io::Result<()> {
        info!("receiving a subscription from device {} for {:?}", device, variable);
        match variable {
            &Var::Named(ref lvar) => {
                let subs = Subscription {
                    device: device,
                    lvar: lvar.clone(),
                    retain: None,
                };
                self.subscriptions.push(subs);
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn unsubscribe_all(&mut self, device: DeviceId) -> io::Result<()> {
        debug!("removing all subscriptions for device ID {}", device);
        self.subscriptions.retain(|s| s.device != device);
        Ok(())
    }

    fn poll(&mut self, events: &mut Vec<Event>) -> io::Result<()> {
        events.clear();
        for sub in self.subscriptions.iter_mut() {
            sub.trigger_event(events);
        }
        Ok(())
    }
}

struct Subscription {
    device: DeviceId,
    lvar: String,
    retain: Option<Value>,
}

impl Subscription {
    fn trigger_event(&mut self, events: &mut Vec<Event>) {
        let id = match check_named_variable(&self.lvar) {
            Some(id) => id,
            None => {
                error!("cannot obtain LVAR ID for variable {}", self.lvar);
                return;
            }
        };
        let val = Value::Number(get_named_variable_value(id) as isize);
        let must_trigger = self.retain.as_ref().map(|v| *v != val).unwrap_or(true);
        if must_trigger {
            let var = Var::Named(self.lvar.clone());
            let event = Event::new(self.device, "lvar", var, val);
            events.push(event);
        }
    }
}

fn check_named_variable(name: &str) -> Option<Id> {
    unsafe {
        let func = (*Panels).check_named_variable;
        let name = CString::new(name).unwrap();
        let id = (func)(name.as_ptr());
        if id != -1 { Some(id) } else { None }
    }
}

fn get_named_variable_value(id: Id) -> f64 {
    unsafe {
        let func = (*Panels).get_named_variable_value;
        (func)(id)
    }
}

fn set_named_variable_value(id: Id, value: f64) {
    unsafe {
        let func = (*Panels).set_named_variable_value;
        (func)(id, value)
    }
}
<|endoftext|>


<commit_before>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);

        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }

    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => { },
            AddressMode::AbsoluteY       => { },
            AddressMode::Accumulator     => { },
            AddressMode::Immediate       => { },
            AddressMode::Implied         => { },
            AddressMode::Indirect        => { },
            AddressMode::IndexedIndirect => { },
            AddressMode::IndirectIndexed => { },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {},
            AddressMode::ZeroPageY       => { },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }
}

<commit_msg>added AbsoluteX/AbsoluteY lookup<commit_after>use cpu::common_defs::OpcodeExecInfo;
use cpu::CpuState;
use cpu::DecodeRegister;
use cpu::common_defs::address_mode::AddressMode;
use memory::Memory;
use std::collections::HashMap;

pub struct CpuExecutor {
    op_table: HashMap<u8,OpcodeExecInfo>,
}

impl CpuExecutor {
    pub fn new(opcodes: Vec<OpcodeExecInfo> ) -> CpuExecutor {
        // Construct the HashMap and copy it over to CpuExecutor's constructor.
        let mut op_table_temp: HashMap<u8, OpcodeExecInfo> = HashMap::new();
        for oc in opcodes {
            if op_table_temp.contains_key(&oc.opcode) {
                println!("Encountered duplicate opcode: {:#x}", oc.opcode);
                println!("Replacing previous opcode, which was {{ opcode: {:#x}, len: {}, cycles: {}, page_cycles: {} }}", oc.opcode, oc.len, oc.cycles, oc.page_cycles);
            }
            op_table_temp.insert(oc.opcode, oc);
        }

        CpuExecutor {
            op_table: op_table_temp,
        }
    }

    pub fn reset(self: &CpuExecutor, cpu_state: &mut CpuState, mem:&mut Memory) {
        cpu_state.pc = mem.read16(0xFFFC).unwrap();
        cpu_state.sp = 0xFD;
        cpu_state.pack_flags(0x24);
    }

    pub fn fetch_and_decode(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        cpu_state.instruction_register = mem.read8(cpu_state.pc).unwrap();
        cpu_state.decode_register = self.decode(cpu_state,mem);

        cpu_state.pc = cpu_state.pc + cpu_state.decode_register.info.len as u16;
    }
    pub fn execute(self: &CpuExecutor, mut cpu_state: &CpuState,mut mem:&Memory) {
    }
    pub fn step(self: &CpuExecutor, cpu_state: &mut CpuState,mem:&mut Memory) {
        self.fetch_and_decode(cpu_state,mem);
        self.execute(cpu_state,mem);
    }

    fn decode(self: &CpuExecutor, cpu_state: &CpuState, mem: &Memory) -> DecodeRegister {
        let mut dr = DecodeRegister {
            info : self.op_table[&cpu_state.instruction_register].clone(),
            ..Default::default()
        };
        match dr.info.address_mode {
            AddressMode::Absolute => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap());
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteX       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.x as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::AbsoluteY       => {
                dr.addr_final  = Some(mem.read16(cpu_state.pc+1).unwrap() + cpu_state.y as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::Accumulator     => { },
            AddressMode::Immediate       => { },
            AddressMode::Implied         => { },
            AddressMode::Indirect        => { },
            AddressMode::IndexedIndirect => { },
            AddressMode::IndirectIndexed => { },
            AddressMode::Relative        => {
                dr.addr_final  = Some(cpu_state.pc+1);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPage        => {
                dr.addr_final  = Some(mem.read8(cpu_state.pc+1).unwrap() as u16);
                dr.value_final = Some(mem.read8(dr.addr_final.unwrap()).unwrap());
            },
            AddressMode::ZeroPageX       => {},
            AddressMode::ZeroPageY       => { },
            _ => panic!("unrecognized addressing mode while decoding instruction_register!")
        }

        return dr;
    }
}

<|endoftext|>


<commit_before>extern crate std;

use resources::ResourcesLoader;
use serialize::json;
use std::ascii::StrAsciiExt;
use std::collections::{ HashSet, HashMap };
use super::EntitiesState;
use super::EntityID;
use super::ComponentID;

pub fn load(loader: &ResourcesLoader, resourceName: &str, output: &mut EntitiesState)
	-> Result<Vec<EntityID>, String>
{
	let mut docs = HashSet::new();
	load_impl(loader, resourceName, output, &mut docs)
}

fn load_impl(loader: &ResourcesLoader, resourceName: &str, output: &mut EntitiesState, loadedDocs: &mut HashSet<String>)
	-> Result<Vec<EntityID>, String>
{
	// checking that the doc has not already been loaded
	if loadedDocs.contains_equiv(&resourceName) {
		return Ok(vec!());
	}
	loadedDocs.insert(resourceName.to_string());

	// if not, loading the resource
	let mut resource = match loader.load(resourceName) {
		Ok(r) => std::io::BufferedReader::new(r),
		Err(err) => return Err(format!("{}", err))
	};

	// building the JSON object
	let data = match json::Builder::new(resource.chars().map(|c| c.unwrap())).build() {
		Ok(d) => d,
		Err(err) => return Err(format!("{}", err))
	};

	// loading the doc into the entities state
	load_all(output, &data, loader, loadedDocs)
}

fn load_all(output: &mut EntitiesState, doc: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<Vec<EntityID>, String>
{
	match doc {
		&json::List(ref entities) => {
			let mut result = Vec::new();

			for elem in entities.iter() {
				match load_entity(output, elem, loader, loadedDocs) {
					Ok(e) => result.push(e),
					Err(err) => {
						for e in result.iter() { output.destroy_entity(e); }
						return Err(err);
					}
				}
			}

			Ok(result)
		},
		_ => return Err(format!("Wrong format for entities document, expected list of entities but got: {}", doc))
	}
}

fn load_entity(output: &mut EntitiesState, entity: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<EntityID, String>
{
	match entity {
		&json::Object(ref entityData) => {
			let name = entityData.find(&"name".to_string()).and_then(|e| e.as_string()).map(|e| e.to_string());
			let visible = entityData.find(&"visible".to_string()).and_then(|e| e.as_boolean()).unwrap_or(true);
			let entityID = output.create_entity(name, visible);

			match entityData.find(&"components".to_string()) {
				Some(cmp) => {
					match load_components_list(output, &entityID, cmp, loader, loadedDocs) {
						Ok(_) => (),
						Err(err) => {
							output.destroy_entity(&entityID);
							return Err(err);
						}
					}
				},
				_ => ()
			};

			Ok(entityID)
		},
		_ => return Err(format!("Wrong format for entity, expected object but got: {}", entity))
	}
}

fn load_components_list(output: &mut EntitiesState, entity: &EntityID, componentsList: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<Vec<ComponentID>, String>
{
	match componentsList {
		&json::List(ref components) => {
			let mut result = Vec::new();

			for elem in components.iter() {
				match load_component(output, entity, elem, loader, loadedDocs) {
					Ok(e) => result.push(e),
					Err(err) => {
						for e in result.iter() { output.destroy_component(e); }
						return Err(err);
					}
				}
			}

			Ok(result)
		},
		_ => return Err(format!("Wrong format for components list, expected list but got: {}", componentsList))
	}
}

fn load_component(output: &mut EntitiesState, entity: &EntityID, component: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<ComponentID, String>
{
	match component {
		&json::Object(ref componentInfos) => {
			let cmptype = match componentInfos.find(&"type".to_string()) {
				Some(t) => t,
				None => return Err(format!("Component does not have a \"type\" field: {}", component))
			};

			let data = match componentInfos.find(&"data".to_string()) {
				Some(cmp) => { try!(load_component_data(output, cmp, loader, loadedDocs)) },
				_ => HashMap::new()
			};

			match cmptype {
				&json::String(ref t) => output.create_native_component(entity, t.as_slice(), data),
				&json::Object(_) => {
					match load_data_entry(output, cmptype, loader, loadedDocs) {
						Ok(super::Entity(id)) => output.create_component_from_entity(entity, &id, data),
						Ok(_) => return Err(format!("Wrong type for component \"type\" field object, expected entity")),
						Err(err) => return Err(err)
					}
				},
				_ => Err(format!("Wrong format for component \"type\" field, expected string or object"))
			}
		},
		_ => return Err(format!("Wrong format for component, expected object but got {}", component))
	}
}

fn load_component_data(output: &mut EntitiesState, componentData: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<HashMap<String, super::Data>, String>
{
	match componentData {
		&json::Object(ref data) => {
			let mut result = HashMap::new();

			for (key, val) in data.iter() {
				result.insert(key.clone(), try!(load_data_entry(output, val, loader, loadedDocs)));
			}

			Ok(result)
		},
		_ => return Err(format!("Wrong format for component data, expected object but got {}", componentData))
	}
}

fn load_data_entry(output: &mut EntitiesState, element: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<super::Data, String>
{
	Ok(match element {
		&json::String(ref data) => {
			super::String(data.clone())
		},
		&json::Number(ref data) => {
			super::Number(data.clone())
		},
		&json::Boolean(ref data) => {
			super::Boolean(data.clone())
		},
		&json::List(ref elems) => {
			let mut result = Vec::new();
			for elem in elems.iter() {
				let val = try!(load_data_entry(output, elem, loader, loadedDocs));
				result.push(val);
			}
			super::List(result)
		},
		&json::Object(ref data) => {
			let (key, val) = match data.iter().next() {
				None => return Err(format!("Empty object found for component data element")),
				Some(a) => a
			};

			if key.as_slice().eq_ignore_ascii_case("prototype") {
				let entityID = output.create_entity(None, false);

				match load_components_list(output, &entityID, val, loader, loadedDocs) {
					Ok(_) => (),
					Err(err) => {
						output.destroy_entity(&entityID);
						return Err(err);
					}
				}

				super::Entity(entityID)

			} else if key.as_slice().eq_ignore_ascii_case("entity") {

				let requestedName = match val.as_string() { Some(a) => a, None => return Err(format!("Component data element object of type Entity expects a string")) };
				super::Entity(try!(load_entity_from_name(output, requestedName, loader, loadedDocs)))

			} else {
				return Err(format!("Got invalid key for component data element object: {}", key));
			}
		},
		_ => return Err(format!("Wrong format for component data element, got {}", element))
	})
}

fn load_entity_from_name(output: &mut EntitiesState, entityName: &str, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<EntityID, String>
{
	// first, we check if there is an existing entity with this name
	{
		let entities = output.get_entities_by_name(entityName);
		if entities.len() >= 2 {
			return Err(format!("Found multiple entities with the same name: {}", entityName))
		}
		if entities.len() == 1 {
			return Ok(entities.get(0).clone())
		}
	}

	let entityName = ::std::path::posix::Path::new(entityName);

	// trying to load the file with the same name as the entity
	let mut nameAsPath = entityName.clone();
	let mut loadedEntitiesList = Vec::new();
	loop {
		match nameAsPath.as_str() {
			 Some(path) =>
				match load_impl(loader, path, output, loadedDocs) {
					Ok(l) => { loadedEntitiesList = l; break },
					Err(e) => ()		// TODO: check for error type! If anything else than "resource doesn't exist", return
				},
			 None => ()
		};

		let newPath = nameAsPath.dir_path();
		if match newPath.as_str() { Some(s) => s == ".", None => false } || newPath == nameAsPath { break };
		nameAsPath = newPath;
	}

	//
	let entityToFind = entityName.path_relative_from(&nameAsPath).expect("internal error in entity path traversing");

	if entityToFind.as_str() == Some(".") {
		return Ok(match loadedEntitiesList.move_iter().next() {
			Some(e) => e,
			None => return Err(format!("Found resource named after entity \"{}\", but resource was empty", entityName.display()))
		})

	} else {
		for entity in loadedEntitiesList.move_iter() {
			if try!(output.get_entity_name(&entity)) == entityToFind.as_str().map(|s| s.to_string()) {
				return Ok(entity)
			}
		}
	}

	Err(format!("Unable to load entity named \"{}\"", entityName.display()))
}
<commit_msg>Changed entities loading rules<commit_after>extern crate std;

use resources::ResourcesLoader;
use serialize::json;
use std::ascii::StrAsciiExt;
use std::collections::{ HashSet, HashMap };
use super::EntitiesState;
use super::EntityID;
use super::ComponentID;

pub fn load(loader: &ResourcesLoader, resourceName: &str, output: &mut EntitiesState)
	-> Result<Vec<EntityID>, String>
{
	let mut docs = HashSet::new();
	load_impl(loader, resourceName, output, &mut docs)
}

fn load_impl(loader: &ResourcesLoader, resourceName: &str, output: &mut EntitiesState, loadedDocs: &mut HashSet<String>)
	-> Result<Vec<EntityID>, String>
{
	// checking that the doc has not already been loaded
	if loadedDocs.contains_equiv(&resourceName) {
		return Ok(vec!());
	}
	loadedDocs.insert(resourceName.to_string());

	// if not, loading the resource
	let mut resource = match loader.load(resourceName) {
		Ok(r) => std::io::BufferedReader::new(r),
		Err(err) => return Err(format!("{}", err))
	};

	// building the JSON object
	let data = match json::Builder::new(resource.chars().map(|c| c.unwrap())).build() {
		Ok(d) => d,
		Err(err) => return Err(format!("{}", err))
	};

	// loading the doc into the entities state
	load_all(output, resourceName, &data, loader, loadedDocs)
}

fn load_all(output: &mut EntitiesState, resourceName: &str, doc: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<Vec<EntityID>, String>
{
	match doc {
		&json::List(ref entities) => {
			let mut result = Vec::new();

			for elem in entities.iter() {
				match load_entity(output, resourceName, elem, loader, loadedDocs) {
					Ok(e) => result.push(e),
					Err(err) => {
						for e in result.iter() { output.destroy_entity(e); }
						return Err(err);
					}
				}
			}

			Ok(result)
		},
		_ => return Err(format!("Wrong format for entities document, expected list of entities but got: {}", doc))
	}
}

fn load_entity(output: &mut EntitiesState, resourceName: &str, entity: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<EntityID, String>
{
	match entity {
		&json::Object(ref entityData) => {
			let name = entityData
				.find(&"name".to_string())
				.and_then(|e| e.as_string())
				.map(|e| e.to_string())
				.map(|name| Path::new(resourceName).join(name).as_str().expect("non-utf8 entity name!").to_string());

			let visible = entityData.find(&"visible".to_string()).and_then(|e| e.as_boolean()).unwrap_or(true);
			let entityID = output.create_entity(name, visible);

			match entityData.find(&"components".to_string()) {
				Some(cmp) => {
					match load_components_list(output, &entityID, cmp, loader, loadedDocs) {
						Ok(_) => (),
						Err(err) => {
							output.destroy_entity(&entityID);
							return Err(err);
						}
					}
				},
				_ => ()
			};

			Ok(entityID)
		},
		_ => return Err(format!("Wrong format for entity, expected object but got: {}", entity))
	}
}

fn load_components_list(output: &mut EntitiesState, entity: &EntityID, componentsList: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<Vec<ComponentID>, String>
{
	match componentsList {
		&json::List(ref components) => {
			let mut result = Vec::new();

			for elem in components.iter() {
				match load_component(output, entity, elem, loader, loadedDocs) {
					Ok(e) => result.push(e),
					Err(err) => {
						for e in result.iter() { output.destroy_component(e); }
						return Err(err);
					}
				}
			}

			Ok(result)
		},
		_ => return Err(format!("Wrong format for components list, expected list but got: {}", componentsList))
	}
}

fn load_component(output: &mut EntitiesState, entity: &EntityID, component: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<ComponentID, String>
{
	match component {
		&json::Object(ref componentInfos) => {
			let cmptype = match componentInfos.find(&"type".to_string()) {
				Some(t) => t,
				None => return Err(format!("Component does not have a \"type\" field: {}", component))
			};

			let data = match componentInfos.find(&"data".to_string()) {
				Some(cmp) => { try!(load_component_data(output, cmp, loader, loadedDocs)) },
				_ => HashMap::new()
			};

			match cmptype {
				&json::String(ref t) => output.create_native_component(entity, t.as_slice(), data),
				&json::Object(_) => {
					match load_data_entry(output, cmptype, loader, loadedDocs) {
						Ok(super::Entity(id)) => output.create_component_from_entity(entity, &id, data),
						Ok(_) => return Err(format!("Wrong type for component \"type\" field object, expected entity")),
						Err(err) => return Err(err)
					}
				},
				_ => Err(format!("Wrong format for component \"type\" field, expected string or object"))
			}
		},
		_ => return Err(format!("Wrong format for component, expected object but got {}", component))
	}
}

fn load_component_data(output: &mut EntitiesState, componentData: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<HashMap<String, super::Data>, String>
{
	match componentData {
		&json::Object(ref data) => {
			let mut result = HashMap::new();

			for (key, val) in data.iter() {
				result.insert(key.clone(), try!(load_data_entry(output, val, loader, loadedDocs)));
			}

			Ok(result)
		},
		_ => return Err(format!("Wrong format for component data, expected object but got {}", componentData))
	}
}

fn load_data_entry(output: &mut EntitiesState, element: &json::Json, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<super::Data, String>
{
	Ok(match element {
		&json::String(ref data) => {
			super::String(data.clone())
		},
		&json::Number(ref data) => {
			super::Number(data.clone())
		},
		&json::Boolean(ref data) => {
			super::Boolean(data.clone())
		},
		&json::List(ref elems) => {
			let mut result = Vec::new();
			for elem in elems.iter() {
				let val = try!(load_data_entry(output, elem, loader, loadedDocs));
				result.push(val);
			}
			super::List(result)
		},
		&json::Object(ref data) => {
			let (key, val) = match data.iter().next() {
				None => return Err(format!("Empty object found for component data element")),
				Some(a) => a
			};

			if key.as_slice().eq_ignore_ascii_case("prototype") {
				let entityID = output.create_entity(None, false);

				match load_components_list(output, &entityID, val, loader, loadedDocs) {
					Ok(_) => (),
					Err(err) => {
						output.destroy_entity(&entityID);
						return Err(err);
					}
				}

				super::Entity(entityID)

			} else if key.as_slice().eq_ignore_ascii_case("entity") {

				let requestedName = match val.as_string() { Some(a) => a, None => return Err(format!("Component data element object of type Entity expects a string")) };
				super::Entity(try!(load_entity_from_name(output, requestedName, loader, loadedDocs)))

			} else {
				return Err(format!("Got invalid key for component data element object: {}", key));
			}
		},
		_ => return Err(format!("Wrong format for component data element, got {}", element))
	})
}

fn load_entity_from_name(output: &mut EntitiesState, entityName: &str, loader: &ResourcesLoader, loadedDocs: &mut HashSet<String>)
	-> Result<EntityID, String>
{
	let entityNameRefined = {
		let path = Path::new(entityName);
		path.join(path.filename().unwrap()).as_str().expect("non-utf8 entity name!").to_string()
	};

	// first, we check if there is an existing entity with this name
	{
		let entities = output.get_entities_by_name(entityName);
		if entities.len() >= 2 {
			return Err(format!("Found multiple entities with the same name: {}", entityName))
		}
		if entities.len() == 1 {
			return Ok(entities.get(0).clone())
		}
	}

	// first, we check if there is an existing entity with this name
	{
		let entities = output.get_entities_by_name(entityNameRefined.as_slice());
		if entities.len() >= 2 {
			return Err(format!("Found multiple entities with the same name: {}", entityNameRefined))
		}
		if entities.len() == 1 {
			return Ok(entities.get(0).clone())
		}
	}

	// trying to load the file with the same name as the entity
	{
		let mut nameAsPath = ::std::path::posix::Path::new(entityName);
		loop {
			match nameAsPath.as_str() {
				 Some(path) =>
					match load_impl(loader, path, output, loadedDocs) {
						Ok(l) => break,
						Err(e) => ()		// TODO: check for error type! If anything else than "resource doesn't exist", return
					},
				 None => ()
			};

			let newPath = nameAsPath.dir_path();
			if match newPath.as_str() { Some(s) => s == ".", None => false } || newPath == nameAsPath { break };
			nameAsPath = newPath;
		}
	}

	// first, we check if there is an existing entity with this name
	{
		let entities = output.get_entities_by_name(entityName);
		if entities.len() >= 2 {
			return Err(format!("Found multiple entities with the same name: {}", entityName))
		}
		if entities.len() == 1 {
			return Ok(entities.get(0).clone())
		}
	}

	// first, we check if there is an existing entity with this name
	{
		let entities = output.get_entities_by_name(entityNameRefined.as_slice());
		if entities.len() >= 2 {
			return Err(format!("Found multiple entities with the same name: {}", entityNameRefined))
		}
		if entities.len() == 1 {
			return Ok(entities.get(0).clone())
		}
	}

	Err(format!("Unable to load entity named \"{}\"", entityName))
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, BTreeMap};
use std::f64;

use chrono::NaiveDate;

use postgres::{Connection};

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Event {
    pub gwno: String,
    pub event_id_cnty: String,
    pub event_id_no_cnty: String,
    pub event_date: NaiveDate,
    pub year: i32,
    pub time_precision: i32,
    pub event_type: String,
    pub actor_1: String,
    pub ally_actor_1: String,
    pub inter_1: String,
    pub actor_2: String,
    pub ally_actor_2: String,
    pub inter_2: String,
    pub interaction: String,
    pub country: String,
    pub admin_1: String,
    pub admin_2: String,
    pub admin_3: String,
    pub location: String,
    pub latitude: f64,
    pub longitude: f64,
    pub geo_precision: i32,
    pub source: String,
    pub notes: String,
    pub fatalities: i32,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct EventTrunc {
    pub event_date: NaiveDate,
    pub year: i32,
    pub event_type: String,
    pub actor_1: String,
    pub ally_actor_1: String,
    pub actor_2: String,
    pub ally_actor_2: String,
    pub country: String,
    pub admin_1: String,
    pub admin_2: String,
    pub admin_3: String,
    pub location: String,
    pub latitude: f64,
    pub longitude: f64,
    pub source: String,
    pub notes: String,
    pub fatalities: i32,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct Country {
    pub events: Vec<Event>,
    pub link: String,
    pub name: String,
    pub num_events: i32,
    pub num_fatalities: i32,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct CountryPageData {
    pub found: bool,
    pub name: String,
    pub years: Vec<Year>,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct CountryNameLink {
    pub name: String,
    pub link: String,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct MainPageData {
    pub countries: Vec<CountryNameLink>,
}

// struct to contain all country objects
// i.e., contains all data basically
// searching for countries (by name/link) will be implemented on this
#[derive(Clone, RustcEncodable)]
pub struct AllCountries {
    pub countries: Vec<Country>,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct Year {
    pub year: i32,
    pub events: i32,
    pub fatalities: i32,
    pub epd: String,
    pub fpe: String,
}

impl Event {
    pub fn from_csv_row(row: &Vec<String>) -> Event {
        let date_str: String = row[3].parse::<String>().unwrap();
        let t_date: NaiveDate = NaiveDate::parse_from_str(&date_str, "%d/%m/%Y").unwrap();
        let t_year: i32 = row[4].parse::<i32>().unwrap();
        let t_tpres: i32 = row[5].parse::<i32>().unwrap();
        let t_lat: f64 = row[19].parse::<f64>().unwrap();
        let t_lon: f64 = row[20].parse::<f64>().unwrap();
        let t_gpres: i32 = row[21].parse::<i32>().unwrap();
        let t_fat: i32 = row[24].parse::<i32>().unwrap();
        Event {
            gwno: row[0].clone(),
            event_id_cnty: row[1].clone(),
            event_id_no_cnty: row[2].clone(),
            event_date: t_date,
            year: t_year,
            time_precision: t_tpres,
            event_type: row[6].clone(),
            actor_1: row[7].clone(),
            ally_actor_1: row[8].clone(),
            inter_1: row[9].clone(),
            actor_2: row[10].clone(),
            ally_actor_2: row[11].clone(),
            inter_2: row[12].clone(),
            interaction: row[13].clone(),
            country: row[14].clone(),
            admin_1: row[15].clone(),
            admin_2: row[16].clone(),
            admin_3: row[17].clone(),
            location: row[18].clone(),
            latitude: t_lat,
            longitude: t_lon,
            geo_precision: t_gpres,
            source: row[22].clone(),
            notes: row[23].clone(),
            fatalities: t_fat,
        }
    }
}

impl EventTrunc {
    pub fn from_event(event: &Event) -> EventTrunc {
        EventTrunc {
            event_date: event.event_date.clone(),
            year: event.year.clone(),
            event_type: event.event_type.clone(),
            actor_1: event.actor_1.clone(),
            ally_actor_1: event.ally_actor_1.clone(),
            actor_2: event.actor_2.clone(),
            ally_actor_2: event.ally_actor_2.clone(),
            country: event.country.clone(),
            admin_1: event.admin_1.clone(),
            admin_2: event.admin_2.clone(),
            admin_3: event.admin_3.clone(),
            location: event.location.clone(),
            latitude: event.latitude.clone(),
            longitude: event.longitude.clone(),
            source: event.source.clone(),
            notes: event.notes.clone(),
            fatalities: event.fatalities.clone(),
        }
    }
    pub fn from_csv_row(row: &Vec<String>) -> EventTrunc {
        let date_str: String = row[3].parse::<String>().unwrap().trim().to_string();
        let t_date: NaiveDate = NaiveDate::parse_from_str(&date_str, "%d/%m/%Y").unwrap();
        let t_year: i32 = row[4].parse::<i32>().unwrap();
        let t_lat: f64 = row[19].parse::<f64>().unwrap();
        let t_lon: f64 = row[20].parse::<f64>().unwrap();
        let t_fat: i32 = row[24].parse::<i32>().unwrap();
        EventTrunc {
            event_date: t_date,
            year: t_year,
            event_type: row[6].clone().trim().to_string(),
            actor_1: row[7].clone().trim().to_string(),
            ally_actor_1: row[8].clone().trim().to_string(),
            actor_2: row[10].clone().trim().to_string(),
            ally_actor_2: row[11].clone().trim().to_string(),
            country: row[14].clone().trim().to_string(),
            admin_1: row[15].clone().trim().to_string(),
            admin_2: row[16].clone().trim().to_string(),
            admin_3: row[17].clone().trim().to_string(),
            location: row[18].clone().trim().to_string(),
            latitude: t_lat,
            longitude: t_lon,
            source: row[22].clone().trim().to_string(),
            notes: row[23].clone().trim().to_string(),
            fatalities: t_fat,
        }
    }
}

impl Country {
    pub fn new(t_link: String, t_name: String, num_eve: i32, num_fat: i32) -> Country {
        Country {
            events: Vec::new(),
            link: t_link,
            name: t_name,
            num_events: num_eve,
            num_fatalities: num_fat,
        }
    }
    pub fn to_page_data(&self) -> CountryPageData {
        let mut t_years: BTreeMap<i32, Year> = BTreeMap::new();
        for event in &self.events {
            let c_year = t_years.entry(event.year).or_insert(Year::new(event.year));
            c_year.events += 1;
            c_year.fatalities += event.fatalities;
        }
        let mut year_vec: Vec<Year> = Vec::new();
        for elem in t_years.values() {
            year_vec.push(elem.clone());
        }
        for elem in &mut year_vec {
            let f_eve: f64 = f64::from(elem.events);
            let f_fat: f64 = f64::from(elem.fatalities);
            let t_epd: f64 = f_eve / 365.0f64;
            let t_fpe: f64 = f_fat / f_eve;
            elem.epd = format!("{:.2}", t_epd);
            elem.fpe = format!("{:.2}", t_fpe);

        }
        let mut t_vec = year_vec.clone();
        t_vec.reverse();
        CountryPageData {
            found: true,
            name: self.name.clone(),
            years: t_vec,
        }

    }
}

impl CountryPageData {
    pub fn new(t_name: String) -> CountryPageData {
        CountryPageData {
            found: true,
            name: t_name,
            years: Vec::new(),
        }
    }
}

impl CountryNameLink {
    pub fn new(t_name: String) -> CountryNameLink {
        let t_link: String = t_name.replace(" ", "");
        CountryNameLink {
            name: t_name,
            link: t_link,
        }
    }
}

impl Year {
    pub fn new(t_year: i32) -> Year {
        Year {
            year: t_year,
            events: 0,
            fatalities: 0,
            epd: "".to_string(),
            fpe: "".to_string(),
        }
    }
}
<commit_msg>added an enum for the csv fields<commit_after>use std::collections::{HashMap, BTreeMap};
use std::f64;

use chrono::NaiveDate;

use postgres::{Connection};

enum Field {
    gwno,
    event_id_cnty,
    event_id_no_cnty,
    event_date,
    year,
    time_precision,
    event_type,
    actor_1,
    ally_actor_1,
    inter_1,
    actor_2,
    ally_actor_2,
    inter_2,
    interaction,
    country,
    admin_1,
    admin_2,
    admin_3,
    location,
    latitude,
    longitude,
    geo_precision,
    source,
    notes,
    fatalities,
}

#[derive(Debug, Clone, RustcDecodable, RustcEncodable)]
pub struct Event {
    pub gwno: String,
    pub event_id_cnty: String,
    pub event_id_no_cnty: String,
    pub event_date: NaiveDate,
    pub year: i32,
    pub time_precision: i32,
    pub event_type: String,
    pub actor_1: String,
    pub ally_actor_1: String,
    pub inter_1: String,
    pub actor_2: String,
    pub ally_actor_2: String,
    pub inter_2: String,
    pub interaction: String,
    pub country: String,
    pub admin_1: String,
    pub admin_2: String,
    pub admin_3: String,
    pub location: String,
    pub latitude: f64,
    pub longitude: f64,
    pub geo_precision: i32,
    pub source: String,
    pub notes: String,
    pub fatalities: i32,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct EventTrunc {
    pub event_date: NaiveDate,
    pub year: i32,
    pub event_type: String,
    pub actor_1: String,
    pub ally_actor_1: String,
    pub actor_2: String,
    pub ally_actor_2: String,
    pub country: String,
    pub admin_1: String,
    pub admin_2: String,
    pub admin_3: String,
    pub location: String,
    pub latitude: f64,
    pub longitude: f64,
    pub source: String,
    pub notes: String,
    pub fatalities: i32,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct Country {
    pub events: Vec<Event>,
    pub link: String,
    pub name: String,
    pub num_events: i32,
    pub num_fatalities: i32,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct CountryPageData {
    pub found: bool,
    pub name: String,
    pub years: Vec<Year>,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct CountryNameLink {
    pub name: String,
    pub link: String,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct MainPageData {
    pub countries: Vec<CountryNameLink>,
}

// struct to contain all country objects
// i.e., contains all data basically
// searching for countries (by name/link) will be implemented on this
#[derive(Clone, RustcEncodable)]
pub struct AllCountries {
    pub countries: Vec<Country>,
}

#[derive(Debug, Clone, RustcEncodable)]
pub struct Year {
    pub year: i32,
    pub events: i32,
    pub fatalities: i32,
    pub epd: String,
    pub fpe: String,
}

impl Event {
    pub fn from_csv_row(row: &Vec<String>) -> Event {
        let date_str: String = row[3].parse::<String>().unwrap();
        let t_date: NaiveDate = NaiveDate::parse_from_str(&date_str, "%d/%m/%Y").unwrap();
        let t_year: i32 = row[4].parse::<i32>().unwrap();
        let t_tpres: i32 = row[5].parse::<i32>().unwrap();
        let t_lat: f64 = row[19].parse::<f64>().unwrap();
        let t_lon: f64 = row[20].parse::<f64>().unwrap();
        let t_gpres: i32 = row[21].parse::<i32>().unwrap();
        let t_fat: i32 = row[24].parse::<i32>().unwrap();
        Event {
            gwno: row[0].clone(),
            event_id_cnty: row[1].clone(),
            event_id_no_cnty: row[2].clone(),
            event_date: t_date,
            year: t_year,
            time_precision: t_tpres,
            event_type: row[6].clone(),
            actor_1: row[7].clone(),
            ally_actor_1: row[8].clone(),
            inter_1: row[9].clone(),
            actor_2: row[10].clone(),
            ally_actor_2: row[11].clone(),
            inter_2: row[12].clone(),
            interaction: row[13].clone(),
            country: row[14].clone(),
            admin_1: row[15].clone(),
            admin_2: row[16].clone(),
            admin_3: row[17].clone(),
            location: row[18].clone(),
            latitude: t_lat,
            longitude: t_lon,
            geo_precision: t_gpres,
            source: row[22].clone(),
            notes: row[23].clone(),
            fatalities: t_fat,
        }
    }
}

impl EventTrunc {
    pub fn from_event(event: &Event) -> EventTrunc {
        EventTrunc {
            event_date: event.event_date.clone(),
            year: event.year.clone(),
            event_type: event.event_type.clone(),
            actor_1: event.actor_1.clone(),
            ally_actor_1: event.ally_actor_1.clone(),
            actor_2: event.actor_2.clone(),
            ally_actor_2: event.ally_actor_2.clone(),
            country: event.country.clone(),
            admin_1: event.admin_1.clone(),
            admin_2: event.admin_2.clone(),
            admin_3: event.admin_3.clone(),
            location: event.location.clone(),
            latitude: event.latitude.clone(),
            longitude: event.longitude.clone(),
            source: event.source.clone(),
            notes: event.notes.clone(),
            fatalities: event.fatalities.clone(),
        }
    }
    pub fn from_csv_row(row: &Vec<String>) -> EventTrunc {
        let date_str: String = row[3].parse::<String>().unwrap().trim().to_string();
        let t_date: NaiveDate = NaiveDate::parse_from_str(&date_str, "%d/%m/%Y").unwrap();
        let t_year: i32 = row[4].parse::<i32>().unwrap();
        let t_lat: f64 = row[19].parse::<f64>().unwrap();
        let t_lon: f64 = row[20].parse::<f64>().unwrap();
        let t_fat: i32 = row[24].parse::<i32>().unwrap();
        EventTrunc {
            event_date: t_date,
            year: t_year,
            event_type: row[6].clone().trim().to_string(),
            actor_1: row[7].clone().trim().to_string(),
            ally_actor_1: row[8].clone().trim().to_string(),
            actor_2: row[10].clone().trim().to_string(),
            ally_actor_2: row[11].clone().trim().to_string(),
            country: row[14].clone().trim().to_string(),
            admin_1: row[15].clone().trim().to_string(),
            admin_2: row[16].clone().trim().to_string(),
            admin_3: row[17].clone().trim().to_string(),
            location: row[18].clone().trim().to_string(),
            latitude: t_lat,
            longitude: t_lon,
            source: row[22].clone().trim().to_string(),
            notes: row[23].clone().trim().to_string(),
            fatalities: t_fat,
        }
    }
}

impl Country {
    pub fn new(t_link: String, t_name: String, num_eve: i32, num_fat: i32) -> Country {
        Country {
            events: Vec::new(),
            link: t_link,
            name: t_name,
            num_events: num_eve,
            num_fatalities: num_fat,
        }
    }
    pub fn to_page_data(&self) -> CountryPageData {
        let mut t_years: BTreeMap<i32, Year> = BTreeMap::new();
        for event in &self.events {
            let c_year = t_years.entry(event.year).or_insert(Year::new(event.year));
            c_year.events += 1;
            c_year.fatalities += event.fatalities;
        }
        let mut year_vec: Vec<Year> = Vec::new();
        for elem in t_years.values() {
            year_vec.push(elem.clone());
        }
        for elem in &mut year_vec {
            let f_eve: f64 = f64::from(elem.events);
            let f_fat: f64 = f64::from(elem.fatalities);
            let t_epd: f64 = f_eve / 365.0f64;
            let t_fpe: f64 = f_fat / f_eve;
            elem.epd = format!("{:.2}", t_epd);
            elem.fpe = format!("{:.2}", t_fpe);

        }
        let mut t_vec = year_vec.clone();
        t_vec.reverse();
        CountryPageData {
            found: true,
            name: self.name.clone(),
            years: t_vec,
        }

    }
}

impl CountryPageData {
    pub fn new(t_name: String) -> CountryPageData {
        CountryPageData {
            found: true,
            name: t_name,
            years: Vec::new(),
        }
    }
}

impl CountryNameLink {
    pub fn new(t_name: String) -> CountryNameLink {
        let t_link: String = t_name.replace(" ", "");
        CountryNameLink {
            name: t_name,
            link: t_link,
        }
    }
}

impl Year {
    pub fn new(t_year: i32) -> Year {
        Year {
            year: t_year,
            events: 0,
            fatalities: 0,
            epd: "".to_string(),
            fpe: "".to_string(),
        }
    }
}
<|endoftext|>


<commit_before>use ::gameboy::Gameboy;

/// Holds the state of the oam dma controller.
/// When a value is written to $FF46, it begins a dma transfer that copies 0xA0 bytes to the range 0xFE00 - 0xFE9F.
/// The address of the beginning of the source memory is the value written to $FF46 * 0x100.
/// The source address must be in the range 0x0000 - 0xF100.
///
/// The oam dma transfer copies 160 bytes. Including the 2 M-Cycles for startup/teardown, the entire process
/// takes 162 M-Cycles.
///
/// During the oam dma transfer, the cpu can't access the oam, and it also can't access the same region of memory that the oam dma controller is reading from.
/// For now I am operating under the impression that if the cpu or oam dma controller is accessing cartridge memory (rom/cart ram), the other can access vram.
/// TODO: can the cpu access the mmio registers or the interrupt enable register at 0xFFFF?
///
/// There is a 1 M-Cycle delay at the beginning and end of the oam dma transfer when the cpu can freely access memory like the oam dma wasn't running.
/// However, if an oam dma is started while another oam dma transfer is running, the old dma transfer will still be running in the 1 M-Cycle start up period,
/// and the cpu won't be able to access the memory region the dma controller is reading from.
pub struct OamDmaState {
	/// Is there currently an active oam dma transfer
	pub active: bool,

	// If this is set to true, then the currently running oam dma transfer was started while another one was running.
	// This means that in the first M-Cycle of this dma transfer, the other dma transfer may still be active, which could block
	// the cpu from accessing certain regions of memory.
	//pub was_reset: bool,

	/// The beginning of the block of memory to be copied to oam.
	pub start_address: u16,

	/// The offset from the start address to the next byte to be copied.
	pub current_offset: u16,

	/// How many 4MHz cycles the oam dma has been running
	pub current_cycle: u16
}

impl OamDmaState {
	pub fn new() -> OamDmaState {
		OamDmaState {
			active: false,
			start_address: 0,
			current_offset: 0,
			current_cycle: 0
		}
	}

	pub fn reset(&mut self) {
		self.active = false;
		self.start_address = 0;
		self.current_offset = 0;
		self.current_cycle = 0;
	}

	/// The address that the oam dma controller is currently reading from.
	pub fn current_src_address(&self) -> Option<u16> {
		if self.active && self.current_cycle > 4 {
			Some(self.start_address + self.current_offset)
		}
		else {
			None
		}
	}

	pub fn start_oam_dma(&mut self, addr_high: u8) {
		self.active = true;
		self.current_cycle = 0;
		self.current_offset = 0;
		self.start_address = (addr_high as u16) << 8;
	}

	/// Reading from $FF46 (the register you write to to start the oam dma) just returns the last value written.
	pub fn read_ff46(&self) -> u8 {
		(self.start_address >> 8) as u8
	}

	/// The oam dma controller and cpu can't read from certain regions of memory at the same time.
	/// When the cpu tries to read from the region that the oam controller is reading from, it reads $FF.
	pub fn should_block_cpu_access(&self, address: u16) -> bool {
		if let Some(src) = self.current_src_address() {
			match address {
				0...0x7FFF | 0xA000...0xFDFF | 0xFF00...0xFF7F => (src < 0x8000) || (src >= 0xA000 && src < 0xFE00) || (src >= 0xFF00 && src < 0xFF7F),
				0x8000...0x9FFF => src >= 0x8000 && src < 0xA000, //vram conflict
				0xFE00...0xFE9F => true, // oam dma is always writing to oam, so cpu can never access it (except during first M-Cycle of oam dma)
				_ => false
			}
		}
		else {
			false
		}
	}
}

pub trait OamDmaController {
	/// Start an oam dma transfer.
	/// addr_high is the high byte of the source address.
	fn start_oam_dma(&mut self, addr_high: u8);

	/// Service any currently running oam dma transfer.
	/// This should be called every 4MHz clock
	fn service_oam_dma_transfer(&mut self);
}

impl OamDmaController for Gameboy {
	/// TODO: What happens when you write an invalid address to $FF46?
	/// For example, what if you try to do an oam dma transfer where the source is in OAM?
	fn start_oam_dma(&mut self, addr_high: u8) {
		self.oam_dma_state.active = true;
		self.oam_dma_state.current_cycle = 0;
		self.oam_dma_state.current_offset = 0;
		self.oam_dma_state.start_address = (addr_high as u16) << 8;
	}

	fn service_oam_dma_transfer(&mut self) {
		use gameboy::mmu::Mmu;
		if !self.cpu.halt && self.oam_dma_state.active {
			if self.oam_dma_state.current_cycle >= 4 {
				if self.oam_dma_state.current_cycle % 4 == 0 {
					//copy a byte
					let src = self.oam_dma_state.start_address + self.oam_dma_state.current_offset;
					let byte = self.read_byte(src);
					let dest = 0xFE00 + self.oam_dma_state.current_offset;
					self.write_byte(dest, byte);
					self.oam_dma_state.current_offset += 1;
				}
			}

			self.oam_dma_state.current_cycle += 1;
			if self.oam_dma_state.current_cycle >= 644 {
				self.oam_dma_state.active = false;
			}
		}
	}
}
<commit_msg>Fixed oam dma timing.<commit_after>use ::gameboy::Gameboy;

/// Holds the state of the oam dma controller.
/// When a value is written to $FF46, it begins a dma transfer that copies 0xA0 bytes to the range 0xFE00 - 0xFE9F.
/// The address of the beginning of the source memory is the value written to $FF46 * 0x100.
/// The source address must be in the range 0x0000 - 0xF100.
///
/// The oam dma transfer copies 160 bytes. Including the 2 M-Cycles for startup/teardown, the entire process
/// takes 162 M-Cycles.
///
/// During the oam dma transfer, the cpu can't access the oam, and it also can't access the same region of memory that the oam dma controller is reading from.
/// For now I am operating under the impression that if the cpu or oam dma controller is accessing cartridge memory (rom/cart ram), the other can access vram.
/// TODO: can the cpu access the mmio registers or the interrupt enable register at 0xFFFF?
///
/// There is a 1 M-Cycle delay at the beginning and end of the oam dma transfer when the cpu can freely access memory like the oam dma wasn't running.
/// However, if an oam dma is started while another oam dma transfer is running, the old dma transfer will still be running in the 1 M-Cycle start up period,
/// and the cpu won't be able to access the memory region the dma controller is reading from.
pub struct OamDmaState {
	/// Is there currently an active oam dma transfer
	pub active: bool,

	// If this is set to true, then the currently running oam dma transfer was started while another one was running.
	// This means that in the first M-Cycle of this dma transfer, the other dma transfer may still be active, which could block
	// the cpu from accessing certain regions of memory.
	//pub was_reset: bool,

	/// The beginning of the block of memory to be copied to oam.
	pub start_address: u16,

	/// The offset from the start address to the next byte to be copied.
	pub current_offset: u16,

	/// How many 4MHz cycles the oam dma has been running
	pub current_cycle: u16
}

impl OamDmaState {
	pub fn new() -> OamDmaState {
		OamDmaState {
			active: false,
			start_address: 0,
			current_offset: 0,
			current_cycle: 0
		}
	}

	pub fn reset(&mut self) {
		self.active = false;
		self.start_address = 0;
		self.current_offset = 0;
		self.current_cycle = 0;
	}

	/// The address that the oam dma controller is currently reading from.
	pub fn current_src_address(&self) -> Option<u16> {
		if self.active && self.current_cycle > 4 {
			Some(self.start_address + self.current_offset)
		}
		else {
			None
		}
	}

	pub fn start_oam_dma(&mut self, addr_high: u8) {
		self.active = true;
		self.current_cycle = 0;
		self.current_offset = 0;
		self.start_address = (addr_high as u16) << 8;
	}

	/// Reading from $FF46 (the register you write to to start the oam dma) just returns the last value written.
	pub fn read_ff46(&self) -> u8 {
		(self.start_address >> 8) as u8
	}

	/// The oam dma controller and cpu can't read from certain regions of memory at the same time.
	/// When the cpu tries to read from the region that the oam controller is reading from, it reads $FF.
	pub fn should_block_cpu_access(&self, address: u16) -> bool {
		if let Some(src) = self.current_src_address() {
			match address {
				0...0x7FFF | 0xA000...0xFDFF | 0xFF00...0xFF7F => (src < 0x8000) || (src >= 0xA000 && src < 0xFE00) || (src >= 0xFF00 && src < 0xFF7F),
				0x8000...0x9FFF => src >= 0x8000 && src < 0xA000, //vram conflict
				0xFE00...0xFE9F => true, // oam dma is always writing to oam, so cpu can never access it (except during first M-Cycle of oam dma)
				_ => false
			}
		}
		else {
			false
		}
	}
}

pub trait OamDmaController {
	/// Start an oam dma transfer.
	/// addr_high is the high byte of the source address.
	fn start_oam_dma(&mut self, addr_high: u8);

	/// Service any currently running oam dma transfer.
	/// This should be called every 4MHz clock
	fn service_oam_dma_transfer(&mut self);
}

impl OamDmaController for Gameboy {
	/// TODO: What happens when you write an invalid address to $FF46?
	/// For example, what if you try to do an oam dma transfer where the source is in OAM?
	fn start_oam_dma(&mut self, addr_high: u8) {
		self.oam_dma_state.active = true;
		self.oam_dma_state.current_cycle = 0;
		self.oam_dma_state.current_offset = 0;
		self.oam_dma_state.start_address = (addr_high as u16) << 8;
	}

	fn service_oam_dma_transfer(&mut self) {
		use gameboy::mmu::Mmu;
		if !self.cpu.halt && self.oam_dma_state.active {
			if self.oam_dma_state.current_cycle >= 4 && self.oam_dma_state.current_cycle <= 644 {
				if self.oam_dma_state.current_cycle % 4 == 0 {
					//copy a byte
					let src = self.oam_dma_state.start_address + self.oam_dma_state.current_offset;
					let byte = self.read_byte(src);
					let dest = 0xFE00 + self.oam_dma_state.current_offset;
					self.write_byte(dest, byte);
					self.oam_dma_state.current_offset += 1;
				}
			}

			self.oam_dma_state.current_cycle += 1;
			if self.oam_dma_state.current_cycle >= 648 {
				self.oam_dma_state.active = false;
			}
		}
	}
}
<|endoftext|>