<commit_before>use kbdgen::{
    bundle::{
        models::{IsoKey, TargetX11},
        KeyValue,
    },
    Load, ProjectBundle, Save,
};
use kbdgen_cli::{update_repo, xkb_dir};
use snafu::{OptionExt, ResultExt, Snafu};
use snafu_cli_debug::SnafuCliDebug;
use std::{
    collections::BTreeMap,
    convert::TryFrom,
    error::Error as StdError,
    path::{Path, PathBuf},
};
use structopt::StructOpt;
use strum::IntoEnumIterator;
use xkb_parser::{ast, parse, Xkb};

const REPO_URL: &str = "https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config.git";

#[derive(Debug, StructOpt)]
struct Cli {
    /// Target directory for `.kbdgen` bundle
    #[structopt(parse(from_os_str))]
    output: PathBuf,

    /// Update bundle if it already exists
    #[structopt(short = "u", long = "update")]
    update_bundle: bool,

    #[structopt(flatten)]
    verbose: clap_verbosity_flag::Verbosity,
}

fn main() -> Result<(), Error> {
    let opts = Cli::from_args();
    let _ = opts.verbose.setup_env_logger("xkb-to-kbdgen");

    let mut bundle = if opts.update_bundle {
        let b = ProjectBundle::load(&opts.output).context(CannotLoadBundle)?;
        log::info!(
            "Bundle `{}` loaded, will try to update it",
            opts.output.display()
        );
        b
    } else {
        log::info!("Will create new bundle in `{}`", opts.output.display());
        ProjectBundle::default()
    };

    update_repo("xkb", &xkb_dir(), REPO_URL).context(FailedRepoUpdate)?;

    let (locale, file_path) = select_base_locale()?;
    let file = std::fs::read_to_string(&file_path).context(CannotOpenFile { path: &file_path })?;
    let section = select_sub_locale(&file).context(CannotReadXkb { path: &file_path })?;
    log::info!(
        "selected locale `{}` with style `{}`",
        locale,
        section.name.as_ref()
    );
    let include_dir = xkb_dir().join("symbols");
    let (keys, dead_keys) = resolve_keys(&section, &include_dir)?;

    // Set X11 target metadata to some values that not completely random
    bundle.targets.x11 = Some(TargetX11 {
        version: chrono::Utc::now().format("%Y-%m-%d").to_string(),
        build: 1,
    });

    // Update X11 layout entry
    let mut layout = bundle.layouts.entry(locale.to_string()).or_default();
    layout.modes.x11 = Some(keys.into_iter().map(|(k, v)| (k, v.into())).collect());

    // Do a neat switcheroo with possibly-None dead keys map
    let mut dead = layout.dead_keys.take().unwrap_or_default();
    dead.insert("x11".to_string(), dead_keys);
    layout.dead_keys = Some(dead);

    bundle.save(&opts.output).context(CannotBeSaved)?;
    log::info!("New bundle written to `{}`.", opts.output.display());
    log::info!(
        "It now contains a X11 target with version `{}`.",
        bundle.targets.x11.unwrap().version
    );

    Ok(())
}

fn select_base_locale() -> Result<(String, PathBuf), Error> {
    let kbd_path = xkb_dir().join("symbols");
    let files: BTreeMap<String, PathBuf> = globwalk::GlobWalkerBuilder::new(&kbd_path, "**")
        .max_depth(8)
        .build()
        .unwrap()
        .filter_map(Result::ok)
        .filter(|entry| !entry.path().ends_with("Makefile.am"))
        .map(|entry| {
            (
                entry
                    .path()
                    .strip_prefix(&kbd_path)
                    .unwrap()
                    .to_string_lossy()
                    .into(),
                entry.path().into(),
            )
        })
        .collect();

    let options = skim::SkimOptionsBuilder::default()
        .prompt(Some("Which keyboard base to load? "))
        .exact(true)
        .ansi(true)
        .build()
        .unwrap();

    let text = files
        .iter()
        .fold(String::new(), |mut text, (file_name, _path)| {
            text.push_str(&file_name);
            text.push_str("\n");
            text
        })
        .into_bytes();
    let cur = std::io::Cursor::new(text);

    let result = skim::Skim::run_with(&options, Some(Box::new(cur))).context(NoLocaleSelected)?;
    let result = result
        .selected_items
        .first()
        .context(NoLocaleSelected)?
        .get_text();

    Ok((result.into(), files[result].clone()))
}

fn select_sub_locale(file: &str) -> Result<ast::XkbSymbols, Box<dyn StdError>> {
    let sections = fetch_symbols(&file)?;

    if sections.len() == 1 {
        return Ok(sections[0].clone());
    }

    let options = skim::SkimOptionsBuilder::default()
        .prompt(Some("Which section to load? "))
        .exact(true)
        .ansi(true)
        .build()
        .unwrap();

    let text = sections
        .iter()
        .fold(String::new(), |mut text, section| {
            text.push_str(&String::from(&section.name));
            text.push_str("\n");
            text
        })
        .into_bytes();
    let cur = std::io::Cursor::new(text);

    let result = skim::Skim::run_with(&options, Some(Box::new(cur))).context(NoLocaleSelected)?;
    let result = result
        .selected_items
        .first()
        .context(NoLocaleSelected)?
        .get_text();

    Ok(sections
        .into_iter()
        .find(|ast::XkbSymbols { name, .. }| name.content == result)
        .context(NoLocaleSelected)?)
}

type LayeredKeyMap = BTreeMap<String, BTreeMap<IsoKey, KeyValue>>;
type DeadKeyMap = BTreeMap<String, Vec<String>>;

/// Build a partial layout from an xkb symbols definition
///
/// It will return two maps:
///
/// 1. A map (`Layer -> IsoKey -> KeyValue`) that will eventually become
///    `layout.modes.x11`
/// 2. A map of dead keys (`Layer -> Vec<KeyValue`) that will become
///    `layout.dead_keys["x11"]`
fn resolve_keys(
    symbols: &ast::XkbSymbols,
    include_dir: &Path,
) -> Result<(LayeredKeyMap, DeadKeyMap), Error> {
    // TODO: figure out the actual names for the layers
    let layers = &["default", "shift", "alt", "shift+alt"];

    // Collect keys into modes while iterating
    let mut map: BTreeMap<String, BTreeMap<IsoKey, KeyValue>> = BTreeMap::new();

    // Collect dead keys while iterating.
    let mut dead_keys: BTreeMap<String, Vec<String>> = BTreeMap::new();

    for key in &extract_keys(symbols, include_dir)? {
        for (codepoint, mode) in key.values.iter().zip(layers) {
            map.entry(mode.to_string())
                .or_default()
                .insert(key.iso_key, KeyValue::from(codepoint.as_ref().to_string()));

            if let Codepoint::Deadkey(c) = codepoint {
                dead_keys
                    .entry(mode.to_string())
                    .or_default()
                    .push(c.clone());
            }
        }
    }

    // Fill in all the ISO keys that were not defined
    for mode in map.values_mut() {
        for key in IsoKey::iter() {
            mode.entry(key).or_insert_with(|| None.into());
        }
    }

    Ok((map, dead_keys))
}

fn fetch_symbols(source: &str) -> Result<Vec<ast::XkbSymbols>, Box<dyn StdError>> {
    let x: Xkb = parse(&source)?;

    let symbols = x
        .definitions
        .iter()
        .filter_map(|x| {
            if let ast::Directive::XkbSymbols(x) = &x.directive {
                Some(x.clone())
            } else {
                None
            }
        })
        .collect();

    Ok(symbols)
}

fn extract_keys(symbols: &ast::XkbSymbols, include_dir: &Path) -> Result<Vec<Key>, Error> {
    symbols.value.iter().try_fold(Vec::new(), |mut res, item| {
        match item {
            ast::XkbSymbolsItem::Include(ast::Include { name }) => {
                res.extend(read_include(name, include_dir)?)
            }
            ast::XkbSymbolsItem::Key(k) => match Key::try_from(k) {
                Ok(k) => res.push(k),
                Err(e) => log::warn!("Skipping `{:?}`: {}", k, e),
            },
            _ => {}
        }

        Ok(res)
    })
}

fn parse_include_name(include: &str) -> Result<(&str, Option<&str>), Error> {
    fn parse(input: &str) -> nom::IResult<&str, (&str, Option<&str>)> {
        use nom::{
            bytes::complete::{is_not, take_until},
            character::complete::char,
            combinator::{complete, opt},
            sequence::{delimited, tuple},
        };

        complete(tuple((
            take_until("("),
            opt(delimited(char('('), is_not(")"), char(')'))),
        )))(input)
    }

    if include.contains('(') {
        let (_, (name, section)) = parse(include).map_err(|_| Error::FailedToParseInclude {
            include: include.to_string(),
        })?;
        Ok((name, section))
    } else {
        Ok((include, None))
    }
}

fn read_include(name: &str, include_dir: &Path) -> Result<Vec<Key>, Error> {
    let (name, section_name) = parse_include_name(name)?;
    let file_path = include_dir.join(name);
    let file = std::fs::read_to_string(&file_path).context(CannotOpenFile { path: &file_path })?;
    let sections = fetch_symbols(&file).context(CannotReadXkb { path: &file_path })?;
    let symbols = if let Some(section_name) = section_name {
        sections
            .iter()
            .find(|ast::XkbSymbols { name, .. }| name.content == section_name)
            .context(SymbolSectionNotFound {
                path: &file_path,
                section_name,
            })?
    } else {
        sections.get(0).context(SymbolSectionNotFound {
            path: &file_path,
            section_name: "first section (no section name specified)",
        })?
    };

    Ok(extract_keys(&symbols, include_dir)?)
}

#[derive(Debug, Clone)]
struct Key {
    iso_key: IsoKey,
    values: Vec<Codepoint>,
}

impl<'a, 'src> TryFrom<&'a ast::Key<'src>> for Key {
    type Error = Error;

    fn try_from(key: &ast::Key) -> Result<Key, Error> {
        let iso_key = iso_key_from_xkb_symbol(&key.id.as_ref()).context(UnknownIsoKey {
            value: key.id.content,
        })?;
        let values = key.values.iter().try_fold(Vec::new(), |mut res, v| {
            if let ast::KeyValue::KeyNames(ast::KeyNames { values }) = v {
                res.extend(
                    values
                        .iter()
                        .map(|x| Codepoint::from_keysym(x.as_ref()))
                        .collect::<Result<Vec<_>, Error>>()?,
                )
            };
            Ok(res)
        })?;
        Ok(Key { iso_key, values })
    }
}

fn iso_key_from_xkb_symbol(s: &str) -> Option<IsoKey> {
    if !s.starts_with('A') {
        return None;
    }
    let s = &s[1..];
    s.parse().ok()
}

#[derive(Debug, Clone)]
enum Codepoint {
    Regular(String),
    Deadkey(String),
}

impl Codepoint {
    fn from_keysym(keysym: &str) -> Result<Codepoint, Error> {
        fn parse_unicode_def(input: &str) -> nom::IResult<&str, &str> {
            use nom::{
                bytes::complete::{tag, take_while1},
                combinator::complete,
            };

            fn is_hex_digit(c: char) -> bool {
                c.is_digit(16)
            }

            let (input, _) = tag("U")(input)?;
            complete(take_while1(is_hex_digit))(input)
        }

        if let Ok((_, c)) = parse_unicode_def(keysym) {
            let c: u32 = u32::from_str_radix(c, 16).unwrap_or_else(|_| {
                panic!(
                    "found unicode-like codepoint but couldn't parse hex `{}`",
                    c
                )
            });
            return Ok(Codepoint::Regular(
                std::char::from_u32(c).unwrap().to_string(),
            ));
        }

        if let Some(k) = x11_keysymdef::lookup_by_name(&keysym) {
            if keysym.starts_with("dead_") {
                return Ok(Codepoint::Deadkey(k.unicode.to_string()));
            } else {
                return Ok(Codepoint::Regular(k.unicode.to_string()));
            }
        }

        Err(Error::UnknownCodepointMapping {
            keysym: keysym.to_string(),
        })
    }
}

impl AsRef<str> for Codepoint {
    fn as_ref(&self) -> &str {
        match self {
            Codepoint::Regular(x) => &x,
            Codepoint::Deadkey(x) => &x,
        }
    }
}

#[derive(Snafu, SnafuCliDebug)]
pub enum Error {
    #[snafu(display("Updating XKB repo failed"))]
    FailedRepoUpdate {
        source: kbdgen_cli::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("No locale selected"))]
    NoLocaleSelected { backtrace: snafu::Backtrace },
    #[snafu(display("Could load XKB file `{}`", path.display()))]
    CannotOpenFile {
        path: PathBuf,
        source: std::io::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could read XKB file `{}`", path.display()))]
    CannotReadXkb {
        path: PathBuf,
        source: Box<dyn StdError>,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could not translate key id `{}` to iso key", value))]
    UnknownIsoKey {
        value: String,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could not translate keysym `{}` to codepoint", keysym))]
    UnknownCodepointMapping { keysym: String },
    #[snafu(display("Failed to parse include `{}`", include))]
    FailedToParseInclude { include: String },
    #[snafu(display("Failed to find/read section `{}` in `{}`", section_name, path.display()))]
    SymbolSectionNotFound {
        path: PathBuf,
        section_name: String,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could not load kbdgen bundle"))]
    CannotLoadBundle {
        source: kbdgen::LoadError,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could write kbdgen bundle"))]
    CannotBeSaved {
        source: kbdgen::SaveError,
        backtrace: snafu::Backtrace,
    },
}
<commit_msg>Add some logging goodness<commit_after>use kbdgen::{
    bundle::{
        models::{IsoKey, TargetX11},
        KeyValue,
    },
    Load, ProjectBundle, Save,
};
use kbdgen_cli::{update_repo, xkb_dir};
use snafu::{OptionExt, ResultExt, Snafu};
use snafu_cli_debug::SnafuCliDebug;
use std::{
    collections::BTreeMap,
    convert::TryFrom,
    error::Error as StdError,
    path::{Path, PathBuf},
};
use structopt::StructOpt;
use strum::IntoEnumIterator;
use xkb_parser::{ast, parse, Xkb};

const REPO_URL: &str = "https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config.git";

#[derive(Debug, StructOpt)]
struct Cli {
    /// Target directory for `.kbdgen` bundle
    #[structopt(parse(from_os_str))]
    output: PathBuf,

    /// Update bundle if it already exists
    #[structopt(short = "u", long = "update")]
    update_bundle: bool,

    #[structopt(flatten)]
    verbose: clap_verbosity_flag::Verbosity,
}

fn main() -> Result<(), Error> {
    let opts = Cli::from_args();
    let _ = opts.verbose.setup_env_logger("xkb-to-kbdgen");

    let mut bundle = if opts.update_bundle {
        let b = ProjectBundle::load(&opts.output).context(CannotLoadBundle)?;
        log::info!(
            "Bundle `{}` loaded, will try to update it",
            opts.output.display()
        );
        b
    } else {
        log::info!("Will create new bundle in `{}`", opts.output.display());
        ProjectBundle::default()
    };

    update_repo("xkb", &xkb_dir(), REPO_URL).context(FailedRepoUpdate)?;

    let (locale, file_path) = select_base_locale()?;
    log::debug!("opening `{}`", file_path.display());
    let file = std::fs::read_to_string(&file_path).context(CannotOpenFile { path: &file_path })?;
    let section = select_sub_locale(&file).context(CannotReadXkb { path: &file_path })?;
    log::info!(
        "selected locale `{}` with style `{}`",
        locale,
        section.name.as_ref()
    );
    let include_dir = xkb_dir().join("symbols");
    let (keys, dead_keys) = resolve_keys(&section, &include_dir)?;

    // Set X11 target metadata to some values that not completely random
    bundle.targets.x11 = Some(TargetX11 {
        version: chrono::Utc::now().format("%Y-%m-%d").to_string(),
        build: 1,
    });

    // Update X11 layout entry
    let mut layout = bundle.layouts.entry(locale.to_string()).or_default();
    layout.modes.x11 = Some(keys.into_iter().map(|(k, v)| (k, v.into())).collect());

    // Do a neat switcheroo with possibly-None dead keys map
    let mut dead = layout.dead_keys.take().unwrap_or_default();
    dead.insert("x11".to_string(), dead_keys);
    layout.dead_keys = Some(dead);

    bundle.save(&opts.output).context(CannotBeSaved)?;
    log::info!("New bundle written to `{}`.", opts.output.display());
    log::info!(
        "It now contains a X11 target with version `{}`.",
        bundle.targets.x11.unwrap().version
    );

    Ok(())
}

fn select_base_locale() -> Result<(String, PathBuf), Error> {
    let kbd_path = xkb_dir().join("symbols");
    let files: BTreeMap<String, PathBuf> = globwalk::GlobWalkerBuilder::new(&kbd_path, "**")
        .max_depth(8)
        .build()
        .unwrap()
        .filter_map(Result::ok)
        .filter(|entry| !entry.path().ends_with("Makefile.am"))
        .map(|entry| {
            (
                entry
                    .path()
                    .strip_prefix(&kbd_path)
                    .unwrap()
                    .to_string_lossy()
                    .into(),
                entry.path().into(),
            )
        })
        .collect();

    let options = skim::SkimOptionsBuilder::default()
        .prompt(Some("Which keyboard base to load? "))
        .exact(true)
        .ansi(true)
        .build()
        .unwrap();

    let text = files
        .iter()
        .fold(String::new(), |mut text, (file_name, _path)| {
            text.push_str(&file_name);
            text.push_str("\n");
            text
        })
        .into_bytes();
    let cur = std::io::Cursor::new(text);

    let result = skim::Skim::run_with(&options, Some(Box::new(cur))).context(NoLocaleSelected)?;
    let result = result
        .selected_items
        .first()
        .context(NoLocaleSelected)?
        .get_text();

    Ok((result.into(), files[result].clone()))
}

fn select_sub_locale(file: &str) -> Result<ast::XkbSymbols, Box<dyn StdError>> {
    let sections = fetch_symbols(&file)?;

    if sections.len() == 1 {
        return Ok(sections[0].clone());
    }

    let options = skim::SkimOptionsBuilder::default()
        .prompt(Some("Which section to load? "))
        .exact(true)
        .ansi(true)
        .build()
        .unwrap();

    let text = sections
        .iter()
        .fold(String::new(), |mut text, section| {
            text.push_str(&String::from(&section.name));
            text.push_str("\n");
            text
        })
        .into_bytes();
    let cur = std::io::Cursor::new(text);

    let result = skim::Skim::run_with(&options, Some(Box::new(cur))).context(NoLocaleSelected)?;
    let result = result
        .selected_items
        .first()
        .context(NoLocaleSelected)?
        .get_text();

    Ok(sections
        .into_iter()
        .find(|ast::XkbSymbols { name, .. }| name.content == result)
        .context(NoLocaleSelected)?)
}

type LayeredKeyMap = BTreeMap<String, BTreeMap<IsoKey, KeyValue>>;
type DeadKeyMap = BTreeMap<String, Vec<String>>;

/// Build a partial layout from an xkb symbols definition
///
/// It will return two maps:
///
/// 1. A map (`Layer -> IsoKey -> KeyValue`) that will eventually become
///    `layout.modes.x11`
/// 2. A map of dead keys (`Layer -> Vec<KeyValue`) that will become
///    `layout.dead_keys["x11"]`
fn resolve_keys(
    symbols: &ast::XkbSymbols,
    include_dir: &Path,
) -> Result<(LayeredKeyMap, DeadKeyMap), Error> {
    // TODO: figure out the actual names for the layers
    let layers = &["default", "shift", "alt", "shift+alt"];

    // Collect keys into modes while iterating
    let mut map: BTreeMap<String, BTreeMap<IsoKey, KeyValue>> = BTreeMap::new();

    // Collect dead keys while iterating.
    let mut dead_keys: BTreeMap<String, Vec<String>> = BTreeMap::new();

    let keys = extract_keys(symbols, include_dir)?;
    log::debug!("found {} keys in {}", keys.len(), symbols.name.content);

    for key in &keys {
        for (codepoint, mode) in key.values.iter().zip(layers) {
            map.entry(mode.to_string())
                .or_default()
                .insert(key.iso_key, KeyValue::from(codepoint.as_ref().to_string()));

            if let Codepoint::Deadkey(c) = codepoint {
                dead_keys
                    .entry(mode.to_string())
                    .or_default()
                    .push(c.clone());
            }
        }
    }

    // Fill in all the ISO keys that were not defined
    for mode in map.values_mut() {
        for key in IsoKey::iter() {
            mode.entry(key).or_insert_with(|| None.into());
        }
    }

    Ok((map, dead_keys))
}

fn fetch_symbols(source: &str) -> Result<Vec<ast::XkbSymbols>, Box<dyn StdError>> {
    let x: Xkb = parse(&source)?;

    let symbols = x
        .definitions
        .iter()
        .filter_map(|x| {
            if let ast::Directive::XkbSymbols(x) = &x.directive {
                Some(x.clone())
            } else {
                None
            }
        })
        .collect();

    Ok(symbols)
}

fn extract_keys(symbols: &ast::XkbSymbols, include_dir: &Path) -> Result<Vec<Key>, Error> {
    symbols.value.iter().try_fold(Vec::new(), |mut res, item| {
        match item {
            ast::XkbSymbolsItem::Include(ast::Include { name }) => {
                res.extend(read_include(name, include_dir)?)
            }
            ast::XkbSymbolsItem::Key(k) => match Key::try_from(k) {
                Ok(k) => res.push(k),
                Err(e) => log::warn!("Skipping `{:?}`: {}", k, e),
            },
            _ => {}
        }

        Ok(res)
    })
}

fn parse_include_name(include: &str) -> Result<(&str, Option<&str>), Error> {
    fn parse(input: &str) -> nom::IResult<&str, (&str, Option<&str>)> {
        use nom::{
            bytes::complete::{is_not, take_until},
            character::complete::char,
            combinator::{complete, opt},
            sequence::{delimited, tuple},
        };

        complete(tuple((
            take_until("("),
            opt(delimited(char('('), is_not(")"), char(')'))),
        )))(input)
    }

    if include.contains('(') {
        let (_, (name, section)) = parse(include).map_err(|_| Error::FailedToParseInclude {
            include: include.to_string(),
        })?;
        Ok((name, section))
    } else {
        Ok((include, None))
    }
}

fn read_include(name: &str, include_dir: &Path) -> Result<Vec<Key>, Error> {
    let (name, section_name) = parse_include_name(name)?;
    let file_path = include_dir.join(name);
    log::debug!("opening `{}` to fetch includes", file_path.display());
    let file = std::fs::read_to_string(&file_path).context(CannotOpenFile { path: &file_path })?;
    let sections = fetch_symbols(&file).context(CannotReadXkb { path: &file_path })?;
    let symbols = if let Some(section_name) = section_name {
        sections
            .iter()
            .find(|ast::XkbSymbols { name, .. }| name.content == section_name)
            .context(SymbolSectionNotFound {
                path: &file_path,
                section_name,
            })?
    } else {
        sections.get(0).context(SymbolSectionNotFound {
            path: &file_path,
            section_name: "first section (no section name specified)",
        })?
    };

    Ok(extract_keys(&symbols, include_dir)?)
}

#[derive(Debug, Clone)]
struct Key {
    iso_key: IsoKey,
    values: Vec<Codepoint>,
}

impl<'a, 'src> TryFrom<&'a ast::Key<'src>> for Key {
    type Error = Error;

    fn try_from(key: &ast::Key) -> Result<Key, Error> {
        let iso_key = iso_key_from_xkb_symbol(&key.id.as_ref()).context(UnknownIsoKey {
            value: key.id.content,
        })?;
        let values = key.values.iter().try_fold(Vec::new(), |mut res, v| {
            if let ast::KeyValue::KeyNames(ast::KeyNames { values }) = v {
                res.extend(
                    values
                        .iter()
                        .map(|x| Codepoint::from_keysym(x.as_ref()))
                        .collect::<Result<Vec<_>, Error>>()?,
                )
            };
            Ok(res)
        })?;
        Ok(Key { iso_key, values })
    }
}

fn iso_key_from_xkb_symbol(s: &str) -> Option<IsoKey> {
    if !s.starts_with('A') {
        return None;
    }
    let s = &s[1..];
    s.parse().ok()
}

#[derive(Debug, Clone)]
enum Codepoint {
    Regular(String),
    Deadkey(String),
}

impl Codepoint {
    fn from_keysym(keysym: &str) -> Result<Codepoint, Error> {
        fn parse_unicode_def(input: &str) -> nom::IResult<&str, &str> {
            use nom::{
                bytes::complete::{tag, take_while1},
                combinator::complete,
            };

            fn is_hex_digit(c: char) -> bool {
                c.is_digit(16)
            }

            let (input, _) = tag("U")(input)?;
            complete(take_while1(is_hex_digit))(input)
        }

        if let Ok((_, c)) = parse_unicode_def(keysym) {
            let c: u32 = u32::from_str_radix(c, 16).unwrap_or_else(|_| {
                panic!(
                    "found unicode-like codepoint but couldn't parse hex `{}`",
                    c
                )
            });
            return Ok(Codepoint::Regular(
                std::char::from_u32(c).unwrap().to_string(),
            ));
        }

        if let Some(k) = x11_keysymdef::lookup_by_name(&keysym) {
            if keysym.starts_with("dead_") {
                return Ok(Codepoint::Deadkey(k.unicode.to_string()));
            } else {
                return Ok(Codepoint::Regular(k.unicode.to_string()));
            }
        }

        Err(Error::UnknownCodepointMapping {
            keysym: keysym.to_string(),
        })
    }
}

impl AsRef<str> for Codepoint {
    fn as_ref(&self) -> &str {
        match self {
            Codepoint::Regular(x) => &x,
            Codepoint::Deadkey(x) => &x,
        }
    }
}

#[derive(Snafu, SnafuCliDebug)]
pub enum Error {
    #[snafu(display("Updating XKB repo failed"))]
    FailedRepoUpdate {
        source: kbdgen_cli::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("No locale selected"))]
    NoLocaleSelected { backtrace: snafu::Backtrace },
    #[snafu(display("Could load XKB file `{}`", path.display()))]
    CannotOpenFile {
        path: PathBuf,
        source: std::io::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could read XKB file `{}`", path.display()))]
    CannotReadXkb {
        path: PathBuf,
        source: Box<dyn StdError>,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could not translate key id `{}` to iso key", value))]
    UnknownIsoKey {
        value: String,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could not translate keysym `{}` to codepoint", keysym))]
    UnknownCodepointMapping { keysym: String },
    #[snafu(display("Failed to parse include `{}`", include))]
    FailedToParseInclude { include: String },
    #[snafu(display("Failed to find/read section `{}` in `{}`", section_name, path.display()))]
    SymbolSectionNotFound {
        path: PathBuf,
        section_name: String,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could not load kbdgen bundle"))]
    CannotLoadBundle {
        source: kbdgen::LoadError,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could write kbdgen bundle"))]
    CannotBeSaved {
        source: kbdgen::SaveError,
        backtrace: snafu::Backtrace,
    },
}
<|endoftext|>


<commit_before>#![crate_name = "docopt_macros"]
#![crate_type = "dylib"]

#![feature(plugin_registrar, quote, rustc_private)]

//! This crate defines the `docopt!` macro. It is documented in the
//! documentation of the `docopt` crate.

extern crate syntax;
extern crate rustc_plugin;
extern crate docopt;

use std::borrow::Borrow;
use std::collections::HashMap;

use rustc_plugin::Registry;
use syntax::{ast, codemap};
use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
use syntax::parse::common::SeqSep;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::util::small_vector::SmallVector;

use docopt::{Docopt, ArgvMap};
use docopt::parse::{Options, Atom, Positional, Zero, One};

type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("docopt", expand);
}

fn expand(cx: &mut ExtCtxt, span: codemap::Span, tts: &[ast::TokenTree])
         -> Box<MacResult+'static> {
    let parsed = match MacParser::new(cx, tts).parse() {
        Ok(parsed) => parsed,
        Err(_) => return DummyResult::any(span),
    };
    parsed.items(cx)
}

/// Parsed corresponds to the result of parsing a `docopt` macro call.
/// It can be used to write a corresponding struct.
struct Parsed {
    struct_info: StructInfo,
    doc: Docopt,
    /// Overrided type annotations for struct members. May be empty.
    /// When a type annotation for an atom doesn't exist, then one is
    /// inferred automatically. It is one of: `bool`, `usize`, `String` or
    /// `Vec<String>`.
    types: HashMap<Atom, P<ast::Ty>>,
}

impl Parsed {
    /// Returns a macro result suitable for expansion.
    /// Contains two items: one for the struct and one for the struct impls.
    fn items(&self, cx: &ExtCtxt) -> Box<MacResult+'static> {
        let mut its = vec!();
        its.push(self.struct_decl(cx));

        let struct_name = self.struct_info.name;
        let full_doc = &*self.doc.parser().full_doc;
        its.push(quote_item!(cx,
            impl $struct_name {
                #[allow(dead_code)]
                fn docopt() -> docopt::Docopt {
                    // The unwrap is justified here because this code
                    // gen only happens if the Docopt usage string is valid.
                    docopt::Docopt::new($full_doc).unwrap()
                }
            }
        ).unwrap());
        MacEager::items(SmallVector::many(its))
    }

    /// Returns an item for the struct definition.
    fn struct_decl(&self, cx: &ExtCtxt) -> P<ast::Item> {
        let name = self.struct_info.name.clone();
        let vis = if self.struct_info.public { ast::Public }
                  else { ast::Inherited };
        let def = ast::VariantData::Struct(
            self.struct_fields(cx), ast::DUMMY_NODE_ID);

        let mut traits = vec!["RustcDecodable".to_string()];
        traits.extend(self.struct_info.deriving.iter().cloned());
        let attrs = vec![attribute(cx, "allow", vec!["non_snake_case"]),
                         attribute(cx, "derive", traits)];

        let st = cx.item_struct(codemap::DUMMY_SP, name.clone(), def);
        cx.item(codemap::DUMMY_SP, name, attrs, st.node.clone()).map(|mut it| {
            it.vis = vis;
            it
        })
    }

    /// Returns a list of fields for the struct definition.
    /// Handles type annotations.
    fn struct_fields(&self, cx: &ExtCtxt) -> Vec<ast::StructField> {
        let mut fields: Vec<ast::StructField> = vec!();
        for (atom, opts) in self.doc.parser().descs.iter() {
            let name = ArgvMap::key_to_struct_field(&*atom.to_string());
            let ty = match self.types.get(atom) {
                None => self.pat_type(cx, atom, opts),
                Some(ty) => ty.clone(),
            };
            fields.push(self.mk_struct_field(&*name, ty));
        }
        fields
    }

    /// Returns an inferred type for a usage pattern.
    /// This is only invoked when a type annotation is not present.
    fn pat_type(&self, cx: &ExtCtxt, atom: &Atom, opts: &Options) -> P<ast::Ty> {
        let sp = codemap::DUMMY_SP;
        match (opts.repeats, &opts.arg) {
            (false, &Zero) => {
                match atom {
                    &Positional(_) => cx.ty_ident(sp, ident("String")),
                    _ => cx.ty_ident(sp, ident("bool")),
                }
            }
            (true, &Zero) => {
                match atom {
                    &Positional(_) => ty_vec_string(cx),
                    _ => cx.ty_ident(sp, ident("usize")),
                }
            }
            (false, &One(_)) => cx.ty_ident(sp, ident("String")),
            (true, &One(_)) => ty_vec_string(cx),
        }
    }

    /// Creates a struct field from a member name and type.
    fn mk_struct_field(&self, name: &str, ty: P<ast::Ty>) -> ast::StructField {
        codemap::dummy_spanned(ast::StructField_ {
            kind: ast::NamedField(ident(name), ast::Public),
            id: ast::DUMMY_NODE_ID,
            ty: ty,
            attrs: vec!(),
        })
    }
}

/// State for parsing a `docopt` macro invocation.
struct MacParser<'a, 'b:'a> {
    cx: &'a mut ExtCtxt<'b>,
    p: Parser<'b>,
}

impl<'a, 'b> MacParser<'a, 'b> {
    fn new(cx: &'a mut ExtCtxt<'b>, tts: &[ast::TokenTree]) -> MacParser<'a, 'b> {
        let p = cx.new_parser_from_tts(tts);
        MacParser { cx: cx, p: p }
    }

    /// Main entry point for parsing arguments to `docopt` macro.
    /// First looks for an identifier for the struct name.
    /// Second, a string containing the docopt usage patterns.
    /// Third, an optional list of type annotations.
    fn parse(&mut self) -> PResult<'b, Parsed> {
        if self.p.token == token::Eof {
            let err = self.cx.struct_span_err(self.cx.call_site(), "macro expects arguments");
            return Err(err);
        }
        let struct_info = try!(self.parse_struct_info());
        let docstr = try!(self.parse_str());

        let sep = SeqSep {
            sep: Some(token::Comma),
            trailing_sep_allowed: true,
        };
        let types = try!(self.p.parse_seq_to_end(
            &token::Eof, sep, |p| MacParser::parse_type_annotation(p)
        )).into_iter()
          .map(|(ident, ty)| {
              let field_name = ident.to_string();
              let key = ArgvMap::struct_field_to_key(&*field_name);
              (Atom::new(&*key), ty)
           })
          .collect::<HashMap<Atom, P<ast::Ty>>>();
        try!(self.p.expect(&token::Eof));

        // This config does not matter because we're only asking for the
        // usage patterns in the Docopt string. The configuration does not
        // affect the retrieval of usage patterns.
        let doc = match Docopt::new(docstr) {
            Ok(doc) => doc,
            Err(err) => {
                let err = self.cx.struct_span_err(self.cx.call_site(),
                                                  &format!("Invalid Docopt usage: {}", err));
                return Err(err);
            }
        };
        Ok(Parsed {
            struct_info: struct_info,
            doc: doc,
            types: types,
        })
    }

    /// Parses a single string literal. On failure, an error is logged and
    /// unit is returned.
    fn parse_str(&mut self) -> PResult<'b, String> {
        fn lit_is_str(lit: &ast::Lit) -> bool {
            match lit.node {
                ast::LitStr(_, _) => true,
                _ => false,
            }
        }
        fn lit_to_string(lit: &ast::Lit) -> String {
            match lit.node {
                ast::LitStr(ref s, _) => s.to_string(),
                _ => panic!("BUG: expected string literal"),
            }
        }
        let exp = self.cx.expander().fold_expr(self.p.parse_expr().unwrap());
        let s = match exp.node {
            ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
                lit_to_string(&**lit)
            }
            _ => {
                let err = format!("Expected string literal but got {}",
                                  pprust::expr_to_string(&*exp));
                let err = self.cx.struct_span_err(exp.span, &*err);
                return Err(err);
            }
        };
        self.p.bump();
        Ok(s)
    }

    /// Parses a type annotation in a `docopt` invocation of the form
    /// `ident: Ty`.
    /// Note that this is a static method as it is used as a HOF.
    fn parse_type_annotation(p: &mut Parser<'b>)
                             -> PResult<'b, (ast::Ident, P<ast::Ty>)> {
        let ident = try!(p.parse_ident());
        try!(p.expect(&token::Colon));
        let ty = p.parse_ty().unwrap();
        Ok((ident, ty))
    }

    /// Parses struct information, like visibility, name and deriving.
    fn parse_struct_info(&mut self) -> PResult<'b, StructInfo> {
        let public = self.p.eat_keyword(token::keywords::Pub);
        let mut info = StructInfo {
            name: try!(self.p.parse_ident()),
            public: public,
            deriving: vec![],
        };
        if self.p.eat(&token::Comma) { return Ok(info); }
        let deriving = try!(self.p.parse_ident());
        if deriving.name.as_str() != "derive" {
            let err = format!("Expected 'derive' keyword but got '{}'",
                              deriving);
            let err = self.cx.struct_span_err(self.cx.call_site(), &*err);
            return Err(err);
        }
        while !self.p.eat(&token::Comma) {
            info.deriving.push(
                try!(self.p.parse_ident()).name.to_string());
        }
        Ok(info)
    }
}

struct StructInfo {
    name: ast::Ident,
    public: bool,
    deriving: Vec<String>,
}

// Convenience functions for building intermediate values.

fn ident(s: &str) -> ast::Ident {
    ast::Ident::with_empty_ctxt(token::intern(s))
}

fn attribute<S, T>(cx: &ExtCtxt, name: S, items: Vec<T>) -> ast::Attribute
            where S: Borrow<str>, T: Borrow<str> {
    let sp = codemap::DUMMY_SP;
    let its = items.into_iter().map(|s| meta_item(cx, s.borrow())).collect();
    let mi = cx.meta_list(sp, intern(name.borrow()), its);
    cx.attribute(sp, mi)
}

fn meta_item(cx: &ExtCtxt, s: &str) -> P<ast::MetaItem> {
    cx.meta_word(codemap::DUMMY_SP, intern(s))
}

fn intern(s: &str) -> token::InternedString {
    token::intern_and_get_ident(s)
}

fn ty_vec_string(cx: &ExtCtxt) -> P<ast::Ty> {
    let sp = codemap::DUMMY_SP;
    let tystr = ast::AngleBracketedParameterData {
        lifetimes: vec![],
        types: P::from_vec(vec![cx.ty_ident(sp, ident("String"))]),
        bindings: P::empty(),
    };
    cx.ty_path(ast::Path {
        span: sp,
        global: false,
        segments: vec![ast::PathSegment {
            identifier: ident("Vec"),
            parameters: ast::PathParameters::AngleBracketed(tystr),
        }]
    })
}
<commit_msg>fix to latest aster<commit_after>#![crate_name = "docopt_macros"]
#![crate_type = "dylib"]

#![feature(plugin_registrar, quote, rustc_private)]

//! This crate defines the `docopt!` macro. It is documented in the
//! documentation of the `docopt` crate.

extern crate syntax;
extern crate rustc_plugin;
extern crate docopt;

use std::borrow::Borrow;
use std::collections::HashMap;

use rustc_plugin::Registry;
use syntax::{ast, codemap};
use syntax::errors::DiagnosticBuilder;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
use syntax::parse::common::SeqSep;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::util::small_vector::SmallVector;

use docopt::{Docopt, ArgvMap};
use docopt::parse::{Options, Atom, Positional, Zero, One};

type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("docopt", expand);
}

fn expand(cx: &mut ExtCtxt, span: codemap::Span, tts: &[ast::TokenTree])
         -> Box<MacResult+'static> {
    let parsed = match MacParser::new(cx, tts).parse() {
        Ok(parsed) => parsed,
        Err(_) => return DummyResult::any(span),
    };
    parsed.items(cx)
}

/// Parsed corresponds to the result of parsing a `docopt` macro call.
/// It can be used to write a corresponding struct.
struct Parsed {
    struct_info: StructInfo,
    doc: Docopt,
    /// Overrided type annotations for struct members. May be empty.
    /// When a type annotation for an atom doesn't exist, then one is
    /// inferred automatically. It is one of: `bool`, `usize`, `String` or
    /// `Vec<String>`.
    types: HashMap<Atom, P<ast::Ty>>,
}

impl Parsed {
    /// Returns a macro result suitable for expansion.
    /// Contains two items: one for the struct and one for the struct impls.
    fn items(&self, cx: &ExtCtxt) -> Box<MacResult+'static> {
        let mut its = vec!();
        its.push(self.struct_decl(cx));

        let struct_name = self.struct_info.name;
        let full_doc = &*self.doc.parser().full_doc;
        its.push(quote_item!(cx,
            impl $struct_name {
                #[allow(dead_code)]
                fn docopt() -> docopt::Docopt {
                    // The unwrap is justified here because this code
                    // gen only happens if the Docopt usage string is valid.
                    docopt::Docopt::new($full_doc).unwrap()
                }
            }
        ).unwrap());
        MacEager::items(SmallVector::many(its))
    }

    /// Returns an item for the struct definition.
    fn struct_decl(&self, cx: &ExtCtxt) -> P<ast::Item> {
        let name = self.struct_info.name.clone();
        let vis = if self.struct_info.public { ast::Visibility::Public }
                  else { ast::Visibility::Inherited };
        let def = ast::VariantData::Struct(
            self.struct_fields(cx), ast::DUMMY_NODE_ID);

        let mut traits = vec!["RustcDecodable".to_string()];
        traits.extend(self.struct_info.deriving.iter().cloned());
        let attrs = vec![attribute(cx, "allow", vec!["non_snake_case"]),
                         attribute(cx, "derive", traits)];

        let st = cx.item_struct(codemap::DUMMY_SP, name.clone(), def);
        cx.item(codemap::DUMMY_SP, name, attrs, st.node.clone()).map(|mut it| {
            it.vis = vis;
            it
        })
    }

    /// Returns a list of fields for the struct definition.
    /// Handles type annotations.
    fn struct_fields(&self, cx: &ExtCtxt) -> Vec<ast::StructField> {
        let mut fields: Vec<ast::StructField> = vec!();
        for (atom, opts) in self.doc.parser().descs.iter() {
            let name = ArgvMap::key_to_struct_field(&*atom.to_string());
            let ty = match self.types.get(atom) {
                None => self.pat_type(cx, atom, opts),
                Some(ty) => ty.clone(),
            };
            fields.push(self.mk_struct_field(&*name, ty));
        }
        fields
    }

    /// Returns an inferred type for a usage pattern.
    /// This is only invoked when a type annotation is not present.
    fn pat_type(&self, cx: &ExtCtxt, atom: &Atom, opts: &Options) -> P<ast::Ty> {
        let sp = codemap::DUMMY_SP;
        match (opts.repeats, &opts.arg) {
            (false, &Zero) => {
                match atom {
                    &Positional(_) => cx.ty_ident(sp, ident("String")),
                    _ => cx.ty_ident(sp, ident("bool")),
                }
            }
            (true, &Zero) => {
                match atom {
                    &Positional(_) => ty_vec_string(cx),
                    _ => cx.ty_ident(sp, ident("usize")),
                }
            }
            (false, &One(_)) => cx.ty_ident(sp, ident("String")),
            (true, &One(_)) => ty_vec_string(cx),
        }
    }

    /// Creates a struct field from a member name and type.
    fn mk_struct_field(&self, name: &str, ty: P<ast::Ty>) -> ast::StructField {
        codemap::dummy_spanned(ast::StructField_ {
            kind: ast::NamedField(ident(name), ast::Visibility::Public),
            id: ast::DUMMY_NODE_ID,
            ty: ty,
            attrs: vec!(),
        })
    }
}

/// State for parsing a `docopt` macro invocation.
struct MacParser<'a, 'b:'a> {
    cx: &'a mut ExtCtxt<'b>,
    p: Parser<'b>,
}

impl<'a, 'b> MacParser<'a, 'b> {
    fn new(cx: &'a mut ExtCtxt<'b>, tts: &[ast::TokenTree]) -> MacParser<'a, 'b> {
        let p = cx.new_parser_from_tts(tts);
        MacParser { cx: cx, p: p }
    }

    /// Main entry point for parsing arguments to `docopt` macro.
    /// First looks for an identifier for the struct name.
    /// Second, a string containing the docopt usage patterns.
    /// Third, an optional list of type annotations.
    fn parse(&mut self) -> PResult<'b, Parsed> {
        if self.p.token == token::Eof {
            let err = self.cx.struct_span_err(self.cx.call_site(), "macro expects arguments");
            return Err(err);
        }
        let struct_info = try!(self.parse_struct_info());
        let docstr = try!(self.parse_str());

        let sep = SeqSep {
            sep: Some(token::Comma),
            trailing_sep_allowed: true,
        };
        let types = try!(self.p.parse_seq_to_end(
            &token::Eof, sep, |p| MacParser::parse_type_annotation(p)
        )).into_iter()
          .map(|(ident, ty)| {
              let field_name = ident.to_string();
              let key = ArgvMap::struct_field_to_key(&*field_name);
              (Atom::new(&*key), ty)
           })
          .collect::<HashMap<Atom, P<ast::Ty>>>();
        try!(self.p.expect(&token::Eof));

        // This config does not matter because we're only asking for the
        // usage patterns in the Docopt string. The configuration does not
        // affect the retrieval of usage patterns.
        let doc = match Docopt::new(docstr) {
            Ok(doc) => doc,
            Err(err) => {
                let err = self.cx.struct_span_err(self.cx.call_site(),
                                                  &format!("Invalid Docopt usage: {}", err));
                return Err(err);
            }
        };
        Ok(Parsed {
            struct_info: struct_info,
            doc: doc,
            types: types,
        })
    }

    /// Parses a single string literal. On failure, an error is logged and
    /// unit is returned.
    fn parse_str(&mut self) -> PResult<'b, String> {
        fn lit_is_str(lit: &ast::Lit) -> bool {
            match lit.node {
                ast::LitKind::Str(_, _) => true,
                _ => false,
            }
        }
        fn lit_to_string(lit: &ast::Lit) -> String {
            match lit.node {
                ast::LitKind::Str(ref s, _) => s.to_string(),
                _ => panic!("BUG: expected string literal"),
            }
        }
        let exp = self.cx.expander().fold_expr(self.p.parse_expr().unwrap());
        let s = match exp.node {
            ast::ExprKind::Lit(ref lit) if lit_is_str(&**lit) => {
                lit_to_string(&**lit)
            }
            _ => {
                let err = format!("Expected string literal but got {}",
                                  pprust::expr_to_string(&*exp));
                let err = self.cx.struct_span_err(exp.span, &*err);
                return Err(err);
            }
        };
        self.p.bump();
        Ok(s)
    }

    /// Parses a type annotation in a `docopt` invocation of the form
    /// `ident: Ty`.
    /// Note that this is a static method as it is used as a HOF.
    fn parse_type_annotation(p: &mut Parser<'b>)
                             -> PResult<'b, (ast::Ident, P<ast::Ty>)> {
        let ident = try!(p.parse_ident());
        try!(p.expect(&token::Colon));
        let ty = p.parse_ty().unwrap();
        Ok((ident, ty))
    }

    /// Parses struct information, like visibility, name and deriving.
    fn parse_struct_info(&mut self) -> PResult<'b, StructInfo> {
        let public = self.p.eat_keyword(token::keywords::Pub);
        let mut info = StructInfo {
            name: try!(self.p.parse_ident()),
            public: public,
            deriving: vec![],
        };
        if self.p.eat(&token::Comma) { return Ok(info); }
        let deriving = try!(self.p.parse_ident());
        if deriving.name.as_str() != "derive" {
            let err = format!("Expected 'derive' keyword but got '{}'",
                              deriving);
            let err = self.cx.struct_span_err(self.cx.call_site(), &*err);
            return Err(err);
        }
        while !self.p.eat(&token::Comma) {
            info.deriving.push(
                try!(self.p.parse_ident()).name.to_string());
        }
        Ok(info)
    }
}

struct StructInfo {
    name: ast::Ident,
    public: bool,
    deriving: Vec<String>,
}

// Convenience functions for building intermediate values.

fn ident(s: &str) -> ast::Ident {
    ast::Ident::with_empty_ctxt(token::intern(s))
}

fn attribute<S, T>(cx: &ExtCtxt, name: S, items: Vec<T>) -> ast::Attribute
            where S: Borrow<str>, T: Borrow<str> {
    let sp = codemap::DUMMY_SP;
    let its = items.into_iter().map(|s| meta_item(cx, s.borrow())).collect();
    let mi = cx.meta_list(sp, intern(name.borrow()), its);
    cx.attribute(sp, mi)
}

fn meta_item(cx: &ExtCtxt, s: &str) -> P<ast::MetaItem> {
    cx.meta_word(codemap::DUMMY_SP, intern(s))
}

fn intern(s: &str) -> token::InternedString {
    token::intern_and_get_ident(s)
}

fn ty_vec_string(cx: &ExtCtxt) -> P<ast::Ty> {
    let sp = codemap::DUMMY_SP;
    let tystr = ast::AngleBracketedParameterData {
        lifetimes: vec![],
        types: P::from_vec(vec![cx.ty_ident(sp, ident("String"))]),
        bindings: P::empty(),
    };
    cx.ty_path(ast::Path {
        span: sp,
        global: false,
        segments: vec![ast::PathSegment {
            identifier: ident("Vec"),
            parameters: ast::PathParameters::AngleBracketed(tystr),
        }]
    })
}
<|endoftext|>


<commit_before>#![feature(alloc)]
#![feature(asm)]
#![feature(heap_api)]

extern crate alloc;
extern crate orbclient;
extern crate syscall;

use std::{env, mem, thread};
use std::fs::File;
use std::io::{Read, Write};
use syscall::{physmap, physunmap, Packet, Scheme, EVENT_READ, MAP_WRITE, MAP_WRITE_COMBINE};

use mode_info::VBEModeInfo;
use primitive::fast_set64;
use scheme::DisplayScheme;

pub mod display;
pub mod mode_info;
pub mod primitive;
pub mod scheme;
pub mod screen;

fn main() {
    let mut spec = Vec::new();

    for arg in env::args().skip(1) {
        if arg == "T" {
            spec.push(false);
        } else if arg == "G" {
            spec.push(true);
        } else {
            println!("vesad: unknown screen type: {}", arg);
        }
    }

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info = unsafe { &*(physmap(0x5200, 4096, 0).expect("vesad: failed to map VBE info") as *const VBEModeInfo) };

        width = mode_info.xresolution as usize;
        height = mode_info.yresolution as usize;
        physbaseptr = mode_info.physbaseptr as usize;

        unsafe { let _ = physunmap(mode_info as *const _ as usize); }
    }

    if physbaseptr > 0 {
        thread::spawn(move || {
            let mut socket = File::create(":display").expect("vesad: failed to create display scheme");

            let size = width * height;

            let onscreen = unsafe { physmap(physbaseptr, size * 4, MAP_WRITE | MAP_WRITE_COMBINE).expect("vesad: failed to map VBE LFB") };
            unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

            let scheme = DisplayScheme::new(width, height, onscreen, &spec);

            let mut blocked = Vec::new();
            loop {
                let mut packet = Packet::default();
                socket.read(&mut packet).expect("vesad: failed to read display scheme");
                //println!("vesad: {:?}", packet);

                // If it is a read packet, and there is no data, block it. Otherwise, handle packet
                if packet.a == syscall::number::SYS_READ && packet.d > 0 && scheme.will_block(packet.b) {
                    blocked.push(packet);
                } else {
                    scheme.handle(&mut packet);
                    socket.write(&packet).expect("vesad: failed to write display scheme");
                }

                // If there are blocked readers, and data is available, handle them
                {
                    let mut i = 0;
                    while i < blocked.len() {
                        if ! scheme.will_block(blocked[i].b) {
                            let mut packet = blocked.remove(i);
                            scheme.handle(&mut packet);
                            socket.write(&packet).expect("vesad: failed to write display scheme");
                        } else {
                            i += 1;
                        }
                    }
                }

                for (screen_id, screen) in scheme.screens.borrow().iter() {
                    if ! screen.will_block() {
                        let event_packet = Packet {
                            id: 0,
                            pid: 0,
                            uid: 0,
                            gid: 0,
                            a: syscall::number::SYS_FEVENT,
                            b: *screen_id,
                            c: EVENT_READ,
                            d: mem::size_of::<Packet>()
                        };

                        socket.write(&event_packet).expect("vesad: failed to write display event");
                    }
                }
            }
        });
    }
}
<commit_msg>Correct size of data<commit_after>#![feature(alloc)]
#![feature(asm)]
#![feature(heap_api)]

extern crate alloc;
extern crate orbclient;
extern crate syscall;

use std::{env, mem, thread};
use std::fs::File;
use std::io::{Read, Write};
use syscall::{physmap, physunmap, Packet, Scheme, EVENT_READ, MAP_WRITE, MAP_WRITE_COMBINE};

use mode_info::VBEModeInfo;
use primitive::fast_set64;
use scheme::DisplayScheme;

pub mod display;
pub mod mode_info;
pub mod primitive;
pub mod scheme;
pub mod screen;

fn main() {
    let mut spec = Vec::new();

    for arg in env::args().skip(1) {
        if arg == "T" {
            spec.push(false);
        } else if arg == "G" {
            spec.push(true);
        } else {
            println!("vesad: unknown screen type: {}", arg);
        }
    }

    let width;
    let height;
    let physbaseptr;

    {
        let mode_info = unsafe { &*(physmap(0x5200, 4096, 0).expect("vesad: failed to map VBE info") as *const VBEModeInfo) };

        width = mode_info.xresolution as usize;
        height = mode_info.yresolution as usize;
        physbaseptr = mode_info.physbaseptr as usize;

        unsafe { let _ = physunmap(mode_info as *const _ as usize); }
    }

    if physbaseptr > 0 {
        thread::spawn(move || {
            let mut socket = File::create(":display").expect("vesad: failed to create display scheme");

            let size = width * height;

            let onscreen = unsafe { physmap(physbaseptr, size * 4, MAP_WRITE | MAP_WRITE_COMBINE).expect("vesad: failed to map VBE LFB") };
            unsafe { fast_set64(onscreen as *mut u64, 0, size/2) };

            let scheme = DisplayScheme::new(width, height, onscreen, &spec);

            let mut blocked = Vec::new();
            loop {
                let mut packet = Packet::default();
                socket.read(&mut packet).expect("vesad: failed to read display scheme");
                //println!("vesad: {:?}", packet);

                // If it is a read packet, and there is no data, block it. Otherwise, handle packet
                if packet.a == syscall::number::SYS_READ && packet.d > 0 && scheme.will_block(packet.b) {
                    blocked.push(packet);
                } else {
                    scheme.handle(&mut packet);
                    socket.write(&packet).expect("vesad: failed to write display scheme");
                }

                // If there are blocked readers, and data is available, handle them
                {
                    let mut i = 0;
                    while i < blocked.len() {
                        if ! scheme.will_block(blocked[i].b) {
                            let mut packet = blocked.remove(i);
                            scheme.handle(&mut packet);
                            socket.write(&packet).expect("vesad: failed to write display scheme");
                        } else {
                            i += 1;
                        }
                    }
                }

                for (screen_id, screen) in scheme.screens.borrow().iter() {
                    if ! screen.will_block() {
                        let event_packet = Packet {
                            id: 0,
                            pid: 0,
                            uid: 0,
                            gid: 0,
                            a: syscall::number::SYS_FEVENT,
                            b: *screen_id,
                            c: EVENT_READ,
                            d: mem::size_of::<orbclient::Event>()
                        };

                        socket.write(&event_packet).expect("vesad: failed to write display event");
                    }
                }
            }
        });
    }
}
<|endoftext|>


<commit_before>#![crate_id="find_max"]
//! Test various syntaxes to find the maximum value of a vector using generic function.
//!
//! @license MIT license <http://www.opensource.org/licenses/mit-license.php>

#[cfg(test)]
extern crate test;

#[cfg(test)]
use test::Bencher;

/**
 * Code from Rust MeetUp (March, 31st 2014) at Mozilla Space, Paris.
 * https://reps.mozilla.org/e/meetup-rust-paris-02/
 */
fn find_max1<'a, T: Ord>(lst: &'a Vec<T>) -> Option<&'a T> {
	let mut max = None;

	for i in lst.iter() {
		max = match max {
			None => Some(i),
			Some(ref m) if i > *m => Some(i),
			_ => max
		}
	}
	max
}

/**
 * Using a closure.
 *
 * Inspired by tutorial "15 Closure"
 * http://static.rust-lang.org/doc/master/tutorial.html#closures
 */
fn find_max2<'a, T: Ord>(lst: &'a Vec<T>) -> Option<&'a T> {
	let mut max = None;

	let find_max = |i: &'a T| {
		max = match max {
			None => Some(i),
			Some(ref m) if i > *m => Some(i),
			_ => max
		}
	};
	for x in lst.iter() {
		find_max(x);
	}

	max
}

/**
 * Using a closure and .map().
 */
fn find_max3<'a, T: Ord>(lst: &'a Vec<T>) -> Option<&'a T> {
	let mut max = None;

	let find_max = |i: &'a T| {
		max = match max {
			None => Some(i),
			Some(ref m) if i > *m => Some(i),
			_ => max
		};
		max
	};
	lst.iter().map(find_max).last().unwrap()
}

#[test]
fn test_find_max1() {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	let nine = 9;
	assert_eq!(Some(&nine), find_max1(&v));
}

#[test]
fn test_find_max2() {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	let nine = 9;
	assert_eq!(Some(&nine), find_max2(&v));
}

#[test]
fn test_find_max3() {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	let nine = 9;
	assert_eq!(Some(&nine), find_max3(&v));
}

#[bench]
fn bench_find_max1(b: &mut Bencher) {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	b.iter(|| {
		find_max1(&v);
	});
}

#[bench]
fn bench_find_max2(b: &mut Bencher) {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	b.iter(|| {
		find_max2(&v);
	});
}

#[bench]
fn bench_find_max3(b: &mut Bencher) {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	b.iter(|| {
		find_max3(&v);
	});
}

#[cfg(not(test))]
fn main () {
	let int_v = vec!(5, 2, 0, 8, 2);
	println!("find_max1 -> {}", find_max1(&int_v));
	println!("find_max2 -> {}", find_max2(&int_v));
	println!("find_max3 -> {}", find_max3(&int_v));
	let v = vec!("qehgesrhsetha", "bqthst", "cthersth");
	let b = find_max3(&v);
	println!("{}", b);

	println!("{}", v);
	println!("{}", b);
}
<commit_msg>find_max.rs : add stdlib expression<commit_after>#![crate_id="find_max"]
//! Test various syntaxes to find the maximum value of a vector using generic function.
//!
//! @license MIT license <http://www.opensource.org/licenses/mit-license.php>

#[cfg(test)]
extern crate test;

#[cfg(test)]
use test::Bencher;

/**
 * Code from Rust MeetUp (March, 31st 2014) at Mozilla Space, Paris.
 * https://reps.mozilla.org/e/meetup-rust-paris-02/
 */
fn find_max1<'a, T: Ord>(lst: &'a Vec<T>) -> Option<&'a T> {
	let mut max = None;

	for i in lst.iter() {
		max = match max {
			None => Some(i),
			Some(ref m) if i > *m => Some(i),
			_ => max
		}
	}
	max
}

/**
 * Using a closure.
 *
 * Inspired by tutorial "15 Closure"
 * http://static.rust-lang.org/doc/master/tutorial.html#closures
 */
fn find_max2<'a, T: Ord>(lst: &'a Vec<T>) -> Option<&'a T> {
	let mut max = None;

	let find_max = |i: &'a T| {
		max = match max {
			None => Some(i),
			Some(ref m) if i > *m => Some(i),
			_ => max
		}
	};
	for x in lst.iter() {
		find_max(x);
	}

	max
}

/**
 * Using a closure and .map().
 */
fn find_max3<'a, T: Ord>(lst: &'a Vec<T>) -> Option<&'a T> {
	let mut max = None;

	let find_max = |i: &'a T| {
		max = match max {
			None => Some(i),
			Some(ref m) if i > *m => Some(i),
			_ => max
		};
		max
	};
	lst.iter().map(find_max).last().unwrap()
}

#[test]
fn test_find_max1() {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	let nine = 9;
	assert_eq!(Some(&nine), find_max1(&v));
}

#[test]
fn test_find_max2() {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	let nine = 9;
	assert_eq!(Some(&nine), find_max2(&v));
}

#[test]
fn test_find_max3() {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	let nine = 9;
	assert_eq!(Some(&nine), find_max3(&v));
}

#[bench]
fn bench_find_max1(b: &mut Bencher) {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	b.iter(|| {
		find_max1(&v);
	});
}

#[bench]
fn bench_find_max2(b: &mut Bencher) {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	b.iter(|| {
		find_max2(&v);
	});
}

#[bench]
fn bench_find_max3(b: &mut Bencher) {
	let v = vec!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
	b.iter(|| {
		find_max3(&v);
	});
}

#[cfg(not(test))]
fn main () {
	let int_v = vec!(5, 2, 0, 8, 2);
	println!("find_max1 -> {}", find_max1(&int_v));
	println!("find_max2 -> {}", find_max2(&int_v));
	println!("find_max3 -> {}", find_max3(&int_v));
	let v = vec!("qehgesrhsetha", "bqthst", "cthersth");
	let b = find_max3(&v);
	println!("{}", b);

	println!("{}", v);
	println!("{}", b);
	// simply using stdlib:
	println!("{}", v.iter().max_by(|x| *x).unwrap())
}
<|endoftext|>


<commit_before>// Options
// Space before /
// Quote style
// Ordering of attributes

#[cfg(test)]
mod test {
    use std::io::{IoResult,MemWriter};

    use super::super::{Document,Element,Text};
    use super::super::{ElementElementChild,TextElementChild,CommentElementChild,PIElementChild};

    macro_rules! assert_str_eq(
        ($l:expr, $r:expr) => (assert_eq!($l.as_slice(), $r.as_slice()));
    )

    enum Content {
        WElement(Element),
        WElementEnd(String),
        WText(Text),
    }

    fn format_element<W : Writer>(element: Element, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        try!(write!(writer, "<{}", element.name()));

        for attr in element.attributes().iter() {
            try!(write!(writer, " {}='{}'", attr.name(), attr.value()));
        }

        let mut children = element.children();
        if children.is_empty() {
            writer.write_str("/>")
        } else {
            try!(writer.write_str(">"));

            todo.push(WElementEnd(element.name()));
            children.reverse();
            let x = children.move_iter().map(|c| match c {
                ElementElementChild(element) => WElement(element),
                TextElementChild(t) => WText(t),
                CommentElementChild(_) => fail!("comment?"),
                PIElementChild(_) => fail!("PI?"),
            }).collect();
            todo.push_all_move(x);

            Ok(())
        }
    }

    fn format_document<W : Writer>(doc: &Document, writer: &mut W) -> IoResult<()> {
        let mut todo = Vec::new();

        try!(writer.write_str("<?xml version='1.0'?>"));
        let e = doc.root().children()[0].element().expect("not an element?");

        todo.push(WElement(e));

        while ! todo.is_empty() {
            match todo.pop().unwrap() {
                WElement(e) => try!(format_element(e, &mut todo, writer)),
                WElementEnd(name) => try!(write!(writer, "</{}>", name)),
                WText(t) => try!(writer.write_str(t.text().as_slice())),
            }
        }

        Ok(())
    }

    fn format_xml(doc: &Document) -> String {
        let mut w = MemWriter::new();
        format_document(doc, &mut w).ok().expect("Not formatted");
        String::from_utf8(w.unwrap()).ok().expect("Not a string")
    }

    #[test]
    fn top_element() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello/>");
    }

    #[test]
    fn element_with_attributes() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        e.set_attribute("a".to_string(), "b".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello a='b'/>");
    }

    #[test]
    fn nested_element() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let world = d.new_element("world".to_string());
        hello.append_child(world);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><world/></hello>");
    }

    #[test]
    fn nested_text() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let text = d.new_text("A fine day to you!".to_string());
        hello.append_child(text);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello>A fine day to you!</hello>");
    }
}
<commit_msg>Write comment nodes<commit_after>// Options
// Space before /
// Quote style
// Ordering of attributes

#[cfg(test)]
mod test {
    use std::io::{IoResult,MemWriter};

    use super::super::{Document,Element,Text,Comment};
    use super::super::{ElementElementChild,TextElementChild,CommentElementChild,PIElementChild};

    macro_rules! assert_str_eq(
        ($l:expr, $r:expr) => (assert_eq!($l.as_slice(), $r.as_slice()));
    )

    enum Content {
        WElement(Element),
        WElementEnd(String),
        WText(Text),
        WComment(Comment),
    }

    fn format_element<W : Writer>(element: Element, todo: &mut Vec<Content>, writer: &mut W) -> IoResult<()> {
        try!(write!(writer, "<{}", element.name()));

        for attr in element.attributes().iter() {
            try!(write!(writer, " {}='{}'", attr.name(), attr.value()));
        }

        let mut children = element.children();
        if children.is_empty() {
            writer.write_str("/>")
        } else {
            try!(writer.write_str(">"));

            todo.push(WElementEnd(element.name()));
            children.reverse();
            let x = children.move_iter().map(|c| match c {
                ElementElementChild(element) => WElement(element),
                TextElementChild(t) => WText(t),
                CommentElementChild(c) => WComment(c),
                PIElementChild(_) => fail!("PI?"),
            }).collect();
            todo.push_all_move(x);

            Ok(())
        }
    }

    fn format_document<W : Writer>(doc: &Document, writer: &mut W) -> IoResult<()> {
        let mut todo = Vec::new();

        try!(writer.write_str("<?xml version='1.0'?>"));
        let e = doc.root().children()[0].element().expect("not an element?");

        todo.push(WElement(e));

        while ! todo.is_empty() {
            match todo.pop().unwrap() {
                WElement(e) => try!(format_element(e, &mut todo, writer)),
                WElementEnd(name) => try!(write!(writer, "</{}>", name)),
                WText(t) => try!(writer.write_str(t.text().as_slice())),
                WComment(c) => try!(write!(writer, "<!--{}-->", c.text())),
            }
        }

        Ok(())
    }

    fn format_xml(doc: &Document) -> String {
        let mut w = MemWriter::new();
        format_document(doc, &mut w).ok().expect("Not formatted");
        String::from_utf8(w.unwrap()).ok().expect("Not a string")
    }

    #[test]
    fn top_element() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello/>");
    }

    #[test]
    fn element_with_attributes() {
        let d = Document::new();
        let e = d.new_element("hello".to_string());
        e.set_attribute("a".to_string(), "b".to_string());
        d.root().append_child(e);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello a='b'/>");
    }

    #[test]
    fn nested_element() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let world = d.new_element("world".to_string());
        hello.append_child(world);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><world/></hello>");
    }

    #[test]
    fn nested_text() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let text = d.new_text("A fine day to you!".to_string());
        hello.append_child(text);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello>A fine day to you!</hello>");
    }

    #[test]
    fn nested_comment() {
        let d = Document::new();
        let hello = d.new_element("hello".to_string());
        let comment = d.new_comment(" Fill this in ".to_string());
        hello.append_child(comment);
        d.root().append_child(hello);

        let xml = format_xml(&d);
        assert_str_eq!(xml, "<?xml version='1.0'?><hello><!-- Fill this in --></hello>");
    }
}
<|endoftext|>


<commit_before>use ansi_term::Style;

use crate::fs::File;
use crate::info::filetype::FileExtensions;
use crate::output::file_name::FileStyle;

pub trait FileIcon {
    fn icon_file(&self, file: &File) -> Option<char>;
}

pub enum Icons {
    Audio,
    Image,
    Video,
}

impl Icons {
    pub fn value(&self) -> char {
        match *self {
            Icons::Audio => '\u{f001}',
            Icons::Image => '\u{f1c5}',
            Icons::Video => '\u{f03d}',
        }
    }
}

pub fn painted_icon(file: &File, style: &FileStyle) -> String {
    let file_icon = icon(&file).to_string();
    let painted = style.exts
            .colour_file(&file)
            .map_or(file_icon.to_string(), |c| {
                // Remove underline from icon
                if c.is_underline {
                    match c.foreground {
                        Some(color) => Style::from(color).paint(file_icon).to_string(),
                        None => Style::default().paint(file_icon).to_string(),
                    }
                } else {
                    c.paint(file_icon).to_string()
                }
            });
    format!("{}  ", painted)
}

fn icon(file: &File) -> char {
    let extensions = Box::new(FileExtensions);
    if file.is_directory() { '\u{f115}' }
    else if let Some(icon) = extensions.icon_file(file) { icon }
    else {
        if let Some(ext) = file.ext.as_ref() {
            match ext.as_str() {
                "ai"        => '\u{e7b4}',
                "android"   => '\u{e70e}',
                "apple"     => '\u{f179}',
                "avro"      => '\u{e60b}',
                "clj"       => '\u{e768}',
                "coffee"    => '\u{f0f4}',
                "cpp"       => '\u{e61d}',
                "hpp"       => '\u{e61d}',
                "c"         => '\u{e61e}',
                "h"         => '\u{e61e}',
                "cs"        => '\u{f81a}',
                "css"       => '\u{e749}',
                "d"         => '\u{e7af}',
                "dart"      => '\u{e798}',
                "db"        => '\u{f1c0}',
                "diff"      => '\u{f440}',
                "patch"     => '\u{f440}',
                "rtf"       => '\u{f1c2}',
                "doc"       => '\u{f1c2}',
                "docx"      => '\u{f1c2}',
                "odt"       => '\u{f1c2}',
                "ebook"     => '\u{e28b}',
                "env"       => '\u{f462}',
                "epub"      => '\u{e28a}',
                "erl"       => '\u{e7b1}',
                "font"      => '\u{f031}',
                "gform"     => '\u{f298}',
                "git"       => '\u{f1d3}',
                "go"        => '\u{e626}',
                "hs"        => '\u{e777}',
                "htm"       => '\u{f13b}',
                "html"      => '\u{f13b}',
                "xhtml"     => '\u{f13b}',
                "iml"       => '\u{e7b5}',
                "java"      => '\u{e204}',
                "js"        => '\u{e74e}',
                "mjs"       => '\u{e74e}',
                "json"      => '\u{e60b}',
                "jsx"       => '\u{e7ba}',
                "vue"       => '\u{fd42}',
                "node"      => '\u{f898}',
                "less"      => '\u{e758}',
                "log"       => '\u{f18d}',
                "lua"       => '\u{e620}',
                "md"        => '\u{f48a}',
                "markdown"  => '\u{f48a}',
                "mustache"  => '\u{e60f}',
                "npmignore" => '\u{e71e}',
                "pdf"       => '\u{f1c1}',
                "djvu"      => '\u{f02d}',
                "mobi"      => '\u{f02d}',
                "php"       => '\u{e73d}',
                "pl"        => '\u{e769}',
                "ppt"       => '\u{f1c4}',
                "pptx"      => '\u{f1c4}',
                "odp"       => '\u{f1c4}',
                "psd"       => '\u{e7b8}',
                "py"        => '\u{e606}',
                "r"         => '\u{f25d}',
                "rb"        => '\u{e21e}',
                "ru"        => '\u{e21e}',
                "erb"       => '\u{e21e}',
                "gem"       => '\u{e21e}',
                "rdb"       => '\u{e76d}',
                "rs"        => '\u{e7a8}',
                "rss"       => '\u{f09e}',
                "rubydoc"   => '\u{e73b}',
                "sass"      => '\u{e74b}',
                "stylus"    => '\u{e759}',
                "scala"     => '\u{e737}',
                "shell"     => '\u{f489}',
                "sqlite3"   => '\u{e7c4}',
                "styl"      => '\u{e600}',
                "latex"     => '\u{e600}',
                "tex"       => '\u{e600}',
                "ts"        => '\u{e628}',
                "tsx"       => '\u{e628}',
                "twig"      => '\u{e61c}',
                "txt"       => '\u{f15c}',
                "video"     => '\u{f03d}',
                "vim"       => '\u{e62b}',
                "xml"       => '\u{e619}',
                "yml"       => '\u{f481}',
                "yaml"      => '\u{f481}',
                "rar"       => '\u{f410}',
                "zip"       => '\u{f410}',
                "bz"        => '\u{f410}',
                "bz2"       => '\u{f410}',
                "xz"        => '\u{f410}',
                "taz"       => '\u{f410}',
                "tbz"       => '\u{f410}',
                "tbz2"      => '\u{f410}',
                "tz"        => '\u{f410}',
                "tar"       => '\u{f410}',
                "tzo"       => '\u{f410}',
                "lz"        => '\u{f410}',
                "lzh"       => '\u{f410}',
                "lzma"      => '\u{f410}',
                "lzo"       => '\u{f410}',
                "gz"        => '\u{f410}',
                "deb"       => '\u{e77d}',
                "rpm"       => '\u{e7bb}',
                "exe"       => '\u{e70f}',
                "msi"       => '\u{e70f}',
                "dll"       => '\u{e70f}',
                "cab"       => '\u{e70f}',
                "bat"       => '\u{e70f}',
                "cmd"       => '\u{e70f}',
                "sh"        => '\u{e795}',
                "bash"      => '\u{e795}',
                "zsh"       => '\u{e795}',
                "fish"      => '\u{e795}',
                "csh"       => '\u{e795}',
                "ini"       => '\u{e615}',
                "toml"      => '\u{e615}',
                "cfg"       => '\u{e615}',
                "conf"      => '\u{e615}',
                "apk"       => '\u{e70e}',
                "ttf"       => '\u{f031}',
                "woff"      => '\u{f031}',
                "woff2"     => '\u{f031}',
                "otf"       => '\u{f031}',
                "csv"       => '\u{f1c3}',
                "tsv"       => '\u{f1c3}',
                "xls"       => '\u{f1c3}',
                "xlsx"      => '\u{f1c3}',
                "ods"       => '\u{f1c3}',
                "so"        => '\u{f17c}',
                "sql"       => '\u{f1c0}',
                "jar"       => '\u{e256}',
                "jad"       => '\u{e256}',
                "class"     => '\u{e256}',
                "war"       => '\u{e256}',
                "groovy"    => '\u{e775}',
                "iso"       => '\u{e271}',
                "lock"      => '\u{f023}',
                _           => '\u{f15b}'
            }
        } else {
            '\u{f15b}'
        }
    }
}
<commit_msg>Improve default file icon<commit_after>use ansi_term::Style;

use crate::fs::File;
use crate::info::filetype::FileExtensions;
use crate::output::file_name::FileStyle;

pub trait FileIcon {
    fn icon_file(&self, file: &File) -> Option<char>;
}

pub enum Icons {
    Audio,
    Image,
    Video,
}

impl Icons {
    pub fn value(&self) -> char {
        match *self {
            Icons::Audio => '\u{f001}',
            Icons::Image => '\u{f1c5}',
            Icons::Video => '\u{f03d}',
        }
    }
}

pub fn painted_icon(file: &File, style: &FileStyle) -> String {
    let file_icon = icon(&file).to_string();
    let painted = style.exts
            .colour_file(&file)
            .map_or(file_icon.to_string(), |c| {
                // Remove underline from icon
                if c.is_underline {
                    match c.foreground {
                        Some(color) => Style::from(color).paint(file_icon).to_string(),
                        None => Style::default().paint(file_icon).to_string(),
                    }
                } else {
                    c.paint(file_icon).to_string()
                }
            });
    format!("{}  ", painted)
}

fn icon(file: &File) -> char {
    let extensions = Box::new(FileExtensions);
    if file.is_directory() { '\u{f115}' }
    else if let Some(icon) = extensions.icon_file(file) { icon }
    else {
        if let Some(ext) = file.ext.as_ref() {
            match ext.as_str() {
                "ai"        => '\u{e7b4}',
                "android"   => '\u{e70e}',
                "apple"     => '\u{f179}',
                "avro"      => '\u{e60b}',
                "clj"       => '\u{e768}',
                "coffee"    => '\u{f0f4}',
                "cpp"       => '\u{e61d}',
                "hpp"       => '\u{e61d}',
                "c"         => '\u{e61e}',
                "h"         => '\u{e61e}',
                "cs"        => '\u{f81a}',
                "css"       => '\u{e749}',
                "d"         => '\u{e7af}',
                "dart"      => '\u{e798}',
                "db"        => '\u{f1c0}',
                "diff"      => '\u{f440}',
                "patch"     => '\u{f440}',
                "rtf"       => '\u{f1c2}',
                "doc"       => '\u{f1c2}',
                "docx"      => '\u{f1c2}',
                "odt"       => '\u{f1c2}',
                "ebook"     => '\u{e28b}',
                "env"       => '\u{f462}',
                "epub"      => '\u{e28a}',
                "erl"       => '\u{e7b1}',
                "font"      => '\u{f031}',
                "gform"     => '\u{f298}',
                "git"       => '\u{f1d3}',
                "go"        => '\u{e626}',
                "hs"        => '\u{e777}',
                "htm"       => '\u{f13b}',
                "html"      => '\u{f13b}',
                "xhtml"     => '\u{f13b}',
                "iml"       => '\u{e7b5}',
                "java"      => '\u{e204}',
                "js"        => '\u{e74e}',
                "mjs"       => '\u{e74e}',
                "json"      => '\u{e60b}',
                "jsx"       => '\u{e7ba}',
                "vue"       => '\u{fd42}',
                "node"      => '\u{f898}',
                "less"      => '\u{e758}',
                "log"       => '\u{f18d}',
                "lua"       => '\u{e620}',
                "md"        => '\u{f48a}',
                "markdown"  => '\u{f48a}',
                "mustache"  => '\u{e60f}',
                "npmignore" => '\u{e71e}',
                "pdf"       => '\u{f1c1}',
                "djvu"      => '\u{f02d}',
                "mobi"      => '\u{f02d}',
                "php"       => '\u{e73d}',
                "pl"        => '\u{e769}',
                "ppt"       => '\u{f1c4}',
                "pptx"      => '\u{f1c4}',
                "odp"       => '\u{f1c4}',
                "psd"       => '\u{e7b8}',
                "py"        => '\u{e606}',
                "r"         => '\u{f25d}',
                "rb"        => '\u{e21e}',
                "ru"        => '\u{e21e}',
                "erb"       => '\u{e21e}',
                "gem"       => '\u{e21e}',
                "rdb"       => '\u{e76d}',
                "rs"        => '\u{e7a8}',
                "rss"       => '\u{f09e}',
                "rubydoc"   => '\u{e73b}',
                "sass"      => '\u{e74b}',
                "stylus"    => '\u{e759}',
                "scala"     => '\u{e737}',
                "shell"     => '\u{f489}',
                "sqlite3"   => '\u{e7c4}',
                "styl"      => '\u{e600}',
                "latex"     => '\u{e600}',
                "tex"       => '\u{e600}',
                "ts"        => '\u{e628}',
                "tsx"       => '\u{e628}',
                "twig"      => '\u{e61c}',
                "txt"       => '\u{f15c}',
                "video"     => '\u{f03d}',
                "vim"       => '\u{e62b}',
                "xml"       => '\u{e619}',
                "yml"       => '\u{f481}',
                "yaml"      => '\u{f481}',
                "rar"       => '\u{f410}',
                "zip"       => '\u{f410}',
                "bz"        => '\u{f410}',
                "bz2"       => '\u{f410}',
                "xz"        => '\u{f410}',
                "taz"       => '\u{f410}',
                "tbz"       => '\u{f410}',
                "tbz2"      => '\u{f410}',
                "tz"        => '\u{f410}',
                "tar"       => '\u{f410}',
                "tzo"       => '\u{f410}',
                "lz"        => '\u{f410}',
                "lzh"       => '\u{f410}',
                "lzma"      => '\u{f410}',
                "lzo"       => '\u{f410}',
                "gz"        => '\u{f410}',
                "deb"       => '\u{e77d}',
                "rpm"       => '\u{e7bb}',
                "exe"       => '\u{e70f}',
                "msi"       => '\u{e70f}',
                "dll"       => '\u{e70f}',
                "cab"       => '\u{e70f}',
                "bat"       => '\u{e70f}',
                "cmd"       => '\u{e70f}',
                "sh"        => '\u{e795}',
                "bash"      => '\u{e795}',
                "zsh"       => '\u{e795}',
                "fish"      => '\u{e795}',
                "csh"       => '\u{e795}',
                "ini"       => '\u{e615}',
                "toml"      => '\u{e615}',
                "cfg"       => '\u{e615}',
                "conf"      => '\u{e615}',
                "apk"       => '\u{e70e}',
                "ttf"       => '\u{f031}',
                "woff"      => '\u{f031}',
                "woff2"     => '\u{f031}',
                "otf"       => '\u{f031}',
                "csv"       => '\u{f1c3}',
                "tsv"       => '\u{f1c3}',
                "xls"       => '\u{f1c3}',
                "xlsx"      => '\u{f1c3}',
                "ods"       => '\u{f1c3}',
                "so"        => '\u{f17c}',
                "sql"       => '\u{f1c0}',
                "jar"       => '\u{e256}',
                "jad"       => '\u{e256}',
                "class"     => '\u{e256}',
                "war"       => '\u{e256}',
                "groovy"    => '\u{e775}',
                "iso"       => '\u{e271}',
                "lock"      => '\u{f023}',
                _           => '\u{f016}'
            }
        } else {
            '\u{f016}'
        }
    }
}
<|endoftext|>


<commit_before>/// get three u8 characters and convert to 4 indexes of ALPHABET table
fn encode_block(block: &[u8]) -> [u8; 4] {
    let mut bitvec: u32 = 0xff_00_00_00;

    // first char place in second octet of bitvec
    // second char to third, etc.
    for (i, chr) in block.iter().enumerate() {
        let x: u32 = (*chr as u32) << (8 * (2 - i));
        bitvec |= x;
    }

    //                              <-----> six bytes
    let mut mask: u32 = 0b0000_0000_1111_1100_0000_0000_0000_0000;
    let mut res: [u8; 4] = [0; 4];

    // divide three octets of bitvec (2, 3, 4) to four six-bytes integers
    for i in 0..4 {
        res[i] = ((bitvec & mask) >> (6 * (3 - i))) as u8;
        mask = mask >> 6;
    }
    res
}

/// get translation table and str data, return encoded string
pub fn encode(table: &[u8], data: &[u8]) -> Vec<u8> {
    let mut data = data.iter();
    let mut block: [u8; 3] = [0; 3];
    let mut res = Vec::new();
    let mut done = false;
    let mut count;

    while !done {
        count = 0;

        // fill block with chars
        // count only those symbols that were actually added
        for i in 0..3 {
            block[i] = match data.next() {
                Some(chr) => {
                    count += 1;
                    *chr
                },
                None => {
                    done = true;
                    0
                },
            }
        }

        // in case of empty iterator
        if count == 0 {
            break
        }

        for idx in &encode_block(&block) {
            if count + 1 != 0 {
                res.push(table[*idx as usize]);
                count -= 1;
            } else {
                res.push('=' as u8);
            }
        }
    }
    res
}
<commit_msg>fix bytes -> bits<commit_after>/// get three u8 characters and convert to 4 indexes of ALPHABET table
fn encode_block(block: &[u8]) -> [u8; 4] {
    let mut bitvec: u32 = 0xff_00_00_00;

    // first char place in second octet of bitvec
    // second char to third, etc.
    for (i, chr) in block.iter().enumerate() {
        let x: u32 = (*chr as u32) << (8 * (2 - i));
        bitvec |= x;
    }

    //                              <-----> six bits
    let mut mask: u32 = 0b0000_0000_1111_1100_0000_0000_0000_0000;
    let mut res: [u8; 4] = [0; 4];

    // divide three octets of bitvec (2, 3, 4) to four six-bits integers
    for i in 0..4 {
        res[i] = ((bitvec & mask) >> (6 * (3 - i))) as u8;
        mask = mask >> 6;
    }
    res
}

/// get translation table and str data, return encoded string
pub fn encode(table: &[u8], data: &[u8]) -> Vec<u8> {
    let mut data = data.iter();
    let mut block: [u8; 3] = [0; 3];
    let mut res = Vec::new();
    let mut done = false;
    let mut count;

    while !done {
        count = 0;

        // fill block with chars
        // count only those symbols that were actually added
        for i in 0..3 {
            block[i] = match data.next() {
                Some(chr) => {
                    count += 1;
                    *chr
                },
                None => {
                    done = true;
                    0
                },
            }
        }

        // in case of empty iterator
        if count == 0 {
            break
        }

        for idx in &encode_block(&block) {
            if count + 1 != 0 {
                res.push(table[*idx as usize]);
                count -= 1;
            } else {
                res.push('=' as u8);
            }
        }
    }
    res
}
<|endoftext|>


<commit_before>pub fn add(num_l: &str, num_r: &str) -> String {
    let mut sum: Vec<u8> = denormalize(num_l).into();
    sum.extend(denormalize(num_r).bytes());

    sum.sort();
    sum.reverse();

    normalize(&String::from_utf8(sum).unwrap())
}

pub fn denormalize(num: &str) -> String {
    match num {
        "IV" => "IIII",
        _ => num,
    }.to_string()
}

pub fn normalize(num: &str) -> String {
    match num {
        "IIII" => "IV".to_string(),
        "IIIII" => "V".to_string(),
        "VV" => "X".to_string(),
        _ => num.to_string(),
    }
}

#[cfg(test)]
mod tests {
    use super::add;
    use super::normalize;
    use super::denormalize;

    #[test]
    fn add_i_i() {
        assert_eq!("II", add("I", "I"));
    }

    #[test]
    fn add_i_ii() {
        assert_eq!("III", add("I", "II"));
    }

    #[test]
    fn add_ii_iii_requires_normalization_to_v() {
        assert_eq!("V", add("II", "III"));

    }

    #[test]
    fn add_v_i() {
        assert_eq!("VI", add("V", "I"));
    }

    #[test]
    fn add_i_v_understands_the_relative_order_of_v_and_i() {
        assert_eq!("VI", add("I", "V"));
    }

    #[test]
    fn add_i_iv_denormalizes_before_adding() {
        assert_eq!("V", add("I", "IV"));
    }

    #[test]
    fn denormalize_iv() {
        assert_eq!("IIII", denormalize("IV"));
    }

    #[test]
    fn normalize_iiiii() {
        assert_eq!("V", normalize("IIIII"));
    }

    #[test]
    fn normalize_iiii() {
        assert_eq!("IV", normalize("IIII"));
    }

    #[test]
    fn normalize_vv() {
        assert_eq!("X", normalize("VV"));
    }

}
<commit_msg>And a couple more ways of adding to X in Rust<commit_after>pub fn add(num_l: &str, num_r: &str) -> String {
    let mut sum: Vec<u8> = denormalize(num_l).into();
    sum.extend(denormalize(num_r).bytes());

    sum.sort();
    sum.reverse();

    normalize(&String::from_utf8(sum).unwrap())
}

pub fn denormalize(num: &str) -> String {
    match num {
        "IV" => "IIII",
        "IX" => "VIIII",
        _ => num,
    }.to_string()
}

pub fn normalize(num: &str) -> String {
    match num {
        "IIII" => "IV".to_string(),
        "IIIII" => "V".to_string(),
        "VV" => "X".to_string(),
        "VIIIII" => "X".to_string(),
        _ => num.to_string(),
    }
}

#[cfg(test)]
mod tests {
    use super::add;
    use super::normalize;
    use super::denormalize;

    #[test]
    fn add_i_i() {
        assert_eq!("II", add("I", "I"));
    }

    #[test]
    fn add_i_ii() {
        assert_eq!("III", add("I", "II"));
    }

    #[test]
    fn add_ii_iii_requires_normalization_to_v() {
        assert_eq!("V", add("II", "III"));

    }

    #[test]
    fn add_v_i() {
        assert_eq!("VI", add("V", "I"));
    }

    #[test]
    fn add_i_v_understands_the_relative_order_of_v_and_i() {
        assert_eq!("VI", add("I", "V"));
    }

    #[test]
    fn add_i_iv_denormalizes_before_adding() {
        assert_eq!("V", add("I", "IV"));
    }

    #[test]
    fn denormalize_iv() {
        assert_eq!("IIII", denormalize("IV"));
    }

    #[test]
    fn denormalize_ix() {
        assert_eq!("VIIII", denormalize("IX"));
    }

    #[test]
    fn normalize_iiiii() {
        assert_eq!("V", normalize("IIIII"));
    }

    #[test]
    fn normalize_iiii() {
        assert_eq!("IV", normalize("IIII"));
    }

    #[test]
    fn normalize_vv() {
        assert_eq!("X", normalize("VV"));
    }

    #[test]
    fn normalize_viiiii() {
        assert_eq!("X", normalize("VIIIII"));
    }

}
<|endoftext|>