<commit_before>use crate::{
    config::resource::Resource,
    config::scaled_annotation::{ScaledAnnotation, ScaledAnnotationParser},
};
use std::path::{Path, PathBuf};

#[derive(Copy, Clone, Debug)]
pub enum Format {
    Jpg,
    Png,
}

#[derive(Clone, Debug)]
pub struct Annotate {
    pub base_image: Resource,
    pub annotations: Vec<ScaledAnnotation>,
    pub output_path: PathBuf,
    pub output_format: Format,
    pub font_name: Option<String>,
    pub debug: bool,
}

#[derive(Clone, Debug)]
pub enum Command {
    Annotate(Annotate),
    ListFonts,
    SearchFonts { query: String },
}

impl Command {
    pub fn from_args() -> Self {
        use clap::{
            clap_app, crate_authors, crate_description, crate_version, value_t_or_exit,
            AppSettings, Arg, ArgGroup, SubCommand,
        };

        let args = {
            // Build app without annotation or format groups
            let app = clap_app!(annatar =>
                (version: crate_version!())
                (author: crate_authors!())
                (about: crate_description!())
                (@arg IMAGE: +takes_value +required "Path to an image to be annotated")
                (@arg OUTPUT: -o --output +takes_value "Sets an output path for the new image (default: <image path>/<image name>-annotated.<ext>")
                (@arg SCALE: -s --scale +takes_value "Sets the global scale multiplier for annotations")
                (@arg FONT: -f --font +takes_value "Sets the name of the font to be used")
                (@arg DEBUG: -d --debug "Save intermediate artifacts to disk")
                (@arg RIGHTSHOLDER_PROTECTIONS: --rightsholder-protections "EU/British compatibility mode")
            );

            let search_command = SubCommand::with_name("search-fonts")
                .about("Search available system fonts")
                .arg(
                    Arg::with_name("QUERY")
                        .required(true)
                        .takes_value(true)
                        .help("The name or partial name of a font"),
                );

            let app = app
                .subcommand(
                    SubCommand::with_name("list-fonts").about("List available system fonts"),
                )
                .subcommand(search_command);

            // Annotation group
            let caption = Arg::with_name("CAPTION").takes_value(true);
            let top = Arg::with_name("TOP")
                .takes_value(true)
                .short("t")
                .long("top");
            let middle = Arg::with_name("MIDDLE")
                .takes_value(true)
                .short("m")
                .long("middle");
            let bottom = Arg::with_name("BOTTOM")
                .takes_value(true)
                .short("b")
                .long("bottom");
            let annotations = ArgGroup::with_name("ANNOTATIONS")
                .required(true)
                .multiple(true)
                .args(&["CAPTION", "TOP", "MIDDLE", "BOTTOM"]);
            let app = app
                .arg(caption)
                .arg(top)
                .arg(middle)
                .arg(bottom)
                .group(annotations);

            // Format group
            let jpg = Arg::with_name("JPG")
                .long("jpg")
                .help("Sets output to JPG format");
            let png = Arg::with_name("PNG")
                .long("png")
                .help("Sets output to PNG format");
            let formats = ArgGroup::with_name("FORMATS")
                .required(false)
                .multiple(false)
                .args(&["JPG", "PNG"]);
            let app = app.arg(jpg).arg(png).group(formats);

            // Return args
            app.settings(&[AppSettings::SubcommandsNegateReqs])
                .get_matches()
        };

        // List fonts
        if let Some(cmd) = args.subcommand_matches("list-fonts") {
            return Command::ListFonts;
        }

        // Search fonts
        if let Some(cmd) = args.subcommand_matches("search-fonts") {
            return Command::SearchFonts {
                query: value_t_or_exit!(cmd.value_of("QUERY"), String),
            };
        }

        // Annotate
        if args.is_present("RIGHTSHOLDER_PROTECTIONS") {
            println!(
                "Rightsholder Protections Active\n\n\
                Your IP has been reported. Please turn off your PC and walk away.\n\
                Trust and Safety personnel have been dispatched to your location.\n\n\
                Have a nice day."
            );
            std::process::exit(1);
        }

        let image = args.value_of("IMAGE").unwrap();
        let output_format = if args.is_present("PNG") {
            Format::Png
        } else {
            Format::Jpg
        };
        let output_path = args
            .value_of("OUTPUT")
            .map(ToOwned::to_owned)
            .unwrap_or_else(|| create_output_file_path(image, output_format))
            .into();
        let scale = value_t_or_exit!(args.value_of("SCALE"), f32);

        let annotations = {
            let parser = ScaledAnnotationParser::new();
            let mut results = Vec::new();

            if let Some(x) = args.value_of("TOP") {
                results.push(parser.top(scale, x));
            }

            if let Some(x) = args.value_of("MIDDLE") {
                results.push(parser.middle(scale, x));
            }

            if let Some(x) = args.value_of("BOTTOM").or(args.value_of("CAPTION")) {
                results.push(parser.bottom(scale, x));
            }

            results
        };

        Command::Annotate(Annotate {
            base_image: Resource::new(image),
            annotations,
            output_path,
            output_format,
            font_name: args.value_of("FONT").map(ToOwned::to_owned),
            debug: args.is_present("DEBUG"),
        })
    }
}

fn create_output_file_path(path: impl AsRef<Path>, format: Format) -> String {
    path.as_ref()
        .file_stem()
        .map(|stem| {
            let stem = stem.to_str().unwrap();
            String::from(stem)
                + match format {
                    Format::Jpg => "-annotated.jpg",
                    Format::Png => "-annotated.png",
                }
        })
        .unwrap_or_else(|| match format {
            Format::Jpg => String::from("annotated.jpg"),
            Format::Png => String::from("annotated.png"),
        })
}
<commit_msg>cargo clippy<commit_after>use crate::{
    config::resource::Resource,
    config::scaled_annotation::{ScaledAnnotation, ScaledAnnotationParser},
};
use std::path::{Path, PathBuf};

#[derive(Copy, Clone, Debug)]
pub enum Format {
    Jpg,
    Png,
}

#[derive(Clone, Debug)]
pub struct Annotate {
    pub base_image: Resource,
    pub annotations: Vec<ScaledAnnotation>,
    pub output_path: PathBuf,
    pub output_format: Format,
    pub font_name: Option<String>,
    pub debug: bool,
}

#[derive(Clone, Debug)]
pub enum Command {
    Annotate(Annotate),
    ListFonts,
    SearchFonts { query: String },
}

impl Command {
    pub fn from_args() -> Self {
        use clap::{
            clap_app, crate_authors, crate_description, crate_version, value_t_or_exit,
            AppSettings, Arg, ArgGroup, SubCommand,
        };

        let args = {
            // Build app without annotation or format groups
            let app = clap_app!(annatar =>
                (version: crate_version!())
                (author: crate_authors!())
                (about: crate_description!())
                (@arg IMAGE: +takes_value +required "Path to an image to be annotated")
                (@arg OUTPUT: -o --output +takes_value "Sets an output path for the new image (default: <image path>/<image name>-annotated.<ext>")
                (@arg SCALE: -s --scale +takes_value "Sets the global scale multiplier for annotations")
                (@arg FONT: -f --font +takes_value "Sets the name of the font to be used")
                (@arg DEBUG: -d --debug "Save intermediate artifacts to disk")
                (@arg RIGHTSHOLDER_PROTECTIONS: --rightsholder-protections "EU/British compatibility mode")
            );

            let search_command = SubCommand::with_name("search-fonts")
                .about("Search available system fonts")
                .arg(
                    Arg::with_name("QUERY")
                        .required(true)
                        .takes_value(true)
                        .help("The name or partial name of a font"),
                );

            let app = app
                .subcommand(
                    SubCommand::with_name("list-fonts").about("List available system fonts"),
                )
                .subcommand(search_command);

            // Annotation group
            let caption = Arg::with_name("CAPTION").takes_value(true);
            let top = Arg::with_name("TOP")
                .takes_value(true)
                .short("t")
                .long("top");
            let middle = Arg::with_name("MIDDLE")
                .takes_value(true)
                .short("m")
                .long("middle");
            let bottom = Arg::with_name("BOTTOM")
                .takes_value(true)
                .short("b")
                .long("bottom");
            let annotations = ArgGroup::with_name("ANNOTATIONS")
                .required(true)
                .multiple(true)
                .args(&["CAPTION", "TOP", "MIDDLE", "BOTTOM"]);
            let app = app
                .arg(caption)
                .arg(top)
                .arg(middle)
                .arg(bottom)
                .group(annotations);

            // Format group
            let jpg = Arg::with_name("JPG")
                .long("jpg")
                .help("Sets output to JPG format");
            let png = Arg::with_name("PNG")
                .long("png")
                .help("Sets output to PNG format");
            let formats = ArgGroup::with_name("FORMATS")
                .required(false)
                .multiple(false)
                .args(&["JPG", "PNG"]);
            let app = app.arg(jpg).arg(png).group(formats);

            // Return args
            app.settings(&[AppSettings::SubcommandsNegateReqs])
                .get_matches()
        };

        // List fonts
        if args.subcommand_matches("list-fonts").is_some() {
            return Command::ListFonts;
        }

        // Search fonts
        if let Some(cmd) = args.subcommand_matches("search-fonts") {
            return Command::SearchFonts {
                query: value_t_or_exit!(cmd.value_of("QUERY"), String),
            };
        }

        // Annotate
        if args.is_present("RIGHTSHOLDER_PROTECTIONS") {
            println!(
                "Rightsholder Protections Active\n\n\
                Your IP has been reported. Please turn off your PC and walk away.\n\
                Trust and Safety personnel have been dispatched to your location.\n\n\
                Have a nice day."
            );
            std::process::exit(1);
        }

        let image = args.value_of("IMAGE").unwrap();
        let output_format = if args.is_present("PNG") {
            Format::Png
        } else {
            Format::Jpg
        };
        let output_path = args
            .value_of("OUTPUT")
            .map(ToOwned::to_owned)
            .unwrap_or_else(|| create_output_file_path(image, output_format))
            .into();
        let scale = value_t_or_exit!(args.value_of("SCALE"), f32);

        let annotations = {
            let parser = ScaledAnnotationParser::new();
            let mut results = Vec::new();

            if let Some(x) = args.value_of("TOP") {
                results.push(parser.top(scale, x));
            }

            if let Some(x) = args.value_of("MIDDLE") {
                results.push(parser.middle(scale, x));
            }

            if let Some(x) = args.value_of("BOTTOM").or_else(|| args.value_of("CAPTION")) {
                results.push(parser.bottom(scale, x));
            }

            results
        };

        Command::Annotate(Annotate {
            base_image: Resource::new(image),
            annotations,
            output_path,
            output_format,
            font_name: args.value_of("FONT").map(ToOwned::to_owned),
            debug: args.is_present("DEBUG"),
        })
    }
}

fn create_output_file_path(path: impl AsRef<Path>, format: Format) -> String {
    path.as_ref()
        .file_stem()
        .map(|stem| {
            let stem = stem.to_str().unwrap();
            String::from(stem)
                + match format {
                    Format::Jpg => "-annotated.jpg",
                    Format::Png => "-annotated.png",
                }
        })
        .unwrap_or_else(|| match format {
            Format::Jpg => String::from("annotated.jpg"),
            Format::Png => String::from("annotated.png"),
        })
}
<|endoftext|>


<commit_before>use crate::core::{Info, Method, Options, Query};
use crate::scripts::{
    grouping::{MultiLangScript, ScriptLangGroup},
    raw_detect_script, RawScriptInfo, Script,
};
use crate::Lang;
use crate::{alphabets, combined, trigrams};

pub fn detect_lang(text: &str) -> Option<Lang> {
    detect(text).map(|output| output.lang())
}

pub fn detect(text: &str) -> Option<Info> {
    let opts = Options::default();
    detect_with_options(text, &opts)
}

pub fn detect_with_options(text: &str, options: &Options) -> Option<Info> {
    let query = Query {
        text,
        filter_list: &options.filter_list,
        method: options.method,
    };
    detect_by_query(&query)
}

pub fn detect_by_query(query: &Query) -> Option<Info> {
    let raw_script_info = raw_detect_script(query.text);
    let script = raw_script_info.main_script()?;

    match script.to_lang_group() {
        ScriptLangGroup::One(lang) => Some(Info::new(script, lang, 1.0)),
        ScriptLangGroup::Multi(multi_lang_script) => {
            detect_by_query_based_on_script(query, multi_lang_script)
        }
        ScriptLangGroup::Mandarin => detect_lang_base_on_mandarin_script(query, &raw_script_info),
    }
}

fn detect_by_query_based_on_script(
    query: &Query,
    multi_lang_script: MultiLangScript,
) -> Option<Info> {
    let mut iquery = query.to_internal(multi_lang_script);
    match query.method {
        Method::Alphabet => alphabets::detect(&mut iquery),
        Method::Trigram => trigrams::detect(&mut iquery),
        Method::Combined => combined::detect(&mut iquery),
    }
}

// Sometimes Mandarin can be Japanese.
// See https://github.com/greyblake/whatlang-rs/pull/45
fn detect_lang_base_on_mandarin_script(
    query: &Query,
    raw_script_info: &RawScriptInfo,
) -> Option<Info> {
    let (lang, confidence) = if query.filter_list.is_allowed(Lang::Cmn) {
        let mandrin_count = raw_script_info.count(Script::Mandarin);
        let katakana_count = raw_script_info.count(Script::Katakana);
        let hiragana_count = raw_script_info.count(Script::Hiragana);
        let japanese_count = katakana_count + hiragana_count;
        let total = mandrin_count + hiragana_count;

        let jpn_pct = japanese_count as f64 / total as f64;

        // If at least 5% of characters are Japanese(Katakana or Hiragana) then it's likely to be Japanese language
        // See https://github.com/greyblake/whatlang-rs/issues/88
        if jpn_pct > 0.2 {
            (Lang::Jpn, 1.0)
        } else if jpn_pct > 0.05 {
            (Lang::Jpn, 0.5)
        } else if jpn_pct > 0.02 {
            (Lang::Cmn, 0.5)
        } else {
            (Lang::Cmn, 1.0)
        }
    } else {
        (Lang::Jpn, 1.0)
    };
    Some(Info::new(Script::Mandarin, lang, confidence))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::FilterList;
    use crate::scripts::Script;

    #[test]
    fn test_detect_spanish() {
        let text = "Además de todo lo anteriormente dicho, también encontramos...";
        let output = detect(text);
        assert_eq!(output.is_some(), true);

        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Spa);
        assert_eq!(info.script(), Script::Latin);
    }

    #[test]
    fn test_detect_lang_ukrainian() {
        let text = "Та нічого, все нормально. А в тебе як?";
        assert_eq!(detect_lang(text), Some(Lang::Ukr));
    }

    #[test]
    fn test_detect_with_options_with_filter_list_except() {
        let text = "I am begging pardon";

        // without filter list
        let output = detect_with_options(text, &Options::default());
        assert_eq!(output.is_some(), true);
        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Jav);

        // with filter list
        let filter_list =
            FilterList::deny(vec![Lang::Jav, Lang::Nld, Lang::Uzb, Lang::Swe, Lang::Nob]);
        let options = Options::new().set_filter_list(filter_list);
        let output = detect_with_options(text, &options);
        assert_eq!(output.is_some(), true);
        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Eng);
    }

    // TODO:  see https://github.com/greyblake/whatlang-rs/issues/78
    #[test]
    fn test_detect_with_options_with_filter_list_except_none() {
        {
            // All languages with Hebrew script are filtered out, so result must be None
            let text = "האקדמיה ללשון העברית";
            let filter_list = FilterList::deny(vec![Lang::Heb, Lang::Yid]);
            let options = Options::new().set_filter_list(filter_list);
            let output = detect_with_options(text, &options);
            assert_eq!(output, None);
        }

        {
            // All Cyrillic languages are filtered out
            let text = "Мы хотим видеть дальше, чем окна дома напротив";
            let filter_list = FilterList::deny(Script::Cyrillic.langs().to_owned());
            let options = Options::new().set_filter_list(filter_list);
            let output = detect_with_options(text, &options);
            assert_eq!(output, None);
        }

        {
            // All Latin languages are filtered out
            let text = "Mit dem Wissen wächst der Zweifel";
            let filter_list = FilterList::deny(Script::Latin.langs().to_owned());
            let options = Options::new().set_filter_list(filter_list);
            let output = detect_with_options(text, &options);
            assert_eq!(output, None);
        }
    }

    #[test]
    fn test_detect_with_options_with_filter_list_only() {
        let filter_list = FilterList::allow(vec![Lang::Epo, Lang::Ukr]);
        let options = Options::new().set_filter_list(filter_list);

        let text = "Mi ne scias!";
        let output = detect_with_options(text, &options);
        assert_eq!(output.is_some(), true);
        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Epo);
    }

    #[test]
    fn test_detect_with_options_with_whitelist_mandarin_japanese() {
        let text = "水";

        let jpn_opts = Options::new().set_filter_list(FilterList::allow(vec![Lang::Jpn]));
        let info = detect_with_options(text, &jpn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Jpn);

        let cmn_opts = Options::new().set_filter_list(FilterList::allow(vec![Lang::Cmn]));
        let info = detect_with_options(text, &cmn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Cmn);
    }

    #[test]
    fn test_detect_with_options_with_blacklist_mandarin_japanese() {
        let text = "水";

        let jpn_opts = Options::new().set_filter_list(FilterList::deny(vec![Lang::Jpn]));
        let info = detect_with_options(text, &jpn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Cmn);

        let cmn_opts = Options::new().set_filter_list(FilterList::deny(vec![Lang::Cmn]));
        let info = detect_with_options(text, &cmn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Jpn);
    }
}
<commit_msg>Fix clippy<commit_after>use crate::core::{Info, Method, Options, Query};
use crate::scripts::{
    grouping::{MultiLangScript, ScriptLangGroup},
    raw_detect_script, RawScriptInfo, Script,
};
use crate::Lang;
use crate::{alphabets, combined, trigrams};

pub fn detect_lang(text: &str) -> Option<Lang> {
    detect(text).map(|output| output.lang())
}

pub fn detect(text: &str) -> Option<Info> {
    let opts = Options::default();
    detect_with_options(text, &opts)
}

pub fn detect_with_options(text: &str, options: &Options) -> Option<Info> {
    let query = Query {
        text,
        filter_list: &options.filter_list,
        method: options.method,
    };
    detect_by_query(&query)
}

pub fn detect_by_query(query: &Query) -> Option<Info> {
    let raw_script_info = raw_detect_script(query.text);
    let script = raw_script_info.main_script()?;

    match script.to_lang_group() {
        ScriptLangGroup::One(lang) => Some(Info::new(script, lang, 1.0)),
        ScriptLangGroup::Multi(multi_lang_script) => {
            detect_by_query_based_on_script(query, multi_lang_script)
        }
        ScriptLangGroup::Mandarin => {
            Some(detect_lang_base_on_mandarin_script(query, &raw_script_info))
        }
    }
}

fn detect_by_query_based_on_script(
    query: &Query,
    multi_lang_script: MultiLangScript,
) -> Option<Info> {
    let mut iquery = query.to_internal(multi_lang_script);
    match query.method {
        Method::Alphabet => alphabets::detect(&mut iquery),
        Method::Trigram => trigrams::detect(&mut iquery),
        Method::Combined => combined::detect(&mut iquery),
    }
}

// Sometimes Mandarin can be Japanese.
// See https://github.com/greyblake/whatlang-rs/pull/45
fn detect_lang_base_on_mandarin_script(query: &Query, raw_script_info: &RawScriptInfo) -> Info {
    let (lang, confidence) = if query.filter_list.is_allowed(Lang::Cmn) {
        let mandrin_count = raw_script_info.count(Script::Mandarin);
        let katakana_count = raw_script_info.count(Script::Katakana);
        let hiragana_count = raw_script_info.count(Script::Hiragana);
        let japanese_count = katakana_count + hiragana_count;
        let total = mandrin_count + hiragana_count;

        let jpn_pct = japanese_count as f64 / total as f64;

        // If at least 5% of characters are Japanese(Katakana or Hiragana) then it's likely to be Japanese language
        // See https://github.com/greyblake/whatlang-rs/issues/88
        if jpn_pct > 0.2 {
            (Lang::Jpn, 1.0)
        } else if jpn_pct > 0.05 {
            (Lang::Jpn, 0.5)
        } else if jpn_pct > 0.02 {
            (Lang::Cmn, 0.5)
        } else {
            (Lang::Cmn, 1.0)
        }
    } else {
        (Lang::Jpn, 1.0)
    };
    Info::new(Script::Mandarin, lang, confidence)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::FilterList;
    use crate::scripts::Script;

    #[test]
    fn test_detect_spanish() {
        let text = "Además de todo lo anteriormente dicho, también encontramos...";
        let output = detect(text);
        assert_eq!(output.is_some(), true);

        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Spa);
        assert_eq!(info.script(), Script::Latin);
    }

    #[test]
    fn test_detect_lang_ukrainian() {
        let text = "Та нічого, все нормально. А в тебе як?";
        assert_eq!(detect_lang(text), Some(Lang::Ukr));
    }

    #[test]
    fn test_detect_with_options_with_filter_list_except() {
        let text = "I am begging pardon";

        // without filter list
        let output = detect_with_options(text, &Options::default());
        assert_eq!(output.is_some(), true);
        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Jav);

        // with filter list
        let filter_list =
            FilterList::deny(vec![Lang::Jav, Lang::Nld, Lang::Uzb, Lang::Swe, Lang::Nob]);
        let options = Options::new().set_filter_list(filter_list);
        let output = detect_with_options(text, &options);
        assert_eq!(output.is_some(), true);
        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Eng);
    }

    // TODO:  see https://github.com/greyblake/whatlang-rs/issues/78
    #[test]
    fn test_detect_with_options_with_filter_list_except_none() {
        {
            // All languages with Hebrew script are filtered out, so result must be None
            let text = "האקדמיה ללשון העברית";
            let filter_list = FilterList::deny(vec![Lang::Heb, Lang::Yid]);
            let options = Options::new().set_filter_list(filter_list);
            let output = detect_with_options(text, &options);
            assert_eq!(output, None);
        }

        {
            // All Cyrillic languages are filtered out
            let text = "Мы хотим видеть дальше, чем окна дома напротив";
            let filter_list = FilterList::deny(Script::Cyrillic.langs().to_owned());
            let options = Options::new().set_filter_list(filter_list);
            let output = detect_with_options(text, &options);
            assert_eq!(output, None);
        }

        {
            // All Latin languages are filtered out
            let text = "Mit dem Wissen wächst der Zweifel";
            let filter_list = FilterList::deny(Script::Latin.langs().to_owned());
            let options = Options::new().set_filter_list(filter_list);
            let output = detect_with_options(text, &options);
            assert_eq!(output, None);
        }
    }

    #[test]
    fn test_detect_with_options_with_filter_list_only() {
        let filter_list = FilterList::allow(vec![Lang::Epo, Lang::Ukr]);
        let options = Options::new().set_filter_list(filter_list);

        let text = "Mi ne scias!";
        let output = detect_with_options(text, &options);
        assert_eq!(output.is_some(), true);
        let info = output.unwrap();
        assert_eq!(info.lang(), Lang::Epo);
    }

    #[test]
    fn test_detect_with_options_with_whitelist_mandarin_japanese() {
        let text = "水";

        let jpn_opts = Options::new().set_filter_list(FilterList::allow(vec![Lang::Jpn]));
        let info = detect_with_options(text, &jpn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Jpn);

        let cmn_opts = Options::new().set_filter_list(FilterList::allow(vec![Lang::Cmn]));
        let info = detect_with_options(text, &cmn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Cmn);
    }

    #[test]
    fn test_detect_with_options_with_blacklist_mandarin_japanese() {
        let text = "水";

        let jpn_opts = Options::new().set_filter_list(FilterList::deny(vec![Lang::Jpn]));
        let info = detect_with_options(text, &jpn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Cmn);

        let cmn_opts = Options::new().set_filter_list(FilterList::deny(vec![Lang::Cmn]));
        let info = detect_with_options(text, &cmn_opts).unwrap();
        assert_eq!(info.lang(), Lang::Jpn);
    }
}
<|endoftext|>


<commit_before>/// Macro to print the current stats of ArrayFire's memory manager.
///
/// `mem_info!` print 4 values:
///
///  Name                    | Description
/// -------------------------|-------------------------
///  Allocated Bytes         | Total number of bytes allocated by the memory manager
///  Allocated Buffers       | Total number of buffers allocated
///  Locked (In Use) Bytes   | Number of bytes that are in use by active arrays
///  Locked (In Use) Buffers | Number of buffers that are in use by active arrays
///
///  The `Allocated Bytes` is always a multiple of the memory step size. The
///  default step size is 1024 bytes. This means when a buffer is to be
///  allocated, the size is always rounded up to a multiple of the step size.
///  You can use [get_mem_step_size](./fn.get_mem_step_size.html) to check the
///  current step size and [set_mem_step_size](./fn.set_mem_step_size.html) to
///  set a custom resolution size.
///
///  The `Allocated Buffers` is the number of buffers that use up the allocated
///  bytes. This includes buffers currently in scope, as well as buffers marked
///  as free, ie, from arrays gone out of scope. The free buffers are available
///  for use by new arrays that might be created.
///
///  The `Locked Bytes` is the number of bytes in use that cannot be
///  reallocated at the moment. The difference of Allocated Bytes and Locked
///  Bytes is the total bytes available for reallocation.
///
///  The `Locked Buffers` is the number of buffer in use that cannot be
///  reallocated at the moment. The difference of Allocated Buffers and Locked
///  Buffers is the number of buffers available for reallocation.
///
/// # Parameters
///
/// - `msg` is the message that is printed to screen before printing stats
///
/// # Examples
///
/// ```rust
/// use arrayfire::{Dim4, device_mem_info, print, randu, mem_info};
///
/// let dims = Dim4::new(&[5, 5, 1, 1]);
/// let a = randu::<f32>(dims);
/// print(&a);
/// mem_info!("Hello!");
/// ```
///
/// Sample Output:
///
/// ```text
/// AF Memory: Here
/// Allocated [ Bytes | Buffers ] = [ 4096 | 4 ]
/// In Use    [ Bytes | Buffers ] = [ 2048 | 2 ]
/// ```
#[macro_export]
macro_rules! mem_info {
    [$msg: expr] => {
        {
            let (abytes, abuffs, lbytes, lbuffs) = $crate::device_mem_info();
            println!("AF Memory: {:?}", $msg);
            println!("Allocated [Bytes | Buffers] = [ {} | {} ]", abytes, abuffs);
            println!("In Use    [Bytes | Buffers] = [ {} | {} ]", lbytes, lbuffs);
        }
    };
}

/// Join multiple Arrays along a given dimension
///
/// All the Arrays provided to this macro should be of type `&Array`
///
/// # Examples
///
/// ```rust
/// use arrayfire::{Dim4, join_many, print, randu};
///
/// let a = &randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
/// let b = &randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
/// let c = &randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
/// let d = join_many![2; a, b, c];
/// print(&d);
/// ```
///
/// # Panics
///
/// This macro just calls [join_many](./fn.join_many.html) function after collecting all
/// the input arrays into a vector.
// Using macro to implement join many wrapper
#[macro_export]
macro_rules! join_many {
    [$dim: expr; $($x:expr),+] => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
             )*
            $crate::join_many($dim, temp_vec)
        }
    };
}

/// Print given message before printing out the Array to standard output
///
/// # Examples
///
/// ```rust
/// use arrayfire::{Dim4, print_gen, randu, af_print};
/// let dims = Dim4::new(&[3, 1, 1, 1]);
/// let a = randu::<f32>(dims);
/// af_print!("Create a 5-by-3 matrix of random floats on the GPU", a);
/// ```
///
#[macro_export]
macro_rules! af_print {
    [$msg: expr, $x: expr] => {
        {
            $crate::print_gen(String::from($msg), &$x, Some(4));
        }
    };
}

/// Evaluate arbitrary number of arrays
#[macro_export]
macro_rules! eval {
    [$($x:expr),+] => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
             )*
            eval_multiple(temp_vec)
        }
    };
}

/// Create a dim4 object from provided dimensions
///
/// The user can pass 1 or more sizes and the left over values will default to 1.
#[macro_export]
macro_rules! dim4 {
    ($dim0:expr) => {
        $crate::Dim4::new(&[$dim0, 1, 1, 1])
    };
    ($dim0:expr, $dim1:expr) => {
        $crate::Dim4::new(&[$dim0, $dim1, 1, 1])
    };
    ($dim0:expr, $dim1:expr, $dim2:expr) => {
        $crate::Dim4::new(&[$dim0, $dim1, $dim2, 1])
    };
    ($dim0:expr, $dim1:expr, $dim2:expr, $dim3:expr) => {
        $crate::Dim4::new(&[$dim0, $dim1, $dim2, $dim3])
    };
}

/// Create a sequence object
///
/// If type is not provided, then the Seq will default to i32 type
#[macro_export]
macro_rules! seq {
    () => {
        $crate::Seq::<i32>::default()
    };
    ($sty:ty; $start:literal : $end:literal : $step:literal) => {
        $crate::Seq::<$sty>::new($start, $end, $step)
    };
    ($start:literal : $end:literal : $step:literal) => {
        $crate::Seq::<i32>::new($start, $end, $step)
    };
    ($sty:ty; $start:expr , $end:expr , $step:expr) => {
        $crate::Seq::<$sty>::new($start, $end, $step)
    };
    ($start:expr , $end:expr , $step:expr) => {
        $crate::Seq::<i32>::new($start, $end, $step)
    };
}

/// Indexing into an existing Array
///
/// This macro call with return an Array that has a view of another Array. The Array returned due to
/// the indexing operation will follow copy-on-write semantics. The Array identifier taken by this
/// macro is passed to the relevant internal functions as a borrowed reference. Thus, this identifier
/// will be still available for futher use after the macro call.
///
/// The following types of inputs are matched by this macro.
///
/// - A simple Array identifier.
/// - An Array with slicing info for indexing.
/// - An Array with slicing info and other arrays used for indexing.
#[macro_export]
macro_rules! view {
    (@af_max_dims) => {
        4
    };
    ( $array_ident:ident ) => {
        $array_ident.clone()
    };
    ( $array_ident:ident [ $($start:literal : $end:literal : $step:literal),+ ] ) => {
        {
            let AF_MAX_DIMS: usize = view!(@af_max_dims);
            let mut seq_vec = Vec::<$crate::Seq<i32>>::with_capacity(AF_MAX_DIMS);
            $(
                seq_vec.push($crate::seq!($start:$end:$step));
             )*
             for span_place_holder in seq_vec.len()..AF_MAX_DIMS {
                 seq_vec.push($crate::seq!());
             }
            $crate::index(&$array_ident, &seq_vec)
        }
    };
    (@set_indexer $idim:expr, $idxr:ident, $lterm:expr) => {
        {
            $idxr.set_index(&$lterm, $idim, None);
        }
    };
    (@set_indexer $idim:expr, $idxr:ident, $hterm:expr, $($tterm:expr),*) => {
        {
            $idxr.set_index(&$hterm, $idim, None);
            view!(@set_indexer $idim + 1, $idxr, $($tterm),*);
        }
    };
    ($array_ident:ident [ $($_e:expr),+ ]) => {
        {
            let AF_MAX_DIMS: u32 = view!(@af_max_dims);
            let span = $crate::seq!();
            let mut idxrs = $crate::Indexer::default();

            view!(@set_indexer 0, idxrs, $($_e),*);

            let mut dimIx = idxrs.len() as u32;
            while dimIx < AF_MAX_DIMS {
                idxrs.set_index(&span, dimIx, None);
                dimIx += 1;
            }
            $crate::index_gen(&$array_ident, idxrs)
        }
    };
}

#[cfg(test)]
mod tests {
    use super::super::array::Array;
    use super::super::index::index;
    use super::super::random::randu;

    #[test]
    fn dim4_construction() {
        let dim1d = dim4!(2);
        let dim2d = dim4!(2, 3);
        let dim3d = dim4!(2, 3, 4);
        let dim4d = dim4!(2, 3, 4, 2);
        let _dimn = dim4!(dim1d[0], dim2d[1], dim3d[2], dim4d[3]);
    }

    #[test]
    fn seq_construction() {
        let default_seq = seq!();
        let _range_1_to_10_step_1 = seq!(0:9:1);
        let _range_1_to_10_step_1_2 = seq!(f32; 0.0:9.0:1.5);
        let _range_from_exprs = seq!(default_seq.begin(), default_seq.end(), default_seq.step());
        let _range_from_exprs2 = seq!(f32; default_seq.begin() as f32,
                 default_seq.end() as f32, default_seq.step() as f32);
    }

    #[test]
    fn seq_view() {
        let mut dim4d = dim4!(5, 3, 2, 1);
        dim4d[2] = 1;

        let a = randu::<f32>(dim4d);
        let seqs = &[seq!(1:3:1), seq!()];
        let sub = index(&a, seqs);
        af_print!("A", a);
        af_print!("Indexed A", sub);
    }

    #[test]
    fn view_macro() {
        let dims = dim4!(5, 5, 2, 1);
        let a = randu::<f32>(dims);
        let b = a.clone();
        let c = a.clone();
        let d = a.clone();
        let e = a.clone();

        let v = view!(a);
        af_print!("v = a[None]", v);

        let m = view!(c[1:3:1, 1:3:2]);
        af_print!("m = c[:, :]", m);

        let x = seq!(1:3:1);
        let y = seq!(1:3:2);
        let u = view!(b[x, y]);
        af_print!("u = b[seq(), seq()]", u);

        let values: [u32; 3] = [1, 2, 3];
        let indices = Array::new(&values, dim4!(3, 1, 1, 1));
        let indices2 = Array::new(&values, dim4!(3, 1, 1, 1));

        let w = view!(d[indices, indices2]);
        af_print!("w = d[Array, Array]", w);

        let z = view!(e[indices, y]);
        af_print!("z = e[Array, Seq]", z);
    }
}
<commit_msg>Fix variable naming convention warnings in marcos<commit_after>/// Macro to print the current stats of ArrayFire's memory manager.
///
/// `mem_info!` print 4 values:
///
///  Name                    | Description
/// -------------------------|-------------------------
///  Allocated Bytes         | Total number of bytes allocated by the memory manager
///  Allocated Buffers       | Total number of buffers allocated
///  Locked (In Use) Bytes   | Number of bytes that are in use by active arrays
///  Locked (In Use) Buffers | Number of buffers that are in use by active arrays
///
///  The `Allocated Bytes` is always a multiple of the memory step size. The
///  default step size is 1024 bytes. This means when a buffer is to be
///  allocated, the size is always rounded up to a multiple of the step size.
///  You can use [get_mem_step_size](./fn.get_mem_step_size.html) to check the
///  current step size and [set_mem_step_size](./fn.set_mem_step_size.html) to
///  set a custom resolution size.
///
///  The `Allocated Buffers` is the number of buffers that use up the allocated
///  bytes. This includes buffers currently in scope, as well as buffers marked
///  as free, ie, from arrays gone out of scope. The free buffers are available
///  for use by new arrays that might be created.
///
///  The `Locked Bytes` is the number of bytes in use that cannot be
///  reallocated at the moment. The difference of Allocated Bytes and Locked
///  Bytes is the total bytes available for reallocation.
///
///  The `Locked Buffers` is the number of buffer in use that cannot be
///  reallocated at the moment. The difference of Allocated Buffers and Locked
///  Buffers is the number of buffers available for reallocation.
///
/// # Parameters
///
/// - `msg` is the message that is printed to screen before printing stats
///
/// # Examples
///
/// ```rust
/// use arrayfire::{Dim4, device_mem_info, print, randu, mem_info};
///
/// let dims = Dim4::new(&[5, 5, 1, 1]);
/// let a = randu::<f32>(dims);
/// print(&a);
/// mem_info!("Hello!");
/// ```
///
/// Sample Output:
///
/// ```text
/// AF Memory: Here
/// Allocated [ Bytes | Buffers ] = [ 4096 | 4 ]
/// In Use    [ Bytes | Buffers ] = [ 2048 | 2 ]
/// ```
#[macro_export]
macro_rules! mem_info {
    [$msg: expr] => {
        {
            let (abytes, abuffs, lbytes, lbuffs) = $crate::device_mem_info();
            println!("AF Memory: {:?}", $msg);
            println!("Allocated [Bytes | Buffers] = [ {} | {} ]", abytes, abuffs);
            println!("In Use    [Bytes | Buffers] = [ {} | {} ]", lbytes, lbuffs);
        }
    };
}

/// Join multiple Arrays along a given dimension
///
/// All the Arrays provided to this macro should be of type `&Array`
///
/// # Examples
///
/// ```rust
/// use arrayfire::{Dim4, join_many, print, randu};
///
/// let a = &randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
/// let b = &randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
/// let c = &randu::<f32>(Dim4::new(&[5, 3, 1, 1]));
/// let d = join_many![2; a, b, c];
/// print(&d);
/// ```
///
/// # Panics
///
/// This macro just calls [join_many](./fn.join_many.html) function after collecting all
/// the input arrays into a vector.
// Using macro to implement join many wrapper
#[macro_export]
macro_rules! join_many {
    [$dim: expr; $($x:expr),+] => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
             )*
            $crate::join_many($dim, temp_vec)
        }
    };
}

/// Print given message before printing out the Array to standard output
///
/// # Examples
///
/// ```rust
/// use arrayfire::{Dim4, print_gen, randu, af_print};
/// let dims = Dim4::new(&[3, 1, 1, 1]);
/// let a = randu::<f32>(dims);
/// af_print!("Create a 5-by-3 matrix of random floats on the GPU", a);
/// ```
///
#[macro_export]
macro_rules! af_print {
    [$msg: expr, $x: expr] => {
        {
            $crate::print_gen(String::from($msg), &$x, Some(4));
        }
    };
}

/// Evaluate arbitrary number of arrays
#[macro_export]
macro_rules! eval {
    [$($x:expr),+] => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
             )*
            eval_multiple(temp_vec)
        }
    };
}

/// Create a dim4 object from provided dimensions
///
/// The user can pass 1 or more sizes and the left over values will default to 1.
#[macro_export]
macro_rules! dim4 {
    ($dim0:expr) => {
        $crate::Dim4::new(&[$dim0, 1, 1, 1])
    };
    ($dim0:expr, $dim1:expr) => {
        $crate::Dim4::new(&[$dim0, $dim1, 1, 1])
    };
    ($dim0:expr, $dim1:expr, $dim2:expr) => {
        $crate::Dim4::new(&[$dim0, $dim1, $dim2, 1])
    };
    ($dim0:expr, $dim1:expr, $dim2:expr, $dim3:expr) => {
        $crate::Dim4::new(&[$dim0, $dim1, $dim2, $dim3])
    };
}

/// Create a sequence object
///
/// If type is not provided, then the Seq will default to i32 type
#[macro_export]
macro_rules! seq {
    () => {
        $crate::Seq::<i32>::default()
    };
    ($sty:ty; $start:literal : $end:literal : $step:literal) => {
        $crate::Seq::<$sty>::new($start, $end, $step)
    };
    ($start:literal : $end:literal : $step:literal) => {
        $crate::Seq::<i32>::new($start, $end, $step)
    };
    ($sty:ty; $start:expr , $end:expr , $step:expr) => {
        $crate::Seq::<$sty>::new($start, $end, $step)
    };
    ($start:expr , $end:expr , $step:expr) => {
        $crate::Seq::<i32>::new($start, $end, $step)
    };
}

/// Indexing into an existing Array
///
/// This macro call with return an Array that has a view of another Array. The Array returned due to
/// the indexing operation will follow copy-on-write semantics. The Array identifier taken by this
/// macro is passed to the relevant internal functions as a borrowed reference. Thus, this identifier
/// will be still available for futher use after the macro call.
///
/// The following types of inputs are matched by this macro.
///
/// - A simple Array identifier.
/// - An Array with slicing info for indexing.
/// - An Array with slicing info and other arrays used for indexing.
#[macro_export]
macro_rules! view {
    (@af_max_dims) => {
        4
    };
    ( $array_ident:ident ) => {
        $array_ident.clone()
    };
    ( $array_ident:ident [ $($start:literal : $end:literal : $step:literal),+ ] ) => {
        {
            #[allow(non_snake_case)]
            let AF_MAX_DIMS: usize = view!(@af_max_dims);
            let mut seq_vec = Vec::<$crate::Seq<i32>>::with_capacity(AF_MAX_DIMS);
            $(
                seq_vec.push($crate::seq!($start:$end:$step));
             )*
             for _span_place_holder in seq_vec.len()..AF_MAX_DIMS {
                 seq_vec.push($crate::seq!());
             }
            $crate::index(&$array_ident, &seq_vec)
        }
    };
    (@set_indexer $idim:expr, $idxr:ident, $lterm:expr) => {
        {
            $idxr.set_index(&$lterm, $idim, None);
        }
    };
    (@set_indexer $idim:expr, $idxr:ident, $hterm:expr, $($tterm:expr),*) => {
        {
            $idxr.set_index(&$hterm, $idim, None);
            view!(@set_indexer $idim + 1, $idxr, $($tterm),*);
        }
    };
    ($array_ident:ident [ $($_e:expr),+ ]) => {
        {
            #[allow(non_snake_case)]
            let AF_MAX_DIMS: u32 = view!(@af_max_dims);
            let span = $crate::seq!();
            let mut idxrs = $crate::Indexer::default();

            view!(@set_indexer 0, idxrs, $($_e),*);

            let mut dim_ix = idxrs.len() as u32;
            while dim_ix < AF_MAX_DIMS {
                idxrs.set_index(&span, dim_ix, None);
                dim_ix += 1;
            }
            $crate::index_gen(&$array_ident, idxrs)
        }
    };
}

#[cfg(test)]
mod tests {
    use super::super::array::Array;
    use super::super::index::index;
    use super::super::random::randu;

    #[test]
    fn dim4_construction() {
        let dim1d = dim4!(2);
        let dim2d = dim4!(2, 3);
        let dim3d = dim4!(2, 3, 4);
        let dim4d = dim4!(2, 3, 4, 2);
        let _dimn = dim4!(dim1d[0], dim2d[1], dim3d[2], dim4d[3]);
    }

    #[test]
    fn seq_construction() {
        let default_seq = seq!();
        let _range_1_to_10_step_1 = seq!(0:9:1);
        let _range_1_to_10_step_1_2 = seq!(f32; 0.0:9.0:1.5);
        let _range_from_exprs = seq!(default_seq.begin(), default_seq.end(), default_seq.step());
        let _range_from_exprs2 = seq!(f32; default_seq.begin() as f32,
                 default_seq.end() as f32, default_seq.step() as f32);
    }

    #[test]
    fn seq_view() {
        let mut dim4d = dim4!(5, 3, 2, 1);
        dim4d[2] = 1;

        let a = randu::<f32>(dim4d);
        let seqs = &[seq!(1:3:1), seq!()];
        let sub = index(&a, seqs);
        af_print!("A", a);
        af_print!("Indexed A", sub);
    }

    #[test]
    fn view_macro() {
        let dims = dim4!(5, 5, 2, 1);
        let a = randu::<f32>(dims);
        let b = a.clone();
        let c = a.clone();
        let d = a.clone();
        let e = a.clone();

        let v = view!(a);
        af_print!("v = a[None]", v);

        let m = view!(c[1:3:1, 1:3:2]);
        af_print!("m = c[:, :]", m);

        let x = seq!(1:3:1);
        let y = seq!(1:3:2);
        let u = view!(b[x, y]);
        af_print!("u = b[seq(), seq()]", u);

        let values: [u32; 3] = [1, 2, 3];
        let indices = Array::new(&values, dim4!(3, 1, 1, 1));
        let indices2 = Array::new(&values, dim4!(3, 1, 1, 1));

        let w = view!(d[indices, indices2]);
        af_print!("w = d[Array, Array]", w);

        let z = view!(e[indices, y]);
        af_print!("z = e[Array, Seq]", z);
    }
}
<|endoftext|>


<commit_before>// Find Cargo's source cache directory for a crate
use super::es;
use std::fs;
use es::traits::*;
use std::env;
use std::path::{Path,PathBuf};
use std::collections::HashMap;

lazy_static! {
    pub static ref RUSTUP_LIB: String = es::shell("rustc --print sysroot") + "/lib";
    pub static ref UNSTABLE: bool = RUSTUP_LIB.find("stable-").is_none();
}

pub fn proper_crate_name(crate_name: &str) -> String {
    crate_name.replace('-',"_")
}

pub fn path_file_name(p: &Path) -> String {
    if let Some(file_name) = p.file_name() {
        file_name.to_string_lossy().to_string()
    } else
    if let Ok(full_path) = p.canonicalize() {
        path_file_name(&full_path)
    } else {
        p.to_string_lossy().to_string()
    }
}

// there's a Crate for This...
fn semver_i (s: &str) -> u64 {
    let v = s.split('.').filter_map(|s| s.parse::<u64>().ok()).to_vec();
    (((v[0] << 8) + v[1]) << 8) + v[2]
}

pub fn cargo_home() -> PathBuf {
    if let Ok(home) = env::var("CARGO_HOME") { // set in cargo runs
        home.into()
    } else {
        env::home_dir().or_die("no home!").join(".cargo")
    }
}

pub fn cache_path(crate_name: &str) -> PathBuf {
    let home = cargo_home();
    let crate_root = PathBuf::from(home.join("registry/src"));
    // actual crate source is in some fairly arbitrary subdirectory of this
    let mut crate_dir = crate_root.clone();
    crate_dir.push(es::files(&crate_root).next().or_die("no crate cache directory"));

    let mut crates = Vec::new();
    for (p,d) in es::paths(&crate_dir) {
        if ! d.is_dir() { continue; }
        let filename = path_file_name(&p);
        if let Some(endc) = filename.rfind('-') {
            if &filename[0..endc] == crate_name {
                crates.push((p,semver_i(&filename[endc+1..])));
            }
        }
    }
    // crate versions in ascending order by semver rules
    crates.sort_by(|a,b| a.1.cmp(&b.1));
    crates.pop().or_die(&format!("no such crate: {}",crate_name)).0
}

// Very hacky stuff - we want the ACTUAL crate name, not the project name
// So look just past [package] and scrape the name...
fn crate_name(cargo_toml: &Path) -> String {
    let name_line = es::lines(es::open(cargo_toml))
        .skip_while(|line| line.trim() != "[package]")
        .skip(1)
        .skip_while(|line| ! line.starts_with("name "))
        .next().or_die("totally fked Cargo.toml");
    let idx = name_line.find('"').or_die("no name?");
    (&name_line[(idx+1)..(name_line.len()-1)]).into()

}

pub fn crate_path(file: &Path, first_arg: &str) -> Result<(PathBuf,String),String> {
    if file.exists() {
        if file.is_dir() { // assumed to be Cargo directory
            let cargo_toml = file.join("Cargo.toml");
            if ! cargo_toml.exists() {
                return Err(format!("not a Cargo project directory: {}",file.display()));
            }
            Ok((file.join("src").join("lib.rs"), crate_name(&cargo_toml)))
        } else { // should be just a Rust source file
            if file.extension().or_die("expecting extension") != "rs" {
                return Err("expecting Rust source file".into());
            }
            let name = path_file_name(&file.with_extension(""));
            Ok((file.to_path_buf(), name))
        }
    } else {
        let project_dir = cache_path(first_arg);
        let cargo_toml = project_dir.join("Cargo.toml");
        Ok((project_dir.join("src").join("lib.rs"), crate_name(&cargo_toml)))
    }
}

pub fn full_crate_name(deps: &Path, crate_name: &str) -> Option<String> {
    let mut res = Vec::new();
    let patt = format!("lib{}-",crate_name);
    for entry in fs::read_dir(deps).or_die("cannot access dependencies dir") {
        let entry = entry.or_die("cannot access deps entry");
        let path = entry.path();
        if let Some(f) = path.file_name() {
            let name = f.to_string_lossy();
            //println!("got {}",name);
            if name.starts_with(&patt) {
                let cname = &name[3..(name.len()-5) ];
                res.push(cname.to_string());
            }
        }
    }
    res.pop()
}

pub type Crates = HashMap<String,Vec<PathBuf>>;

pub fn get_cache_deps(deps: &Path) ->  Crates {
    let mut res: Crates = HashMap::new();
    for entry in fs::read_dir(deps).or_die("cannot access dependencies dir") {
        let entry = entry.or_die("cannot access deps entry");
        let path = entry.path();
        if let Some(f) = path.file_name() {
            let rlib = f.to_string_lossy();
            // pull out the crate name
            if let Some(idx) = rlib.rfind('-') {
                let name = &rlib[3..idx];
                let mut v = res.entry(name.into())
                    .or_insert_with(|| Vec::new());
                v.push(path.clone());
            }
        }
    }
    res
}

pub fn remove_duplicates(crates: Crates) {
    for (name,paths) in crates {
        if paths.len() > 1 {
            // Sort the paths in ascending time of modification
            let mut mpaths: Vec<_> = paths.into_iter()
                .map(|p| {
                    let time = p.metadata().unwrap().modified().unwrap();
                    (p,time)
                }).collect();
            mpaths.sort_by(|a,b| a.1.cmp(&b.1));

            // ignore the latest, and delete the rest
            mpaths.pop();
            println!("crate {} removing {} items",name,mpaths.len());
            for (p,_) in mpaths {
                fs::remove_file(&p).expect("can't remove rlib");
            }
        }
    }
}

pub fn remove_duplicate_cache_deps(deps: &Path) {
    let deps = get_cache_deps(deps);
    remove_duplicates(deps);
}

<commit_msg>borrow shell from easy_shortcuts; Windows fix for stable detection<commit_after>// Find Cargo's source cache directory for a crate
use super::es;
use std::fs;
use es::traits::*;
use std::env;
use std::path::{Path,PathBuf};
use std::collections::HashMap;

lazy_static! {
    pub static ref RUSTUP_LIB: String = shell("rustc --print sysroot") + "/lib";
    pub static ref UNSTABLE: bool = RUSTUP_LIB.find("stable").is_none();
}

pub fn shell(cmd: &str) -> String {
    let o = ::std::process::Command::new("sh")
     .arg("-c")
     .arg(&format!("{} 2>&1",cmd))
     .output()
     .expect("failed to execute shell");
    String::from_utf8(o.stdout).expect("not UTF-8 output").trim_right_matches('\n').to_string()
}

pub fn proper_crate_name(crate_name: &str) -> String {
    crate_name.replace('-',"_")
}

pub fn path_file_name(p: &Path) -> String {
    if let Some(file_name) = p.file_name() {
        file_name.to_string_lossy().to_string()
    } else
    if let Ok(full_path) = p.canonicalize() {
        path_file_name(&full_path)
    } else {
        p.to_string_lossy().to_string()
    }
}

// there's a Crate for This...
fn semver_i (s: &str) -> u64 {
    let v = s.split('.').filter_map(|s| s.parse::<u64>().ok()).to_vec();
    (((v[0] << 8) + v[1]) << 8) + v[2]
}

pub fn cargo_home() -> PathBuf {
    if let Ok(home) = env::var("CARGO_HOME") { // set in cargo runs
        home.into()
    } else {
        env::home_dir().or_die("no home!").join(".cargo")
    }
}

pub fn cache_path(crate_name: &str) -> PathBuf {
    let home = cargo_home();
    let crate_root = PathBuf::from(home.join("registry/src"));
    // actual crate source is in some fairly arbitrary subdirectory of this
    let mut crate_dir = crate_root.clone();
    crate_dir.push(es::files(&crate_root).next().or_die("no crate cache directory"));

    let mut crates = Vec::new();
    for (p,d) in es::paths(&crate_dir) {
        if ! d.is_dir() { continue; }
        let filename = path_file_name(&p);
        if let Some(endc) = filename.rfind('-') {
            if &filename[0..endc] == crate_name {
                crates.push((p,semver_i(&filename[endc+1..])));
            }
        }
    }
    // crate versions in ascending order by semver rules
    crates.sort_by(|a,b| a.1.cmp(&b.1));
    crates.pop().or_die(&format!("no such crate: {}",crate_name)).0
}

// Very hacky stuff - we want the ACTUAL crate name, not the project name
// So look just past [package] and scrape the name...
fn crate_name(cargo_toml: &Path) -> String {
    let name_line = es::lines(es::open(cargo_toml))
        .skip_while(|line| line.trim() != "[package]")
        .skip(1)
        .skip_while(|line| ! line.starts_with("name "))
        .next().or_die("totally fked Cargo.toml");
    let idx = name_line.find('"').or_die("no name?");
    (&name_line[(idx+1)..(name_line.len()-1)]).into()

}

pub fn crate_path(file: &Path, first_arg: &str) -> Result<(PathBuf,String),String> {
    if file.exists() {
        if file.is_dir() { // assumed to be Cargo directory
            let cargo_toml = file.join("Cargo.toml");
            if ! cargo_toml.exists() {
                return Err(format!("not a Cargo project directory: {}",file.display()));
            }
            Ok((file.join("src").join("lib.rs"), crate_name(&cargo_toml)))
        } else { // should be just a Rust source file
            if file.extension().or_die("expecting extension") != "rs" {
                return Err("expecting Rust source file".into());
            }
            let name = path_file_name(&file.with_extension(""));
            Ok((file.to_path_buf(), name))
        }
    } else {
        let project_dir = cache_path(first_arg);
        let cargo_toml = project_dir.join("Cargo.toml");
        Ok((project_dir.join("src").join("lib.rs"), crate_name(&cargo_toml)))
    }
}

pub fn full_crate_name(deps: &Path, crate_name: &str) -> Option<String> {
    let mut res = Vec::new();
    let patt = format!("lib{}-",crate_name);
    for entry in fs::read_dir(deps).or_die("cannot access dependencies dir") {
        let entry = entry.or_die("cannot access deps entry");
        let path = entry.path();
        if let Some(f) = path.file_name() {
            let name = f.to_string_lossy();
            //println!("got {}",name);
            if name.starts_with(&patt) {
                let cname = &name[3..(name.len()-5) ];
                res.push(cname.to_string());
            }
        }
    }
    res.pop()
}

pub type Crates = HashMap<String,Vec<PathBuf>>;

pub fn get_cache_deps(deps: &Path) ->  Crates {
    let mut res: Crates = HashMap::new();
    for entry in fs::read_dir(deps).or_die("cannot access dependencies dir") {
        let entry = entry.or_die("cannot access deps entry");
        let path = entry.path();
        if let Some(f) = path.file_name() {
            let rlib = f.to_string_lossy();
            // pull out the crate name
            if let Some(idx) = rlib.rfind('-') {
                let name = &rlib[3..idx];
                let v = res.entry(name.into())
                    .or_insert_with(|| Vec::new());
                v.push(path.clone());
            }
        }
    }
    res
}

pub fn remove_duplicates(crates: Crates) {
    for (name,paths) in crates {
        if paths.len() > 1 {
            // Sort the paths in ascending time of modification
            let mut mpaths: Vec<_> = paths.into_iter()
                .map(|p| {
                    let time = p.metadata().unwrap().modified().unwrap();
                    (p,time)
                }).collect();
            mpaths.sort_by(|a,b| a.1.cmp(&b.1));

            // ignore the latest, and delete the rest
            mpaths.pop();
            println!("crate {} removing {} items",name,mpaths.len());
            for (p,_) in mpaths {
                fs::remove_file(&p).expect("can't remove rlib");
            }
        }
    }
}

pub fn remove_duplicate_cache_deps(deps: &Path) {
    let deps = get_cache_deps(deps);
    remove_duplicates(deps);
}

<|endoftext|>


<commit_before>use calx::{RngExt, hex_disc, HexGeom, hex_neighbors};
use euclid::{self, vec2};
use rand::{self, Rng};
use std::collections::HashSet;
use std::cmp::Ordering;

pub type Point2D = euclid::Point2D<i32>;
pub type Size2D = euclid::Size2D<i32>;

pub trait Dungeon {
    type Prefab: Prefab;

    /// Return a random prefab room.
    fn sample_prefab<R: Rng>(&mut self, rng: &mut R) -> Self::Prefab;

    /// Add a large open continuous region to dungeon.
    fn dig_chamber<I: IntoIterator<Item = Point2D>>(&mut self, area: I);

    /// Add a narrow corridor to dungeon.
    fn dig_corridor<I: IntoIterator<Item = Point2D>>(&mut self, path: I);

    fn add_prefab(&mut self, prefab: &Self::Prefab, pos: Point2D);

    fn add_door(&mut self, pos: Point2D);

    fn add_up_stairs(&mut self, pos: Point2D);

    fn add_down_stairs(&mut self, pos: Point2D);
}

pub trait Prefab {
    fn contains(&self, pos: Point2D) -> bool;
    fn can_make_door(&self, pos: Point2D) -> bool;
    fn size(&self) -> Size2D;
}

pub struct DigCavesGen {
    domain: Vec<Point2D>,
}

impl DigCavesGen {
    pub fn new<I: IntoIterator<Item = Point2D>>(domain: I) -> DigCavesGen {
        DigCavesGen { domain: domain.into_iter().collect() }
    }

    pub fn dig<R: Rng, D, P>(&self, rng: &mut R, d: &mut D)
    where
        D: Dungeon<Prefab = P>,
        P: Prefab,
    {
        const INIT_P: f32 = 0.65;
        const N_ITERS: usize = 3;
        const WALL_THRESHOLD: u32 = 3;
        const MINIMAL_REGION_SIZE: usize = 8;

        let available: HashSet<Point2D> = self.domain.iter().cloned().collect();

        // Initial cellular automata run, produces okay looking but usually not fully connected
        // cave map.
        let mut dug: HashSet<Point2D> = self.domain
            .iter()
            .filter(|_| rng.with_chance(INIT_P))
            .cloned()
            .collect();

        for _ in 0..N_ITERS {
            let mut dug2 = dug.clone();

            for &p in &self.domain {
                let n_walls: u32 = hex_disc(p, 1)
                    .filter(|&p2| p2 != p)
                    .map(|p2| !dug.contains(&p2) as u32)
                    .sum();
                let n_walls_2: u32 = hex_disc(p, 2)
                    .filter(|&p2| p2 != p)
                    .map(|p2| !dug.contains(&p2) as u32)
                    .sum();

                // Add a wall if there are either neighboring walls or if the cell is in the
                // centes of a large empty region.
                if n_walls >= WALL_THRESHOLD || n_walls_2 == 0 {
                    dug2.remove(&p);
                } else {
                    dug2.insert(p);
                }
            }

            dug = dug2;
        }

        // Connect separate regions
        let mut regions = separate_regions(dug.clone());
        // Remove uselessly small regions
        regions.retain(|a| a.len() >= MINIMAL_REGION_SIZE);
        // Sort unconnected regions from largest to smallest
        regions.sort_unstable_by(|a, b| b.len().cmp(&a.len()));

        dug = regions.pop().unwrap().into_iter().collect();

        while let Some(next_region) = regions.pop() {
            // FIXME: Get sorted container for points
            let sorted_dug = sorted(&dug);
            let sorted_region = sorted(&next_region);

            // Add each secondary region to the dug set, carving a tunnel from dug to its random
            // point.
            // XXX: Might be neater to try to minimize the length of the tunnel to carve
            let p1: Point2D = rand::sample(rng, sorted_dug.into_iter(), 1)[0];
            let p2: Point2D = rand::sample(rng, sorted_region.into_iter(), 1)[0];

            dug.extend(next_region.into_iter());
            dug.extend(jaggly_line(rng, &available, p1, p2).into_iter());
        }

        debug_assert!(!dug.is_empty());
        debug_assert!(is_connected(dug.clone()));

        d.dig_chamber(dug.clone());

        // Pick stair sites
        let mut upstair_sites: Vec<Point2D> = self.domain
            .iter()
            .filter(|&&p| is_upstair_pos(&dug, p))
            .cloned()
            .collect();
        upstair_sites.sort_by(pt_ord);

        let mut downstair_sites: Vec<Point2D> = self.domain
            .iter()
            .filter(|&&p| is_downstair_pos(&dug, p))
            .cloned()
            .collect();
        downstair_sites.sort_by(pt_ord);

        d.add_up_stairs(rand::sample(rng, upstair_sites.into_iter(), 1)[0]);
        d.add_down_stairs(rand::sample(rng, downstair_sites.into_iter(), 1)[0]);

        fn is_upstair_pos(dug: &HashSet<Point2D>, pos: Point2D) -> bool {
            // XXX: Enclosure descriptions, still awful to write...
            dug.contains(&(pos + vec2(1, 1))) && !dug.contains(&(pos + vec2(1, 0))) &&
                !dug.contains(&(pos + vec2(0, 1))) &&
                !dug.contains(&(pos + vec2(-1, -1))) &&
                !dug.contains(&(pos + vec2(-1, 0))) &&
                !dug.contains(&(pos + vec2(0, -1)))
        }

        fn is_downstair_pos(dug: &HashSet<Point2D>, pos: Point2D) -> bool {
            // Downstairs needs an extra enclosure behind it for a tile graphics hack.
            dug.contains(&(pos + vec2(-1, -1))) && !dug.contains(&(pos + vec2(-1, 0))) &&
                !dug.contains(&(pos + vec2(0, -1))) &&
                !dug.contains(&(pos + vec2(1, 1))) &&
                !dug.contains(&(pos + vec2(1, 0))) &&
                !dug.contains(&(pos + vec2(0, 1))) &&
                !dug.contains(&(pos + vec2(2, 2))) &&
                !dug.contains(&(pos + vec2(2, 1))) && !dug.contains(&(pos + vec2(1, 2)))
        }
    }
}

fn separate_regions(mut points: HashSet<Point2D>) -> Vec<HashSet<Point2D>> {
    let mut ret = Vec::new();

    while !points.is_empty() {
        let seed = *points.iter().min_by(|a, b| (a.x, a.y).cmp(&(b.x, b.y))).unwrap();
        let subset = flood_fill(points.clone(), seed);
        debug_assert!(!subset.is_empty());
        points = points.difference(&subset).cloned().collect();
        ret.push(subset);
    }

    ret
}

fn flood_fill(mut points: HashSet<Point2D>, seed: Point2D) -> HashSet<Point2D> {
    let mut ret = HashSet::new();
    let mut edge = HashSet::new();

    if !points.remove(&seed) {
        return ret;
    }
    edge.insert(seed);

    while !edge.is_empty() {
        // XXX: Is there a set data type that supports 'pop'?
        let pos = *edge.iter().next().unwrap();
        edge.remove(&pos);
        ret.insert(pos);

        for p in hex_neighbors(pos) {
            if points.contains(&p) {
                debug_assert!(!ret.contains(&p) && !edge.contains(&p));
                points.remove(&p);
                edge.insert(p);
            }
        }
    }

    ret
}

fn is_connected(points: HashSet<Point2D>) -> bool { separate_regions(points).len() <= 1 }

// TODO return impl
fn jaggly_line<R: Rng>(rng: &mut R, available: &HashSet<Point2D>, p1: Point2D, p2: Point2D) -> Vec<Point2D> {
    let mut ret = Vec::new();
    let mut p = p1;
    ret.push(p);

    while p != p2 {
        let dist = (p2 - p).hex_dist();
        let options = hex_neighbors(p).filter(|&q| (p2 - q).hex_dist() < dist && available.contains(&q));
        p = rand::sample(rng, options, 1)[0];
        ret.push(p);
    }

    ret
}

// FIXME: Get Points in a fixed-order container to maintain determinism. Failing that, here's a
// helper function for turning a set into an ordered list.
fn sorted(set: &HashSet<Point2D>) -> Vec<Point2D> {
    let mut ret: Vec<Point2D> = set.iter().cloned().collect();
    ret.sort_by(pt_ord);
    ret
}

fn pt_ord(a: &Point2D, b: &Point2D) -> Ordering {
    (a.x, a.y).cmp(&(b.x, b.y))
}
<commit_msg>Revert "Determinize mapgen"<commit_after>use calx::{RngExt, hex_disc, HexGeom, hex_neighbors};
use euclid::{self, vec2};
use rand::{self, Rng};
use std::collections::HashSet;

pub type Point2D = euclid::Point2D<i32>;
pub type Size2D = euclid::Size2D<i32>;

pub trait Dungeon {
    type Prefab: Prefab;

    /// Return a random prefab room.
    fn sample_prefab<R: Rng>(&mut self, rng: &mut R) -> Self::Prefab;

    /// Add a large open continuous region to dungeon.
    fn dig_chamber<I: IntoIterator<Item = Point2D>>(&mut self, area: I);

    /// Add a narrow corridor to dungeon.
    fn dig_corridor<I: IntoIterator<Item = Point2D>>(&mut self, path: I);

    fn add_prefab(&mut self, prefab: &Self::Prefab, pos: Point2D);

    fn add_door(&mut self, pos: Point2D);

    fn add_up_stairs(&mut self, pos: Point2D);

    fn add_down_stairs(&mut self, pos: Point2D);
}

pub trait Prefab {
    fn contains(&self, pos: Point2D) -> bool;
    fn can_make_door(&self, pos: Point2D) -> bool;
    fn size(&self) -> Size2D;
}

pub struct DigCavesGen {
    domain: Vec<Point2D>,
}

impl DigCavesGen {
    pub fn new<I: IntoIterator<Item = Point2D>>(domain: I) -> DigCavesGen {
        DigCavesGen { domain: domain.into_iter().collect() }
    }

    pub fn dig<R: Rng, D, P>(&self, rng: &mut R, d: &mut D)
    where
        D: Dungeon<Prefab = P>,
        P: Prefab,
    {
        const INIT_P: f32 = 0.65;
        const N_ITERS: usize = 3;
        const WALL_THRESHOLD: u32 = 3;
        const MINIMAL_REGION_SIZE: usize = 8;

        let available: HashSet<Point2D> = self.domain.iter().cloned().collect();

        // Initial cellular automata run, produces okay looking but usually not fully connected
        // cave map.
        let mut dug: HashSet<Point2D> = self.domain
            .iter()
            .filter(|_| rng.with_chance(INIT_P))
            .cloned()
            .collect();

        for _ in 0..N_ITERS {
            let mut dug2 = dug.clone();

            for &p in &self.domain {
                let n_walls: u32 = hex_disc(p, 1)
                    .filter(|&p2| p2 != p)
                    .map(|p2| !dug.contains(&p2) as u32)
                    .sum();
                let n_walls_2: u32 = hex_disc(p, 2)
                    .filter(|&p2| p2 != p)
                    .map(|p2| !dug.contains(&p2) as u32)
                    .sum();

                // Add a wall if there are either neighboring walls or if the cell is in the
                // centes of a large empty region.
                if n_walls >= WALL_THRESHOLD || n_walls_2 == 0 {
                    dug2.remove(&p);
                } else {
                    dug2.insert(p);
                }
            }

            dug = dug2;
        }

        // Connect separate regions
        let mut regions = separate_regions(dug.clone());
        // Remove uselessly small regions
        regions.retain(|a| a.len() >= MINIMAL_REGION_SIZE);
        // Sort unconnected regions from largest to smallest
        regions.sort_unstable_by(|a, b| b.len().cmp(&a.len()));

        dug = regions.pop().unwrap().into_iter().collect();

        while let Some(next_region) = regions.pop() {
            // Add each secondary region to the dug set, carving a tunnel from dug to its random
            // point.
            // XXX: Might be neater to try to minimize the length of the tunnel to carve
            let p1: Point2D = *rand::sample(rng, dug.iter(), 1)[0];
            let p2: Point2D = *rand::sample(rng, next_region.iter(), 1)[0];

            dug.extend(next_region.into_iter());
            dug.extend(jaggly_line(rng, &available, p1, p2).into_iter());
        }

        debug_assert!(!dug.is_empty());
        debug_assert!(is_connected(dug.clone()));

        d.dig_chamber(dug.clone());

        // Pick stair sites
        let upstair_sites: Vec<Point2D> = self.domain
            .iter()
            .filter(|&&p| is_upstair_pos(&dug, p))
            .cloned()
            .collect();
        let downstair_sites: Vec<Point2D> = self.domain
            .iter()
            .filter(|&&p| is_downstair_pos(&dug, p))
            .cloned()
            .collect();
        d.add_up_stairs(rand::sample(rng, upstair_sites.into_iter(), 1)[0]);
        d.add_down_stairs(rand::sample(rng, downstair_sites.into_iter(), 1)[0]);

        fn is_upstair_pos(dug: &HashSet<Point2D>, pos: Point2D) -> bool {
            // XXX: Enclosure descriptions, still awful to write...
            dug.contains(&(pos + vec2(1, 1))) && !dug.contains(&(pos + vec2(1, 0))) &&
                !dug.contains(&(pos + vec2(0, 1))) &&
                !dug.contains(&(pos + vec2(-1, -1))) &&
                !dug.contains(&(pos + vec2(-1, 0))) &&
                !dug.contains(&(pos + vec2(0, -1)))
        }

        fn is_downstair_pos(dug: &HashSet<Point2D>, pos: Point2D) -> bool {
            // Downstairs needs an extra enclosure behind it for a tile graphics hack.
            dug.contains(&(pos + vec2(-1, -1))) && !dug.contains(&(pos + vec2(-1, 0))) &&
                !dug.contains(&(pos + vec2(0, -1))) &&
                !dug.contains(&(pos + vec2(1, 1))) &&
                !dug.contains(&(pos + vec2(1, 0))) &&
                !dug.contains(&(pos + vec2(0, 1))) &&
                !dug.contains(&(pos + vec2(2, 2))) &&
                !dug.contains(&(pos + vec2(2, 1))) && !dug.contains(&(pos + vec2(1, 2)))
        }
    }
}

fn separate_regions(mut points: HashSet<Point2D>) -> Vec<HashSet<Point2D>> {
    let mut ret = Vec::new();

    while !points.is_empty() {
        let seed = *points.iter().next().unwrap();
        let subset = flood_fill(points.clone(), seed);
        debug_assert!(!subset.is_empty());
        points = points.difference(&subset).cloned().collect();
        ret.push(subset);
    }

    ret
}

fn flood_fill(mut points: HashSet<Point2D>, seed: Point2D) -> HashSet<Point2D> {
    let mut ret = HashSet::new();
    let mut edge = HashSet::new();

    if !points.remove(&seed) {
        return ret;
    }
    edge.insert(seed);

    while !edge.is_empty() {
        // XXX: Is there a set data type that supports 'pop'?
        let pos = *edge.iter().next().unwrap();
        edge.remove(&pos);
        ret.insert(pos);

        for p in hex_neighbors(pos) {
            if points.contains(&p) {
                debug_assert!(!ret.contains(&p) && !edge.contains(&p));
                points.remove(&p);
                edge.insert(p);
            }
        }
    }

    ret
}

fn is_connected(points: HashSet<Point2D>) -> bool { separate_regions(points).len() <= 1 }

// TODO return impl
fn jaggly_line<R: Rng>(rng: &mut R, available: &HashSet<Point2D>, p1: Point2D, p2: Point2D) -> Vec<Point2D> {
    let mut ret = Vec::new();
    let mut p = p1;
    ret.push(p);

    while p != p2 {
        let dist = (p2 - p).hex_dist();
        let options = hex_neighbors(p).filter(|&q| (p2 - q).hex_dist() < dist && available.contains(&q));
        p = rand::sample(rng, options, 1)[0];
        ret.push(p);
    }

    ret
}
<|endoftext|>


<commit_before>use crate::{Geometry, Wkt, WktFloat};
use serde::de::{Deserializer, Error, Visitor};
use std::{
    default::Default,
    fmt::{self, Debug},
    marker::PhantomData,
    str::FromStr,
};

struct WktVisitor<T> {
    _marker: PhantomData<T>,
}

impl<T> Default for WktVisitor<T> {
    fn default() -> Self {
        WktVisitor {
            _marker: PhantomData::default(),
        }
    }
}

impl<'de, T> Visitor<'de> for WktVisitor<T>
where
    T: FromStr + Default + Debug + WktFloat,
{
    type Value = Wkt<T>;
    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "a valid WKT format")
    }
    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
    where
        E: Error,
    {
        Wkt::from_str(s).map_err(|e| serde::de::Error::custom(e))
    }
}

impl<'de, T> serde::Deserialize<'de> for Wkt<T>
where
    T: FromStr + Default + Debug + WktFloat,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(WktVisitor::default())
    }
}

struct GeometryVisitor<T> {
    _marker: PhantomData<T>,
}

impl<T> Default for GeometryVisitor<T> {
    fn default() -> Self {
        GeometryVisitor {
            _marker: PhantomData::default(),
        }
    }
}

impl<'de, T> Visitor<'de> for GeometryVisitor<T>
where
    T: FromStr + Default + WktFloat,
{
    type Value = Geometry<T>;
    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "a valid WKT format")
    }
    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
    where
        E: Error,
    {
        let mut wkt = Wkt::from_str(s).map_err(|e| serde::de::Error::custom(e))?;
        if wkt.items.len() == 1 {
            Ok(wkt.items.remove(0))
        } else {
            Err(serde::de::Error::custom(
                "WKT should have only 1 Geometry item",
            ))
        }
    }
}

impl<'de, T> serde::Deserialize<'de> for Geometry<T>
where
    T: FromStr + Default + WktFloat,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(GeometryVisitor::default())
    }
}

/// This is a helper function to convert directly from WKT format into a geo_types::Geometry.
/// ```
/// # extern crate wkt;
/// # extern crate geo_types;
/// # extern crate serde_json;
/// use geo_types::Geometry;
///
/// #[derive(serde::Deserialize)]
/// struct MyType {
///     #[serde(deserialize_with = "wkt::deserialize_geometry")]
///     pub geometry: Geometry<f64>,
/// }
///
/// let json = r#"{ "geometry": "POINT (3.14 42)" }"#;
/// let my_type: MyType = serde_json::from_str(json).unwrap();
/// assert!(matches!(my_type.geometry, Geometry::Point(_)));
/// ```
#[cfg(feature = "geo-types")]
pub fn deserialize_geometry<'de, D>(deserializer: D) -> Result<geo_types::Geometry<f64>, D::Error>
where
    D: Deserializer<'de>,
{
    use serde::Deserialize;
    use std::convert::TryInto;
    let geometry: Geometry<f64> = Geometry::deserialize(deserializer)?;
    let geometry: geo_types::Geometry<f64> = geometry.try_into().unwrap();
    Ok(geometry)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        types::{Coord, Point},
        Geometry,
    };
    use serde::de::{
        value::{Error, StrDeserializer},
        Deserializer, Error as _, IntoDeserializer,
    };

    mod wkt {
        use super::*;

        #[test]
        fn deserialize() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (10 20.1)".into_deserializer();
            let wkt = deserializer
                .deserialize_any(WktVisitor::<f64>::default())
                .unwrap();
            assert!(matches!(
                wkt.items[0],
                Geometry::Point(Point(Some(Coord {
                    x: _, // floating-point types cannot be used in patterns
                    y: _, // floating-point types cannot be used in patterns
                    z: None,
                    m: None,
                })))
            ));
        }

        #[test]
        fn deserialize_error() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (10 20.1A)".into_deserializer();
            let wkt = deserializer.deserialize_any(WktVisitor::<f64>::default());
            assert_eq!(
                wkt.unwrap_err(),
                Error::custom("Expected a number for the Y coordinate")
            );
        }
    }

    mod geometry {
        use super::*;

        #[test]
        fn deserialize() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (42 3.14)".into_deserializer();
            let geometry = deserializer
                .deserialize_any(GeometryVisitor::<f64>::default())
                .unwrap();
            assert!(matches!(
                geometry,
                Geometry::Point(Point(Some(Coord {
                    x: _, // floating-point types cannot be used in patterns
                    y: _, // floating-point types cannot be used in patterns
                    z: None,
                    m: None,
                })))
            ));
        }

        #[test]
        fn deserialize_error() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (42 PI3.14)".into_deserializer();
            let geometry = deserializer.deserialize_any(GeometryVisitor::<f64>::default());
            assert_eq!(
                geometry.unwrap_err(),
                Error::custom("Expected a number for the Y coordinate")
            );
        }
    }
}
<commit_msg>remove unwrap in deserialize helper function<commit_after>use crate::{Geometry, Wkt, WktFloat};
use serde::de::{Deserializer, Error, Visitor};
use std::{
    default::Default,
    fmt::{self, Debug},
    marker::PhantomData,
    str::FromStr,
};

struct WktVisitor<T> {
    _marker: PhantomData<T>,
}

impl<T> Default for WktVisitor<T> {
    fn default() -> Self {
        WktVisitor {
            _marker: PhantomData::default(),
        }
    }
}

impl<'de, T> Visitor<'de> for WktVisitor<T>
where
    T: FromStr + Default + Debug + WktFloat,
{
    type Value = Wkt<T>;
    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "a valid WKT format")
    }
    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
    where
        E: Error,
    {
        Wkt::from_str(s).map_err(|e| serde::de::Error::custom(e))
    }
}

impl<'de, T> serde::Deserialize<'de> for Wkt<T>
where
    T: FromStr + Default + Debug + WktFloat,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(WktVisitor::default())
    }
}

struct GeometryVisitor<T> {
    _marker: PhantomData<T>,
}

impl<T> Default for GeometryVisitor<T> {
    fn default() -> Self {
        GeometryVisitor {
            _marker: PhantomData::default(),
        }
    }
}

impl<'de, T> Visitor<'de> for GeometryVisitor<T>
where
    T: FromStr + Default + WktFloat,
{
    type Value = Geometry<T>;
    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "a valid WKT format")
    }
    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
    where
        E: Error,
    {
        let mut wkt = Wkt::from_str(s).map_err(|e| serde::de::Error::custom(e))?;
        if wkt.items.len() == 1 {
            Ok(wkt.items.remove(0))
        } else {
            Err(serde::de::Error::custom(
                "WKT should have only 1 Geometry item",
            ))
        }
    }
}

impl<'de, T> serde::Deserialize<'de> for Geometry<T>
where
    T: FromStr + Default + WktFloat,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(GeometryVisitor::default())
    }
}

/// This is a helper function to convert directly from WKT format into a geo_types::Geometry.
/// ```
/// # extern crate wkt;
/// # extern crate geo_types;
/// # extern crate serde_json;
/// use geo_types::Geometry;
///
/// #[derive(serde::Deserialize)]
/// struct MyType {
///     #[serde(deserialize_with = "wkt::deserialize_geometry")]
///     pub geometry: Geometry<f64>,
/// }
///
/// let json = r#"{ "geometry": "POINT (3.14 42)" }"#;
/// let my_type: MyType = serde_json::from_str(json).unwrap();
/// assert!(matches!(my_type.geometry, Geometry::Point(_)));
/// ```
#[cfg(feature = "geo-types")]
pub fn deserialize_geometry<'de, D>(deserializer: D) -> Result<geo_types::Geometry<f64>, D::Error>
where
    D: Deserializer<'de>,
{
    use serde::Deserialize;
    Geometry::deserialize(deserializer).and_then(|g: Geometry<f64>| {
        use std::convert::TryInto;
        g.try_into().map_err(|e| D::Error::custom(e))
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        types::{Coord, Point},
        Geometry,
    };
    use serde::de::{
        value::{Error, StrDeserializer},
        Deserializer, Error as _, IntoDeserializer,
    };

    mod wkt {
        use super::*;

        #[test]
        fn deserialize() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (10 20.1)".into_deserializer();
            let wkt = deserializer
                .deserialize_any(WktVisitor::<f64>::default())
                .unwrap();
            assert!(matches!(
                wkt.items[0],
                Geometry::Point(Point(Some(Coord {
                    x: _, // floating-point types cannot be used in patterns
                    y: _, // floating-point types cannot be used in patterns
                    z: None,
                    m: None,
                })))
            ));
        }

        #[test]
        fn deserialize_error() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (10 20.1A)".into_deserializer();
            let wkt = deserializer.deserialize_any(WktVisitor::<f64>::default());
            assert_eq!(
                wkt.unwrap_err(),
                Error::custom("Expected a number for the Y coordinate")
            );
        }
    }

    mod geometry {
        use super::*;

        #[test]
        fn deserialize() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (42 3.14)".into_deserializer();
            let geometry = deserializer
                .deserialize_any(GeometryVisitor::<f64>::default())
                .unwrap();
            assert!(matches!(
                geometry,
                Geometry::Point(Point(Some(Coord {
                    x: _, // floating-point types cannot be used in patterns
                    y: _, // floating-point types cannot be used in patterns
                    z: None,
                    m: None,
                })))
            ));
        }

        #[test]
        fn deserialize_error() {
            let deserializer: StrDeserializer<'_, Error> = "POINT (42 PI3.14)".into_deserializer();
            let geometry = deserializer.deserialize_any(GeometryVisitor::<f64>::default());
            assert_eq!(
                geometry.unwrap_err(),
                Error::custom("Expected a number for the Y coordinate")
            );
        }
    }
}
<|endoftext|>


<commit_before>use std;

pub trait Color<T> : Clone + Copy + PartialEq<T> {
    fn from_byte(b: u8) -> T;
    fn empty_color() -> T;
    fn ojama_color() -> T;
    fn wall_color() -> T;

    fn as_usize(&self) -> usize;
    fn to_char(&self) -> char;
    fn is_normal_color(&self) -> bool;
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PuyoColor {
    EMPTY  = 0,
    OJAMA  = 1,
    WALL   = 2,
    IRON   = 3,
    RED    = 4,
    BLUE   = 5,
    YELLOW = 6,
    GREEN  = 7
}

impl PuyoColor {
    pub fn to_string(&self) -> &'static str {
        match *self {
            PuyoColor::EMPTY  => "EMPTY",
            PuyoColor::OJAMA  => "OJAMA",
            PuyoColor::WALL   => "WALL",
            PuyoColor::IRON   => "IRON",
            PuyoColor::RED    => "RED",
            PuyoColor::BLUE   => "BLUE",
            PuyoColor::YELLOW => "YELLOW",
            PuyoColor::GREEN  => "GREEN",
        }
    }
}

impl Color<PuyoColor> for PuyoColor {
    fn from_byte(c: u8) -> PuyoColor {
        match c {
            b' ' | b'.' => PuyoColor::EMPTY,
            b'O' | b'@' => PuyoColor::OJAMA,
            b'#' => PuyoColor::WALL,
            b'&' => PuyoColor::IRON,
            b'R' | b'r' => PuyoColor::RED,
            b'B' | b'b' => PuyoColor::BLUE,
            b'Y' | b'y' => PuyoColor::YELLOW,
            b'G' | b'g' => PuyoColor::GREEN,
            _ => PuyoColor::EMPTY,
        }
    }

    fn empty_color() -> PuyoColor {
        PuyoColor::EMPTY
    }

    fn ojama_color() -> PuyoColor {
        PuyoColor::OJAMA
    }

    fn wall_color() -> PuyoColor {
        PuyoColor::WALL
    }

    fn as_usize(&self) -> usize {
        *self as usize
    }

    fn is_normal_color(&self) -> bool {
        let x = *self as i32;
        (x & 4) != 0
    }

    fn to_char(&self) -> char {
        match *self {
            PuyoColor::EMPTY  => ' ',
            PuyoColor::OJAMA  => 'O',
            PuyoColor::WALL   => '#',
            PuyoColor::IRON   => '&',
            PuyoColor::RED    => 'R',
            PuyoColor::BLUE   => 'B',
            PuyoColor::YELLOW => 'Y',
            PuyoColor::GREEN  => 'G',
        }
    }
}

impl std::fmt::Display for PuyoColor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.to_char())
    }
}

pub const ALL_PUYO_COLORS: [PuyoColor; 8] = [
    PuyoColor::EMPTY, PuyoColor::OJAMA, PuyoColor::WALL, PuyoColor::IRON,
    PuyoColor::RED, PuyoColor::BLUE, PuyoColor::YELLOW, PuyoColor::GREEN,
];

pub const NORMAL_PUYO_COLORS: [PuyoColor; 4] = [
    PuyoColor::RED, PuyoColor::BLUE, PuyoColor::YELLOW, PuyoColor::GREEN,
];

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

    #[test]
    fn it_works() {
    }

    #[test]
    fn test_normal_color() {
        assert!(!PuyoColor::EMPTY.is_normal_color());
        assert!(!PuyoColor::OJAMA.is_normal_color());
        assert!(!PuyoColor::WALL.is_normal_color());
        assert!(!PuyoColor::IRON.is_normal_color());
        assert!(PuyoColor::RED.is_normal_color());
        assert!(PuyoColor::BLUE.is_normal_color());
        assert!(PuyoColor::YELLOW.is_normal_color());
        assert!(PuyoColor::GREEN.is_normal_color());
    }

    #[test]
    fn test_to_char() {
        assert_eq!(PuyoColor::EMPTY.to_string(), "EMPTY");
        assert_eq!(PuyoColor::OJAMA.to_string(), "OJAMA");
        assert_eq!(PuyoColor::WALL.to_string(), "WALL");
        assert_eq!(PuyoColor::IRON.to_string(), "IRON");
        assert_eq!(PuyoColor::RED.to_string(), "RED");
        assert_eq!(PuyoColor::BLUE.to_string(), "BLUE");
        assert_eq!(PuyoColor::YELLOW.to_string(), "YELLOW");
        assert_eq!(PuyoColor::GREEN.to_string(), "GREEN");
    }

    #[test]
    fn test_from_byte() {
        assert_eq!(PuyoColor::from_byte(b' '), PuyoColor::EMPTY);
        assert_eq!(PuyoColor::from_byte(b'.'), PuyoColor::EMPTY);
        assert_eq!(PuyoColor::from_byte(b'&'), PuyoColor::IRON);
        assert_eq!(PuyoColor::from_byte(b'#'), PuyoColor::WALL);
        assert_eq!(PuyoColor::from_byte(b'O'), PuyoColor::OJAMA);
        assert_eq!(PuyoColor::from_byte(b'R'), PuyoColor::RED);
        assert_eq!(PuyoColor::from_byte(b'B'), PuyoColor::BLUE);
        assert_eq!(PuyoColor::from_byte(b'Y'), PuyoColor::YELLOW);
        assert_eq!(PuyoColor::from_byte(b'G'), PuyoColor::GREEN);
    }
}

#[derive(Clone, Copy, PartialEq)]
pub enum RealColor {
    EMPTY  = 0,
    WALL   = 1,
    OJAMA  = 2,
    RED    = 3,
    BLUE   = 4,
    YELLOW = 5,
    GREEN  = 6,
    PURPLE = 7,
}
<commit_msg>Add as_str etc in Color<commit_after>use std;

pub trait Color<T> : Clone + Copy + PartialEq<T> {
    fn from_byte(b: u8) -> T;
    fn empty_color() -> T;
    fn ojama_color() -> T;
    fn wall_color() -> T;

    fn as_usize(&self) -> usize;
    fn to_char(&self) -> char;
    fn is_normal_color(&self) -> bool;

    fn as_str(&self) -> &'static str;
    // 2 byte string
    fn as_str_wide(&self) -> &'static str;
    // 2 byte string (with color escape sequence)
    fn as_colored_str_wide(&self) -> &'static str;
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PuyoColor {
    EMPTY  = 0,
    OJAMA  = 1,
    WALL   = 2,
    IRON   = 3,
    RED    = 4,
    BLUE   = 5,
    YELLOW = 6,
    GREEN  = 7
}

impl PuyoColor {
    pub fn to_string(&self) -> &'static str {
        match *self {
            PuyoColor::EMPTY  => "EMPTY",
            PuyoColor::OJAMA  => "OJAMA",
            PuyoColor::WALL   => "WALL",
            PuyoColor::IRON   => "IRON",
            PuyoColor::RED    => "RED",
            PuyoColor::BLUE   => "BLUE",
            PuyoColor::YELLOW => "YELLOW",
            PuyoColor::GREEN  => "GREEN",
        }
    }
}

impl Color<PuyoColor> for PuyoColor {
    fn from_byte(c: u8) -> PuyoColor {
        match c {
            b' ' | b'.' => PuyoColor::EMPTY,
            b'O' | b'@' => PuyoColor::OJAMA,
            b'#' => PuyoColor::WALL,
            b'&' => PuyoColor::IRON,
            b'R' | b'r' => PuyoColor::RED,
            b'B' | b'b' => PuyoColor::BLUE,
            b'Y' | b'y' => PuyoColor::YELLOW,
            b'G' | b'g' => PuyoColor::GREEN,
            _ => PuyoColor::EMPTY,
        }
    }

    fn empty_color() -> PuyoColor {
        PuyoColor::EMPTY
    }

    fn ojama_color() -> PuyoColor {
        PuyoColor::OJAMA
    }

    fn wall_color() -> PuyoColor {
        PuyoColor::WALL
    }

    fn as_usize(&self) -> usize {
        *self as usize
    }

    fn is_normal_color(&self) -> bool {
        let x = *self as i32;
        (x & 4) != 0
    }

    fn to_char(&self) -> char {
        match *self {
            PuyoColor::EMPTY  => ' ',
            PuyoColor::OJAMA  => 'O',
            PuyoColor::WALL   => '#',
            PuyoColor::IRON   => '&',
            PuyoColor::RED    => 'R',
            PuyoColor::BLUE   => 'B',
            PuyoColor::YELLOW => 'Y',
            PuyoColor::GREEN  => 'G',
        }
    }

    fn as_str(&self) -> &'static str {
        match *self {
            PuyoColor::EMPTY  => " ",
            PuyoColor::OJAMA  => "@",
            PuyoColor::WALL   => "#",
            PuyoColor::IRON   => "&",
            PuyoColor::RED    => "R",
            PuyoColor::BLUE   => "B",
            PuyoColor::YELLOW => "Y",
            PuyoColor::GREEN  => "G",
        }
    }

    fn as_str_wide(&self) -> &'static str {
        match *self {
            PuyoColor::EMPTY  => "  ",
            PuyoColor::OJAMA  => "@ ",
            PuyoColor::WALL   => "# ",
            PuyoColor::IRON   => "& ",
            PuyoColor::RED    => "R ",
            PuyoColor::BLUE   => "B ",
            PuyoColor::YELLOW => "Y ",
            PuyoColor::GREEN  => "G ",
        }
    }

    fn as_colored_str_wide(&self) -> &'static str {
        match *self {
            PuyoColor::EMPTY  => "  ",
            PuyoColor::OJAMA  => "@@",
            PuyoColor::WALL   => "##",
            PuyoColor::IRON   => "&&",
            PuyoColor::RED    => "\x1b[41m  \x1b[49m",
            PuyoColor::BLUE   => "\x1b[44m  \x1b[49m",
            PuyoColor::YELLOW => "\x1b[43m  \x1b[49m",
            PuyoColor::GREEN  => "\x1b[42m  \x1b[49m",
        }
    }
}

impl std::fmt::Display for PuyoColor {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.to_char())
    }
}

pub const ALL_PUYO_COLORS: [PuyoColor; 8] = [
    PuyoColor::EMPTY, PuyoColor::OJAMA, PuyoColor::WALL, PuyoColor::IRON,
    PuyoColor::RED, PuyoColor::BLUE, PuyoColor::YELLOW, PuyoColor::GREEN,
];

pub const NORMAL_PUYO_COLORS: [PuyoColor; 4] = [
    PuyoColor::RED, PuyoColor::BLUE, PuyoColor::YELLOW, PuyoColor::GREEN,
];

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

    #[test]
    fn it_works() {
    }

    #[test]
    fn test_normal_color() {
        assert!(!PuyoColor::EMPTY.is_normal_color());
        assert!(!PuyoColor::OJAMA.is_normal_color());
        assert!(!PuyoColor::WALL.is_normal_color());
        assert!(!PuyoColor::IRON.is_normal_color());
        assert!(PuyoColor::RED.is_normal_color());
        assert!(PuyoColor::BLUE.is_normal_color());
        assert!(PuyoColor::YELLOW.is_normal_color());
        assert!(PuyoColor::GREEN.is_normal_color());
    }

    #[test]
    fn test_to_char() {
        assert_eq!(PuyoColor::EMPTY.to_string(), "EMPTY");
        assert_eq!(PuyoColor::OJAMA.to_string(), "OJAMA");
        assert_eq!(PuyoColor::WALL.to_string(), "WALL");
        assert_eq!(PuyoColor::IRON.to_string(), "IRON");
        assert_eq!(PuyoColor::RED.to_string(), "RED");
        assert_eq!(PuyoColor::BLUE.to_string(), "BLUE");
        assert_eq!(PuyoColor::YELLOW.to_string(), "YELLOW");
        assert_eq!(PuyoColor::GREEN.to_string(), "GREEN");
    }

    #[test]
    fn test_from_byte() {
        assert_eq!(PuyoColor::from_byte(b' '), PuyoColor::EMPTY);
        assert_eq!(PuyoColor::from_byte(b'.'), PuyoColor::EMPTY);
        assert_eq!(PuyoColor::from_byte(b'&'), PuyoColor::IRON);
        assert_eq!(PuyoColor::from_byte(b'#'), PuyoColor::WALL);
        assert_eq!(PuyoColor::from_byte(b'O'), PuyoColor::OJAMA);
        assert_eq!(PuyoColor::from_byte(b'R'), PuyoColor::RED);
        assert_eq!(PuyoColor::from_byte(b'B'), PuyoColor::BLUE);
        assert_eq!(PuyoColor::from_byte(b'Y'), PuyoColor::YELLOW);
        assert_eq!(PuyoColor::from_byte(b'G'), PuyoColor::GREEN);
    }
}

#[derive(Clone, Copy, PartialEq)]
pub enum RealColor {
    EMPTY  = 0,
    WALL   = 1,
    OJAMA  = 2,
    RED    = 3,
    BLUE   = 4,
    YELLOW = 5,
    GREEN  = 6,
    PURPLE = 7,
}
<|endoftext|>