<commit_before>//! SCIP generator

use std::{
    collections::{HashMap, HashSet},
    time::Instant,
};

use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
use hir::Name;
use ide::{
    LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
    TokenStaticData,
};
use ide_db::LineIndexDatabase;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
use scip::types as scip_types;
use std::env;

use crate::cli::{
    flags,
    load_cargo::{load_workspace, LoadCargoConfig},
    Result,
};

impl flags::Scip {
    pub fn run(self) -> Result<()> {
        eprintln!("Generating SCIP start...");
        let now = Instant::now();
        let cargo_config = CargoConfig::default();

        let no_progress = &|s| (eprintln!("rust-analyzer: Loading {}", s));
        let load_cargo_config = LoadCargoConfig {
            load_out_dirs_from_check: true,
            with_proc_macro: true,
            prefill_caches: true,
        };
        let path = vfs::AbsPathBuf::assert(env::current_dir()?.join(&self.path));
        let rootpath = path.normalize();
        let manifest = ProjectManifest::discover_single(&path)?;

        let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;

        let (host, vfs, _) =
            load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
        let db = host.raw_database();
        let analysis = host.analysis();

        let si = StaticIndex::compute(&analysis);

        let mut index = scip_types::Index {
            metadata: Some(scip_types::Metadata {
                version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
                tool_info: Some(scip_types::ToolInfo {
                    name: "rust-analyzer".to_owned(),
                    version: "0.1".to_owned(),
                    arguments: vec![],
                    ..Default::default()
                })
                .into(),
                project_root: format!(
                    "file://{}",
                    path.normalize()
                        .as_os_str()
                        .to_str()
                        .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
                        .to_string()
                ),
                text_document_encoding: scip_types::TextEncoding::UTF8.into(),
                ..Default::default()
            })
            .into(),
            ..Default::default()
        };

        let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
        let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();

        for StaticIndexedFile { file_id, tokens, .. } in si.files {
            let mut local_count = 0;
            let mut new_local_symbol = || {
                let new_symbol = scip::types::Symbol::new_local(local_count);
                local_count += 1;

                new_symbol
            };

            let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) {
                Some(relative_path) => relative_path,
                None => continue,
            };

            let line_index = LineIndex {
                index: db.line_index(file_id),
                encoding: PositionEncoding::Utf8,
                endings: LineEndings::Unix,
            };

            let mut doc = scip_types::Document {
                relative_path,
                language: "rust".to_string(),
                ..Default::default()
            };

            tokens.into_iter().for_each(|(range, id)| {
                let token = si.tokens.get(id).unwrap();

                let mut occurrence = scip_types::Occurrence::default();
                occurrence.range = text_range_to_scip_range(&line_index, range);
                occurrence.symbol = tokens_to_symbol
                    .entry(id)
                    .or_insert_with(|| {
                        let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol);
                        scip::symbol::format_symbol(symbol)
                    })
                    .clone();

                if let Some(def) = token.definition {
                    if def.range == range {
                        occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32;
                    }

                    if symbols_emitted.insert(id) {
                        let mut symbol_info = scip_types::SymbolInformation::default();
                        symbol_info.symbol = occurrence.symbol.clone();
                        if let Some(hover) = &token.hover {
                            if !hover.markup.as_str().is_empty() {
                                symbol_info.documentation = vec![hover.markup.as_str().to_string()];
                            }
                        }

                        doc.symbols.push(symbol_info)
                    }
                }

                doc.occurrences.push(occurrence);
            });

            if doc.occurrences.is_empty() {
                continue;
            }

            index.documents.push(doc);
        }

        scip::write_message_to_file("index.scip", index)
            .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;

        eprintln!("Generating SCIP finished {:?}", now.elapsed());
        Ok(())
    }
}

fn get_relative_filepath(
    vfs: &vfs::Vfs,
    rootpath: &vfs::AbsPathBuf,
    file_id: ide::FileId,
) -> Option<String> {
    Some(vfs.file_path(file_id).as_path()?.strip_prefix(&rootpath)?.as_ref().to_str()?.to_string())
}

// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
// only encode as a vector of [start_line, start_col, end_col].
//
// This transforms a line index into the optimized SCIP Range.
fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec<i32> {
    let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start());
    let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end());

    if start_line == end_line {
        vec![start_line as i32, start_col as i32, end_col as i32]
    } else {
        vec![start_line as i32, start_col as i32, end_line as i32, end_col as i32]
    }
}

fn new_descriptor_str(
    name: &str,
    suffix: scip_types::descriptor::Suffix,
) -> scip_types::Descriptor {
    scip_types::Descriptor {
        name: name.to_string(),
        disambiguator: "".to_string(),
        suffix: suffix.into(),
        ..Default::default()
    }
}

fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
    let mut name = name.to_string();
    if name.contains("'") {
        name = format!("`{}`", name);
    }

    new_descriptor_str(name.as_str(), suffix)
}

/// Loosely based on `def_to_moniker`
///
/// Only returns a Symbol when it's a non-local symbol.
///     So if the visibility isn't outside of a document, then it will return None
fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
    use scip_types::descriptor::Suffix::*;

    let moniker = token.moniker.as_ref()?;

    let package_name = moniker.package_information.name.clone();
    let version = moniker.package_information.version.clone();
    let descriptors = moniker
        .identifier
        .description
        .iter()
        .map(|desc| {
            new_descriptor(
                desc.name.clone(),
                match desc.desc {
                    MonikerDescriptorKind::Namespace => Namespace,
                    MonikerDescriptorKind::Type => Type,
                    MonikerDescriptorKind::Term => Term,
                    MonikerDescriptorKind::Method => Method,
                    MonikerDescriptorKind::TypeParameter => TypeParameter,
                    MonikerDescriptorKind::Parameter => Parameter,
                    MonikerDescriptorKind::Macro => Macro,
                    MonikerDescriptorKind::Meta => Meta,
                },
            )
        })
        .collect();

    Some(scip_types::Symbol {
        scheme: "rust-analyzer".into(),
        package: Some(scip_types::Package {
            manager: "cargo".to_string(),
            name: package_name,
            version: version.unwrap_or_else(|| ".".to_string()),
            ..Default::default()
        })
        .into(),
        descriptors,
        ..Default::default()
    })
}

#[cfg(test)]
mod test {
    use super::*;
    use ide::{AnalysisHost, FilePosition, StaticIndex, TextSize};
    use ide_db::base_db::fixture::ChangeFixture;
    use scip::symbol::format_symbol;

    fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
        let mut host = AnalysisHost::default();
        let change_fixture = ChangeFixture::parse(ra_fixture);
        host.raw_database_mut().apply_change(change_fixture.change);
        let (file_id, range_or_offset) =
            change_fixture.file_position.expect("expected a marker ($0)");
        let offset = range_or_offset.expect_offset();
        (host, FilePosition { file_id, offset })
    }

    /// If expected == "", then assert that there are no symbols (this is basically local symbol)
    #[track_caller]
    fn check_symbol(ra_fixture: &str, expected: &str) {
        let (host, position) = position(ra_fixture);

        let analysis = host.analysis();
        let si = StaticIndex::compute(&analysis);

        let FilePosition { file_id, offset } = position;

        let mut found_symbol = None;
        for file in &si.files {
            if file.file_id != file_id {
                continue;
            }
            for &(range, id) in &file.tokens {
                if range.contains(offset - TextSize::from(1)) {
                    let token = si.tokens.get(id).unwrap();
                    found_symbol = token_to_symbol(token);
                    break;
                }
            }
        }

        if expected == "" {
            assert!(found_symbol.is_none(), "must have no symbols {:?}", found_symbol);
            return;
        }

        assert!(found_symbol.is_some(), "must have one symbol {:?}", found_symbol);
        let res = found_symbol.unwrap();
        let formatted = format_symbol(res);
        assert_eq!(formatted, expected);
    }

    #[test]
    fn basic() {
        check_symbol(
            r#"
//- /lib.rs crate:main deps:foo
use foo::example_mod::func;
fn main() {
    func$0();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod example_mod {
    pub fn func() {}
}
"#,
            "rust-analyzer cargo foo 0.1.0 example_mod/func().",
        );
    }

    #[test]
    fn symbol_for_trait() {
        check_symbol(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        pub fn func$0() {}
    }
}
"#,
            "rust-analyzer cargo foo 0.1.0 module/MyTrait#func().",
        );
    }

    #[test]
    fn symbol_for_trait_constant() {
        check_symbol(
            r#"
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub trait MyTrait {
            const MY_CONST$0: u8;
        }
    }
    "#,
            "rust-analyzer cargo foo 0.1.0 module/MyTrait#MY_CONST.",
        );
    }

    #[test]
    fn symbol_for_trait_type() {
        check_symbol(
            r#"
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub trait MyTrait {
            type MyType$0;
        }
    }
    "#,
            // "foo::module::MyTrait::MyType",
            "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]",
        );
    }

    #[test]
    fn symbol_for_trait_impl_function() {
        check_symbol(
            r#"
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub trait MyTrait {
            pub fn func() {}
        }

        struct MyStruct {}

        impl MyTrait for MyStruct {
            pub fn func$0() {}
        }
    }
    "#,
            // "foo::module::MyStruct::MyTrait::func",
            "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().",
        );
    }

    #[test]
    fn symbol_for_field() {
        check_symbol(
            r#"
    //- /lib.rs crate:main deps:foo
    use foo::St;
    fn main() {
        let x = St { a$0: 2 };
    }
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub struct St {
        pub a: i32,
    }
    "#,
            "rust-analyzer cargo foo 0.1.0 St#a.",
        );
    }

    #[test]
    fn local_symbol_for_local() {
        check_symbol(
            r#"
    //- /lib.rs crate:main deps:foo
    use foo::module::func;
    fn main() {
        func();
    }
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub fn func() {
            let x$0 = 2;
        }
    }
    "#,
            "",
        );
    }

    #[test]
    fn global_symbol_for_pub_struct() {
        check_symbol(
            r#"
    //- /lib.rs crate:main
    mod foo;

    fn main() {
        let _bar = foo::Bar { i: 0 };
    }
    //- /foo.rs
    pub struct Bar$0 {
        pub i: i32,
    }
    "#,
            "rust-analyzer cargo main . foo/Bar#",
        );
    }

    #[test]
    fn global_symbol_for_pub_struct_reference() {
        check_symbol(
            r#"
    //- /lib.rs crate:main
    mod foo;

    fn main() {
        let _bar = foo::Bar$0 { i: 0 };
    }
    //- /foo.rs
    pub struct Bar {
        pub i: i32,
    }
    "#,
            "rust-analyzer cargo main . foo/Bar#",
        );
    }
}
<commit_msg>Auto merge of #13632 - Veykril:scip, r=Veykril<commit_after>//! SCIP generator

use std::{
    collections::{HashMap, HashSet},
    time::Instant,
};

use crate::line_index::{LineEndings, LineIndex, PositionEncoding};
use hir::Name;
use ide::{
    LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
    TokenStaticData,
};
use ide_db::LineIndexDatabase;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
use scip::types as scip_types;
use std::env;

use crate::cli::{
    flags,
    load_cargo::{load_workspace, LoadCargoConfig},
    Result,
};

impl flags::Scip {
    pub fn run(self) -> Result<()> {
        eprintln!("Generating SCIP start...");
        let now = Instant::now();
        let cargo_config = CargoConfig::default();

        let no_progress = &|s| (eprintln!("rust-analyzer: Loading {}", s));
        let load_cargo_config = LoadCargoConfig {
            load_out_dirs_from_check: true,
            with_proc_macro: true,
            prefill_caches: true,
        };
        let path = vfs::AbsPathBuf::assert(env::current_dir()?.join(&self.path));
        let rootpath = path.normalize();
        let manifest = ProjectManifest::discover_single(&path)?;

        let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;

        let (host, vfs, _) =
            load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
        let db = host.raw_database();
        let analysis = host.analysis();

        let si = StaticIndex::compute(&analysis);

        let metadata = scip_types::Metadata {
            version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
            tool_info: Some(scip_types::ToolInfo {
                name: "rust-analyzer".to_owned(),
                version: "0.1".to_owned(),
                arguments: vec![],
                special_fields: Default::default(),
            })
            .into(),
            project_root: format!(
                "file://{}",
                path.normalize()
                    .as_os_str()
                    .to_str()
                    .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
                    .to_string()
            ),
            text_document_encoding: scip_types::TextEncoding::UTF8.into(),
            special_fields: Default::default(),
        };
        let mut documents = Vec::new();

        let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
        let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();

        for StaticIndexedFile { file_id, tokens, .. } in si.files {
            let mut local_count = 0;
            let mut new_local_symbol = || {
                let new_symbol = scip::types::Symbol::new_local(local_count);
                local_count += 1;

                new_symbol
            };

            let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) {
                Some(relative_path) => relative_path,
                None => continue,
            };

            let line_index = LineIndex {
                index: db.line_index(file_id),
                encoding: PositionEncoding::Utf8,
                endings: LineEndings::Unix,
            };

            let mut occurrences = Vec::new();
            let mut symbols = Vec::new();

            tokens.into_iter().for_each(|(text_range, id)| {
                let token = si.tokens.get(id).unwrap();

                let range = text_range_to_scip_range(&line_index, text_range);
                let symbol = tokens_to_symbol
                    .entry(id)
                    .or_insert_with(|| {
                        let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol);
                        scip::symbol::format_symbol(symbol)
                    })
                    .clone();

                let mut symbol_roles = Default::default();

                if let Some(def) = token.definition {
                    if def.range == text_range {
                        symbol_roles |= scip_types::SymbolRole::Definition as i32;
                    }

                    if symbols_emitted.insert(id) {
                        let documentation = token
                            .hover
                            .as_ref()
                            .map(|hover| hover.markup.as_str())
                            .filter(|it| !it.is_empty())
                            .map(|it| vec![it.to_owned()]);
                        let symbol_info = scip_types::SymbolInformation {
                            symbol: symbol.clone(),
                            documentation: documentation.unwrap_or_default(),
                            relationships: Vec::new(),
                            special_fields: Default::default(),
                        };

                        symbols.push(symbol_info)
                    }
                }

                occurrences.push(scip_types::Occurrence {
                    range,
                    symbol,
                    symbol_roles,
                    override_documentation: Vec::new(),
                    syntax_kind: Default::default(),
                    diagnostics: Vec::new(),
                    special_fields: Default::default(),
                });
            });

            if occurrences.is_empty() {
                continue;
            }

            documents.push(scip_types::Document {
                relative_path,
                language: "rust".to_string(),
                occurrences,
                symbols,
                special_fields: Default::default(),
            });
        }

        let index = scip_types::Index {
            metadata: Some(metadata).into(),
            documents,
            external_symbols: Vec::new(),
            special_fields: Default::default(),
        };

        scip::write_message_to_file("index.scip", index)
            .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;

        eprintln!("Generating SCIP finished {:?}", now.elapsed());
        Ok(())
    }
}

fn get_relative_filepath(
    vfs: &vfs::Vfs,
    rootpath: &vfs::AbsPathBuf,
    file_id: ide::FileId,
) -> Option<String> {
    Some(vfs.file_path(file_id).as_path()?.strip_prefix(&rootpath)?.as_ref().to_str()?.to_string())
}

// SCIP Ranges have a (very large) optimization that ranges if they are on the same line
// only encode as a vector of [start_line, start_col, end_col].
//
// This transforms a line index into the optimized SCIP Range.
fn text_range_to_scip_range(line_index: &LineIndex, range: TextRange) -> Vec<i32> {
    let LineCol { line: start_line, col: start_col } = line_index.index.line_col(range.start());
    let LineCol { line: end_line, col: end_col } = line_index.index.line_col(range.end());

    if start_line == end_line {
        vec![start_line as i32, start_col as i32, end_col as i32]
    } else {
        vec![start_line as i32, start_col as i32, end_line as i32, end_col as i32]
    }
}

fn new_descriptor_str(
    name: &str,
    suffix: scip_types::descriptor::Suffix,
) -> scip_types::Descriptor {
    scip_types::Descriptor {
        name: name.to_string(),
        disambiguator: "".to_string(),
        suffix: suffix.into(),
        special_fields: Default::default(),
    }
}

fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
    let mut name = name.to_string();
    if name.contains("'") {
        name = format!("`{}`", name);
    }

    new_descriptor_str(name.as_str(), suffix)
}

/// Loosely based on `def_to_moniker`
///
/// Only returns a Symbol when it's a non-local symbol.
///     So if the visibility isn't outside of a document, then it will return None
fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
    use scip_types::descriptor::Suffix::*;

    let moniker = token.moniker.as_ref()?;

    let package_name = moniker.package_information.name.clone();
    let version = moniker.package_information.version.clone();
    let descriptors = moniker
        .identifier
        .description
        .iter()
        .map(|desc| {
            new_descriptor(
                desc.name.clone(),
                match desc.desc {
                    MonikerDescriptorKind::Namespace => Namespace,
                    MonikerDescriptorKind::Type => Type,
                    MonikerDescriptorKind::Term => Term,
                    MonikerDescriptorKind::Method => Method,
                    MonikerDescriptorKind::TypeParameter => TypeParameter,
                    MonikerDescriptorKind::Parameter => Parameter,
                    MonikerDescriptorKind::Macro => Macro,
                    MonikerDescriptorKind::Meta => Meta,
                },
            )
        })
        .collect();

    Some(scip_types::Symbol {
        scheme: "rust-analyzer".into(),
        package: Some(scip_types::Package {
            manager: "cargo".to_string(),
            name: package_name,
            version: version.unwrap_or_else(|| ".".to_string()),
            special_fields: Default::default(),
        })
        .into(),
        descriptors,
        special_fields: Default::default(),
    })
}

#[cfg(test)]
mod test {
    use super::*;
    use ide::{AnalysisHost, FilePosition, StaticIndex, TextSize};
    use ide_db::base_db::fixture::ChangeFixture;
    use scip::symbol::format_symbol;

    fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
        let mut host = AnalysisHost::default();
        let change_fixture = ChangeFixture::parse(ra_fixture);
        host.raw_database_mut().apply_change(change_fixture.change);
        let (file_id, range_or_offset) =
            change_fixture.file_position.expect("expected a marker ($0)");
        let offset = range_or_offset.expect_offset();
        (host, FilePosition { file_id, offset })
    }

    /// If expected == "", then assert that there are no symbols (this is basically local symbol)
    #[track_caller]
    fn check_symbol(ra_fixture: &str, expected: &str) {
        let (host, position) = position(ra_fixture);

        let analysis = host.analysis();
        let si = StaticIndex::compute(&analysis);

        let FilePosition { file_id, offset } = position;

        let mut found_symbol = None;
        for file in &si.files {
            if file.file_id != file_id {
                continue;
            }
            for &(range, id) in &file.tokens {
                if range.contains(offset - TextSize::from(1)) {
                    let token = si.tokens.get(id).unwrap();
                    found_symbol = token_to_symbol(token);
                    break;
                }
            }
        }

        if expected == "" {
            assert!(found_symbol.is_none(), "must have no symbols {:?}", found_symbol);
            return;
        }

        assert!(found_symbol.is_some(), "must have one symbol {:?}", found_symbol);
        let res = found_symbol.unwrap();
        let formatted = format_symbol(res);
        assert_eq!(formatted, expected);
    }

    #[test]
    fn basic() {
        check_symbol(
            r#"
//- /lib.rs crate:main deps:foo
use foo::example_mod::func;
fn main() {
    func$0();
}
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod example_mod {
    pub fn func() {}
}
"#,
            "rust-analyzer cargo foo 0.1.0 example_mod/func().",
        );
    }

    #[test]
    fn symbol_for_trait() {
        check_symbol(
            r#"
//- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
pub mod module {
    pub trait MyTrait {
        pub fn func$0() {}
    }
}
"#,
            "rust-analyzer cargo foo 0.1.0 module/MyTrait#func().",
        );
    }

    #[test]
    fn symbol_for_trait_constant() {
        check_symbol(
            r#"
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub trait MyTrait {
            const MY_CONST$0: u8;
        }
    }
    "#,
            "rust-analyzer cargo foo 0.1.0 module/MyTrait#MY_CONST.",
        );
    }

    #[test]
    fn symbol_for_trait_type() {
        check_symbol(
            r#"
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub trait MyTrait {
            type MyType$0;
        }
    }
    "#,
            // "foo::module::MyTrait::MyType",
            "rust-analyzer cargo foo 0.1.0 module/MyTrait#[MyType]",
        );
    }

    #[test]
    fn symbol_for_trait_impl_function() {
        check_symbol(
            r#"
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub trait MyTrait {
            pub fn func() {}
        }

        struct MyStruct {}

        impl MyTrait for MyStruct {
            pub fn func$0() {}
        }
    }
    "#,
            // "foo::module::MyStruct::MyTrait::func",
            "rust-analyzer cargo foo 0.1.0 module/MyStruct#MyTrait#func().",
        );
    }

    #[test]
    fn symbol_for_field() {
        check_symbol(
            r#"
    //- /lib.rs crate:main deps:foo
    use foo::St;
    fn main() {
        let x = St { a$0: 2 };
    }
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub struct St {
        pub a: i32,
    }
    "#,
            "rust-analyzer cargo foo 0.1.0 St#a.",
        );
    }

    #[test]
    fn local_symbol_for_local() {
        check_symbol(
            r#"
    //- /lib.rs crate:main deps:foo
    use foo::module::func;
    fn main() {
        func();
    }
    //- /foo/lib.rs crate:foo@CratesIo:0.1.0,https://a.b/foo.git
    pub mod module {
        pub fn func() {
            let x$0 = 2;
        }
    }
    "#,
            "",
        );
    }

    #[test]
    fn global_symbol_for_pub_struct() {
        check_symbol(
            r#"
    //- /lib.rs crate:main
    mod foo;

    fn main() {
        let _bar = foo::Bar { i: 0 };
    }
    //- /foo.rs
    pub struct Bar$0 {
        pub i: i32,
    }
    "#,
            "rust-analyzer cargo main . foo/Bar#",
        );
    }

    #[test]
    fn global_symbol_for_pub_struct_reference() {
        check_symbol(
            r#"
    //- /lib.rs crate:main
    mod foo;

    fn main() {
        let _bar = foo::Bar$0 { i: 0 };
    }
    //- /foo.rs
    pub struct Bar {
        pub i: i32,
    }
    "#,
            "rust-analyzer cargo main . foo/Bar#",
        );
    }
}
<|endoftext|>


<commit_before>#![feature(associated_type_defaults)]
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use macros::* ;
use adapton_sigs::* ;
use collection_traits::*;
use quickcheck::Arbitrary;
use quickcheck::Gen;
use std::num::Zero;

use rand::{Rng,Rand};

#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub enum List<A:Adapton,Elm> {
    Nil,
    Cons(Elm, Box<List<A,Elm>>),
    Tree(Tree<A,Elm,u32>, Dir2, Box<List<A,Elm>>),
    Rc(Rc<List<A,Elm>>),
    Name(A::Name, Box<List<A,Elm>>),
    Art(Art<List<A,Elm>, A::Loc>),
}

#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub enum Tree<A:Adapton,X,Lev> {
    Nil,
    Leaf(X),
    Bin(          Lev, Box<Tree<A,X,Lev>>, Box<Tree<A,X,Lev>> ),
    Name(A::Name, Lev, Box<Tree<A,X,Lev>>, Box<Tree<A,X,Lev>> ),
    Rc(                 Rc<Tree<A,X,Lev>>),
    Art(               Art<Tree<A,X,Lev>, A::Loc>),
}

// TODO: Why Does Adapton have to implement all of these?
//       It's not actually *contained* within the List structure; it cannot be ecountered there.
//       It's only ever present in a negative position (as a function parameter).
impl< A:Adapton+Debug+Hash+PartialEq+Eq+Clone
    , Elm:Debug+Hash+PartialEq+Eq+Clone
    >
    ListT<A,Elm>
    for List<A,Elm>
{
    type List = List<A,Elm>;

    // XXX
    // type Tree = Tree<A,Elm,u32>;

    fn nil  (_:&mut A)                              -> Self::List { List::Nil }
    fn cons (_:&mut A, hd:Elm, tl:Self::List)       -> Self::List { List::Cons(hd,Box::new(tl)) }
    fn name (_:&mut A, nm:A::Name, tl:Self::List)   -> Self::List { List::Name(nm, Box::new(tl)) }
    fn rc   (_:&mut A, rc:Rc<List<A,Elm>>)          -> Self::List { List::Rc(rc) }
    fn art  (_:&mut A, art:Art<List<A,Elm>,A::Loc>) -> Self::List { List::Art(art) }

    // fn tree (_:&mut A, tr:Self::Tree, dir:Dir2, tl:Self::List) -> Self::List {
    //     List::Tree(tr, dir, Box::new(tl))
    // }

    fn elim<Res,Nil,Cons,Name>
        (st:&mut A, list:Self::List, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, Elm, Self::List) -> Res
        ,    Name:FnOnce(&mut A, A::Name, Self::List) -> Res
    {
        match list {
            List::Nil => nilf(st),
            List::Cons(hd, tl) => consf(st, hd, *tl),
            List::Name(nm, tl) => namef(st, nm, *tl),
            List::Rc(rc) => Self::elim(st, (*rc).clone(), nilf, consf, namef),
            List::Art(ref art) => {
                let list = st.force(art);
                Self::elim(st, list, nilf, consf, namef)
            },
            List::Tree(tree, dir, tl) => {
              let res = List::next_leaf_rec(st, tree, dir, *tl) ;
              match res {
                None => nilf(st),
                Some((elm, rest)) => consf(st, elm, rest),
              }
            }
        }
    }

    fn elim_move<Arg,Res,Nil,Cons,Name>
        (st:&mut A, list:Self::List, arg:Arg, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A, Arg) -> Res
        ,    Cons:FnOnce(&mut A, Elm, Self::List, Arg) -> Res
        ,    Name:FnOnce(&mut A, A::Name, Self::List, Arg) -> Res
    {
        match list {
            List::Nil => nilf(st, arg),
            List::Cons(hd, tl) => consf(st, hd, *tl, arg),
            List::Name(nm, tl) => namef(st, nm, *tl, arg),
            List::Rc(rc) => Self::elim_move(st, (*rc).clone(), arg, nilf, consf, namef),
            List::Art(ref art) => {
                let list = st.force(art);
                Self::elim_move(st, list, arg, nilf, consf, namef)
            },
            List::Tree(tree, dir, tl) => {
              let res = List::next_leaf_rec(st, tree, dir, *tl) ;
              match res {
                None => nilf(st, arg),
                Some((elm, rest)) => consf(st, elm, rest, arg),
              }
            }
        }
    }
}

impl< A:Adapton+Debug+Hash+PartialEq+Eq+Clone
    , Elm:Debug+Hash+PartialEq+Eq+Clone
    >
    TreeListT<A,Elm,Tree<A,Elm,u32>>
    for List<A,Elm>
{
  fn tree (_:&mut A, tr:Tree<A,Elm,u32>, dir:Dir2, tl:Self::List) -> Self::List {
    List::Tree(tr, dir, Box::new(tl))
  }

  fn next_leaf_rec (st:&mut A, tree:Tree<A,Elm,u32>, dir:Dir2, rest:Self::List) -> Option<(Elm,Self::List)> {
    unimplemented!()
  }

  fn next_leaf (st:&mut A, tree:Tree<A,Elm,u32>, dir:Dir2) -> Option<(Elm,Self::List)> {
    Self::next_leaf_rec(st, tree, dir, List::Nil)
  }

  fn tree_elim_move<Arg,Res,Treef,Nil,Cons,Name>
    (st:&mut A, list:Self::List, arg:Arg, treef:Treef, nilf:Nil, consf:Cons, namef:Name) -> Res
    where Treef:FnOnce(&mut A, Tree<A,Elm,u32>, Dir2, Self::List, Arg) -> Res
    ,       Nil:FnOnce(&mut A, Arg) -> Res
    ,      Cons:FnOnce(&mut A, Elm, Self::List, Arg) -> Res
    ,     Name:FnOnce(&mut A, A::Name, Self::List, Arg) -> Res
  {
    match list {
      List::Nil => nilf(st, arg),
      List::Cons(hd, tl) => consf(st, hd, *tl, arg),
      List::Name(nm, tl) => namef(st, nm, *tl, arg),
      List::Rc(rc) => Self::elim_move(st, (*rc).clone(), arg, nilf, consf, namef),
      List::Art(ref art) => {
        let list = st.force(art);
        Self::elim_move(st, list, arg, nilf, consf, namef)
      },
      List::Tree(tree, dir, tl) => treef(st, tree, dir, *tl, arg),
    }
  }
}

// TODO: Why Does Adapton have to implement all of these?
//       It's not actually *contained* within the List structure; it cannot be ecountered there.
//       It's only ever present in a negative position (as a function parameter).
impl
    <A:Adapton+Debug+Hash+PartialEq+Eq+Clone
    ,Leaf:     Debug+Hash+PartialEq+Eq+Clone
    >
    TreeT<A,Leaf>
    for Tree<A,Leaf,u32>
{
    type Tree = Tree<A,Leaf,Self::Lev>;
    type Lev  = u32 ;

    //  See Pugh+Teiltelbaum in POPL 1989 for an explanation of this notion of "level":
    fn lev<X:Hash>(x:&X) -> Self::Lev  {
        my_hash(&x).trailing_zeros() as Self::Lev
    }
    fn lev_of_tree (st:&mut A, tree:&Self::Tree) -> Self::Lev {
        Tree::elim_ref(st, tree,
                       |_| 0,
                       |_,leaf| Self::lev(leaf),
                       |_,lev,_,_| lev.clone(),
                       |_,_,lev,_,_| lev.clone(),
                       )
    }
    fn lev_bits () -> Self::Lev { 32 }
    fn lev_zero () -> Self::Lev { 0 }
    fn lev_max_val () -> Self::Lev { u32::max_value() }
    fn lev_add (x:&Self::Lev,y:&Self::Lev) -> Self::Lev { x + y }
    fn lev_inc (x:&Self::Lev) -> Self::Lev { x + 1 }
    fn lev_lte (x:&Self::Lev,y:&Self::Lev) -> bool { x <= y }

    fn nil  (_:&mut A)                                                     -> Self::Tree { Tree::Nil }
    fn leaf (_:&mut A, x:Leaf)                                             -> Self::Tree { Tree::Leaf(x) }
    fn bin  (_:&mut A, lev:Self::Lev, l:Self::Tree, r:Self::Tree)            -> Self::Tree { Tree::Bin(lev,Box::new(l),Box::new(r)) }
    fn name (_:&mut A, nm:A::Name, lev:Self::Lev, l:Self::Tree,r:Self::Tree) -> Self::Tree { Tree::Name(nm, lev, Box::new(l),Box::new(r)) }
    fn rc   (_:&mut A, rc:Rc<Self::Tree>)                                  -> Self::Tree { Tree::Rc(rc) }
    fn art  (_:&mut A, art:Art<Self::Tree,A::Loc>)                         -> Self::Tree { Tree::Art(art) }

    fn elim_move<Arg,Res,NilC,LeafC,BinC,NameC>
        (st:&mut A, tree:Self::Tree, arg:Arg, nil:NilC, leaf:LeafC, bin:BinC, name:NameC) -> Res
        where NilC  : FnOnce(&mut A, Arg) -> Res
        ,     LeafC : FnOnce(&mut A, Leaf, Arg) -> Res
        ,     BinC  : FnOnce(&mut A, Self::Lev,  Self::Tree, Self::Tree, Arg) -> Res
        ,     NameC : FnOnce(&mut A, A::Name, Self::Lev,  Self::Tree, Self::Tree, Arg) -> Res
    {
        match tree {
            Tree::Nil => nil(st,arg),
            Tree::Leaf(x) => leaf(st, x, arg),
            Tree::Bin(b, l, r) => bin(st, b, *l, *r, arg),
            Tree::Name(nm, b, l, r) => name(st, nm, b, *l, *r, arg),
            Tree::Rc(rc) => Self::elim_move(st, (*rc).clone(), arg, nil, leaf, bin, name),
            Tree::Art(art) => {
                let list = st.force(&art);
                Self::elim_move(st, list, arg, nil, leaf, bin, name)
            }
        }
    }

    fn elim<Res,NilC,LeafC,BinC,NameC>
        (st:&mut A, tree:Self::Tree, nil:NilC, leaf:LeafC, bin:BinC, name:NameC) -> Res
        where NilC  : FnOnce(&mut A) -> Res
        ,     LeafC : FnOnce(&mut A, Leaf) -> Res
        ,     BinC  : FnOnce(&mut A, Self::Lev,  Self::Tree, Self::Tree) -> Res
        ,     NameC : FnOnce(&mut A, A::Name, Self::Lev, Self::Tree, Self::Tree) -> Res
    {
        match tree {
            Tree::Nil => nil(st),
            Tree::Leaf(x) => leaf(st, x),
            Tree::Bin(b, l, r) => bin(st, b, *l, *r),
            Tree::Name(nm, b, l, r) => name(st, nm, b, *l, *r),
            Tree::Rc(rc) => Self::elim(st, (*rc).clone(), nil, leaf, bin, name),
            Tree::Art(art) => {
                let list = st.force(&art);
                Self::elim(st, list, nil, leaf, bin, name)
            }
        }
    }

    fn elim_ref<Res,NilC,LeafC,BinC,NameC>
        (st:&mut A, tree:&Self::Tree, nil:NilC, leaf:LeafC, bin:BinC, name:NameC) -> Res
        where NilC  : FnOnce(&mut A) -> Res
        ,     LeafC : FnOnce(&mut A, &Leaf) -> Res
        ,     BinC  : FnOnce(&mut A, &Self::Lev, &Self::Tree, &Self::Tree) -> Res
        ,     NameC : FnOnce(&mut A, &A::Name, &Self::Lev, &Self::Tree, &Self::Tree) -> Res
    {
        match *tree {
            Tree::Nil => nil(st),
            Tree::Leaf(ref x) => leaf(st, x),
            Tree::Bin(ref b, ref l, ref r) => bin(st, b, &*l, &*r),
            Tree::Name(ref nm, ref b, ref l, ref r) => name(st, nm, b, &*l, &*r),
            Tree::Rc(ref rc) => Self::elim_ref(st, &*rc, nil, leaf, bin, name),
            Tree::Art(ref art) => {
                let list = st.force(art);
                Self::elim_ref(st, &list, nil, leaf, bin, name)
            }
        }
    }
}
<commit_msg>used new IH to implement next_leaf_rec<commit_after>#![feature(associated_type_defaults)]
use std::fmt::Debug;
use std::hash::Hash;
use std::rc::Rc;

use macros::* ;
use adapton_sigs::* ;
use collection_traits::*;
use quickcheck::Arbitrary;
use quickcheck::Gen;
use std::num::Zero;

use rand::{Rng,Rand};

#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub enum List<A:Adapton,Elm> {
    Nil,
    Cons(Elm, Box<List<A,Elm>>),
    Tree(Tree<A,Elm,u32>, Dir2, Box<List<A,Elm>>),
    Rc(Rc<List<A,Elm>>),
    Name(A::Name, Box<List<A,Elm>>),
    Art(Art<List<A,Elm>, A::Loc>),
}

#[derive(Debug,PartialEq,Eq,Hash,Clone)]
pub enum Tree<A:Adapton,X,Lev> {
    Nil,
    Leaf(X),
    Bin(          Lev, Box<Tree<A,X,Lev>>, Box<Tree<A,X,Lev>> ),
    Name(A::Name, Lev, Box<Tree<A,X,Lev>>, Box<Tree<A,X,Lev>> ),
    Rc(                 Rc<Tree<A,X,Lev>>),
    Art(               Art<Tree<A,X,Lev>, A::Loc>),
}

// TODO: Why Does Adapton have to implement all of these?
//       It's not actually *contained* within the List structure; it cannot be ecountered there.
//       It's only ever present in a negative position (as a function parameter).
impl< A:Adapton+Debug+Hash+PartialEq+Eq+Clone
    , Elm:Debug+Hash+PartialEq+Eq+Clone
    >
    ListT<A,Elm>
    for List<A,Elm>
{
    type List = List<A,Elm>;

    // XXX
    // type Tree = Tree<A,Elm,u32>;

    fn nil  (_:&mut A)                              -> Self::List { List::Nil }
    fn cons (_:&mut A, hd:Elm, tl:Self::List)       -> Self::List { List::Cons(hd,Box::new(tl)) }
    fn name (_:&mut A, nm:A::Name, tl:Self::List)   -> Self::List { List::Name(nm, Box::new(tl)) }
    fn rc   (_:&mut A, rc:Rc<List<A,Elm>>)          -> Self::List { List::Rc(rc) }
    fn art  (_:&mut A, art:Art<List<A,Elm>,A::Loc>) -> Self::List { List::Art(art) }

    // fn tree (_:&mut A, tr:Self::Tree, dir:Dir2, tl:Self::List) -> Self::List {
    //     List::Tree(tr, dir, Box::new(tl))
    // }

    fn elim<Res,Nil,Cons,Name>
        (st:&mut A, list:Self::List, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A) -> Res
        ,    Cons:FnOnce(&mut A, Elm, Self::List) -> Res
        ,    Name:FnOnce(&mut A, A::Name, Self::List) -> Res
    {
        match list {
            List::Nil => nilf(st),
            List::Cons(hd, tl) => consf(st, hd, *tl),
            List::Name(nm, tl) => namef(st, nm, *tl),
            List::Rc(rc) => Self::elim(st, (*rc).clone(), nilf, consf, namef),
            List::Art(ref art) => {
                let list = st.force(art);
                Self::elim(st, list, nilf, consf, namef)
            },
            List::Tree(tree, dir, tl) => {
              let res = List::next_leaf_rec(st, tree, dir, *tl) ;
              match res {
                None => nilf(st),
                Some((elm, rest)) => consf(st, elm, rest),
              }
            }
        }
    }

    fn elim_move<Arg,Res,Nil,Cons,Name>
        (st:&mut A, list:Self::List, arg:Arg, nilf:Nil, consf:Cons, namef:Name) -> Res
        where Nil:FnOnce(&mut A, Arg) -> Res
        ,    Cons:FnOnce(&mut A, Elm, Self::List, Arg) -> Res
        ,    Name:FnOnce(&mut A, A::Name, Self::List, Arg) -> Res
    {
        match list {
            List::Nil => nilf(st, arg),
            List::Cons(hd, tl) => consf(st, hd, *tl, arg),
            List::Name(nm, tl) => namef(st, nm, *tl, arg),
            List::Rc(rc) => Self::elim_move(st, (*rc).clone(), arg, nilf, consf, namef),
            List::Art(ref art) => {
                let list = st.force(art);
                Self::elim_move(st, list, arg, nilf, consf, namef)
            },
            List::Tree(tree, dir, tl) => {
              let res = List::next_leaf_rec(st, tree, dir, *tl) ;
              match res {
                None => nilf(st, arg),
                Some((elm, rest)) => consf(st, elm, rest, arg),
              }
            }
        }
    }
}

impl< A:Adapton+Debug+Hash+PartialEq+Eq+Clone
    , Elm:Debug+Hash+PartialEq+Eq+Clone
    >
    TreeListT<A,Elm,Tree<A,Elm,u32>>
    for List<A,Elm>
{
  fn tree (_:&mut A, tr:Tree<A,Elm,u32>, dir:Dir2, tl:Self::List) -> Self::List {
    List::Tree(tr, dir, Box::new(tl))
  }

  fn next_leaf_rec (st:&mut A, tree:Tree<A,Elm,u32>, dir:Dir2, rest:Self::List) -> Option<(Elm,Self::List)> {
    match tree {
      Tree::Nil => {
        List::elim_move(st, rest, dir,
                        |_, _| None,
                        |st, x, xs, _  | Some((x,xs)),
                        |st, _, xs, dir| Self::next_leaf_rec(st, Tree::Nil, dir, xs)
                        )
      },
      Tree::Rc(rc) => Self::next_leaf_rec(st, (*rc).clone(), dir, rest),
      Tree::Art(ref art) => {
        let tree = st.force(art);
        Self::next_leaf_rec(st, tree, dir, rest)
      }
      Tree::Leaf(leaf) => {
        Some((leaf, rest))
      }
      Tree::Bin(_,l,r) | Tree::Name(_,_,l,r) => {
        match dir {
          Dir2::Left  => Self::next_leaf_rec(st, *l, Dir2::Left,  List::Tree(*r, Dir2::Left,  Box::new(rest))),
          Dir2::Right => Self::next_leaf_rec(st, *r, Dir2::Right, List::Tree(*l, Dir2::Right, Box::new(rest))),
        }
      },
    }
  }

  fn next_leaf (st:&mut A, tree:Tree<A,Elm,u32>, dir:Dir2) -> Option<(Elm,Self::List)> {
    Self::next_leaf_rec(st, tree, dir, List::Nil)
  }

  fn tree_elim_move<Arg,Res,Treef,Nil,Cons,Name>
    (st:&mut A, list:Self::List, arg:Arg, treef:Treef, nilf:Nil, consf:Cons, namef:Name) -> Res
    where Treef:FnOnce(&mut A, Tree<A,Elm,u32>, Dir2, Self::List, Arg) -> Res
    ,       Nil:FnOnce(&mut A, Arg) -> Res
    ,      Cons:FnOnce(&mut A, Elm, Self::List, Arg) -> Res
    ,     Name:FnOnce(&mut A, A::Name, Self::List, Arg) -> Res
  {
    match list {
      List::Nil => nilf(st, arg),
      List::Cons(hd, tl) => consf(st, hd, *tl, arg),
      List::Name(nm, tl) => namef(st, nm, *tl, arg),
      List::Rc(rc) => Self::elim_move(st, (*rc).clone(), arg, nilf, consf, namef),
      List::Art(ref art) => {
        let list = st.force(art);
        Self::elim_move(st, list, arg, nilf, consf, namef)
      },
      List::Tree(tree, dir, tl) => treef(st, tree, dir, *tl, arg),
    }
  }
}

// TODO: Why Does Adapton have to implement all of these?
//       It's not actually *contained* within the List structure; it cannot be ecountered there.
//       It's only ever present in a negative position (as a function parameter).
impl
    <A:Adapton+Debug+Hash+PartialEq+Eq+Clone
    ,Leaf:     Debug+Hash+PartialEq+Eq+Clone
    >
    TreeT<A,Leaf>
    for Tree<A,Leaf,u32>
{
    type Tree = Tree<A,Leaf,Self::Lev>;
    type Lev  = u32 ;

    //  See Pugh+Teiltelbaum in POPL 1989 for an explanation of this notion of "level":
    fn lev<X:Hash>(x:&X) -> Self::Lev  {
        my_hash(&x).trailing_zeros() as Self::Lev
    }
    fn lev_of_tree (st:&mut A, tree:&Self::Tree) -> Self::Lev {
        Tree::elim_ref(st, tree,
                       |_| 0,
                       |_,leaf| Self::lev(leaf),
                       |_,lev,_,_| lev.clone(),
                       |_,_,lev,_,_| lev.clone(),
                       )
    }
    fn lev_bits () -> Self::Lev { 32 }
    fn lev_zero () -> Self::Lev { 0 }
    fn lev_max_val () -> Self::Lev { u32::max_value() }
    fn lev_add (x:&Self::Lev,y:&Self::Lev) -> Self::Lev { x + y }
    fn lev_inc (x:&Self::Lev) -> Self::Lev { x + 1 }
    fn lev_lte (x:&Self::Lev,y:&Self::Lev) -> bool { x <= y }

    fn nil  (_:&mut A)                                                     -> Self::Tree { Tree::Nil }
    fn leaf (_:&mut A, x:Leaf)                                             -> Self::Tree { Tree::Leaf(x) }
    fn bin  (_:&mut A, lev:Self::Lev, l:Self::Tree, r:Self::Tree)            -> Self::Tree { Tree::Bin(lev,Box::new(l),Box::new(r)) }
    fn name (_:&mut A, nm:A::Name, lev:Self::Lev, l:Self::Tree,r:Self::Tree) -> Self::Tree { Tree::Name(nm, lev, Box::new(l),Box::new(r)) }
    fn rc   (_:&mut A, rc:Rc<Self::Tree>)                                  -> Self::Tree { Tree::Rc(rc) }
    fn art  (_:&mut A, art:Art<Self::Tree,A::Loc>)                         -> Self::Tree { Tree::Art(art) }

    fn elim_move<Arg,Res,NilC,LeafC,BinC,NameC>
        (st:&mut A, tree:Self::Tree, arg:Arg, nil:NilC, leaf:LeafC, bin:BinC, name:NameC) -> Res
        where NilC  : FnOnce(&mut A, Arg) -> Res
        ,     LeafC : FnOnce(&mut A, Leaf, Arg) -> Res
        ,     BinC  : FnOnce(&mut A, Self::Lev,  Self::Tree, Self::Tree, Arg) -> Res
        ,     NameC : FnOnce(&mut A, A::Name, Self::Lev,  Self::Tree, Self::Tree, Arg) -> Res
    {
        match tree {
            Tree::Nil => nil(st,arg),
            Tree::Leaf(x) => leaf(st, x, arg),
            Tree::Bin(b, l, r) => bin(st, b, *l, *r, arg),
            Tree::Name(nm, b, l, r) => name(st, nm, b, *l, *r, arg),
            Tree::Rc(rc) => Self::elim_move(st, (*rc).clone(), arg, nil, leaf, bin, name),
            Tree::Art(art) => {
                let list = st.force(&art);
                Self::elim_move(st, list, arg, nil, leaf, bin, name)
            }
        }
    }

    fn elim<Res,NilC,LeafC,BinC,NameC>
        (st:&mut A, tree:Self::Tree, nil:NilC, leaf:LeafC, bin:BinC, name:NameC) -> Res
        where NilC  : FnOnce(&mut A) -> Res
        ,     LeafC : FnOnce(&mut A, Leaf) -> Res
        ,     BinC  : FnOnce(&mut A, Self::Lev,  Self::Tree, Self::Tree) -> Res
        ,     NameC : FnOnce(&mut A, A::Name, Self::Lev, Self::Tree, Self::Tree) -> Res
    {
        match tree {
            Tree::Nil => nil(st),
            Tree::Leaf(x) => leaf(st, x),
            Tree::Bin(b, l, r) => bin(st, b, *l, *r),
            Tree::Name(nm, b, l, r) => name(st, nm, b, *l, *r),
            Tree::Rc(rc) => Self::elim(st, (*rc).clone(), nil, leaf, bin, name),
            Tree::Art(art) => {
                let list = st.force(&art);
                Self::elim(st, list, nil, leaf, bin, name)
            }
        }
    }

    fn elim_ref<Res,NilC,LeafC,BinC,NameC>
        (st:&mut A, tree:&Self::Tree, nil:NilC, leaf:LeafC, bin:BinC, name:NameC) -> Res
        where NilC  : FnOnce(&mut A) -> Res
        ,     LeafC : FnOnce(&mut A, &Leaf) -> Res
        ,     BinC  : FnOnce(&mut A, &Self::Lev, &Self::Tree, &Self::Tree) -> Res
        ,     NameC : FnOnce(&mut A, &A::Name, &Self::Lev, &Self::Tree, &Self::Tree) -> Res
    {
        match *tree {
            Tree::Nil => nil(st),
            Tree::Leaf(ref x) => leaf(st, x),
            Tree::Bin(ref b, ref l, ref r) => bin(st, b, &*l, &*r),
            Tree::Name(ref nm, ref b, ref l, ref r) => name(st, nm, b, &*l, &*r),
            Tree::Rc(ref rc) => Self::elim_ref(st, &*rc, nil, leaf, bin, name),
            Tree::Art(ref art) => {
                let list = st.force(art);
                Self::elim_ref(st, &list, nil, leaf, bin, name)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::convert::TryInto;
use std::fmt::{Binary, Debug, Display, Error, Formatter, LowerHex, Octal, UpperHex};
use std::ops::{Index, IndexMut};

/// Maximum size of a [`Memory`](struct.Memory.html) block in
/// [`Word`](struct.Word.html)s
///
/// = 2^24
pub const MAX_MEM_SIZE: u32 = 16_777_216;

pub const DEFAULT_MEM_SIZE: u32 = 4096;

#[derive(Clone, Copy, Default)]
pub struct Word {
    // little endian
    val: [u8; 3],
}

impl Word {
    pub fn new() -> Self {
        Word { val: [0; 3] }
    }

    pub fn set(&mut self, val: u32) {
        let bytes = val.to_le_bytes();
        self.val[0] = bytes[0];
        self.val[1] = bytes[1];
        self.val[2] = bytes[2];
    }
}

impl Binary for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        Binary::fmt(&u32::from(*self), f)
    }
}

impl Debug for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        f.debug_struct("Word")
            .field("val", &format_args!("[{:?}, {:?}, {:?}]",
                                        &self.val[2],
                                        &self.val[1],
                                        &self.val[0]))
            .finish()
    }
}

impl Display for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let tmp = u32::from(*self).to_string();
        f.pad_integral(true, "", &tmp)
    }
}

impl LowerHex for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        LowerHex::fmt(&u32::from(*self), f)
    }
}

impl Octal for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        Octal::fmt(&u32::from(*self), f)
    }
}

impl UpperHex for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        UpperHex::fmt(&u32::from(*self), f)
    }
}

impl From<Word> for u32 {
    fn from(word: Word) -> u32 {
        word.val[0] as u32
            + ((word.val[1] as u32) << 8)
            + ((word.val[2] as u32) << 16)
    }
}

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

    #[test]
    fn can_set_word() {
        let mut w = Word::new();
        assert_eq!(0, u32::from(w));
        w.set(0x5a);
        assert_eq!(0x5a, u32::from(w));
    }
}

#[derive(Debug)]
pub struct Memory {
    data: Vec<Word>,
    size: u32,
}

impl Memory {
    /// Creates a new `Memory` block of the given `size`.
    ///
    /// The maximum size of a `Memory` block is
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html). If `size` is greater
    /// than that, the created `Memory` block will be
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html) long.
    pub fn new(size: u32) -> Self {
        let size = if size > MAX_MEM_SIZE {
            MAX_MEM_SIZE
        } else {
            size
        };
        Memory { data: vec![Word::default(); size.try_into().unwrap()], size }
    }
}

impl Index<usize> for Memory {
    type Output = Word;

    fn index(&self, i: usize) -> &Self::Output {
        &self.data[i]
    }
}

impl IndexMut<usize> for Memory {
    fn index_mut(&mut self, i: usize) -> &mut Self::Output {
        &mut self.data[i]
    }
}
<commit_msg>Add size() to Memory<commit_after>use std::convert::TryInto;
use std::fmt::{Binary, Debug, Display, Error, Formatter, LowerHex, Octal, UpperHex};
use std::ops::{Index, IndexMut};

/// Maximum size of a [`Memory`](struct.Memory.html) block in
/// [`Word`](struct.Word.html)s
///
/// = 2^24
pub const MAX_MEM_SIZE: u32 = 16_777_216;

pub const DEFAULT_MEM_SIZE: u32 = 4096;

#[derive(Clone, Copy, Default)]
pub struct Word {
    // little endian
    val: [u8; 3],
}

impl Word {
    pub fn new() -> Self {
        Word { val: [0; 3] }
    }

    pub fn set(&mut self, val: u32) {
        let bytes = val.to_le_bytes();
        self.val[0] = bytes[0];
        self.val[1] = bytes[1];
        self.val[2] = bytes[2];
    }
}

impl Binary for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        Binary::fmt(&u32::from(*self), f)
    }
}

impl Debug for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        f.debug_struct("Word")
            .field("val", &format_args!("[{:?}, {:?}, {:?}]",
                                        &self.val[2],
                                        &self.val[1],
                                        &self.val[0]))
            .finish()
    }
}

impl Display for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let tmp = u32::from(*self).to_string();
        f.pad_integral(true, "", &tmp)
    }
}

impl LowerHex for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        LowerHex::fmt(&u32::from(*self), f)
    }
}

impl Octal for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        Octal::fmt(&u32::from(*self), f)
    }
}

impl UpperHex for Word {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        UpperHex::fmt(&u32::from(*self), f)
    }
}

impl From<Word> for u32 {
    fn from(word: Word) -> u32 {
        word.val[0] as u32
            + ((word.val[1] as u32) << 8)
            + ((word.val[2] as u32) << 16)
    }
}

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

    #[test]
    fn can_set_word() {
        let mut w = Word::new();
        assert_eq!(0, u32::from(w));
        w.set(0x5a);
        assert_eq!(0x5a, u32::from(w));
    }
}

#[derive(Debug)]
pub struct Memory {
    data: Vec<Word>,
    size: u32,
}

impl Memory {
    /// Creates a new `Memory` block of the given `size`.
    ///
    /// The maximum size of a `Memory` block is
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html). If `size` is greater
    /// than that, the created `Memory` block will be
    /// [`MAX_MEM_SIZE`](constant.MAX_MEM_SIZE.html) long.
    pub fn new(size: u32) -> Self {
        let size = if size > MAX_MEM_SIZE {
            MAX_MEM_SIZE
        } else {
            size
        };
        Memory { data: vec![Word::default(); size.try_into().unwrap()], size }
    }

    pub fn size(&self) -> u32 {
        self.size
    }
}

impl Index<usize> for Memory {
    type Output = Word;

    fn index(&self, i: usize) -> &Self::Output {
        &self.data[i]
    }
}

impl IndexMut<usize> for Memory {
    fn index_mut(&mut self, i: usize) -> &mut Self::Output {
        &mut self.data[i]
    }
}
<|endoftext|>


<commit_before>use CHIP8_HEIGHT;
use CHIP8_WIDTH;
use CHIP8_RAM;

pub struct OutputState<'a> {
    pub vram: &'a [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    pub vram_changed: bool,
    pub beep: bool,
}

pub struct Processor {
    vram: [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    vram_changed: bool,
    ram: [u8; CHIP8_RAM],
    stack: [usize; 16],
    v: [u8; 16],
    i: usize,
    pc: usize,
    sp: usize,
    delay_timer: u8,
    sound_timer: u8,
}

impl Processor {
    pub fn new() -> Self {
        Processor {
            vram: [[0; CHIP8_WIDTH]; CHIP8_HEIGHT],
            vram_changed: false,
            ram: [0; CHIP8_RAM],
            stack: [0; 16],
            v: [0; 16],
            i: 0,
            pc: 0x200,
            sp: 0,
            delay_timer: 0,
            sound_timer: 0,
        }
    }

    pub fn tick(&mut self, keypad: &[bool; 16]) -> OutputState {
        self.vram_changed = false;
        let opcode = self.get_opcode();
        self.run_opcode(opcode);
        OutputState {
            vram: &self.vram,
            vram_changed: self.vram_changed,
            beep: self.sound_timer > 0,
        }
    }

    fn get_opcode(&self) -> u16 {
        (self.ram[self.pc] as u16) << 8 | (self.ram[self.pc + 1] as u16)
    }

    fn run_opcode(&mut self, opcode: u16) {

        let nibbles = (
            (opcode & 0xF000) >> 12 as u8,
            (opcode & 0x0F00) >> 8 as u8,
            (opcode & 0x00F0) >> 4 as u8,
            (opcode & 0x000F) as u8,
        );

        let nnn = (opcode & 0x0FFF) as usize;
        let kk = (opcode & 0x00FF) as u8;
        let x = nibbles.1 as usize;
        let y = nibbles.2 as usize;
        let n = nibbles.3 as u8;

        match nibbles {
            (0x00, 0x00, 0x0e, 0x00) => self.op_00e0(),
            (0x00, 0x00, 0x0e, 0x0e) => self.op_00ee(),
            (0x01, _, _, _) => self.op_1nnn(nnn),
            (0x02, _, _, _) => self.op_2nnn(nnn),
            (0x03, _, _, _) => self.op_3xkk(x, kk),
            (0x04, _, _, _) => self.op_4xkk(x, kk),
            (0x05, _, _, 0x00) => self.op_5xy0(x, y),
            (0x06, _, _, _) => self.op_6xkk(x, kk),
            (0x07, _, _, _) => self.op_7xkk(x, kk),
            (0x08, _, _, 0x00) => self.op_8xy0(x, y),
            (0x08, _, _, 0x01) => self.op_8xy1(x, y),
            (0x08, _, _, 0x02) => self.op_8xy2(x, y),
            (0x08, _, _, 0x03) => self.op_8xy3(x, y),
            (0x08, _, _, 0x04) => self.op_8xy4(x, y),
            (0x08, _, _, 0x05) => self.op_8xy5(x, y),
            (0x08, _, _, 0x06) => self.op_8xy6(x, y),
            (0x08, _, _, 0x07) => self.op_8xy7(x, y),
            (0x08, _, _, 0x0e) => self.op_8xye(x, y),
            (0x09, _, _, 0x00) => self.op_9xy0(x, y),
            (0x0a, _, _, _) => self.op_annn(nnn),
            (0x0b, _, _, _) => self.op_bnnn(nnn),
            (0x0c, _, _, _) => self.op_cxkk(x, kk),
            (0x0d, _, _, _) => self.op_dxyn(x, y, n),
            (0x0e, _, 0x09, 0x0e) => self.op_ex9e(x),
            (0x0e, _, 0x0a, 0x01) => self.op_exa1(x),
            (0x0f, _, 0x00, 0x07) => self.op_fx07(x),
            (0x0f, _, 0x00, 0x0a) => self.op_fx0a(x),
            (0x0f, _, 0x01, 0x05) => self.op_fx15(x),
            (0x0f, _, 0x01, 0x08) => self.op_fx18(x),
            (0x0f, _, 0x01, 0x0e) => self.op_fx1e(x),
            (0x0f, _, 0x02, 0x09) => self.op_fx29(x),
            (0x0f, _, 0x03, 0x03) => self.op_fx33(x),
            (0x0f, _, 0x05, 0x05) => self.op_fx55(x),
            (0x0f, _, 0x06, 0x05) => self.op_fx65(x),
            _ => return,
        }

    }
    // CLS: Clear the display.
    fn op_00e0(&mut self) {
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                self.vram[y][x] = 0;
            }
        }
        self.vram_changed = true;
    }

    // RET:  Return from a subroutine.
    fn op_00ee(&mut self) {
        self.sp -= 1;
        self.pc = self.stack[self.sp];
    }

    // JP addr
    fn op_1nnn(&mut self, nnn: usize) {
        println!("hello: {:X}", nnn);
        self.pc = nnn;
    }

    // CALL addr
    fn op_2nnn(&mut self, nnn: usize) {
        self.stack[self.sp] = self.pc;
        self.sp += 1;
        self.pc = nnn;
    }

    // SE Vx, byte
    fn op_3xkk(&mut self, x: usize, kk: u8) {}
    // SNE Vx, byte
    fn op_4xkk(&mut self, x: usize, kk: u8) {}
    // SE Vx, Vy
    fn op_5xy0(&mut self, x: usize, y: usize) {}
    // LD Vx, byte
    fn op_6xkk(&mut self, x: usize, kk: u8) {}
    // ADD Vx, byte
    fn op_7xkk(&mut self, x: usize, kk: u8) {}
    // LD Vx, Vy
    fn op_8xy0(&mut self, x: usize, y: usize) {}
    // OR Vx, Vy
    fn op_8xy1(&mut self, x: usize, y: usize) {}
    // AND Vx, Vy
    fn op_8xy2(&mut self, x: usize, y: usize) {}
    // XOR Vx, Vy
    fn op_8xy3(&mut self, x: usize, y: usize) {}
    // ADD Vx, Vy
    fn op_8xy4(&mut self, x: usize, y: usize) {}
    // SUB Vx, Vy
    fn op_8xy5(&mut self, x: usize, y: usize) {}
    // SHR Vx {, Vy}
    fn op_8xy6(&mut self, x: usize, y: usize) {}
    // SUBN Vx, Vy
    fn op_8xy7(&mut self, x: usize, y: usize) {}
    // SHL Vx {, Vy}
    fn op_8xye(&mut self, x: usize, y: usize) {}
    // SNE Vx, Vy
    fn op_9xy0(&mut self, x: usize, y: usize) {}
    // LD I, addr
    fn op_annn(&mut self, nnn: usize) {}
    // JP V0, addr
    fn op_bnnn(&mut self, nnn: usize) {}
    // RND Vx, byte
    fn op_cxkk(&mut self, x: usize, kk: u8) {}
    // DRW Vx, Vy, nibble
    fn op_dxyn(&mut self, x: usize, y: usize, n: u8) {}
    // SKP Vx
    fn op_ex9e(&mut self, x: usize) {}
    // SKNP Vx
    fn op_exa1(&mut self, x: usize) {}
    // LD Vx, DT
    fn op_fx07(&mut self, x: usize) {}
    // LD Vx, K
    fn op_fx0a(&mut self, x: usize) {}
    // LD DT, Vx
    fn op_fx15(&mut self, x: usize) {}
    // LD ST, Vx
    fn op_fx18(&mut self, x: usize) {}
    // ADD I, Vx
    fn op_fx1e(&mut self, x: usize) {}
    // LD F, Vx
    fn op_fx29(&mut self, x: usize) {}
    // LD B, Vx
    fn op_fx33(&mut self, x: usize) {}
    // LD [I], Vx
    fn op_fx55(&mut self, x: usize) {}
    // LD Vx, [I]
    fn op_fx65(&mut self, x: usize) {}
}

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

    #[test]
    fn test_initial_state() {
        let processor = Processor::new();
        assert_eq!(processor.sp, 0);
        assert_eq!(processor.stack, [0; 16]);
    }

    // CLS
    #[test]
    fn test_op_00e0() {
        let mut processor = Processor::new();
        processor.vram = [[128; CHIP8_WIDTH]; CHIP8_HEIGHT];
        processor.run_opcode(0x00e0);
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                assert_eq!(processor.vram[y][x], 0);
            }
        }
    }

    // RET
    #[test]
    fn test_op_00ee() {
        let mut processor = Processor::new();
        processor.sp = 5;
        processor.stack[4] = 0x6666;
        processor.run_opcode(0x00ee);
        assert_eq!(processor.sp, 4);
        assert_eq!(processor.pc, 0x6666);
    }

    // JP
    #[test]
    fn test_op_1nnn() {
        let mut processor = Processor::new();
        processor.run_opcode(0x1666);
        assert_eq!(processor.pc, 0x0666);
    }

    // CALL
    #[test]
    fn test_op_2nnn() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.run_opcode(0x2666);
        assert_eq!(processor.pc, 0x0666);
        assert_eq!(processor.sp, 1);
        assert_eq!(processor.stack[0], 0x400);
    }

}
<commit_msg>implement SE Vx<commit_after>use CHIP8_HEIGHT;
use CHIP8_WIDTH;
use CHIP8_RAM;

const OPCODE_SIZE: usize = 2;

pub struct OutputState<'a> {
    pub vram: &'a [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    pub vram_changed: bool,
    pub beep: bool,
}

pub struct Processor {
    vram: [[u8; CHIP8_WIDTH]; CHIP8_HEIGHT],
    vram_changed: bool,
    ram: [u8; CHIP8_RAM],
    stack: [usize; 16],
    v: [u8; 16],
    i: usize,
    pc: usize,
    sp: usize,
    delay_timer: u8,
    sound_timer: u8,
}

impl Processor {
    pub fn new() -> Self {
        Processor {
            vram: [[0; CHIP8_WIDTH]; CHIP8_HEIGHT],
            vram_changed: false,
            ram: [0; CHIP8_RAM],
            stack: [0; 16],
            v: [0; 16],
            i: 0,
            pc: 0x200,
            sp: 0,
            delay_timer: 0,
            sound_timer: 0,
        }
    }

    pub fn tick(&mut self, keypad: &[bool; 16]) -> OutputState {
        self.vram_changed = false;
        let opcode = self.get_opcode();
        self.run_opcode(opcode);
        OutputState {
            vram: &self.vram,
            vram_changed: self.vram_changed,
            beep: self.sound_timer > 0,
        }
    }

    fn get_opcode(&self) -> u16 {
        (self.ram[self.pc] as u16) << 8 | (self.ram[self.pc + 1] as u16)
    }

    fn run_opcode(&mut self, opcode: u16) {

        let nibbles = (
            (opcode & 0xF000) >> 12 as u8,
            (opcode & 0x0F00) >> 8 as u8,
            (opcode & 0x00F0) >> 4 as u8,
            (opcode & 0x000F) as u8,
        );

        let nnn = (opcode & 0x0FFF) as usize;
        let kk = (opcode & 0x00FF) as u8;
        let x = nibbles.1 as usize;
        let y = nibbles.2 as usize;
        let n = nibbles.3 as u8;

        match nibbles {
            (0x00, 0x00, 0x0e, 0x00) => self.op_00e0(),
            (0x00, 0x00, 0x0e, 0x0e) => self.op_00ee(),
            (0x01, _, _, _) => self.op_1nnn(nnn),
            (0x02, _, _, _) => self.op_2nnn(nnn),
            (0x03, _, _, _) => self.op_3xkk(x, kk),
            (0x04, _, _, _) => self.op_4xkk(x, kk),
            (0x05, _, _, 0x00) => self.op_5xy0(x, y),
            (0x06, _, _, _) => self.op_6xkk(x, kk),
            (0x07, _, _, _) => self.op_7xkk(x, kk),
            (0x08, _, _, 0x00) => self.op_8xy0(x, y),
            (0x08, _, _, 0x01) => self.op_8xy1(x, y),
            (0x08, _, _, 0x02) => self.op_8xy2(x, y),
            (0x08, _, _, 0x03) => self.op_8xy3(x, y),
            (0x08, _, _, 0x04) => self.op_8xy4(x, y),
            (0x08, _, _, 0x05) => self.op_8xy5(x, y),
            (0x08, _, _, 0x06) => self.op_8xy6(x, y),
            (0x08, _, _, 0x07) => self.op_8xy7(x, y),
            (0x08, _, _, 0x0e) => self.op_8xye(x, y),
            (0x09, _, _, 0x00) => self.op_9xy0(x, y),
            (0x0a, _, _, _) => self.op_annn(nnn),
            (0x0b, _, _, _) => self.op_bnnn(nnn),
            (0x0c, _, _, _) => self.op_cxkk(x, kk),
            (0x0d, _, _, _) => self.op_dxyn(x, y, n),
            (0x0e, _, 0x09, 0x0e) => self.op_ex9e(x),
            (0x0e, _, 0x0a, 0x01) => self.op_exa1(x),
            (0x0f, _, 0x00, 0x07) => self.op_fx07(x),
            (0x0f, _, 0x00, 0x0a) => self.op_fx0a(x),
            (0x0f, _, 0x01, 0x05) => self.op_fx15(x),
            (0x0f, _, 0x01, 0x08) => self.op_fx18(x),
            (0x0f, _, 0x01, 0x0e) => self.op_fx1e(x),
            (0x0f, _, 0x02, 0x09) => self.op_fx29(x),
            (0x0f, _, 0x03, 0x03) => self.op_fx33(x),
            (0x0f, _, 0x05, 0x05) => self.op_fx55(x),
            (0x0f, _, 0x06, 0x05) => self.op_fx65(x),
            _ => return,
        }

    }
    // CLS: Clear the display.
    fn op_00e0(&mut self) {
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                self.vram[y][x] = 0;
            }
        }
        self.vram_changed = true;
        self.pc += OPCODE_SIZE;
    }

    // RET:  Return from a subroutine.
    fn op_00ee(&mut self) {
        self.sp -= 1;
        self.pc = self.stack[self.sp];
    }

    // JP addr
    fn op_1nnn(&mut self, nnn: usize) {
        self.pc = nnn;
    }

    // CALL addr
    fn op_2nnn(&mut self, nnn: usize) {
        self.stack[self.sp] = self.pc;
        self.sp += 1;
        self.pc = nnn;
    }

    // SE Vx, byte:  Skip next instruction if Vx = kk.
    fn op_3xkk(&mut self, x: usize, kk: u8) {
        self.pc += OPCODE_SIZE * (if self.v[x] == kk { 2 } else { 1 });
    }

    // SNE Vx, byte
    fn op_4xkk(&mut self, x: usize, kk: u8) {}
    // SE Vx, Vy
    fn op_5xy0(&mut self, x: usize, y: usize) {}
    // LD Vx, byte
    fn op_6xkk(&mut self, x: usize, kk: u8) {}
    // ADD Vx, byte
    fn op_7xkk(&mut self, x: usize, kk: u8) {}
    // LD Vx, Vy
    fn op_8xy0(&mut self, x: usize, y: usize) {}
    // OR Vx, Vy
    fn op_8xy1(&mut self, x: usize, y: usize) {}
    // AND Vx, Vy
    fn op_8xy2(&mut self, x: usize, y: usize) {}
    // XOR Vx, Vy
    fn op_8xy3(&mut self, x: usize, y: usize) {}
    // ADD Vx, Vy
    fn op_8xy4(&mut self, x: usize, y: usize) {}
    // SUB Vx, Vy
    fn op_8xy5(&mut self, x: usize, y: usize) {}
    // SHR Vx {, Vy}
    fn op_8xy6(&mut self, x: usize, y: usize) {}
    // SUBN Vx, Vy
    fn op_8xy7(&mut self, x: usize, y: usize) {}
    // SHL Vx {, Vy}
    fn op_8xye(&mut self, x: usize, y: usize) {}
    // SNE Vx, Vy
    fn op_9xy0(&mut self, x: usize, y: usize) {}
    // LD I, addr
    fn op_annn(&mut self, nnn: usize) {}
    // JP V0, addr
    fn op_bnnn(&mut self, nnn: usize) {}
    // RND Vx, byte
    fn op_cxkk(&mut self, x: usize, kk: u8) {}
    // DRW Vx, Vy, nibble
    fn op_dxyn(&mut self, x: usize, y: usize, n: u8) {}
    // SKP Vx
    fn op_ex9e(&mut self, x: usize) {}
    // SKNP Vx
    fn op_exa1(&mut self, x: usize) {}
    // LD Vx, DT
    fn op_fx07(&mut self, x: usize) {}
    // LD Vx, K
    fn op_fx0a(&mut self, x: usize) {}
    // LD DT, Vx
    fn op_fx15(&mut self, x: usize) {}
    // LD ST, Vx
    fn op_fx18(&mut self, x: usize) {}
    // ADD I, Vx
    fn op_fx1e(&mut self, x: usize) {}
    // LD F, Vx
    fn op_fx29(&mut self, x: usize) {}
    // LD B, Vx
    fn op_fx33(&mut self, x: usize) {}
    // LD [I], Vx
    fn op_fx55(&mut self, x: usize) {}
    // LD Vx, [I]
    fn op_fx65(&mut self, x: usize) {}
}

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

    #[test]
    fn test_initial_state() {
        let processor = Processor::new();
        assert_eq!(processor.sp, 0);
        assert_eq!(processor.stack, [0; 16]);
    }

    // CLS
    #[test]
    fn test_op_00e0() {
        let mut processor = Processor::new();
        processor.vram = [[128; CHIP8_WIDTH]; CHIP8_HEIGHT];
        processor.pc = 0x600;
        processor.run_opcode(0x00e0);
        for y in 0..CHIP8_HEIGHT {
            for x in 0..CHIP8_WIDTH {
                assert_eq!(processor.vram[y][x], 0);
            }
        }
        assert_eq!(processor.pc, 0x600 + OPCODE_SIZE);
    }

    // RET
    #[test]
    fn test_op_00ee() {
        let mut processor = Processor::new();
        processor.sp = 5;
        processor.stack[4] = 0x6666;
        processor.run_opcode(0x00ee);
        assert_eq!(processor.sp, 4);
        assert_eq!(processor.pc, 0x6666);
    }

    // JP
    #[test]
    fn test_op_1nnn() {
        let mut processor = Processor::new();
        processor.run_opcode(0x1666);
        assert_eq!(processor.pc, 0x0666);
    }

    // CALL
    #[test]
    fn test_op_2nnn() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.run_opcode(0x2666);
        assert_eq!(processor.pc, 0x0666);
        assert_eq!(processor.sp, 1);
        assert_eq!(processor.stack[0], 0x400);
    }

    // SE VX, byte
    #[test]
    fn test_op_3xkk() {
        let mut processor = Processor::new();
        processor.pc = 0x400;
        processor.v[5] = 0xfb;
        processor.run_opcode(0x35fb);
        assert_eq!(processor.pc, 0x0400 + (2 * OPCODE_SIZE));
    }

}
<|endoftext|>


<commit_before>extern crate bip_disk;
extern crate bip_metainfo;
extern crate futures;

use bip_disk::{DiskManagerBuilder, IDiskMessage, ODiskMessage};
use bip_disk::fs::NativeFileSystem;
use bip_metainfo::MetainfoFile;
use futures::{Stream, Sink, Future};

fn main() {
    let metainfo_bytes = include_bytes!("C://Users//GG/Desktop//Test.torrent");
    let metainfo_file = MetainfoFile::from_bytes(&metainfo_bytes[..]).unwrap();

    let native_fs = NativeFileSystem::with_directory("C://Users//GG//Downloads");
    let disk_manager = DiskManagerBuilder::new()
        .build(native_fs);

    let (send, recv) = disk_manager.split();

    send.send(IDiskMessage::AddTorrent(metainfo_file)).wait().unwrap();

    for out_msg in recv.wait() {
        match out_msg.unwrap() {
            ODiskMessage::TorrentAdded(hash) => println!("Torrent Added: {:?}", hash),
            ODiskMessage::FoundGoodPiece(_, index) => println!("Found {} As Good Piece", index),
            ODiskMessage::TorrentError(_, err) => println!("Got Torrent Error: {:?}", err),
            _ => panic!("ASD")
        }
    }
}<commit_msg>Comment Out Example<commit_after>extern crate bip_disk;
extern crate bip_metainfo;
extern crate futures;

use bip_disk::{DiskManagerBuilder, IDiskMessage, ODiskMessage};
use bip_disk::fs::NativeFileSystem;
use bip_metainfo::MetainfoFile;
use futures::{Stream, Sink, Future};

fn main() {
    /*
    let metainfo_bytes = include_bytes!("C://Users//GG/Desktop//Test.torrent");
    let metainfo_file = MetainfoFile::from_bytes(&metainfo_bytes[..]).unwrap();

    let native_fs = NativeFileSystem::with_directory("C://Users//GG//Downloads");
    let disk_manager = DiskManagerBuilder::new()
        .build(native_fs);

    let (send, recv) = disk_manager.split();

    send.send(IDiskMessage::AddTorrent(metainfo_file)).wait().unwrap();

    for out_msg in recv.wait() {
        match out_msg.unwrap() {
            ODiskMessage::TorrentAdded(hash) => println!("Torrent Added: {:?}", hash),
            ODiskMessage::FoundGoodPiece(_, index) => println!("Found {} As Good Piece", index),
            ODiskMessage::TorrentError(_, err) => println!("Got Torrent Error: {:?}", err),
            _ => panic!("ASD")
        }
    }
    */
}<|endoftext|>


<commit_before>#![feature(test)]

/*#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 1, y: 2 };
    let serialized = serde_json::to_string(&point).unwrap();

    println!("{}", serialized);

    let deserialized: Point = serde_json::from_str(&serialized).unwrap();

    println!("{:?}", deserialized);
}

*/
extern crate serde_json;

extern crate bitcrust_lib;

extern crate byteorder;

use std::io::{Read, BufReader};
use std::fs::File;
use byteorder::{ReadBytesExt, LittleEndian};
use std::result::Result;
use bitcrust_lib::{decode,encode};
use bitcrust_lib::block;

fn read_block(rdr: &mut Read) -> Result<block::Block, std::io::Error> {
    let _          = try!(rdr.read_u32::<LittleEndian>());
    let length     = try!(rdr.read_u32::<LittleEndian>());
    let mut buffer = vec![0; length as usize];

    try!(rdr.read_exact(&mut buffer));
    Ok(decode(&buffer).unwrap())

}

fn main() {

    let f = File::open("/home/tomas/.bitcoin/blocks/blk00020.dat").unwrap();
    let mut rdr = BufReader::new(f);

    for _ in 0..10000 {
        let blk = read_block(&mut rdr).unwrap();

        //println!("{:?}", blk);

        let serialized = serde_json::to_string(&blk.header).unwrap();

        //println!("{}", serialized);

        //let hex = encode(&blk).unwrap();

        //println!("Block: {:?}", hex);
    }

}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;
    use std::fs::File;
    use std::io::{Read, BufReader, Error, Cursor};
   // use rustc_serialize::hex::ToHex;

    #[test]
    fn test_read_block() {
        let f = File::open("/home/tomas/.bitcoin/blocks/blk00000.dat").unwrap();
        let mut rdr = BufReader::new(f);
        let blk = super::read_block(&mut rdr).unwrap();

        let serialized = super::serde_json::to_string(&blk.header).unwrap();

    }

    #[bench]
    fn bench_read(b: &mut Bencher) {
        b.iter(|| {
            let f = File::open("/home/tomas/.bitcoin/blocks/blk00020.dat").unwrap();
            let mut rdr = BufReader::new(f);
            let _ = super::read_block(&mut rdr).unwrap();

        });
    }
}<commit_msg>Disable local tests<commit_after>#![feature(test)]

/*#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 1, y: 2 };
    let serialized = serde_json::to_string(&point).unwrap();

    println!("{}", serialized);

    let deserialized: Point = serde_json::from_str(&serialized).unwrap();

    println!("{:?}", deserialized);
}

*/
extern crate serde_json;

extern crate bitcrust_lib;

extern crate byteorder;

use std::io::{Read, BufReader};
use std::fs::File;
use byteorder::{ReadBytesExt, LittleEndian};
use std::result::Result;
use bitcrust_lib::{decode,encode};
use bitcrust_lib::block;

fn read_block(rdr: &mut Read) -> Result<block::Block, std::io::Error> {
    let _          = try!(rdr.read_u32::<LittleEndian>());
    let length     = try!(rdr.read_u32::<LittleEndian>());
    let mut buffer = vec![0; length as usize];

    try!(rdr.read_exact(&mut buffer));
    Ok(decode(&buffer).unwrap())

}

fn main() {

    let f = File::open("/home/tomas/.bitcoin/blocks/blk00020.dat").unwrap();
    let mut rdr = BufReader::new(f);

    for _ in 0..10000 {
        let blk = read_block(&mut rdr).unwrap();

        //println!("{:?}", blk);

        let serialized = serde_json::to_string(&blk.header).unwrap();

        //println!("{}", serialized);

        //let hex = encode(&blk).unwrap();

        //println!("Block: {:?}", hex);
    }

}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;
    use std::fs::File;
    use std::io::{Read, BufReader, Error, Cursor};
   // use rustc_serialize::hex::ToHex;
    /*
    #[test]
    fn test_read_block() {
        let f = File::open("/home/tomas/.bitcoin/blocks/blk00000.dat").unwrap();
        let mut rdr = BufReader::new(f);
        let blk = super::read_block(&mut rdr).unwrap();

        let serialized = super::serde_json::to_string(&blk.header).unwrap();

    }

    #[bench]
    fn bench_read(b: &mut Bencher) {
        b.iter(|| {
            let f = File::open("/home/tomas/.bitcoin/blocks/blk00020.dat").unwrap();
            let mut rdr = BufReader::new(f);
            let _ = super::read_block(&mut rdr).unwrap();

        });
    }
    */
}<|endoftext|>


<commit_before>use clap::{App, AppSettings, Arg, Shell, SubCommand};

pub fn build_cli() -> App<'static, 'static> {
    let database_arg = Arg::with_name("DATABASE_URL")
        .long("database-url")
        .help(
            "Specifies the database URL to connect to. Falls back to \
             the DATABASE_URL environment variable if unspecified.",
        )
        .global(true)
        .takes_value(true);

    let migration_subcommand = SubCommand::with_name("migration")
        .about(
            "A group of commands for generating, running, and reverting \
             migrations.",
        )
        .setting(AppSettings::VersionlessSubcommands)
        .arg(migration_dir_arg())
        .subcommand(SubCommand::with_name("run").about("Runs all pending migrations"))
        .subcommand(SubCommand::with_name("revert").about("Reverts the latest run migration"))
        .subcommand(SubCommand::with_name("redo").about(
            "Reverts and re-runs the latest migration. Useful \
             for testing that a migration can in fact be reverted.",
        ))
        .subcommand(
            SubCommand::with_name("list")
                .about("Lists all available migrations, marking those that have been applied."),
        )
        .subcommand(
            SubCommand::with_name("pending")
                .about("Returns true if there are any pending migrations."),
        )
        .subcommand(
            SubCommand::with_name("generate")
                .about(
                    "Generate a new migration with the given name, and \
                     the current timestamp as the version",
                )
                .arg(
                    Arg::with_name("MIGRATION_NAME")
                        .help("The name of the migration to create")
                        .required(true),
                )
                .arg(
                    Arg::with_name("MIGRATION_VERSION")
                        .long("version")
                        .help(
                            "The version number to use when generating the migration. \
                             Defaults to the current timestamp, which should suffice \
                             for most use cases.",
                        )
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name("MIGRATION_FORMAT")
                        .long("format")
                        .possible_values(&["sql", "barrel"])
                        .default_value("sql")
                        .takes_value(true)
                        .help("The format of the migration to be generated."),
                ),
        )
        .setting(AppSettings::SubcommandRequiredElseHelp);

    let setup_subcommand = SubCommand::with_name("setup")
        .arg(migration_dir_arg())
        .about(
            "Creates the migrations directory, creates the database \
             specified in your DATABASE_URL, and runs existing migrations.",
        );

    let database_subcommand = SubCommand::with_name("database")
        .alias("db")
        .arg(migration_dir_arg())
        .about("A group of commands for setting up and resetting your database.")
        .setting(AppSettings::VersionlessSubcommands)
        .subcommand(SubCommand::with_name("setup").about(
            "Creates the database specified in your DATABASE_URL, \
             and then runs any existing migrations.",
        ))
        .subcommand(SubCommand::with_name("reset").about(
            "Resets your database by dropping the database specified \
             in your DATABASE_URL and then running `diesel database setup`.",
        ))
        .subcommand(
            SubCommand::with_name("drop")
                .about("Drops the database specified in your DATABASE_URL.")
                .setting(AppSettings::Hidden),
        )
        .setting(AppSettings::SubcommandRequiredElseHelp);

    let generate_bash_completion_subcommand = SubCommand::with_name("bash-completion")
        .about("DEPRECATED: Generate bash completion script for the diesel command.");

    let generate_completions_subcommand = SubCommand::with_name("completions")
        .about("Generate shell completion scripts for the diesel command.")
        .arg(
            Arg::with_name("SHELL")
                .index(1)
                .required(true)
                .possible_values(&Shell::variants()),
        );

    let infer_schema_subcommand = SubCommand::with_name("print-schema")
        .setting(AppSettings::VersionlessSubcommands)
        .about("Print table definitions for database schema.")
        .arg(
            Arg::with_name("schema")
                .long("schema")
                .short("s")
                .takes_value(true)
                .help("The name of the schema."),
        )
        .arg(
            Arg::with_name("table-name")
                .index(1)
                .takes_value(true)
                .multiple(true)
                .help("Table names to filter (default only-tables if not empty)"),
        )
        .arg(
            Arg::with_name("only-tables")
                .short("o")
                .long("only-tables")
                .help("Only include tables from table-name that matches regexp")
                .conflicts_with("except-tables")
                .conflicts_with("blacklist"),
        )
        .arg(
            Arg::with_name("whitelist")
                .short("w")
                .long("whitelist")
                .hidden(true)
                .conflicts_with("only-table-regexes")
                .conflicts_with("blacklist")
                .conflicts_with("except-tables")
                .conflicts_with("except-table-regexes"),
        )
        .arg(
            Arg::with_name("except-tables")
                .short("e")
                .long("except-tables")
                .help("Exclude tables from table-name that matches regex")
                .conflicts_with("only-tables")
                .conflicts_with("whitelist")
        )
        .arg(
            Arg::with_name("blacklist")
                .short("b")
                .long("blacklist")
                .hidden(true)
                .conflicts_with("whitelist")
                .conflicts_with("only-tables")
                .conflicts_with("only-table-regexes")
                .conflicts_with("except-table-regexes"),
        )
        .arg(
            Arg::with_name("with-docs")
                .long("with-docs")
                .help("Render documentation comments for tables and columns"),
        )
        .arg(
            Arg::with_name("patch-file")
                .long("patch-file")
                .takes_value(true)
                .help("A unified diff file to be applied to the final schema"),
        )
        .arg(
            Arg::with_name("import-types")
                .long("import-types")
                .takes_value(true)
                .multiple(true)
                .number_of_values(1)
                .help("A list of types to import for every table, separated by commas"),
        );

    let config_arg = Arg::with_name("CONFIG_FILE")
        .long("config-file")
        .help(
            "The location of the configuration file to use. Falls back to the \
             `DIESEL_CONFIG_FILE` environment variable if unspecified. Defaults \
             to `diesel.toml` in your project root. See \
             diesel.rs/guides/configuring-diesel-cli for documentation on this file.",
        )
        .global(true)
        .takes_value(true);

    let locked_schema_arg = Arg::with_name("LOCKED_SCHEMA")
        .long("locked-schema")
        .help("Require that the schema file is up to date")
        .long_help(
            "When `print_schema.file` is specified in your config file, this \
             flag will cause Diesel CLI to error if any command would result in \
             changes to that file. It is recommended that you use this flag when \
             running migrations in CI or production.",
        )
        .global(true);

    App::new("diesel")
        .version(env!("CARGO_PKG_VERSION"))
        .setting(AppSettings::VersionlessSubcommands)
        .after_help(
            "You can also run `diesel SUBCOMMAND -h` to get more information about that subcommand.",
        )
        .arg(database_arg)
        .arg(config_arg)
        .arg(locked_schema_arg)
        .subcommand(migration_subcommand)
        .subcommand(setup_subcommand)
        .subcommand(database_subcommand)
        .subcommand(generate_bash_completion_subcommand)
        .subcommand(generate_completions_subcommand)
        .subcommand(infer_schema_subcommand)
        .setting(AppSettings::SubcommandRequiredElseHelp)
}

fn migration_dir_arg<'a, 'b>() -> Arg<'a, 'b> {
    Arg::with_name("MIGRATION_DIRECTORY")
        .long("migration-dir")
        .help(
            "The location of your migration directory. By default this \
             will look for a directory called `migrations` in the \
             current directory and its parents.",
        )
        .takes_value(true)
        .global(true)
}
<commit_msg>cleanup flags<commit_after>use clap::{App, AppSettings, Arg, Shell, SubCommand};

pub fn build_cli() -> App<'static, 'static> {
    let database_arg = Arg::with_name("DATABASE_URL")
        .long("database-url")
        .help(
            "Specifies the database URL to connect to. Falls back to \
             the DATABASE_URL environment variable if unspecified.",
        )
        .global(true)
        .takes_value(true);

    let migration_subcommand = SubCommand::with_name("migration")
        .about(
            "A group of commands for generating, running, and reverting \
             migrations.",
        )
        .setting(AppSettings::VersionlessSubcommands)
        .arg(migration_dir_arg())
        .subcommand(SubCommand::with_name("run").about("Runs all pending migrations"))
        .subcommand(SubCommand::with_name("revert").about("Reverts the latest run migration"))
        .subcommand(SubCommand::with_name("redo").about(
            "Reverts and re-runs the latest migration. Useful \
             for testing that a migration can in fact be reverted.",
        ))
        .subcommand(
            SubCommand::with_name("list")
                .about("Lists all available migrations, marking those that have been applied."),
        )
        .subcommand(
            SubCommand::with_name("pending")
                .about("Returns true if there are any pending migrations."),
        )
        .subcommand(
            SubCommand::with_name("generate")
                .about(
                    "Generate a new migration with the given name, and \
                     the current timestamp as the version",
                )
                .arg(
                    Arg::with_name("MIGRATION_NAME")
                        .help("The name of the migration to create")
                        .required(true),
                )
                .arg(
                    Arg::with_name("MIGRATION_VERSION")
                        .long("version")
                        .help(
                            "The version number to use when generating the migration. \
                             Defaults to the current timestamp, which should suffice \
                             for most use cases.",
                        )
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name("MIGRATION_FORMAT")
                        .long("format")
                        .possible_values(&["sql", "barrel"])
                        .default_value("sql")
                        .takes_value(true)
                        .help("The format of the migration to be generated."),
                ),
        )
        .setting(AppSettings::SubcommandRequiredElseHelp);

    let setup_subcommand = SubCommand::with_name("setup")
        .arg(migration_dir_arg())
        .about(
            "Creates the migrations directory, creates the database \
             specified in your DATABASE_URL, and runs existing migrations.",
        );

    let database_subcommand = SubCommand::with_name("database")
        .alias("db")
        .arg(migration_dir_arg())
        .about("A group of commands for setting up and resetting your database.")
        .setting(AppSettings::VersionlessSubcommands)
        .subcommand(SubCommand::with_name("setup").about(
            "Creates the database specified in your DATABASE_URL, \
             and then runs any existing migrations.",
        ))
        .subcommand(SubCommand::with_name("reset").about(
            "Resets your database by dropping the database specified \
             in your DATABASE_URL and then running `diesel database setup`.",
        ))
        .subcommand(
            SubCommand::with_name("drop")
                .about("Drops the database specified in your DATABASE_URL.")
                .setting(AppSettings::Hidden),
        )
        .setting(AppSettings::SubcommandRequiredElseHelp);

    let generate_bash_completion_subcommand = SubCommand::with_name("bash-completion")
        .about("DEPRECATED: Generate bash completion script for the diesel command.");

    let generate_completions_subcommand = SubCommand::with_name("completions")
        .about("Generate shell completion scripts for the diesel command.")
        .arg(
            Arg::with_name("SHELL")
                .index(1)
                .required(true)
                .possible_values(&Shell::variants()),
        );

    let infer_schema_subcommand = SubCommand::with_name("print-schema")
        .setting(AppSettings::VersionlessSubcommands)
        .about("Print table definitions for database schema.")
        .arg(
            Arg::with_name("schema")
                .long("schema")
                .short("s")
                .takes_value(true)
                .help("The name of the schema."),
        )
        .arg(
            Arg::with_name("table-name")
                .index(1)
                .takes_value(true)
                .multiple(true)
                .help("Table names to filter (default only-tables if not empty)"),
        )
        .arg(
            Arg::with_name("only-tables")
                .short("o")
                .long("only-tables")
                .help("Only include tables from table-name that matches regexp")
                .conflicts_with("except-tables")
                .conflicts_with("blacklist"),
        )
        .arg(
            Arg::with_name("whitelist")
                .short("w")
                .long("whitelist")
                .hidden(true)
                .conflicts_with("blacklist")
                .conflicts_with("except-tables")
        )
        .arg(
            Arg::with_name("except-tables")
                .short("e")
                .long("except-tables")
                .help("Exclude tables from table-name that matches regex")
                .conflicts_with("only-tables")
                .conflicts_with("whitelist")
        )
        .arg(
            Arg::with_name("blacklist")
                .short("b")
                .long("blacklist")
                .hidden(true)
                .conflicts_with("whitelist")
                .conflicts_with("only-tables")
        )
        .arg(
            Arg::with_name("with-docs")
                .long("with-docs")
                .help("Render documentation comments for tables and columns"),
        )
        .arg(
            Arg::with_name("patch-file")
                .long("patch-file")
                .takes_value(true)
                .help("A unified diff file to be applied to the final schema"),
        )
        .arg(
            Arg::with_name("import-types")
                .long("import-types")
                .takes_value(true)
                .multiple(true)
                .number_of_values(1)
                .help("A list of types to import for every table, separated by commas"),
        );

    let config_arg = Arg::with_name("CONFIG_FILE")
        .long("config-file")
        .help(
            "The location of the configuration file to use. Falls back to the \
             `DIESEL_CONFIG_FILE` environment variable if unspecified. Defaults \
             to `diesel.toml` in your project root. See \
             diesel.rs/guides/configuring-diesel-cli for documentation on this file.",
        )
        .global(true)
        .takes_value(true);

    let locked_schema_arg = Arg::with_name("LOCKED_SCHEMA")
        .long("locked-schema")
        .help("Require that the schema file is up to date")
        .long_help(
            "When `print_schema.file` is specified in your config file, this \
             flag will cause Diesel CLI to error if any command would result in \
             changes to that file. It is recommended that you use this flag when \
             running migrations in CI or production.",
        )
        .global(true);

    App::new("diesel")
        .version(env!("CARGO_PKG_VERSION"))
        .setting(AppSettings::VersionlessSubcommands)
        .after_help(
            "You can also run `diesel SUBCOMMAND -h` to get more information about that subcommand.",
        )
        .arg(database_arg)
        .arg(config_arg)
        .arg(locked_schema_arg)
        .subcommand(migration_subcommand)
        .subcommand(setup_subcommand)
        .subcommand(database_subcommand)
        .subcommand(generate_bash_completion_subcommand)
        .subcommand(generate_completions_subcommand)
        .subcommand(infer_schema_subcommand)
        .setting(AppSettings::SubcommandRequiredElseHelp)
}

fn migration_dir_arg<'a, 'b>() -> Arg<'a, 'b> {
    Arg::with_name("MIGRATION_DIRECTORY")
        .long("migration-dir")
        .help(
            "The location of your migration directory. By default this \
             will look for a directory called `migrations` in the \
             current directory and its parents.",
        )
        .takes_value(true)
        .global(true)
}
<|endoftext|>