<commit_before>//! FIXME: write short doc here

use hir::Semantics;
use itertools::Itertools;
use ra_ide_db::RootDatabase;
use ra_syntax::{
    ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
    match_ast, SyntaxNode, TextRange,
};

use crate::FileId;
use ast::DocCommentsOwner;
use std::fmt::Display;

#[derive(Debug)]
pub struct Runnable {
    pub range: TextRange,
    pub kind: RunnableKind,
}

#[derive(Debug)]
pub enum TestId {
    Name(String),
    Path(String),
}

impl Display for TestId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            TestId::Name(name) => write!(f, "{}", name),
            TestId::Path(path) => write!(f, "{}", path),
        }
    }
}

#[derive(Debug)]
pub enum RunnableKind {
    Test { test_id: TestId, attr: TestAttr },
    TestMod { path: String },
    Bench { test_id: TestId },
    DocTest { test_id: TestId },
    Bin,
}

pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
    let sema = Semantics::new(db);
    let source_file = sema.parse(file_id);
    source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect()
}

fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
    match_ast! {
        match item {
            ast::FnDef(it) => runnable_fn(sema, it),
            ast::Module(it) => runnable_mod(sema, it),
            _ => None,
        }
    }
}

fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> {
    let name_string = fn_def.name()?.text().to_string();

    let kind = if name_string == "main" {
        RunnableKind::Bin
    } else {
        let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
            let path = module
                .path_to_root(sema.db)
                .into_iter()
                .rev()
                .filter_map(|it| it.name(sema.db))
                .map(|name| name.to_string())
                .chain(std::iter::once(name_string))
                .join("::");
            TestId::Path(path)
        } else {
            TestId::Name(name_string)
        };

        if has_test_related_attribute(&fn_def) {
            let attr = TestAttr::from_fn(&fn_def);
            RunnableKind::Test { test_id, attr }
        } else if fn_def.has_atom_attr("bench") {
            RunnableKind::Bench { test_id }
        } else if has_doc_test(&fn_def) {
            RunnableKind::DocTest { test_id }
        } else {
            return None;
        }
    };
    Some(Runnable { range: fn_def.syntax().text_range(), kind })
}

#[derive(Debug)]
pub struct TestAttr {
    pub ignore: bool,
}

impl TestAttr {
    fn from_fn(fn_def: &ast::FnDef) -> TestAttr {
        let ignore = fn_def
            .attrs()
            .filter_map(|attr| attr.simple_name())
            .any(|attribute_text| attribute_text == "ignore");
        TestAttr { ignore }
    }
}

/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
/// `#[test_case(...)]`, `#[tokio::test]` and similar.
/// Also a regular `#[test]` annotation is supported.
///
/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
/// but it's better than not to have the runnables for the tests at all.
fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
    fn_def
        .attrs()
        .filter_map(|attr| attr.path())
        .map(|path| path.syntax().to_string().to_lowercase())
        .any(|attribute_text| attribute_text.contains("test"))
}

fn has_doc_test(fn_def: &ast::FnDef) -> bool {
    fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
}

fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> {
    let has_test_function = module
        .item_list()?
        .items()
        .filter_map(|it| match it {
            ast::ModuleItem::FnDef(it) => Some(it),
            _ => None,
        })
        .any(|f| has_test_related_attribute(&f));
    if !has_test_function {
        return None;
    }
    let range = module.syntax().text_range();
    let module = sema.to_def(&module)?;

    let path =
        module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
    Some(Runnable { range, kind: RunnableKind::TestMod { path } })
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;

    use crate::mock_analysis::analysis_and_position;

    #[test]
    fn test_runnables() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        #[test]
        fn test_foo() {}

        #[test]
        #[ignore]
        fn test_foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
            },
            Runnable {
                range: 22..46,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
            Runnable {
                range: 47..81,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: true,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_doc_test() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        /// ```
        /// let x = 5;
        /// ```
        fn foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
            },
            Runnable {
                range: 22..64,
                kind: DocTest {
                    test_id: Path(
                        "foo",
                    ),
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            #[test]
            fn test_foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..59,
                kind: TestMod {
                    path: "test_mod",
                },
            },
            Runnable {
                range: 28..57,
                kind: Test {
                    test_id: Path(
                        "test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_one_depth_layer_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod test_mod {
                #[test]
                fn test_foo1() {}
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 23..85,
                kind: TestMod {
                    path: "foo::test_mod",
                },
            },
            Runnable {
                range: 46..79,
                kind: Test {
                    test_id: Path(
                        "foo::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_multiple_depth_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod bar {
                mod test_mod {
                    #[test]
                    fn test_foo1() {}
                }
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 41..115,
                kind: TestMod {
                    path: "foo::bar::test_mod",
                },
            },
            Runnable {
                range: 68..105,
                kind: Test {
                    test_id: Path(
                        "foo::bar::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_no_test_function_in_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            fn foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert!(runnables.is_empty())
    }
}
<commit_msg>fix doctest inside impl block #4449<commit_after>//! FIXME: write short doc here

use hir::{AsAssocItem, HirDisplay, Semantics};
use itertools::Itertools;
use ra_ide_db::RootDatabase;
use ra_syntax::{
    ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
    match_ast, SyntaxNode, TextRange,
};

use crate::FileId;
use ast::DocCommentsOwner;
use std::fmt::Display;

#[derive(Debug)]
pub struct Runnable {
    pub range: TextRange,
    pub kind: RunnableKind,
}

#[derive(Debug)]
pub enum TestId {
    Name(String),
    Path(String),
}

impl Display for TestId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            TestId::Name(name) => write!(f, "{}", name),
            TestId::Path(path) => write!(f, "{}", path),
        }
    }
}

#[derive(Debug)]
pub enum RunnableKind {
    Test { test_id: TestId, attr: TestAttr },
    TestMod { path: String },
    Bench { test_id: TestId },
    DocTest { test_id: TestId },
    Bin,
}

pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
    let sema = Semantics::new(db);
    let source_file = sema.parse(file_id);
    source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect()
}

fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> {
    match_ast! {
        match item {
            ast::FnDef(it) => runnable_fn(sema, it),
            ast::Module(it) => runnable_mod(sema, it),
            _ => None,
        }
    }
}

fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> {
    let name_string = fn_def.name()?.text().to_string();

    let kind = if name_string == "main" {
        RunnableKind::Bin
    } else {
        let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
            let def = sema.to_def(&fn_def)?;
            let impl_trait_name =
                def.as_assoc_item(sema.db).and_then(|assoc_item| {
                    match assoc_item.container(sema.db) {
                        hir::AssocItemContainer::Trait(trait_item) => {
                            Some(trait_item.name(sema.db).to_string())
                        }
                        hir::AssocItemContainer::ImplDef(impl_def) => impl_def
                            .target_ty(sema.db)
                            .as_adt()
                            .map(|adt| adt.name(sema.db).to_string()),
                    }
                });

            let path_iter = module
                .path_to_root(sema.db)
                .into_iter()
                .rev()
                .filter_map(|it| it.name(sema.db))
                .map(|name| name.to_string());

            let path = if let Some(impl_trait_name) = impl_trait_name {
                path_iter
                    .chain(std::iter::once(impl_trait_name))
                    .chain(std::iter::once(name_string))
                    .join("::")
            } else {
                path_iter.chain(std::iter::once(name_string)).join("::")
            };

            TestId::Path(path)
        } else {
            TestId::Name(name_string)
        };

        if has_test_related_attribute(&fn_def) {
            let attr = TestAttr::from_fn(&fn_def);
            RunnableKind::Test { test_id, attr }
        } else if fn_def.has_atom_attr("bench") {
            RunnableKind::Bench { test_id }
        } else if has_doc_test(&fn_def) {
            RunnableKind::DocTest { test_id }
        } else {
            return None;
        }
    };
    Some(Runnable { range: fn_def.syntax().text_range(), kind })
}

#[derive(Debug)]
pub struct TestAttr {
    pub ignore: bool,
}

impl TestAttr {
    fn from_fn(fn_def: &ast::FnDef) -> TestAttr {
        let ignore = fn_def
            .attrs()
            .filter_map(|attr| attr.simple_name())
            .any(|attribute_text| attribute_text == "ignore");
        TestAttr { ignore }
    }
}

/// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
/// `#[test_case(...)]`, `#[tokio::test]` and similar.
/// Also a regular `#[test]` annotation is supported.
///
/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
/// but it's better than not to have the runnables for the tests at all.
fn has_test_related_attribute(fn_def: &ast::FnDef) -> bool {
    fn_def
        .attrs()
        .filter_map(|attr| attr.path())
        .map(|path| path.syntax().to_string().to_lowercase())
        .any(|attribute_text| attribute_text.contains("test"))
}

fn has_doc_test(fn_def: &ast::FnDef) -> bool {
    fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
}

fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> {
    let has_test_function = module
        .item_list()?
        .items()
        .filter_map(|it| match it {
            ast::ModuleItem::FnDef(it) => Some(it),
            _ => None,
        })
        .any(|f| has_test_related_attribute(&f));
    if !has_test_function {
        return None;
    }
    let range = module.syntax().text_range();
    let module = sema.to_def(&module)?;

    let path =
        module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
    Some(Runnable { range, kind: RunnableKind::TestMod { path } })
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;

    use crate::mock_analysis::analysis_and_position;

    #[test]
    fn test_runnables() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        #[test]
        fn test_foo() {}

        #[test]
        #[ignore]
        fn test_foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
            },
            Runnable {
                range: 22..46,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
            Runnable {
                range: 47..81,
                kind: Test {
                    test_id: Path(
                        "test_foo",
                    ),
                    attr: TestAttr {
                        ignore: true,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_doc_test() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        /// ```
        /// let x = 5;
        /// ```
        fn foo() {}
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
            },
            Runnable {
                range: 22..64,
                kind: DocTest {
                    test_id: Path(
                        "foo",
                    ),
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_doc_test_in_impl() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        fn main() {}

        struct Data;
        impl Data {
            /// ```
            /// let x = 5;
            /// ```
            fn foo() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..21,
                kind: Bin,
            },
            Runnable {
                range: 52..106,
                kind: DocTest {
                    test_id: Path(
                        "Data::foo",
                    ),
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            #[test]
            fn test_foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 1..59,
                kind: TestMod {
                    path: "test_mod",
                },
            },
            Runnable {
                range: 28..57,
                kind: Test {
                    test_id: Path(
                        "test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_one_depth_layer_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod test_mod {
                #[test]
                fn test_foo1() {}
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 23..85,
                kind: TestMod {
                    path: "foo::test_mod",
                },
            },
            Runnable {
                range: 46..79,
                kind: Test {
                    test_id: Path(
                        "foo::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_multiple_depth_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod foo {
            mod bar {
                mod test_mod {
                    #[test]
                    fn test_foo1() {}
                }
            }
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert_debug_snapshot!(&runnables,
        @r###"
        [
            Runnable {
                range: 41..115,
                kind: TestMod {
                    path: "foo::bar::test_mod",
                },
            },
            Runnable {
                range: 68..105,
                kind: Test {
                    test_id: Path(
                        "foo::bar::test_mod::test_foo1",
                    ),
                    attr: TestAttr {
                        ignore: false,
                    },
                },
            },
        ]
        "###
                );
    }

    #[test]
    fn test_runnables_no_test_function_in_module() {
        let (analysis, pos) = analysis_and_position(
            r#"
        //- /lib.rs
        <|> //empty
        mod test_mod {
            fn foo1() {}
        }
        "#,
        );
        let runnables = analysis.runnables(pos.file_id).unwrap();
        assert!(runnables.is_empty())
    }
}
<|endoftext|>


<commit_before>//! # The Rust Prelude
//!
//! Rust comes with a variety of things in its standard library. However, if
//! you had to manually import every single thing that you used, it would be
//! very verbose. But importing a lot of things that a program never uses isn't
//! good either. A balance needs to be struck.
//!
//! The *prelude* is the list of things that Rust automatically imports into
//! every Rust program. It's kept as small as possible, and is focused on
//! things, particularly traits, which are used in almost every single Rust
//! program.
//!
//! # Other preludes
//!
//! Preludes can be seen as a pattern to make using multiple types more
//! convenient. As such, you'll find other preludes in the standard library,
//! such as [`std::io::prelude`]. Various libraries in the Rust ecosystem may
//! also define their own preludes.
//!
//! [`std::io::prelude`]: crate::io::prelude
//!
//! The difference between 'the prelude' and these other preludes is that they
//! are not automatically `use`'d, and must be imported manually. This is still
//! easier than importing all of their constituent components.
//!
//! # Prelude contents
//!
//! The current version of the prelude (version 1) lives in
//! [`std::prelude::v1`], and re-exports the following:
//!
//! * [`std::marker`]::{[`Copy`], [`Send`], [`Sized`], [`Sync`], [`Unpin`]}:
//!   marker traits that indicate fundamental properties of types.
//! * [`std::ops`]::{[`Drop`], [`Fn`], [`FnMut`], [`FnOnce`]}: various
//!   operations for both destructors and overloading `()`.
//! * [`std::mem`]::[`drop`][`mem::drop`]: a convenience function for explicitly
//!   dropping a value.
//! * [`std::boxed`]::[`Box`]: a way to allocate values on the heap.
//! * [`std::borrow`]::[`ToOwned`]: the conversion trait that defines
//!   [`to_owned`], the generic method for creating an owned type from a
//!   borrowed type.
//! * [`std::clone`]::[`Clone`]: the ubiquitous trait that defines
//!   [`clone`][`Clone::clone`], the method for producing a copy of a value.
//! * [`std::cmp`]::{[`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`]}: the
//!   comparison traits, which implement the comparison operators and are often
//!   seen in trait bounds.
//! * [`std::convert`]::{[`AsRef`], [`AsMut`], [`Into`], [`From`]}: generic
//!   conversions, used by savvy API authors to create overloaded methods.
//! * [`std::default`]::[`Default`], types that have default values.
//! * [`std::iter`]::{[`Iterator`], [`Extend`], [`IntoIterator`],
//!   [`DoubleEndedIterator`], [`ExactSizeIterator`]}: iterators of various
//!   kinds.
//! * [`std::option`]::[`Option`]::{[`self`][`Option`], [`Some`], [`None`]}, a
//!   type which expresses the presence or absence of a value. This type is so
//!   commonly used, its variants are also exported.
//! * [`std::result`]::[`Result`]::{[`self`][`Result`], [`Ok`], [`Err`]}: a type
//!   for functions that may succeed or fail. Like [`Option`], its variants are
//!   exported as well.
//! * [`std::string`]::{[`String`], [`ToString`]}: heap-allocated strings.
//! * [`std::vec`]::[`Vec`]: a growable, heap-allocated vector.
//!
//! [`mem::drop`]: crate::mem::drop
//! [`std::borrow`]: crate::borrow
//! [`std::boxed`]: crate::boxed
//! [`std::clone`]: crate::clone
//! [`std::cmp`]: crate::cmp
//! [`std::convert`]: crate::convert
//! [`std::default`]: crate::default
//! [`std::iter`]: crate::iter
//! [`std::marker`]: crate::marker
//! [`std::mem`]: crate::mem
//! [`std::ops`]: crate::ops
//! [`std::option`]: crate::option
//! [`std::prelude::v1`]: v1
//! [`std::result`]: crate::result
//! [`std::slice`]: crate::slice
//! [`std::string`]: crate::string
//! [`std::vec`]: mod@crate::vec
//! [`to_owned`]: crate::borrow::ToOwned::to_owned
//! [book-closures]: ../../book/ch13-01-closures.html
//! [book-dtor]: ../../book/ch15-03-drop.html
//! [book-enums]: ../../book/ch06-01-defining-an-enum.html
//! [book-iter]: ../../book/ch13-02-iterators.html

#![stable(feature = "rust1", since = "1.0.0")]

pub mod v1;
<commit_msg>Make documentation of which items the prelude exports more readably.<commit_after>//! The Rust Prelude.
//!
//! Rust comes with a variety of things in its standard library. However, if
//! you had to manually import every single thing that you used, it would be
//! very verbose. But importing a lot of things that a program never uses isn't
//! good either. A balance needs to be struck.
//!
//! The *prelude* is the list of things that Rust automatically imports into
//! every Rust program. It's kept as small as possible, and is focused on
//! things, particularly traits, which are used in almost every single Rust
//! program.
//!
//! # Other preludes
//!
//! Preludes can be seen as a pattern to make using multiple types more
//! convenient. As such, you'll find other preludes in the standard library,
//! such as [`std::io::prelude`]. Various libraries in the Rust ecosystem may
//! also define their own preludes.
//!
//! [`std::io::prelude`]: crate::io::prelude
//!
//! The difference between 'the prelude' and these other preludes is that they
//! are not automatically `use`'d, and must be imported manually. This is still
//! easier than importing all of their constituent components.
//!
//! # Prelude contents
//!
//! The current version of the prelude (version 1) lives in
//! [`std::prelude::v1`], and re-exports the following:
//!
//! * [std::marker]::{[Copy], [Send], [Sized], [Sync], [Unpin]},
//!   marker traits that indicate fundamental properties of types.
//! * [std::ops]::{[Drop], [Fn], [FnMut], [FnOnce]}, various
//!   operations for both destructors and overloading `()`.
//! * [std::mem]::[drop][mem::drop], a convenience function for explicitly
//!   dropping a value.
//! * [std::boxed]::[Box], a way to allocate values on the heap.
//! * [std::borrow]::[ToOwned], the conversion trait that defines
//!   [`to_owned`], the generic method for creating an owned type from a
//!   borrowed type.
//! * [std::clone]::[Clone], the ubiquitous trait that defines
//!   [`clone`][Clone::clone], the method for producing a copy of a value.
//! * [std::cmp]::{[PartialEq], [PartialOrd], [Eq], [Ord]}, the
//!   comparison traits, which implement the comparison operators and are often
//!   seen in trait bounds.
//! * [std::convert]::{[AsRef], [AsMut], [Into], [From]}, generic
//!   conversions, used by savvy API authors to create overloaded methods.
//! * [std::default]::[Default], types that have default values.
//! * [std::iter]::{[Iterator], [Extend], [IntoIterator], [DoubleEndedIterator], [ExactSizeIterator]},
//!   iterators of various
//!   kinds.
//! * [std::option]::[Option]::{[self][Option], [Some], [None]}, a
//!   type which expresses the presence or absence of a value. This type is so
//!   commonly used, its variants are also exported.
//! * [std::result]::[Result]::{[self][Result], [Ok], [Err]}, a type
//!   for functions that may succeed or fail. Like [`Option`], its variants are
//!   exported as well.
//! * [std::string]::{[String], [ToString]}, heap-allocated strings.
//! * [std::vec]::[Vec], a growable, heap-allocated vector.
//!
//! [mem::drop]: crate::mem::drop
//! [std::borrow]: crate::borrow
//! [std::boxed]: crate::boxed
//! [std::clone]: crate::clone
//! [std::cmp]: crate::cmp
//! [std::convert]: crate::convert
//! [std::default]: crate::default
//! [std::iter]: crate::iter
//! [std::marker]: crate::marker
//! [std::mem]: crate::mem
//! [std::ops]: crate::ops
//! [std::option]: crate::option
//! [`std::prelude::v1`]: v1
//! [std::result]: crate::result
//! [std::slice]: crate::slice
//! [std::string]: crate::string
//! [std::vec]: mod@crate::vec
//! [`to_owned`]: crate::borrow::ToOwned::to_owned
//! [book-closures]: ../../book/ch13-01-closures.html
//! [book-dtor]: ../../book/ch15-03-drop.html
//! [book-enums]: ../../book/ch06-01-defining-an-enum.html
//! [book-iter]: ../../book/ch13-02-iterators.html

#![stable(feature = "rust1", since = "1.0.0")]

pub mod v1;
<|endoftext|>


<commit_before>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Interactions with the file system.

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use tabs::BufferId;

#[cfg(feature = "notify")]
use tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use watcher::FileWatcher;

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            let _ = File::create(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8)
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding).map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mod_time =
        f.metadata().map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?.modified().ok();
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo { encoding, mod_time, path: path.as_ref().to_owned(), has_changed: false };
    Ok((rope, info))
}

fn try_save(path: &Path, text: &Rope, encoding: CharacterEncoding) -> io::Result<()> {
    use std::fs;

    let tmp_extension = path
        .extension()
        .and_then(|extension| extension.to_str()?.to_string().into())
        .map_or_else(|| "swp".to_string(), |extension| extension + ".swp");
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P>(path: P) -> Option<SystemTime>
where
    P: AsRef<Path>,
{
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<commit_msg>Keep tmp_extension as OsString<commit_after>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Interactions with the file system.

use std::collections::HashMap;
use std::fmt;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use tabs::BufferId;

#[cfg(feature = "notify")]
use tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use watcher::FileWatcher;

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            let _ = File::create(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8)
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding).map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mod_time =
        f.metadata().map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?.modified().ok();
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo { encoding, mod_time, path: path.as_ref().to_owned(), has_changed: false };
    Ok((rope, info))
}

fn try_save(path: &Path, text: &Rope, encoding: CharacterEncoding) -> io::Result<()> {
    let tmp_extension = path
        .extension()
        .map_or_else(|| OsString::from("swp"), |ext| {
            let mut ext = ext.to_os_string();
            ext.push(".swp");
            ext
        });
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P>(path: P) -> Option<SystemTime>
where
    P: AsRef<Path>,
{
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<|endoftext|>


<commit_before>extern crate uuid;
extern crate symbolserver;

use std::env;
use std::fs;
use std::path::Path;

use uuid::Uuid;

use symbolserver::sdk::SdkProcessor;
use symbolserver::memdb::{MemDbBuilder, MemDb};
use symbolserver::Result;

fn do_main() -> Result<()> {
    let args : Vec<_> = env::args().collect();
    if args.len() > 1 {
        let p = Path::new(&args[1]);
        let out = fs::File::create("/tmp/test.memdb")?;
        let sdk_proc = SdkProcessor::new(p)?;
        let mut builder = MemDbBuilder::new(out, sdk_proc.info())?;

        for obj_res in sdk_proc.objects()? {
            let (filename, obj) = obj_res?;
            builder.write_object(&obj, Some(&filename))?;
        }
        builder.flush()?;
    }

    let mdb = MemDb::from_path("/tmp/test.memdb")?;
    let sym = mdb.lookup_by_uuid(&"63d32ddb-095d-3974-afc9-8a6cf7c8bbd6".parse::<Uuid>().unwrap(), 6815851772);
    println!("{:?}", sym);

    Ok(())
}

fn main() {
    do_main().unwrap();
}
<commit_msg>Also write out symbols in example<commit_after>extern crate uuid;
extern crate symbolserver;

use std::env;
use std::fs;
use std::path::Path;
use std::io::Write;

use uuid::Uuid;

use symbolserver::sdk::SdkProcessor;
use symbolserver::memdb::{MemDbBuilder, MemDb};
use symbolserver::Result;

fn do_main() -> Result<()> {
    let args : Vec<_> = env::args().collect();
    if args.len() > 1 {
        let p = Path::new(&args[1]);
        let out = fs::File::create("/tmp/test.memdb")?;
        let sdk_proc = SdkProcessor::new(p)?;
        let mut symout = fs::File::create("/tmp/symbols")?;
        let mut builder = MemDbBuilder::new(out, sdk_proc.info())?;

        for obj_res in sdk_proc.objects()? {
            let (filename, obj) = obj_res?;
            for var in obj.variants() {
                let mut symbols = obj.symbols(var.arch())?;
                for (_, sym) in symbols.iter() {
                    symout.write_all(sym.as_bytes())?;
                    symout.write_all(b"\n")?;
                }
            }
            builder.write_object(&obj, Some(&filename))?;
        }
        builder.flush()?;
    }

    let mdb = MemDb::from_path("/tmp/test.memdb")?;
    let sym = mdb.lookup_by_uuid(&"63d32ddb-095d-3974-afc9-8a6cf7c8bbd6".parse::<Uuid>().unwrap(), 6815851772);
    println!("{:?}", sym);

    Ok(())
}

fn main() {
    do_main().unwrap();
}
<|endoftext|>


<commit_before>use core::mem::*;

#[test]
fn size_of_basic() {
    assert_eq!(size_of::<u8>(), 1);
    assert_eq!(size_of::<u16>(), 2);
    assert_eq!(size_of::<u32>(), 4);
    assert_eq!(size_of::<u64>(), 8);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn size_of_16() {
    assert_eq!(size_of::<usize>(), 2);
    assert_eq!(size_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn size_of_32() {
    assert_eq!(size_of::<usize>(), 4);
    assert_eq!(size_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn size_of_64() {
    assert_eq!(size_of::<usize>(), 8);
    assert_eq!(size_of::<*const usize>(), 8);
}

#[test]
fn size_of_val_basic() {
    assert_eq!(size_of_val(&1u8), 1);
    assert_eq!(size_of_val(&1u16), 2);
    assert_eq!(size_of_val(&1u32), 4);
    assert_eq!(size_of_val(&1u64), 8);
}

#[test]
fn align_of_basic() {
    assert_eq!(align_of::<u8>(), 1);
    assert_eq!(align_of::<u16>(), 2);
    assert_eq!(align_of::<u32>(), 4);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn align_of_16() {
    assert_eq!(align_of::<usize>(), 2);
    assert_eq!(align_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn align_of_32() {
    assert_eq!(align_of::<usize>(), 4);
    assert_eq!(align_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn align_of_64() {
    assert_eq!(align_of::<usize>(), 8);
    assert_eq!(align_of::<*const usize>(), 8);
}

#[test]
fn align_of_val_basic() {
    assert_eq!(align_of_val(&1u8), 1);
    assert_eq!(align_of_val(&1u16), 2);
    assert_eq!(align_of_val(&1u32), 4);
}

#[test]
fn test_swap() {
    let mut x = 31337;
    let mut y = 42;
    swap(&mut x, &mut y);
    assert_eq!(x, 42);
    assert_eq!(y, 31337);
}

#[test]
fn test_replace() {
    let mut x = Some("test".to_string());
    let y = replace(&mut x, None);
    assert!(x.is_none());
    assert!(y.is_some());
}

#[test]
fn test_transmute_copy() {
    assert_eq!(1, unsafe { transmute_copy(&1) });
}

#[test]
fn test_transmute() {
    trait Foo {
        fn dummy(&self) {}
    }
    impl Foo for isize {}

    let a = box 100isize as Box<dyn Foo>;
    unsafe {
        let x: ::core::raw::TraitObject = transmute(a);
        assert!(*(x.data as *const isize) == 100);
        let _x: Box<dyn Foo> = transmute(x);
    }

    unsafe {
        assert_eq!(transmute::<_, Vec<u8>>("L".to_string()), [76]);
    }
}

#[test]
#[allow(dead_code)]
fn test_discriminant_send_sync() {
    enum Regular {
        A,
        B(i32),
    }
    enum NotSendSync {
        A(*const i32),
    }

    fn is_send_sync<T: Send + Sync>() {}

    is_send_sync::<Discriminant<Regular>>();
    is_send_sync::<Discriminant<NotSendSync>>();
}
<commit_msg>Added tests for assume_init<commit_after>use core::mem::*;

#[test]
fn size_of_basic() {
    assert_eq!(size_of::<u8>(), 1);
    assert_eq!(size_of::<u16>(), 2);
    assert_eq!(size_of::<u32>(), 4);
    assert_eq!(size_of::<u64>(), 8);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn size_of_16() {
    assert_eq!(size_of::<usize>(), 2);
    assert_eq!(size_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn size_of_32() {
    assert_eq!(size_of::<usize>(), 4);
    assert_eq!(size_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn size_of_64() {
    assert_eq!(size_of::<usize>(), 8);
    assert_eq!(size_of::<*const usize>(), 8);
}

#[test]
fn size_of_val_basic() {
    assert_eq!(size_of_val(&1u8), 1);
    assert_eq!(size_of_val(&1u16), 2);
    assert_eq!(size_of_val(&1u32), 4);
    assert_eq!(size_of_val(&1u64), 8);
}

#[test]
fn align_of_basic() {
    assert_eq!(align_of::<u8>(), 1);
    assert_eq!(align_of::<u16>(), 2);
    assert_eq!(align_of::<u32>(), 4);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn align_of_16() {
    assert_eq!(align_of::<usize>(), 2);
    assert_eq!(align_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn align_of_32() {
    assert_eq!(align_of::<usize>(), 4);
    assert_eq!(align_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn align_of_64() {
    assert_eq!(align_of::<usize>(), 8);
    assert_eq!(align_of::<*const usize>(), 8);
}

#[test]
fn align_of_val_basic() {
    assert_eq!(align_of_val(&1u8), 1);
    assert_eq!(align_of_val(&1u16), 2);
    assert_eq!(align_of_val(&1u32), 4);
}

#[test]
fn test_swap() {
    let mut x = 31337;
    let mut y = 42;
    swap(&mut x, &mut y);
    assert_eq!(x, 42);
    assert_eq!(y, 31337);
}

#[test]
fn test_replace() {
    let mut x = Some("test".to_string());
    let y = replace(&mut x, None);
    assert!(x.is_none());
    assert!(y.is_some());
}

#[test]
fn test_transmute_copy() {
    assert_eq!(1, unsafe { transmute_copy(&1) });
}

#[test]
fn test_transmute() {
    trait Foo {
        fn dummy(&self) {}
    }
    impl Foo for isize {}

    let a = box 100isize as Box<dyn Foo>;
    unsafe {
        let x: ::core::raw::TraitObject = transmute(a);
        assert!(*(x.data as *const isize) == 100);
        let _x: Box<dyn Foo> = transmute(x);
    }

    unsafe {
        assert_eq!(transmute::<_, Vec<u8>>("L".to_string()), [76]);
    }
}

#[test]
#[allow(dead_code)]
fn test_discriminant_send_sync() {
    enum Regular {
        A,
        B(i32),
    }
    enum NotSendSync {
        A(*const i32),
    }

    fn is_send_sync<T: Send + Sync>() {}

    is_send_sync::<Discriminant<Regular>>();
    is_send_sync::<Discriminant<NotSendSync>>();
}

#[test]
fn assume_init_good() {
    const TRUE: bool = {
        let mut x = MaybeUninit::<bool>::uninit();
        x.as_mut_ptr().write(true);
        x.assume_init()
    };
    assert!(TRUE);
}

#[test]
#[should_panic]
fn assume_init_bad() {
    const BAD: () = {
        MaybeUninit::<!>::uninit().assume_init();
    };
}
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(not(test), no_std)]
#![feature(linkage)]

extern "C" {
    // NB: components may not have a logging connection in which case
    //   logger_log will be undefined/null.
    #[linkage = "extern_weak"]
    static logger_log: *const ();
}

use core2::io::{Cursor, Write};
use cstr_core::CStr;
use log::{Metadata, Record};

// TODO(sleffler): until we can copy directly into shared memory limit
//   stack allocation (can be up to 4096).
const MAX_MSG_LEN: usize = 2048;

pub struct KataLogger;

impl log::Log for KataLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool { true }

    fn log(&self, record: &Record) {
        let typed_logger_log: Option<extern "C" fn(level: u8, msg: *const u8)> =
            unsafe { core::mem::transmute(logger_log) };
        if typed_logger_log.is_none() {
            return;
        }
        if self.enabled(record.metadata()) {
            let mut buf = [0 as u8; MAX_MSG_LEN];
            let mut cur = Cursor::new(&mut buf[..]);
            // Log msgs are of the form: '<target>::<fmt'd-msg>
            write!(&mut cur, "{}::{}\0", record.target(), record.args()).unwrap_or_else(|_| {
                // Too big, indicate overflow with a trailing "...".
                cur.set_position((MAX_MSG_LEN - 4) as u64);
                cur.write(b"...\0").expect("write!");
                ()
            });
            // If an embedded nul is identified, replace the message; there
            // are likely better solutions but this should not happen.
            fn embedded_nul_cstr<'a>(
                buf: &'a mut [u8; MAX_MSG_LEN],
                record: &Record,
            ) -> &'a cstr_core::CStr {
                let mut cur = Cursor::new(&mut buf[..]);
                write!(&mut cur, "{}::<embedded nul>\0", record.target()).expect("nul!");
                let pos = cur.position() as usize;
                CStr::from_bytes_with_nul(&buf[..pos]).unwrap()
            }
            // NB: this releases the ref on buf held by the Cursor
            let pos = cur.position() as usize;
            (typed_logger_log.unwrap())(
                record.level() as u8,
                match CStr::from_bytes_with_nul(&buf[..pos]) {
                    Ok(cstr) => cstr,
                    Err(_) => embedded_nul_cstr(&mut buf, record),
                }
                .as_ptr(),
            );
        }
    }

    fn flush(&self) {}
}

#[cfg(test)]
mod tests {
    use super::*;
    use arrayvec::ArrayVec;
    use log::{debug, error, info, trace, warn};

    static KATA_LOGGER: KataLogger = KataLogger;
    static mut MSGS: ArrayVec<[u8; MAX_MSG_LEN], 8> = ArrayVec::new_const();

    // Resets msg collection; used at the start of each test.
    fn reset_msgs() {
        unsafe {
            MSGS.clear();
        }
    }

    #[no_mangle]
    pub extern "C" fn logger_log(_level: u8, msg: *const cstr_core::c_char) {
        unsafe {
            // NB: this depends on msg pointing to the caller's array
            MSGS.push(*(msg as *const [u8; MAX_MSG_LEN]));
        }
    }

    // Formats a log message as done by KataLogger and returns a zero-padded
    // Vec holding the message.
    fn log_msg(_level: log::Level, msg: &str) -> Result<Vec<u8>, std::io::Error> {
        let mut v = Vec::new();
        use std::io::Write;
        write!(&mut v, "{}::{}\0", "logger::tests", msg)?;
        v.resize(MAX_MSG_LEN, 0);
        assert_eq!(v.len(), MAX_MSG_LEN);
        Ok(v)
    }

    fn pop_and_expect_none(level: log::Level) {
        unsafe {
            let msg = MSGS.pop();
            assert_eq!(
                msg, None,
                "Assertioon failed for level {}: expected None, got {:?}",
                level, msg
            );
        }
    }

    fn pop_and_check_result(level: log::Level, test_msg: &str) {
        unsafe {
            let msg = MSGS.pop();
            assert!(
                msg != None,
                "Assertion failed for level {}: no log msg collected",
                level
            );
            let expected = log_msg(level, test_msg).unwrap();
            let observed = msg.unwrap().to_vec();
            assert_eq!(
                expected,
                observed,
                "Assertion failed for level {}: expected {}, got {}",
                level,
                String::from_utf8_lossy(expected.as_slice()),
                String::from_utf8_lossy(observed.as_slice())
            );
        }
    }

    // NB: to run these sequentially use --test-threads=1; otherwise
    // cargo will use multiple threads and you will get failures from
    // multiple users of MSGS and the global logger; e.g.
    //     cargo test -- --test-threads=1

    #[test]
    fn test_each_log_level_works() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Trace);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_check_result(log::Level::Trace, trace_msg);
    }

    #[test]
    fn test_max_log_level() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        // With filtering at Debug level, levels below should come through.
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_expect_none(log::Level::Trace);

        // Dynamically adjust the log level
        log::set_max_level(log::LevelFilter::Info);

        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        debug!("{}", debug_msg);
        pop_and_expect_none(log::Level::Debug);
    }

    #[test]
    fn test_formatting() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        debug!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32);
        pop_and_check_result(
            log::Level::Debug,
            &format!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32)[..],
        );
    }

    #[test]
    fn test_too_long() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        // Guarantee formatted message is > MAX_MSG_LEN
        let mut debug_msg = "debug".repeat((MAX_MSG_LEN / 5) + 1);
        debug!("{}", debug_msg);

        // Blech, must take into account log msg formatting.
        debug_msg.truncate(MAX_MSG_LEN - 4 - "logger::tests::".len());
        debug_msg.push_str("...");
        pop_and_check_result(log::Level::Debug, &debug_msg[..]);
    }

    #[test]
    fn test_embedded_nul() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "bar\0foo";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, "<embedded nul>");
    }
}
<commit_msg>kata-os-logger: correctly match types<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg_attr(not(test), no_std)]
#![feature(linkage)]

extern "C" {
    // NB: components may not have a logging connection in which case
    //   logger_log will be undefined/null.
    #[linkage = "extern_weak"]
    static logger_log: *const ();
}

use core2::io::{Cursor, Write};
use cstr_core::CStr;
use log::{Metadata, Record};

// TODO(sleffler): until we can copy directly into shared memory limit
//   stack allocation (can be up to 4096).
const MAX_MSG_LEN: usize = 2048;

pub struct KataLogger;

impl log::Log for KataLogger {
    fn enabled(&self, _metadata: &Metadata) -> bool { true }

    fn log(&self, record: &Record) {
        let typed_logger_log: Option<extern "C" fn(level: u8, msg: *const cstr_core::c_char)> =
            unsafe { core::mem::transmute(logger_log) };
        if typed_logger_log.is_none() {
            return;
        }
        if self.enabled(record.metadata()) {
            let mut buf = [0 as u8; MAX_MSG_LEN];
            let mut cur = Cursor::new(&mut buf[..]);
            // Log msgs are of the form: '<target>::<fmt'd-msg>
            write!(&mut cur, "{}::{}\0", record.target(), record.args()).unwrap_or_else(|_| {
                // Too big, indicate overflow with a trailing "...".
                cur.set_position((MAX_MSG_LEN - 4) as u64);
                cur.write(b"...\0").expect("write!");
                ()
            });
            // If an embedded nul is identified, replace the message; there
            // are likely better solutions but this should not happen.
            fn embedded_nul_cstr<'a>(
                buf: &'a mut [u8; MAX_MSG_LEN],
                record: &Record,
            ) -> &'a cstr_core::CStr {
                let mut cur = Cursor::new(&mut buf[..]);
                write!(&mut cur, "{}::<embedded nul>\0", record.target()).expect("nul!");
                let pos = cur.position() as usize;
                CStr::from_bytes_with_nul(&buf[..pos]).unwrap()
            }
            // NB: this releases the ref on buf held by the Cursor
            let pos = cur.position() as usize;
            (typed_logger_log.unwrap())(
                record.level() as u8,
                match CStr::from_bytes_with_nul(&buf[..pos]) {
                    Ok(cstr) => cstr,
                    Err(_) => embedded_nul_cstr(&mut buf, record),
                }
                .as_ptr(),
            );
        }
    }

    fn flush(&self) {}
}

#[cfg(test)]
mod tests {
    use super::*;
    use arrayvec::ArrayVec;
    use log::{debug, error, info, trace, warn};

    static KATA_LOGGER: KataLogger = KataLogger;
    static mut MSGS: ArrayVec<[u8; MAX_MSG_LEN], 8> = ArrayVec::new_const();

    // Resets msg collection; used at the start of each test.
    fn reset_msgs() {
        unsafe {
            MSGS.clear();
        }
    }

    #[no_mangle]
    pub extern "C" fn logger_log(_level: u8, msg: *const cstr_core::c_char) {
        unsafe {
            // NB: this depends on msg pointing to the caller's array
            MSGS.push(*(msg as *const [u8; MAX_MSG_LEN]));
        }
    }

    // Formats a log message as done by KataLogger and returns a zero-padded
    // Vec holding the message.
    fn log_msg(_level: log::Level, msg: &str) -> Result<Vec<u8>, std::io::Error> {
        let mut v = Vec::new();
        use std::io::Write;
        write!(&mut v, "{}::{}\0", "logger::tests", msg)?;
        v.resize(MAX_MSG_LEN, 0);
        assert_eq!(v.len(), MAX_MSG_LEN);
        Ok(v)
    }

    fn pop_and_expect_none(level: log::Level) {
        unsafe {
            let msg = MSGS.pop();
            assert_eq!(
                msg, None,
                "Assertioon failed for level {}: expected None, got {:?}",
                level, msg
            );
        }
    }

    fn pop_and_check_result(level: log::Level, test_msg: &str) {
        unsafe {
            let msg = MSGS.pop();
            assert!(
                msg != None,
                "Assertion failed for level {}: no log msg collected",
                level
            );
            let expected = log_msg(level, test_msg).unwrap();
            let observed = msg.unwrap().to_vec();
            assert_eq!(
                expected,
                observed,
                "Assertion failed for level {}: expected {}, got {}",
                level,
                String::from_utf8_lossy(expected.as_slice()),
                String::from_utf8_lossy(observed.as_slice())
            );
        }
    }

    // NB: to run these sequentially use --test-threads=1; otherwise
    // cargo will use multiple threads and you will get failures from
    // multiple users of MSGS and the global logger; e.g.
    //     cargo test -- --test-threads=1

    #[test]
    fn test_each_log_level_works() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Trace);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_check_result(log::Level::Trace, trace_msg);
    }

    #[test]
    fn test_max_log_level() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        // With filtering at Debug level, levels below should come through.
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "hello debug";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, debug_msg);

        let info_msg = "hello info";
        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        let warn_msg = "hello warn";
        warn!("{}", warn_msg);
        pop_and_check_result(log::Level::Warn, warn_msg);

        let error_msg = "hello error";
        error!("{}", error_msg);
        pop_and_check_result(log::Level::Error, error_msg);

        let trace_msg = "hello trace";
        trace!("{}", trace_msg);
        pop_and_expect_none(log::Level::Trace);

        // Dynamically adjust the log level
        log::set_max_level(log::LevelFilter::Info);

        info!("{}", info_msg);
        pop_and_check_result(log::Level::Info, info_msg);

        debug!("{}", debug_msg);
        pop_and_expect_none(log::Level::Debug);
    }

    #[test]
    fn test_formatting() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        debug!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32);
        pop_and_check_result(
            log::Level::Debug,
            &format!("a {} b {} c {} d {:#x}", 99, "foo", 3.4, 32)[..],
        );
    }

    #[test]
    fn test_too_long() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        // Guarantee formatted message is > MAX_MSG_LEN
        let mut debug_msg = "debug".repeat((MAX_MSG_LEN / 5) + 1);
        debug!("{}", debug_msg);

        // Blech, must take into account log msg formatting.
        debug_msg.truncate(MAX_MSG_LEN - 4 - "logger::tests::".len());
        debug_msg.push_str("...");
        pop_and_check_result(log::Level::Debug, &debug_msg[..]);
    }

    #[test]
    fn test_embedded_nul() {
        reset_msgs();

        let _ = log::set_logger(&KATA_LOGGER);
        log::set_max_level(log::LevelFilter::Debug);

        let debug_msg = "bar\0foo";
        debug!("{}", debug_msg);
        pop_and_check_result(log::Level::Debug, "<embedded nul>");
    }
}
<|endoftext|>


<commit_before>use card_strategy::CardStrategy;

use card::Card;
use game_status::GameStatus;
use player::PlayerName;

use std::collections::BTreeMap;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

/*
    Play 2 of clubs if in hand.
    If going to win a deal, do so with highest ranking card.
    Need strategy for winning negetive penalty hands
*/
impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus, player_name: &PlayerName) -> (i32, i32, i32, i32) {
        let card_penalty_to_me = Self::card_penalty_to_me(card, game_status, player_name);
        let card_penalty = 0 - Self::card_penalty(card, game_status);
        let trouble = Self::trouble_score(card, game_status);
        let card_rank = 0 - (u32::from(card.rank) as i32);
        (card_penalty_to_me, card_penalty, trouble, card_rank)
    }

    fn card_penalty(card: &Card, game_status: &GameStatus) -> i32 {
        game_status.round_parameters.card_points.get(card)
            .map(|penalty| *penalty)
            .unwrap_or_default()
    }

    fn card_penalty_to_me(card: &Card, game_status: &GameStatus, player_name: &PlayerName) -> i32 {
        if Self::will_win_deal(card, game_status) {
            Self::card_penalty(card, game_status)
        } else {
            0
        }
    }

    fn trouble_score(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::will_win_deal(card, game_status) {
            u32::from(card.rank) as i32
        } else {
            0
        }
    }

    fn will_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        game_status.my_in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }
}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        // Need to order cards by potential to win lowest penalty hands
        let mut initial_hand: Vec<&Card> = game_status.my_initial_hand.iter().collect();
        initial_hand.reverse();
        initial_hand.into_iter().take(3).collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let current_suit = game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit);
        let mut valid_cards: Vec<&Card> = game_status.my_current_hand.iter().filter(|card| Some(card.suit) == current_suit).collect();

        if valid_cards.is_empty() {
            valid_cards.extend(&game_status.my_current_hand);
        }

        valid_cards.into_iter()
            .map(|card| ((Self::score_card(card, game_status, player_name), card), card))
            .collect::<BTreeMap<((i32, i32, i32, i32), &Card), &Card>>()
            .values()
            .next()
            .expect("No valid cards to play!")
    }

}<commit_msg>cleanup<commit_after>use card_strategy::CardStrategy;

use card::Card;
use game_status::GameStatus;
use player::PlayerName;

use std::collections::BTreeMap;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

/*
    Play 2 of clubs if in hand.
    If going to win a deal, do so with highest ranking card.
    Need strategy for winning negetive penalty hands
*/
impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus, player_name: &PlayerName) -> (i32, i32, i32, i32) {
        let card_penalty_to_me = Self::card_penalty_to_me(card, game_status, player_name);
        let card_penalty = 0 - Self::card_penalty(card, game_status);
        let trouble = Self::trouble_score(card, game_status);
        let card_rank = 0 - (u32::from(card.rank) as i32);
        (card_penalty_to_me, card_penalty, trouble, card_rank)
    }

    fn card_penalty(card: &Card, game_status: &GameStatus) -> i32 {
        game_status.round_parameters.card_points.get(card)
            .map(|penalty| *penalty)
            .unwrap_or_default()
    }

    fn card_penalty_to_me(card: &Card, game_status: &GameStatus, player_name: &PlayerName) -> i32 {
        if Self::will_win_deal(card, game_status) {
            Self::card_penalty(card, game_status)
        } else {
            0
        }
    }

    fn trouble_score(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::will_win_deal(card, game_status) {
            u32::from(card.rank) as i32
        } else {
            0
        }
    }

    fn will_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        game_status.my_in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }
}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        // Need to order cards by potential to win lowest penalty hands
        let mut initial_hand = game_status.my_initial_hand.iter().collect::<Vec<&Card>>();
        initial_hand.reverse();
        initial_hand.into_iter().take(3).collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let current_suit = game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit);
        let mut valid_cards = game_status.my_current_hand.iter()
            .filter(|card| Some(card.suit) == current_suit)
            .collect::<Vec<&Card>>();

        if valid_cards.is_empty() {
            valid_cards.extend(&game_status.my_current_hand);
        }

        valid_cards.into_iter()
            .map(|card| ((Self::score_card(card, game_status, player_name), card), card))
            .collect::<BTreeMap<((i32, i32, i32, i32), &Card), &Card>>()
            .values()
            .next()
            .expect("No valid cards to play!")
    }

}<|endoftext|>


<commit_before>use fall_tree::AstNode;
use ast_ext::{PartKind, SelectorKind};
use util::{scream, snake};
use tera::{Tera, Context};

use syntax::{File, Alt, Part};

pub fn generate(file: File) -> String {

    #[derive(Serialize)]
    struct CtxSynRule<'f> { is_public: bool, name: &'f str, alts: Vec<String> };

    #[derive(Serialize)]
    struct CtxLexRule<'f> { ty: &'f str, re: String, f: Option<&'f str> };

    #[derive(Serialize)]
    struct CtxAstNode<'f> { struct_name: String, node_type_name: String, methods: Vec<CtxMethod<'f>> }

    #[derive(Serialize)]
    struct CtxMethod<'f> { name: &'f str, ret_type: String, body: String }

    let mut context = Context::new();
    context.add("node_types", &file.nodes_def().expect("no nodes defined").nodes());
    context.add("syn_rules", &file.syn_rules().map(|r| {
        CtxSynRule {
            is_public: r.is_public(),
            name: r.name(),
            alts: r.alts().map(generate_alt).collect()
        }
    }).collect::<Vec<_>>());
    context.add("lex_rules", &file.tokenizer_def().expect("no tokens defined").lex_rules().map(|r| {
        CtxLexRule { ty: r.node_type(), re: format!("{:?}", r.token_re()), f: r.extern_fn() }
    }).collect::<Vec<_>>());
    context.add("verbatim", &file.verbatim_def().map(|v| v.contents()));

    if let Some(ast) = file.ast_def() {
        context.add("ast_nodes", &ast.ast_nodes().map(|node| {
            CtxAstNode {
                struct_name: snake(node.name()),
                node_type_name: scream(node.name()),
                methods: node.methods().map(|method| {
                    CtxMethod {
                        name: method.name(),
                        ret_type: match method.selector_kind() {
                            SelectorKind::Single(name) => format!("{}<'f>", snake(name)),
                            SelectorKind::Opt(name) => format!("Option<{}<'f>>", snake(name)),
                            SelectorKind::Many(name) => format!("AstChildren<'f, {}<'f>>", snake(name)),
                            SelectorKind::Text(_) => "&'f str".to_owned(),
                        },
                        body: match method.selector_kind() {
                            SelectorKind::Single(_) => format!("AstChildren::new(self.node.children()).next().unwrap()"),
                            SelectorKind::Opt(_) => format!("AstChildren::new(self.node.children()).next()"),
                            SelectorKind::Many(_) => format!("AstChildren::new(self.node.children())"),
                            SelectorKind::Text(name) => format!("child_of_type_exn(self.node, {}).text()", name),
                        }
                    }
                }).collect()
            }
        }).collect::<Vec<_>>());
    }

    Tera::one_off(TEMPLATE.trim(), &context, false).unwrap()
}

fn generate_alt(alt: Alt) -> String {
    fn is_commit(part: Part) -> bool {
        part.node().text() == "<commit>"
    }
    let commit = alt.parts().position(is_commit);

    let parts = alt.parts()
        .filter(|&p| !is_commit(p))
        .map(|p| generate_part(p))
        .collect::<Vec<_>>();
    format!("Alt {{ parts: &[{}], commit: {:?} }}", parts.join(", "), commit)
}

fn generate_part(part: Part) -> String {
    match part.kind().expect("unresolved reference") {
        PartKind::Token(t) => format!("Part::Token({})", scream(t)),
        PartKind::RuleReference { idx } => format!("Part::Rule({:?})", idx),
        PartKind::Call { name, mut alts } => {
            let op = match name {
                "rep" => "Rep",
                "opt" => "Opt",
                _ => unimplemented!(),
            };
            format!("Part::{}({})", op, generate_alt(alts.next().unwrap()))
        }
    }
}

const TEMPLATE: &'static str = r#####"
use fall_tree::{NodeType, NodeTypeInfo, Language, LanguageImpl};
pub use fall_tree::{ERROR, WHITESPACE};

{% for node_type in node_types %}
pub const {{ node_type | upper }}: NodeType = NodeType({{ 100 + loop.index0 }});
{% endfor %}

lazy_static! {
    pub static ref LANG: Language = {
        use fall_parse::{LexRule, SynRule, Alt, Part, Parser};

        const PARSER: &'static [SynRule] = &[
            {% for rule in syn_rules %}
            SynRule {
                ty: {% if rule.is_public %}Some({{ rule.name | upper }}){% else %}None{% endif %},
                alts: &[{% for alt in rule.alts %}{% if loop.first %}{{ alt }}{% else %}, {{ alt }}{% endif %}{% endfor %}],
            },
            {% endfor %}
        ];

        struct Impl { tokenizer: Vec<LexRule> };
        impl LanguageImpl for Impl {
            fn parse(&self, lang: Language, text: String) -> ::fall_tree::File {
                ::fall_parse::parse(lang, text, FILE, &self.tokenizer, &|b| Parser::new(PARSER).parse(b))
            }

            fn node_type_info(&self, ty: NodeType) -> NodeTypeInfo {
                match ty {
                    ERROR => NodeTypeInfo { name: "ERROR" },
                    WHITESPACE => NodeTypeInfo { name: "WHITESPACE" },
                    {% for node_type in node_types %}
                    {{ node_type | upper }} => NodeTypeInfo { name: "{{ node_type | upper }}" },
                    {% endfor %}
                    _ => panic!("Unknown NodeType: {:?}", ty)
                }
            }
        }

        Language::new(Impl {
            tokenizer: vec![
                {% for rule in lex_rules %}
                LexRule::new({{ rule.ty | upper }}, {{ rule.re }}, {% if rule.f is string %} Some({{ rule.f }}) {% else %} None {% endif %}),
                {% endfor %}
            ]
        })
    };
}
{% if verbatim is string %}
{{ verbatim }}
{% endif %}

{% if ast_nodes is defined %}
use fall_tree::{AstNode, AstChildren, Node};
use fall_tree::search::child_of_type_exn;

{% for node in ast_nodes %}
#[derive(Clone, Copy)]
pub struct {{ node.struct_name }}<'f> { node: Node<'f> }

impl<'f> AstNode<'f> for {{ node.struct_name }}<'f> {
    fn ty() -> NodeType { {{ node.node_type_name }} }
    fn new(node: Node<'f>) -> Self {
        assert_eq!(node.ty(), Self::ty());
        {{ node.struct_name }} { node: node }
    }
    fn node(&self) -> Node<'f> { self.node }
}

impl<'f> {{ node.struct_name }}<'f> {
    {% for method in node.methods %}
    pub fn {{ method.name }}(&self) -> {{ method.ret_type }} {
        {{ method.body }}
    }
    {% endfor %}
}
{% endfor %}
{% endif %}
"#####;
<commit_msg>Better panic<commit_after>use fall_tree::AstNode;
use ast_ext::{PartKind, SelectorKind};
use util::{scream, snake};
use tera::{Tera, Context};

use syntax::{File, Alt, Part};

pub fn generate(file: File) -> String {

    #[derive(Serialize)]
    struct CtxSynRule<'f> { is_public: bool, name: &'f str, alts: Vec<String> };

    #[derive(Serialize)]
    struct CtxLexRule<'f> { ty: &'f str, re: String, f: Option<&'f str> };

    #[derive(Serialize)]
    struct CtxAstNode<'f> { struct_name: String, node_type_name: String, methods: Vec<CtxMethod<'f>> }

    #[derive(Serialize)]
    struct CtxMethod<'f> { name: &'f str, ret_type: String, body: String }

    let mut context = Context::new();
    context.add("node_types", &file.nodes_def().expect("no nodes defined").nodes());
    context.add("syn_rules", &file.syn_rules().map(|r| {
        CtxSynRule {
            is_public: r.is_public(),
            name: r.name(),
            alts: r.alts().map(generate_alt).collect()
        }
    }).collect::<Vec<_>>());
    context.add("lex_rules", &file.tokenizer_def().expect("no tokens defined").lex_rules().map(|r| {
        CtxLexRule { ty: r.node_type(), re: format!("{:?}", r.token_re()), f: r.extern_fn() }
    }).collect::<Vec<_>>());
    context.add("verbatim", &file.verbatim_def().map(|v| v.contents()));

    if let Some(ast) = file.ast_def() {
        context.add("ast_nodes", &ast.ast_nodes().map(|node| {
            CtxAstNode {
                struct_name: snake(node.name()),
                node_type_name: scream(node.name()),
                methods: node.methods().map(|method| {
                    CtxMethod {
                        name: method.name(),
                        ret_type: match method.selector_kind() {
                            SelectorKind::Single(name) => format!("{}<'f>", snake(name)),
                            SelectorKind::Opt(name) => format!("Option<{}<'f>>", snake(name)),
                            SelectorKind::Many(name) => format!("AstChildren<'f, {}<'f>>", snake(name)),
                            SelectorKind::Text(_) => "&'f str".to_owned(),
                        },
                        body: match method.selector_kind() {
                            SelectorKind::Single(_) => format!("AstChildren::new(self.node.children()).next().unwrap()"),
                            SelectorKind::Opt(_) => format!("AstChildren::new(self.node.children()).next()"),
                            SelectorKind::Many(_) => format!("AstChildren::new(self.node.children())"),
                            SelectorKind::Text(name) => format!("child_of_type_exn(self.node, {}).text()", name),
                        }
                    }
                }).collect()
            }
        }).collect::<Vec<_>>());
    }

    Tera::one_off(TEMPLATE.trim(), &context, false).unwrap()
}

fn generate_alt(alt: Alt) -> String {
    fn is_commit(part: Part) -> bool {
        part.node().text() == "<commit>"
    }
    let commit = alt.parts().position(is_commit);

    let parts = alt.parts()
        .filter(|&p| !is_commit(p))
        .map(|p| generate_part(p))
        .collect::<Vec<_>>();
    format!("Alt {{ parts: &[{}], commit: {:?} }}", parts.join(", "), commit)
}

fn generate_part(part: Part) -> String {
    match part.kind().unwrap_or_else(|| panic!("unresolved reference: {}", part.node().text())) {
        PartKind::Token(t) => format!("Part::Token({})", scream(t)),
        PartKind::RuleReference { idx } => format!("Part::Rule({:?})", idx),
        PartKind::Call { name, mut alts } => {
            let op = match name {
                "rep" => "Rep",
                "opt" => "Opt",
                _ => unimplemented!(),
            };
            format!("Part::{}({})", op, generate_alt(alts.next().unwrap()))
        }
    }
}

const TEMPLATE: &'static str = r#####"
use fall_tree::{NodeType, NodeTypeInfo, Language, LanguageImpl};
pub use fall_tree::{ERROR, WHITESPACE};

{% for node_type in node_types %}
pub const {{ node_type | upper }}: NodeType = NodeType({{ 100 + loop.index0 }});
{% endfor %}

lazy_static! {
    pub static ref LANG: Language = {
        use fall_parse::{LexRule, SynRule, Alt, Part, Parser};

        const PARSER: &'static [SynRule] = &[
            {% for rule in syn_rules %}
            SynRule {
                ty: {% if rule.is_public %}Some({{ rule.name | upper }}){% else %}None{% endif %},
                alts: &[{% for alt in rule.alts %}{% if loop.first %}{{ alt }}{% else %}, {{ alt }}{% endif %}{% endfor %}],
            },
            {% endfor %}
        ];

        struct Impl { tokenizer: Vec<LexRule> };
        impl LanguageImpl for Impl {
            fn parse(&self, lang: Language, text: String) -> ::fall_tree::File {
                ::fall_parse::parse(lang, text, FILE, &self.tokenizer, &|b| Parser::new(PARSER).parse(b))
            }

            fn node_type_info(&self, ty: NodeType) -> NodeTypeInfo {
                match ty {
                    ERROR => NodeTypeInfo { name: "ERROR" },
                    WHITESPACE => NodeTypeInfo { name: "WHITESPACE" },
                    {% for node_type in node_types %}
                    {{ node_type | upper }} => NodeTypeInfo { name: "{{ node_type | upper }}" },
                    {% endfor %}
                    _ => panic!("Unknown NodeType: {:?}", ty)
                }
            }
        }

        Language::new(Impl {
            tokenizer: vec![
                {% for rule in lex_rules %}
                LexRule::new({{ rule.ty | upper }}, {{ rule.re }}, {% if rule.f is string %} Some({{ rule.f }}) {% else %} None {% endif %}),
                {% endfor %}
            ]
        })
    };
}
{% if verbatim is string %}
{{ verbatim }}
{% endif %}

{% if ast_nodes is defined %}
use fall_tree::{AstNode, AstChildren, Node};
use fall_tree::search::child_of_type_exn;

{% for node in ast_nodes %}
#[derive(Clone, Copy)]
pub struct {{ node.struct_name }}<'f> { node: Node<'f> }

impl<'f> AstNode<'f> for {{ node.struct_name }}<'f> {
    fn ty() -> NodeType { {{ node.node_type_name }} }
    fn new(node: Node<'f>) -> Self {
        assert_eq!(node.ty(), Self::ty());
        {{ node.struct_name }} { node: node }
    }
    fn node(&self) -> Node<'f> { self.node }
}

impl<'f> {{ node.struct_name }}<'f> {
    {% for method in node.methods %}
    pub fn {{ method.name }}(&self) -> {{ method.ret_type }} {
        {{ method.body }}
    }
    {% endfor %}
}
{% endfor %}
{% endif %}
"#####;
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Read;

struct VM {
  stack: Vec<String>
}

fn main() {
	if let Some(path) = env::args().nth(1) {
		println!("Loading file {}", path);

    match File::open(path.to_string()) {
      Ok(file) => {
        let mut file = file;
        let mut content = String::new();
        if let Ok(_) = file.read_to_string(&mut content) {
          println!("The file contains {}", content);
        }
      },
      Err(err) => {
        println!("Can't read file {} (error: {})", path, err);
      }
    }
	} else {
    println!("Please give a file argument");
  }

  //     let path = Path::new("chry.fa");
  // let mut file = BufferedReader::new(File::open(&path));
  // for line in file.lines() {
  //  print!("{}", line.unwrap());
  //      }
}
<commit_msg>LIFETIMES!1<commit_after>use std::env;
use std::fs::File;
use std::io::Read;

struct VM<'a> {
  stack: Vec<String>,
  instructions: Vec<&'a str>
}

impl<'a> VM<'a> {
  pub fn new(instructions: Vec<&'a str>) -> VM {
    VM {
      stack: Vec::new(),
      instructions: instructions
    }
  }
}

fn main() {
	if let Some(path) = env::args().nth(1) {
		println!("Loading file {}", path);

    match File::open(path.to_string()) {
      Ok(file) => {
        let mut file = file;
        let mut content = String::new();
        if let Ok(_) = file.read_to_string(&mut content) {
          println!("The file contains {}", content);

          let vm = VM::new(content.split('\n').collect());
        }
      },
      Err(err) => {
        println!("Can't read file {} (error: {})", path, err);
      }
    }
	} else {
    println!("Please give a file argument");
  }

  //     let path = Path::new("chry.fa");
  // let mut file = BufferedReader::new(File::open(&path));
  // for line in file.lines() {
  //  print!("{}", line.unwrap());
  //      }
}
<|endoftext|>