<commit_before>use std::iter::FromIterator;

use cargo::core::Workspace;
use cargo::ops::{self, MessageFormat, Packages};
use cargo::util::{CliResult, CliError, Config, CargoErrorKind};
use cargo::util::important_paths::{find_root_manifest_for_wd};

#[derive(RustcDecodable)]
pub struct Options {
    flag_bin: Option<String>,
    flag_example: Option<String>,
    flag_package: Option<String>,
    flag_jobs: Option<u32>,
    flag_features: Vec<String>,
    flag_all_features: bool,
    flag_no_default_features: bool,
    flag_target: Option<String>,
    flag_manifest_path: Option<String>,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_message_format: MessageFormat,
    flag_release: bool,
    flag_frozen: bool,
    flag_locked: bool,
    arg_args: Vec<String>,
}

pub const USAGE: &'static str = "
Run the main binary of the local package (src/main.rs)

Usage:
    cargo run [options] [--] [<args>...]

Options:
    -h, --help                   Print this message
    --bin NAME                   Name of the bin target to run
    --example NAME               Name of the example target to run
    -p SPEC, --package SPEC      Package with the target to run
    -j N, --jobs N               Number of parallel jobs, defaults to # of CPUs
    --release                    Build artifacts in release mode, with optimizations
    --features FEATURES          Space-separated list of features to also build
    --all-features               Build all available features
    --no-default-features        Do not build the `default` feature
    --target TRIPLE              Build for the target triple
    --manifest-path PATH         Path to the manifest to execute
    -v, --verbose ...            Use verbose output (-vv very verbose/build.rs output)
    -q, --quiet                  No output printed to stdout
    --color WHEN                 Coloring: auto, always, never
    --message-format FMT         Error format: human, json [default: human]
    --frozen                     Require Cargo.lock and cache are up to date
    --locked                     Require Cargo.lock is up to date

If neither `--bin` nor `--example` are given, then if the project only has one
bin target it will be run. Otherwise `--bin` specifies the bin target to run,
and `--example` specifies the example target to run. At most one of `--bin` or
`--example` can be provided.

All of the trailing arguments are passed to the binary to run. If you're passing
arguments to both Cargo and the binary, the ones after `--` go to the binary,
the ones before go to Cargo.
";

pub fn execute(options: Options, config: &Config) -> CliResult {
    config.configure(options.flag_verbose,
                     options.flag_quiet,
                     &options.flag_color,
                     options.flag_frozen,
                     options.flag_locked)?;

    let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;

    let (mut examples, mut bins) = (Vec::new(), Vec::new());
    if let Some(s) = options.flag_bin {
        bins.push(s);
    }
    if let Some(s) = options.flag_example {
        examples.push(s);
    }

    let packages = Vec::from_iter(options.flag_package.iter().cloned());
    let spec = Packages::Packages(&packages);

    let compile_opts = ops::CompileOptions {
        config: config,
        jobs: options.flag_jobs,
        target: options.flag_target.as_ref().map(|t| &t[..]),
        features: &options.flag_features,
        all_features: options.flag_all_features,
        no_default_features: options.flag_no_default_features,
        spec: spec,
        release: options.flag_release,
        mode: ops::CompileMode::Build,
        filter: if examples.is_empty() && bins.is_empty() {
            ops::CompileFilter::Everything { required_features_filterable: false, }
        } else {
            ops::CompileFilter::new(false,
                                    &bins, false,
                                    &[], false,
                                    &examples, false,
                                    &[], false)
        },
        message_format: options.flag_message_format,
        target_rustdoc_args: None,
        target_rustc_args: None,
    };

    let ws = Workspace::new(&root, config)?;
    match ops::run(&ws, &compile_opts, &options.arg_args)? {
        None => Ok(()),
        Some(err) => {
            // If we never actually spawned the process then that sounds pretty
            // bad and we always want to forward that up.
            let exit = match err.exit.clone() {
                Some(exit) => exit,
                None => return Err(CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), 101)),
            };

            // If `-q` was passed then we suppress extra error information about
            // a failed process, we assume the process itself printed out enough
            // information about why it failed so we don't do so as well
            let exit_code = exit.code().unwrap_or(101);
            Err(if options.flag_quiet == Some(true) {
                CliError::code(exit_code)
            } else {
                CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), exit_code)
            })
        }
    }
}
<commit_msg>Fix formatting issue blocking build<commit_after>use std::iter::FromIterator;

use cargo::core::Workspace;
use cargo::ops::{self, MessageFormat, Packages};
use cargo::util::{CliResult, CliError, Config, CargoErrorKind};
use cargo::util::important_paths::{find_root_manifest_for_wd};

#[derive(RustcDecodable)]
pub struct Options {
    flag_bin: Option<String>,
    flag_example: Option<String>,
    flag_package: Option<String>,
    flag_jobs: Option<u32>,
    flag_features: Vec<String>,
    flag_all_features: bool,
    flag_no_default_features: bool,
    flag_target: Option<String>,
    flag_manifest_path: Option<String>,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_message_format: MessageFormat,
    flag_release: bool,
    flag_frozen: bool,
    flag_locked: bool,
    arg_args: Vec<String>,
}

pub const USAGE: &'static str = "
Run the main binary of the local package (src/main.rs)

Usage:
    cargo run [options] [--] [<args>...]

Options:
    -h, --help                   Print this message
    --bin NAME                   Name of the bin target to run
    --example NAME               Name of the example target to run
    -p SPEC, --package SPEC      Package with the target to run
    -j N, --jobs N               Number of parallel jobs, defaults to # of CPUs
    --release                    Build artifacts in release mode, with optimizations
    --features FEATURES          Space-separated list of features to also build
    --all-features               Build all available features
    --no-default-features        Do not build the `default` feature
    --target TRIPLE              Build for the target triple
    --manifest-path PATH         Path to the manifest to execute
    -v, --verbose ...            Use verbose output (-vv very verbose/build.rs output)
    -q, --quiet                  No output printed to stdout
    --color WHEN                 Coloring: auto, always, never
    --message-format FMT         Error format: human, json [default: human]
    --frozen                     Require Cargo.lock and cache are up to date
    --locked                     Require Cargo.lock is up to date

If neither `--bin` nor `--example` are given, then if the project only has one
bin target it will be run. Otherwise `--bin` specifies the bin target to run,
and `--example` specifies the example target to run. At most one of `--bin` or
`--example` can be provided.

All of the trailing arguments are passed to the binary to run. If you're passing
arguments to both Cargo and the binary, the ones after `--` go to the binary,
the ones before go to Cargo.
";

pub fn execute(options: Options, config: &Config) -> CliResult {
    config.configure(options.flag_verbose,
                     options.flag_quiet,
                     &options.flag_color,
                     options.flag_frozen,
                     options.flag_locked)?;

    let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;

    let (mut examples, mut bins) = (Vec::new(), Vec::new());
    if let Some(s) = options.flag_bin {
        bins.push(s);
    }
    if let Some(s) = options.flag_example {
        examples.push(s);
    }

    let packages = Vec::from_iter(options.flag_package.iter().cloned());
    let spec = Packages::Packages(&packages);

    let compile_opts = ops::CompileOptions {
        config: config,
        jobs: options.flag_jobs,
        target: options.flag_target.as_ref().map(|t| &t[..]),
        features: &options.flag_features,
        all_features: options.flag_all_features,
        no_default_features: options.flag_no_default_features,
        spec: spec,
        release: options.flag_release,
        mode: ops::CompileMode::Build,
        filter: if examples.is_empty() && bins.is_empty() {
            ops::CompileFilter::Everything { required_features_filterable: false, }
        } else {
            ops::CompileFilter::new(false,
                                    &bins, false,
                                    &[], false,
                                    &examples, false,
                                    &[], false)
        },
        message_format: options.flag_message_format,
        target_rustdoc_args: None,
        target_rustc_args: None,
    };

    let ws = Workspace::new(&root, config)?;
    match ops::run(&ws, &compile_opts, &options.arg_args)? {
        None => Ok(()),
        Some(err) => {
            // If we never actually spawned the process then that sounds pretty
            // bad and we always want to forward that up.
            let exit = match err.exit.clone() {
                Some(exit) => exit,
                None => return Err(
                    CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), 101)),
            };

            // If `-q` was passed then we suppress extra error information about
            // a failed process, we assume the process itself printed out enough
            // information about why it failed so we don't do so as well
            let exit_code = exit.code().unwrap_or(101);
            Err(if options.flag_quiet == Some(true) {
                CliError::code(exit_code)
            } else {
                CliError::new(CargoErrorKind::ProcessErrorKind(err).into(), exit_code)
            })
        }
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate tar;
extern crate tree_magic;
extern crate lzma;
extern crate libflate;

use std::{env, process};
use std::io::{stdin, stdout, stderr, copy, Error, ErrorKind, Result, Read, Write, BufReader};
use std::fs::{self, File};
use std::os::unix::fs::OpenOptionsExt;
use std::path::Path;

use tar::{Archive, Builder, EntryType};
use lzma::LzmaReader;
use libflate::gzip::Decoder as GzipDecoder;

fn create_inner<T: Write>(input: &str, ar: &mut Builder<T>) -> Result<()> {
    if try!(fs::metadata(input)).is_dir() {
        for entry_result in try!(fs::read_dir(input)) {
            let entry = try!(entry_result);
            if try!(fs::metadata(entry.path())).is_dir() {
                try!(create_inner(entry.path().to_str().unwrap(), ar));
            } else {
                println!("{}", entry.path().display());
                try!(ar.append_path(entry.path()));
            }
        }
    } else {
        println!("{}", input);
        try!(ar.append_path(input));
    }

    Ok(())
}

fn create(input: &str, tar: &str) -> Result<()> {
    if tar == "-" {
        create_inner(input, &mut Builder::new(stdout()))
    } else {
        create_inner(input, &mut Builder::new(try!(File::create(tar))))
    }
}

fn list_inner<T: Read>(ar: &mut Archive<T>) -> Result<()> {
    for entry_result in try!(ar.entries()) {
        let entry = try!(entry_result);
        let path = try!(entry.path());
        println!("{}", path.display());
    }

    Ok(())
}

fn list(tar: &str) -> Result<()> {
    if tar == "-" {
        list_inner(&mut Archive::new(stdin()))
    } else {
        list_inner(&mut Archive::new(try!(File::open(tar))))
    }
}

fn extract_inner<T: Read>(ar: &mut Archive<T>) -> Result<()> {
    for entry_result in try!(ar.entries()) {
        let mut entry = try!(entry_result);
        match entry.header().entry_type() {
            EntryType::Regular => {
                let mut file = {
                    let path = try!(entry.path());
                    if let Some(parent) = path.parent() {
                        try!(fs::create_dir_all(parent));
                    }
                    try!(
                        fs::OpenOptions::new()
                            .read(true)
                            .write(true)
                            .truncate(true)
                            .create(true)
                            .mode(entry.header().mode().unwrap_or(644))
                            .open(path)
                    )
                };
                try!(copy(&mut entry, &mut file));
            },
            EntryType::Directory => {
                try!(fs::create_dir_all(try!(entry.path())));
            },
            other => {
                panic!("Unsupported entry type {:?}", other);
            }
        }
    }

    Ok(())
}

fn extract(tar: &str) -> Result<()> {
    if tar == "-" {
        extract_inner(&mut Archive::new(stdin()))
    } else {
        let mime = tree_magic::from_filepath(Path::new(&tar));
        let file = BufReader::new(File::open(tar)?);
        if mime == "application/x-xz" {
            extract_inner(&mut Archive::new(LzmaReader::new_decompressor(file)
                                            .map_err(|e| Error::new(ErrorKind::Other, e))?))
        } else if mime == "application/gzip" {
            extract_inner(&mut Archive::new(GzipDecoder::new(file)
                                            .map_err(|e| Error::new(ErrorKind::Other, e))?))
        } else {
            extract_inner(&mut Archive::new(file))
        }
    }
}

fn main() {
    let mut args = env::args().skip(1);
    if let Some(op) = args.next() {
        match op.as_str() {
            "c" => if let Some(input) = args.next() {
                if let Err(err) = create(&input, "-") {
                    write!(stderr(), "tar: create: failed: {}\n", err).unwrap();
                    process::exit(1);
                }
            } else {
                write!(stderr(), "tar: create: no input specified: {}\n", op).unwrap();
                process::exit(1);
            },
            "cf" => if let Some(tar) = args.next() {
                if let Some(input) = args.next() {
                    if let Err(err) = create(&input, &tar) {
                        write!(stderr(), "tar: create: failed: {}\n", err).unwrap();
                        process::exit(1);
                    }
                } else {
                    write!(stderr(), "tar: create: no input specified: {}\n", op).unwrap();
                    process::exit(1);
                }
            } else {
                write!(stderr(), "tar: create: no tarfile specified: {}\n", op).unwrap();
                process::exit(1);
            },
            "t" | "tf" => {
                let tar = args.next().unwrap_or("-".to_string());
                if let Err(err) = list(&tar) {
                    write!(stderr(), "tar: list: failed: {}\n", err).unwrap();
                    process::exit(1);
                }
            },
            "x" | "xf" => {
                let tar = args.next().unwrap_or("-".to_string());
                if let Err(err) = extract(&tar) {
                    write!(stderr(), "tar: extract: failed: {}\n", err).unwrap();
                    process::exit(1);
                }
            },
            _ => {
                write!(stderr(), "tar: {}: unknown operation\n", op).unwrap();
                write!(stderr(), "tar: need to specify c[f] (create), t[f] (list), or x[f] (extract)\n").unwrap();
                process::exit(1);
            }
        }
    } else {
        write!(stderr(), "tar: no operation\n").unwrap();
        write!(stderr(), "tar: need to specify cf (create), tf (list), or xf (extract)\n").unwrap();
        process::exit(1);
    }
}
<commit_msg>Verbose extract<commit_after>#![deny(warnings)]

extern crate tar;
extern crate tree_magic;
extern crate lzma;
extern crate libflate;

use std::{env, process};
use std::io::{stdin, stdout, stderr, copy, Error, ErrorKind, Result, Read, Write, BufReader};
use std::fs::{self, File};
use std::os::unix::fs::OpenOptionsExt;
use std::path::Path;

use tar::{Archive, Builder, EntryType};
use lzma::LzmaReader;
use libflate::gzip::Decoder as GzipDecoder;

fn create_inner<T: Write>(input: &str, ar: &mut Builder<T>) -> Result<()> {
    if try!(fs::metadata(input)).is_dir() {
        for entry_result in try!(fs::read_dir(input)) {
            let entry = try!(entry_result);
            if try!(fs::metadata(entry.path())).is_dir() {
                try!(create_inner(entry.path().to_str().unwrap(), ar));
            } else {
                println!("{}", entry.path().display());
                try!(ar.append_path(entry.path()));
            }
        }
    } else {
        println!("{}", input);
        try!(ar.append_path(input));
    }

    Ok(())
}

fn create(input: &str, tar: &str) -> Result<()> {
    if tar == "-" {
        create_inner(input, &mut Builder::new(stdout()))
    } else {
        create_inner(input, &mut Builder::new(try!(File::create(tar))))
    }
}

fn list_inner<T: Read>(ar: &mut Archive<T>) -> Result<()> {
    for entry_result in try!(ar.entries()) {
        let entry = try!(entry_result);
        let path = try!(entry.path());
        println!("{}", path.display());
    }

    Ok(())
}

fn list(tar: &str) -> Result<()> {
    if tar == "-" {
        list_inner(&mut Archive::new(stdin()))
    } else {
        list_inner(&mut Archive::new(try!(File::open(tar))))
    }
}

fn extract_inner<T: Read>(ar: &mut Archive<T>, verbose: bool) -> Result<()> {
    for entry_result in try!(ar.entries()) {
        let mut entry = try!(entry_result);
        match entry.header().entry_type() {
            EntryType::Regular => {
                let mut file = {
                    let path = try!(entry.path());
                    if let Some(parent) = path.parent() {
                        try!(fs::create_dir_all(parent));
                    }
                    try!(
                        fs::OpenOptions::new()
                            .read(true)
                            .write(true)
                            .truncate(true)
                            .create(true)
                            .mode(entry.header().mode().unwrap_or(644))
                            .open(path)
                    )
                };
                try!(copy(&mut entry, &mut file));
            },
            EntryType::Directory => {
                try!(fs::create_dir_all(try!(entry.path())));
            },
            other => {
                panic!("Unsupported entry type {:?}", other);
            }
        }

        if verbose {
            println!("{}", entry.path()?.display());
        }
    }

    Ok(())
}

fn extract(tar: &str, verbose: bool) -> Result<()> {
    if tar == "-" {
        extract_inner(&mut Archive::new(stdin()), verbose)
    } else {
        let mime = tree_magic::from_filepath(Path::new(&tar));
        let file = BufReader::new(File::open(tar)?);
        if mime == "application/x-xz" {
            extract_inner(&mut Archive::new(LzmaReader::new_decompressor(file)
                                            .map_err(|e| Error::new(ErrorKind::Other, e))?), verbose)
        } else if mime == "application/gzip" {
            extract_inner(&mut Archive::new(GzipDecoder::new(file)
                                            .map_err(|e| Error::new(ErrorKind::Other, e))?), verbose)
        } else {
            extract_inner(&mut Archive::new(file), verbose)
        }
    }
}

fn main() {
    let mut args = env::args().skip(1);
    if let Some(op) = args.next() {
        match op.as_str() {
            "c" => if let Some(input) = args.next() {
                if let Err(err) = create(&input, "-") {
                    write!(stderr(), "tar: create: failed: {}\n", err).unwrap();
                    process::exit(1);
                }
            } else {
                write!(stderr(), "tar: create: no input specified: {}\n", op).unwrap();
                process::exit(1);
            },
            "cf" => if let Some(tar) = args.next() {
                if let Some(input) = args.next() {
                    if let Err(err) = create(&input, &tar) {
                        write!(stderr(), "tar: create: failed: {}\n", err).unwrap();
                        process::exit(1);
                    }
                } else {
                    write!(stderr(), "tar: create: no input specified: {}\n", op).unwrap();
                    process::exit(1);
                }
            } else {
                write!(stderr(), "tar: create: no tarfile specified: {}\n", op).unwrap();
                process::exit(1);
            },
            "t" | "tf" => {
                let tar = args.next().unwrap_or("-".to_string());
                if let Err(err) = list(&tar) {
                    write!(stderr(), "tar: list: failed: {}\n", err).unwrap();
                    process::exit(1);
                }
            },
            "x" | "xf" | "xvf" => {
                let tar = args.next().unwrap_or("-".to_string());
                let verbose = op.contains('v');
                if let Err(err) = extract(&tar, verbose) {
                    write!(stderr(), "tar: extract: failed: {}\n", err).unwrap();
                    process::exit(1);
                }
            },
            _ => {
                write!(stderr(), "tar: {}: unknown operation\n", op).unwrap();
                write!(stderr(), "tar: need to specify c[f] (create), t[f] (list), or x[f] (extract)\n").unwrap();
                process::exit(1);
            }
        }
    } else {
        write!(stderr(), "tar: no operation\n").unwrap();
        write!(stderr(), "tar: need to specify cf (create), tf (list), or xf (extract)\n").unwrap();
        process::exit(1);
    }
}
<|endoftext|>


<commit_before>/// Custom event handlers
use crate::{
    Cmd, EditMode, InputMode, InputState, KeyCode, KeyEvent, Modifiers, Refresher, RepeatCount,
};

use radix_trie::TrieKey;
use smallvec::{smallvec, SmallVec};

/// Input event
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Event {
    /// Wildcard.
    /// Useful if you want to filter out some keys.
    Any,
    /// Key sequence
    // TODO Validate 2 ?
    KeySeq(SmallVec<[KeyEvent; 2]>),
    /// TODO Mouse event
    Mouse(),
}

impl Event {
    /// See [`KeyEvent::normalize`]
    pub(crate) fn normalize(mut self) -> Self {
        if let Event::KeySeq(ref mut keys) = self {
            for key in keys.iter_mut() {
                *key = KeyEvent::normalize(*key);
            }
        }
        self
    }

    /// Return `i`th key event
    #[must_use]
    pub fn get(&self, i: usize) -> Option<&KeyEvent> {
        if let Event::KeySeq(ref ks) = self {
            ks.get(i)
        } else {
            None
        }
    }
}

impl From<KeyEvent> for Event {
    fn from(k: KeyEvent) -> Event {
        Event::KeySeq(smallvec![k])
    }
}

const BASE: u32 = 0x0010_ffff + 1;
const BASE_CONTROL: u32 = 0x0200_0000;
const BASE_META: u32 = 0x0400_0000;
const BASE_SHIFT: u32 = 0x0100_0000;
const ESCAPE: u32 = 27;
const PAGE_UP: u32 = BASE + 1;
const PAGE_DOWN: u32 = PAGE_UP + 1;
const DOWN: u32 = PAGE_DOWN + 1;
const UP: u32 = DOWN + 1;
const LEFT: u32 = UP + 1;
const RIGHT: u32 = LEFT + 1;
const HOME: u32 = RIGHT + 1;
const END: u32 = HOME + 1;
const DELETE: u32 = END + 1;
const INSERT: u32 = DELETE + 1;
//const F1: u32 = INSERT + 1;
const MOUSE: u32 = /* F24 + 1 */ INSERT + 25;
const PASTE_START: u32 = MOUSE + 1;
const PASTE_FINISH: u32 = PASTE_START + 1;
const ANY: u32 = PASTE_FINISH + 1;

impl KeyEvent {
    fn encode(&self) -> u32 {
        let mut u = match self.0 {
            KeyCode::UnknownEscSeq | KeyCode::Null => 0,
            KeyCode::Backspace => u32::from('\x7f'),
            KeyCode::BackTab => u32::from('\t') | BASE_SHIFT,
            KeyCode::BracketedPasteStart => PASTE_START,
            KeyCode::BracketedPasteEnd => PASTE_FINISH,
            KeyCode::Char(c) => u32::from(c),
            KeyCode::Delete => DELETE,
            KeyCode::Down => DOWN,
            KeyCode::End => END,
            KeyCode::Enter => u32::from('\r'),
            KeyCode::F(i) => INSERT + u32::from(i),
            KeyCode::Esc => ESCAPE,
            KeyCode::Home => HOME,
            KeyCode::Insert => INSERT,
            KeyCode::Left => LEFT,
            KeyCode::PageDown => PAGE_DOWN,
            KeyCode::PageUp => PAGE_UP,
            KeyCode::Right => RIGHT,
            KeyCode::Tab => u32::from('\t'),
            KeyCode::Up => UP,
        };
        if self.1.contains(Modifiers::CTRL) {
            u |= BASE_CONTROL;
        }
        if self.1.contains(Modifiers::ALT) {
            u |= BASE_META;
        }
        if self.1.contains(Modifiers::SHIFT) {
            u |= BASE_SHIFT;
        }
        u
    }
}

impl TrieKey for Event {
    fn encode_bytes(&self) -> Vec<u8> {
        match self {
            Event::Any => ANY.to_be_bytes().to_vec(),
            Event::KeySeq(keys) => {
                let mut dst = Vec::with_capacity(keys.len() * 4);
                for key in keys {
                    dst.extend_from_slice(&key.encode().to_be_bytes());
                }
                dst
            }
            Event::Mouse() => MOUSE.to_be_bytes().to_vec(),
        }
    }
}

/// Event handler
pub enum EventHandler {
    /// unconditional command
    Simple(Cmd),
    /// handler behaviour depends on input state
    Conditional(Box<dyn ConditionalEventHandler>),
    /* invoke multiple actions
     * TODO Macro(), */
}

impl From<Cmd> for EventHandler {
    fn from(c: Cmd) -> EventHandler {
        EventHandler::Simple(c)
    }
}

/// Give access to user input.
pub struct EventContext<'r> {
    mode: EditMode,
    input_mode: InputMode,
    wrt: &'r dyn Refresher,
}

impl<'r> EventContext<'r> {
    pub(crate) fn new(is: &InputState, wrt: &'r dyn Refresher) -> Self {
        EventContext {
            mode: is.mode,
            input_mode: is.input_mode,
            wrt,
        }
    }

    /// emacs or vi mode
    #[must_use]
    pub fn mode(&self) -> EditMode {
        self.mode
    }

    /// vi input mode
    #[must_use]
    pub fn input_mode(&self) -> InputMode {
        self.input_mode
    }

    /// Returns `true` if there is a hint displayed.
    #[must_use]
    pub fn has_hint(&self) -> bool {
        self.wrt.has_hint()
    }

    /// Returns the hint text that is shown after the current cursor position.
    #[must_use]
    pub fn hint_text(&self) -> Option<&str> {
        self.wrt.hint_text()
    }

    /// currently edited line
    #[must_use]
    pub fn line(&self) -> &str {
        self.wrt.line()
    }

    /// Current cursor position (byte position)
    #[must_use]
    pub fn pos(&self) -> usize {
        self.wrt.pos()
    }
}

/// May behave differently depending on:
///  * edit mode (emacs vs vi)
///  * vi input mode (insert vs replace vs command modes)
///  * empty line
///  * cursor position
///  * repeat count
///  * original key pressed (when same command is bound to different key)
///  * hint
///  * ...
pub trait ConditionalEventHandler: Send + Sync {
    /// Takes the current input state and
    /// returns the command to be performed or `None` to perform the default
    /// one.
    fn handle(
        &self,
        evt: &Event,
        n: RepeatCount,
        positive: bool,
        ctx: &EventContext,
    ) -> Option<Cmd>;
}

#[cfg(test)]
mod test {
    use super::{Event, EventHandler};
    use crate::{Cmd, KeyCode, KeyEvent, Modifiers};
    use radix_trie::Trie;
    use smallvec::smallvec;

    #[test]
    fn encode() {
        let mut trie = Trie::new();
        let evt = Event::KeySeq(smallvec![KeyEvent::ctrl('X'), KeyEvent::ctrl('E')]);
        trie.insert(evt.clone(), EventHandler::from(Cmd::Noop));
        let prefix = Event::from(KeyEvent::ctrl('X'));
        let subtrie = trie.get_raw_descendant(&prefix);
        assert!(subtrie.is_some());
        let subtrie = subtrie.unwrap();
        let sub_result = subtrie.get(&evt);
        assert!(sub_result.is_ok());
        assert!(sub_result.unwrap().is_some());
        let prefix = Event::from(KeyEvent::ctrl('O'));
        let subtrie = trie.get_raw_descendant(&prefix);
        assert!(subtrie.is_none())
    }

    #[test]
    fn no_collision() {
        use {Event as E, EventHandler as H, KeyCode as C, KeyEvent as K, Modifiers as M};
        let mut trie = Trie::new();
        trie.insert(E::from(K(C::Backspace, M::NONE)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Enter, M::NONE)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Tab, M::NONE)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Backspace, M::CTRL)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Enter, M::CTRL)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Tab, M::CTRL)), H::from(Cmd::Noop));
    }
}
<commit_msg>Add test for size of Event enum<commit_after>/// Custom event handlers
use crate::{
    Cmd, EditMode, InputMode, InputState, KeyCode, KeyEvent, Modifiers, Refresher, RepeatCount,
};

use radix_trie::TrieKey;
use smallvec::{smallvec, SmallVec};

/// Input event
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Event {
    /// Wildcard.
    /// Useful if you want to filter out some keys.
    Any,
    /// Key sequence
    // TODO Validate 2 ?
    KeySeq(SmallVec<[KeyEvent; 2]>),
    /// TODO Mouse event
    Mouse(),
}

impl Event {
    /// See [`KeyEvent::normalize`]
    pub(crate) fn normalize(mut self) -> Self {
        if let Event::KeySeq(ref mut keys) = self {
            for key in keys.iter_mut() {
                *key = KeyEvent::normalize(*key);
            }
        }
        self
    }

    /// Return `i`th key event
    #[must_use]
    pub fn get(&self, i: usize) -> Option<&KeyEvent> {
        if let Event::KeySeq(ref ks) = self {
            ks.get(i)
        } else {
            None
        }
    }
}

impl From<KeyEvent> for Event {
    fn from(k: KeyEvent) -> Event {
        Event::KeySeq(smallvec![k])
    }
}

const BASE: u32 = 0x0010_ffff + 1;
const BASE_CONTROL: u32 = 0x0200_0000;
const BASE_META: u32 = 0x0400_0000;
const BASE_SHIFT: u32 = 0x0100_0000;
const ESCAPE: u32 = 27;
const PAGE_UP: u32 = BASE + 1;
const PAGE_DOWN: u32 = PAGE_UP + 1;
const DOWN: u32 = PAGE_DOWN + 1;
const UP: u32 = DOWN + 1;
const LEFT: u32 = UP + 1;
const RIGHT: u32 = LEFT + 1;
const HOME: u32 = RIGHT + 1;
const END: u32 = HOME + 1;
const DELETE: u32 = END + 1;
const INSERT: u32 = DELETE + 1;
//const F1: u32 = INSERT + 1;
const MOUSE: u32 = /* F24 + 1 */ INSERT + 25;
const PASTE_START: u32 = MOUSE + 1;
const PASTE_FINISH: u32 = PASTE_START + 1;
const ANY: u32 = PASTE_FINISH + 1;

impl KeyEvent {
    fn encode(&self) -> u32 {
        let mut u = match self.0 {
            KeyCode::UnknownEscSeq | KeyCode::Null => 0,
            KeyCode::Backspace => u32::from('\x7f'),
            KeyCode::BackTab => u32::from('\t') | BASE_SHIFT,
            KeyCode::BracketedPasteStart => PASTE_START,
            KeyCode::BracketedPasteEnd => PASTE_FINISH,
            KeyCode::Char(c) => u32::from(c),
            KeyCode::Delete => DELETE,
            KeyCode::Down => DOWN,
            KeyCode::End => END,
            KeyCode::Enter => u32::from('\r'),
            KeyCode::F(i) => INSERT + u32::from(i),
            KeyCode::Esc => ESCAPE,
            KeyCode::Home => HOME,
            KeyCode::Insert => INSERT,
            KeyCode::Left => LEFT,
            KeyCode::PageDown => PAGE_DOWN,
            KeyCode::PageUp => PAGE_UP,
            KeyCode::Right => RIGHT,
            KeyCode::Tab => u32::from('\t'),
            KeyCode::Up => UP,
        };
        if self.1.contains(Modifiers::CTRL) {
            u |= BASE_CONTROL;
        }
        if self.1.contains(Modifiers::ALT) {
            u |= BASE_META;
        }
        if self.1.contains(Modifiers::SHIFT) {
            u |= BASE_SHIFT;
        }
        u
    }
}

impl TrieKey for Event {
    fn encode_bytes(&self) -> Vec<u8> {
        match self {
            Event::Any => ANY.to_be_bytes().to_vec(),
            Event::KeySeq(keys) => {
                let mut dst = Vec::with_capacity(keys.len() * 4);
                for key in keys {
                    dst.extend_from_slice(&key.encode().to_be_bytes());
                }
                dst
            }
            Event::Mouse() => MOUSE.to_be_bytes().to_vec(),
        }
    }
}

/// Event handler
pub enum EventHandler {
    /// unconditional command
    Simple(Cmd),
    /// handler behaviour depends on input state
    Conditional(Box<dyn ConditionalEventHandler>),
    /* invoke multiple actions
     * TODO Macro(), */
}

impl From<Cmd> for EventHandler {
    fn from(c: Cmd) -> EventHandler {
        EventHandler::Simple(c)
    }
}

/// Give access to user input.
pub struct EventContext<'r> {
    mode: EditMode,
    input_mode: InputMode,
    wrt: &'r dyn Refresher,
}

impl<'r> EventContext<'r> {
    pub(crate) fn new(is: &InputState, wrt: &'r dyn Refresher) -> Self {
        EventContext {
            mode: is.mode,
            input_mode: is.input_mode,
            wrt,
        }
    }

    /// emacs or vi mode
    #[must_use]
    pub fn mode(&self) -> EditMode {
        self.mode
    }

    /// vi input mode
    #[must_use]
    pub fn input_mode(&self) -> InputMode {
        self.input_mode
    }

    /// Returns `true` if there is a hint displayed.
    #[must_use]
    pub fn has_hint(&self) -> bool {
        self.wrt.has_hint()
    }

    /// Returns the hint text that is shown after the current cursor position.
    #[must_use]
    pub fn hint_text(&self) -> Option<&str> {
        self.wrt.hint_text()
    }

    /// currently edited line
    #[must_use]
    pub fn line(&self) -> &str {
        self.wrt.line()
    }

    /// Current cursor position (byte position)
    #[must_use]
    pub fn pos(&self) -> usize {
        self.wrt.pos()
    }
}

/// May behave differently depending on:
///  * edit mode (emacs vs vi)
///  * vi input mode (insert vs replace vs command modes)
///  * empty line
///  * cursor position
///  * repeat count
///  * original key pressed (when same command is bound to different key)
///  * hint
///  * ...
pub trait ConditionalEventHandler: Send + Sync {
    /// Takes the current input state and
    /// returns the command to be performed or `None` to perform the default
    /// one.
    fn handle(
        &self,
        evt: &Event,
        n: RepeatCount,
        positive: bool,
        ctx: &EventContext,
    ) -> Option<Cmd>;
}

#[cfg(test)]
mod test {
    use core::mem::size_of;

    use super::{Event, EventHandler};
    use crate::{Cmd, KeyCode, KeyEvent, Modifiers};
    use radix_trie::Trie;
    use smallvec::smallvec;

    #[test]
    fn encode() {
        let mut trie = Trie::new();
        let evt = Event::KeySeq(smallvec![KeyEvent::ctrl('X'), KeyEvent::ctrl('E')]);
        trie.insert(evt.clone(), EventHandler::from(Cmd::Noop));
        let prefix = Event::from(KeyEvent::ctrl('X'));
        let subtrie = trie.get_raw_descendant(&prefix);
        assert!(subtrie.is_some());
        let subtrie = subtrie.unwrap();
        let sub_result = subtrie.get(&evt);
        assert!(sub_result.is_ok());
        assert!(sub_result.unwrap().is_some());
        let prefix = Event::from(KeyEvent::ctrl('O'));
        let subtrie = trie.get_raw_descendant(&prefix);
        assert!(subtrie.is_none())
    }

    #[test]
    fn no_collision() {
        use {Event as E, EventHandler as H, KeyCode as C, KeyEvent as K, Modifiers as M};
        let mut trie = Trie::new();
        trie.insert(E::from(K(C::Backspace, M::NONE)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Enter, M::NONE)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Tab, M::NONE)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Backspace, M::CTRL)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Enter, M::CTRL)), H::from(Cmd::Noop));
        trie.insert(E::from(K(C::Tab, M::CTRL)), H::from(Cmd::Noop));
    }

    #[test]
    fn size_of_event() {
        assert_eq!(size_of::<Event>(), 40);
    }
}
<|endoftext|>


<commit_before>use std::ops::BitOrAssign;
use std::slice;

#[derive(Clone,Default,Debug)]
pub struct Bitset {
    head: usize,
    tail: Option<Box<Vec<usize>>>,
}

fn bits_per_word() -> usize {
    usize::max_value().count_ones() as usize
}

impl Bitset {
    pub fn new() -> Bitset {
        Bitset {
            head: 0,
            tail: None,
        }
    }

    fn tail(&self) -> &[usize] {
        match self.tail {
            None => Default::default(),
            Some(ref bx) => &bx,
        }
    }

    fn tail_mut(&mut self) -> &mut Vec<usize> {
        if self.tail.is_none() {
            self.tail = Some(Box::new(Vec::new()));
        }
        self.tail.as_mut().unwrap()
    }

    pub fn set_bit(&mut self, bit: usize) {
        if bit < bits_per_word() {
            self.head |= 1 << bit;
        } else {
            let word = bit / bits_per_word() - 1;
            let tail = self.tail_mut();
            if word >= tail.len() {
                tail.resize(word + 1, 0);
            }
            tail[word] |= 1 << (bit & (bits_per_word() - 1));
        }
    }

    pub fn has_bit(&self, bit: usize) -> bool {
        if bit < bits_per_word() {
            (self.head & (1 << bit)) != 0
        } else {
            let word = bit / bits_per_word() - 1;
            let tail = self.tail();
            if word >= tail.len() {
                false
            } else {
                (tail[word] & (1 << (bit & (bits_per_word() - 1)))) != 0
            }
        }
    }
}

impl<'a> BitOrAssign<&'a Bitset> for Bitset {
    fn bitor_assign(&mut self, rhs: &'a Bitset) {
        self.head |= rhs.head;
        if !rhs.tail().is_empty() {
            let rtail = rhs.tail();
            let stail = self.tail_mut();
            if rtail.len() > stail.len() {
                stail.resize(rtail.len(), 0);
            }
            for i in 0..rtail.len() {
                stail[i] |= rtail[i];
            }
        }
    }
}

impl<'a> IntoIterator for &'a Bitset {
    type Item = usize;
    type IntoIter = BitsetIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        BitsetIter {
            bits: self.head,
            offset: 0,
            buffer: self.tail().iter(),
        }
    }
}

pub struct BitsetIter<'a> {
    bits: usize,
    offset: usize,
    buffer: slice::Iter<'a, usize>,
}

impl<'a> Iterator for BitsetIter<'a> {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        while self.bits == 0 {
            self.offset += bits_per_word();
            match self.buffer.next() {
                Some(bits) => self.bits = *bits,
                None => return None,
            }
        }
        let tz = self.bits.trailing_zeros() as usize;
        self.bits &= self.bits - 1;
        Some(tz + self.offset)
    }
}
<commit_msg>opt: play with inlining to avoid a stall<commit_after>use std::ops::BitOrAssign;
use std::slice;

#[derive(Default,Debug)]
pub struct Bitset {
    head: usize,
    tail: Option<Box<Vec<usize>>>,
}

fn bits_per_word() -> usize {
    usize::max_value().count_ones() as usize
}

#[inline(never)]
fn clone_slow(arg: &Box<Vec<usize>>) -> Box<Vec<usize>> {
    arg.clone()
}

impl Clone for Bitset {
    #[inline]
    fn clone(&self) -> Bitset {
        Bitset {
            head: self.head,
            tail: match self.tail {
                None => None,
                Some(ref tail) => Some(clone_slow(&tail)),
            }
        }
    }
}

impl Bitset {
    pub fn new() -> Bitset {
        Bitset {
            head: 0,
            tail: None,
        }
    }

    fn tail(&self) -> &[usize] {
        match self.tail {
            None => Default::default(),
            Some(ref bx) => &bx,
        }
    }

    fn tail_mut(&mut self) -> &mut Vec<usize> {
        if self.tail.is_none() {
            self.tail = Some(Box::new(Vec::new()));
        }
        self.tail.as_mut().unwrap()
    }

    pub fn set_bit(&mut self, bit: usize) {
        if bit < bits_per_word() {
            self.head |= 1 << bit;
        } else {
            let word = bit / bits_per_word() - 1;
            let tail = self.tail_mut();
            if word >= tail.len() {
                tail.resize(word + 1, 0);
            }
            tail[word] |= 1 << (bit & (bits_per_word() - 1));
        }
    }

    pub fn has_bit(&self, bit: usize) -> bool {
        if bit < bits_per_word() {
            (self.head & (1 << bit)) != 0
        } else {
            let word = bit / bits_per_word() - 1;
            let tail = self.tail();
            if word >= tail.len() {
                false
            } else {
                (tail[word] & (1 << (bit & (bits_per_word() - 1)))) != 0
            }
        }
    }
}

impl<'a> BitOrAssign<&'a Bitset> for Bitset {
    fn bitor_assign(&mut self, rhs: &'a Bitset) {
        self.head |= rhs.head;
        if !rhs.tail().is_empty() {
            let rtail = rhs.tail();
            let stail = self.tail_mut();
            if rtail.len() > stail.len() {
                stail.resize(rtail.len(), 0);
            }
            for i in 0..rtail.len() {
                stail[i] |= rtail[i];
            }
        }
    }
}

impl<'a> IntoIterator for &'a Bitset {
    type Item = usize;
    type IntoIter = BitsetIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        BitsetIter {
            bits: self.head,
            offset: 0,
            buffer: self.tail().iter(),
        }
    }
}

pub struct BitsetIter<'a> {
    bits: usize,
    offset: usize,
    buffer: slice::Iter<'a, usize>,
}

impl<'a> Iterator for BitsetIter<'a> {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        while self.bits == 0 {
            self.offset += bits_per_word();
            match self.buffer.next() {
                Some(bits) => self.bits = *bits,
                None => return None,
            }
        }
        let tz = self.bits.trailing_zeros() as usize;
        self.bits &= self.bits - 1;
        Some(tz + self.offset)
    }
}
<|endoftext|>


<commit_before>use std::{fs, io, path};

use syntax::print::pprust;
use syntax::{ast, source_map, visit};

use tree::{Tree, Visibility};

pub struct Config {
    pub include_orphans: bool,
    pub ignored_files: Vec<path::PathBuf>,
}

pub struct Builder<'a> {
    tree: Tree,
    path: Vec<String>,
    config: Config,
    source_map: &'a source_map::SourceMap,
}

impl<'a> Builder<'a> {
    pub fn new(config: Config, crate_name: String, source_map: &'a source_map::SourceMap) -> Self {
        let tree = Tree::new_crate(crate_name);
        Builder {
            tree,
            path: vec![],
            config,
            source_map,
        }
    }

    pub fn tree(&self) -> &Tree {
        &self.tree
    }

    fn find_orphan_candidates(
        path: &[String],
        ignored_paths: &[path::PathBuf],
    ) -> Result<Vec<String>, io::Error> {
        let mut dir_path = "./src/".to_string();
        for name in path.iter() {
            dir_path.push_str(name);
            dir_path.push_str("/");
        }
        fn is_mod(entry: &fs::DirEntry) -> bool {
            let path = entry.path();
            if path.is_file() {
                if path.extension().map_or(false, |ext| ext == "rs") {
                    return file_name(entry)
                        .map_or(false, |s| (s != "mod") && (s != "lib") && (s != "main"));
                }
            } else if path.join("mod.rs").exists() {
                return true;
            }
            false
        }
        fn file_name(entry: &fs::DirEntry) -> Option<String> {
            entry
                .path()
                .file_stem()
                .and_then(|s| s.to_str().map(|s| s.to_string()))
        }
        Ok(try!(fs::read_dir(&dir_path))
            .filter_map(|e| e.ok())
            .filter(is_mod)
            .filter(|e| !ignored_paths.contains(&e.path()))
            .filter_map(|e| file_name(&e))
            .collect::<Vec<_>>())
    }

    #[allow(unknown_lints)]
    #[allow(needless_pass_by_value)]
    fn sanitize_condition(condition: String) -> String {
        let words: Vec<&str> = condition.split_whitespace().collect();
        words.join(" ")
    }
}

impl<'a> visit::Visitor<'a> for Builder<'a> {
    fn visit_item(&mut self, item: &'a ast::Item) {
        let condition = item
            .attrs
            .iter()
            .find(|attr| attr.check_name("cfg"))
            .map(|attr| {
                self.source_map
                    .span_to_snippet(attr.span)
                    .map(Builder::sanitize_condition)
                    .unwrap_or_else(|_| "".to_string())
            });

        match item.node {
            ast::ItemKind::Mod(_) => {
                let name = item.ident.to_string();
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };
                    tree.insert(Tree::new_module(name.clone(), visibility, condition));
                }
                self.path.push(name);
                visit::walk_item(self, item);
                let _ = self.path.pop();
            }
            ast::ItemKind::Use(ref use_tree) => {
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };

                    add_use_tree(tree, visibility, "", use_tree);
                }

                visit::walk_item(self, item);
            }
            _ => {
                visit::walk_item(self, item);
            }
        }
    }

    fn visit_mod(
        &mut self,
        m: &'a ast::Mod,
        _s: source_map::Span,
        _attrs: &[ast::Attribute],
        _n: ast::NodeId,
    ) {
        visit::walk_mod(self, m);
        if !self.config.include_orphans {
            return;
        }
        let path = &self.path;
        if let Ok(candidates) = Builder::find_orphan_candidates(path, &self.config.ignored_files) {
            let tree = self.tree.subtree_at_path(path).unwrap();
            let names: Vec<_> = tree.subtree_names();
            for candidate in candidates {
                if !names.contains(&candidate) {
                    tree.insert(Tree::new_orphan(candidate.clone()));
                }
            }
        }
    }

    fn visit_mac(&mut self, mac: &ast::Mac) {
        visit::walk_mac(self, mac);
    }
}

fn add_use_tree(tree: &mut Tree, visibility: Visibility, prefix: &str, use_tree: &ast::UseTree) {
    let prefix = if prefix.is_empty() {
        String::new()
    } else {
        prefix.to_string()
    };

    match use_tree.kind {
        ast::UseTreeKind::Simple(_, ..) => {
            let path = pprust::path_to_string(&use_tree.prefix);
            if path != "self" {
                tree.insert_use((visibility, prefix + &path));
            } else {
                let mut prefix = prefix;
                let new_len = prefix.len() - 2;
                prefix.truncate(new_len);
                tree.insert_use((visibility, prefix));
            }
        }
        ast::UseTreeKind::Glob => {
            let mut use_path = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            use_path += "*";
            tree.insert_use((visibility, use_path));
        }
        ast::UseTreeKind::Nested(ref items) => {
            let mut prefix = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            for (sub_tree, _) in items {
                add_use_tree(tree, visibility, &prefix, sub_tree);
            }
        }
    }
}
<commit_msg>Remove unnecessary allow<commit_after>use std::{fs, io, path};

use syntax::print::pprust;
use syntax::{ast, source_map, visit};

use tree::{Tree, Visibility};

pub struct Config {
    pub include_orphans: bool,
    pub ignored_files: Vec<path::PathBuf>,
}

pub struct Builder<'a> {
    tree: Tree,
    path: Vec<String>,
    config: Config,
    source_map: &'a source_map::SourceMap,
}

impl<'a> Builder<'a> {
    pub fn new(config: Config, crate_name: String, source_map: &'a source_map::SourceMap) -> Self {
        let tree = Tree::new_crate(crate_name);
        Builder {
            tree,
            path: vec![],
            config,
            source_map,
        }
    }

    pub fn tree(&self) -> &Tree {
        &self.tree
    }

    fn find_orphan_candidates(
        path: &[String],
        ignored_paths: &[path::PathBuf],
    ) -> Result<Vec<String>, io::Error> {
        let mut dir_path = "./src/".to_string();
        for name in path.iter() {
            dir_path.push_str(name);
            dir_path.push_str("/");
        }
        fn is_mod(entry: &fs::DirEntry) -> bool {
            let path = entry.path();
            if path.is_file() {
                if path.extension().map_or(false, |ext| ext == "rs") {
                    return file_name(entry)
                        .map_or(false, |s| (s != "mod") && (s != "lib") && (s != "main"));
                }
            } else if path.join("mod.rs").exists() {
                return true;
            }
            false
        }
        fn file_name(entry: &fs::DirEntry) -> Option<String> {
            entry
                .path()
                .file_stem()
                .and_then(|s| s.to_str().map(|s| s.to_string()))
        }
        Ok(try!(fs::read_dir(&dir_path))
            .filter_map(|e| e.ok())
            .filter(is_mod)
            .filter(|e| !ignored_paths.contains(&e.path()))
            .filter_map(|e| file_name(&e))
            .collect::<Vec<_>>())
    }

    fn sanitize_condition(condition: String) -> String {
        let words: Vec<&str> = condition.split_whitespace().collect();
        words.join(" ")
    }
}

impl<'a> visit::Visitor<'a> for Builder<'a> {
    fn visit_item(&mut self, item: &'a ast::Item) {
        let condition = item
            .attrs
            .iter()
            .find(|attr| attr.check_name("cfg"))
            .map(|attr| {
                self.source_map
                    .span_to_snippet(attr.span)
                    .map(Builder::sanitize_condition)
                    .unwrap_or_else(|_| "".to_string())
            });

        match item.node {
            ast::ItemKind::Mod(_) => {
                let name = item.ident.to_string();
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };
                    tree.insert(Tree::new_module(name.clone(), visibility, condition));
                }
                self.path.push(name);
                visit::walk_item(self, item);
                let _ = self.path.pop();
            }
            ast::ItemKind::Use(ref use_tree) => {
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };

                    add_use_tree(tree, visibility, "", use_tree);
                }

                visit::walk_item(self, item);
            }
            _ => {
                visit::walk_item(self, item);
            }
        }
    }

    fn visit_mod(
        &mut self,
        m: &'a ast::Mod,
        _s: source_map::Span,
        _attrs: &[ast::Attribute],
        _n: ast::NodeId,
    ) {
        visit::walk_mod(self, m);
        if !self.config.include_orphans {
            return;
        }
        let path = &self.path;
        if let Ok(candidates) = Builder::find_orphan_candidates(path, &self.config.ignored_files) {
            let tree = self.tree.subtree_at_path(path).unwrap();
            let names: Vec<_> = tree.subtree_names();
            for candidate in candidates {
                if !names.contains(&candidate) {
                    tree.insert(Tree::new_orphan(candidate.clone()));
                }
            }
        }
    }

    fn visit_mac(&mut self, mac: &ast::Mac) {
        visit::walk_mac(self, mac);
    }
}

fn add_use_tree(tree: &mut Tree, visibility: Visibility, prefix: &str, use_tree: &ast::UseTree) {
    let prefix = if prefix.is_empty() {
        String::new()
    } else {
        prefix.to_string()
    };

    match use_tree.kind {
        ast::UseTreeKind::Simple(_, ..) => {
            let path = pprust::path_to_string(&use_tree.prefix);
            if path != "self" {
                tree.insert_use((visibility, prefix + &path));
            } else {
                let mut prefix = prefix;
                let new_len = prefix.len() - 2;
                prefix.truncate(new_len);
                tree.insert_use((visibility, prefix));
            }
        }
        ast::UseTreeKind::Glob => {
            let mut use_path = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            use_path += "*";
            tree.insert_use((visibility, use_path));
        }
        ast::UseTreeKind::Nested(ref items) => {
            let mut prefix = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            for (sub_tree, _) in items {
                add_use_tree(tree, visibility, &prefix, sub_tree);
            }
        }
    }
}
<|endoftext|>


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

pub const UNSTABLE_CHANNEL: &'static str = "unstable";
pub const STABLE_CHANNEL: &'static str = "stable";

/// Default Depot Channel environment variable
pub const DEPOT_CHANNEL_ENVVAR: &'static str = "HAB_DEPOT_CHANNEL";

/// Helper function for Builder dynamic channels
pub fn bldr_channel_name(id: u64) -> String {
    format!("bldr-{}", id)
}
<commit_msg>Explicitly use 'stable' for default value of channel args<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use env;

pub const UNSTABLE_CHANNEL: &'static str = "unstable";
pub const STABLE_CHANNEL: &'static str = "stable";

/// Default Depot Channel environment variable
pub const DEPOT_CHANNEL_ENVVAR: &'static str = "HAB_DEPOT_CHANNEL";

/// Helper function for Builder dynamic channels
pub fn bldr_channel_name(id: u64) -> String {
    format!("bldr-{}", id)
}

/// Return the default release channel to use
pub fn default() -> String {
    env::var(DEPOT_CHANNEL_ENVVAR)
        .ok()
        .and_then(|c| Some(c.to_string()))
        .unwrap_or(STABLE_CHANNEL.to_string())
}
<|endoftext|>


<commit_before>use super::elm::Module;
use super::representation::Representation;
use std::env;
use std::fs::{create_dir, File};
use std::io::BufWriter;

pub fn generate_elm(module: &Module) {
    let mut path = env::current_dir().unwrap();
    path.push("generated");
    if path.exists() && path.is_file() {
        panic!("expecting \"generated\" to be a directory");
    }
    if !path.exists() {
        create_dir(path.clone()).expect("problem creating \"generated\" directory");
    }
    let name = &module.name;
    path.push(format!("{}.elm", name));

    let file = File::create(path).unwrap();
    let ref mut w = BufWriter::new(file);

    module.write_representation(w).unwrap();
}<commit_msg>Run cargo fmt<commit_after>use super::elm::Module;
use super::representation::Representation;
use std::env;
use std::fs::{create_dir, File};
use std::io::BufWriter;

pub fn generate_elm(module: &Module) {
    let mut path = env::current_dir().unwrap();
    path.push("generated");
    if path.exists() && path.is_file() {
        panic!("expecting \"generated\" to be a directory");
    }
    if !path.exists() {
        create_dir(path.clone()).expect("problem creating \"generated\" directory");
    }
    let name = &module.name;
    path.push(format!("{}.elm", name));

    let file = File::create(path).unwrap();
    let ref mut w = BufWriter::new(file);

    module.write_representation(w).unwrap();
}
<|endoftext|>


<commit_before>use crate::ast;
use crate::evaluation::object;
use crate::evaluation::object::ObjectT;
use crate::lexer;
use crate::parser;
use crate::token;

// TODO: Consider this to be a hack.
//
// We have 3 types of untis now.
// 1. Program
// 2. Statement
// 3. Expression
//
// To generalize eval API we would wrap program in a special
// wrapper enum, which knows how to handle every unit.

#[derive(Debug)]
pub enum WrappedNode {
    P(ast::Program),
    S(token::Statements),
    E(token::Expression),
    B(token::BlockStatement), // special case for block statement
}
pub type WN = WrappedNode; // Just alias to avoid typing :)

// Option number 2.
// Write 3 different functions to work with 3
// different types of values.
//
// It's super bad design decision, cause I'll have to
// use different evalers in my REPL implementation,
// but I don't see the design clearly for now.

// For now we'll stick with design approach with WrappedNode.
// Perhaps use From/Into modification?

// To save some typing in creating basic object
// we will use constants.

pub const NIL: object::Object = object::Object::Nil(object::Nil {});
pub const TRUE: object::Object = object::Object::Boolean(object::Boolean { value: true });
pub const FALSE: object::Object = object::Object::Boolean(object::Boolean { value: false });

pub fn eval(node: WN) -> object::Object {
    match node {
        WN::P(program) => eval_program(program),
        WN::S(statement) => match statement {
            token::Statements::ExpressionStatement(expr) => eval(WN::E(expr.expression)),
            token::Statements::LetStatement(_) => panic!("don't know how to handle let statement"),
            token::Statements::ReturnStatement(rs) => {
                let val = eval(WN::E(rs.return_value));
                // TODO: Do I really need this?
                // Maybe move this plumbing somewhere else?
                if is_error(&val) {
                    return val;
                }
                object::Object::ReturnValue(Box::new(object::ReturnValue { value: val }))
            }
        },
        WN::B(block) => eval_block_statement(block.statements),
        WN::E(expression) => match expression {
            token::Expression::IntegerLiteral(il) => {
                object::Object::Integer(object::Integer { value: il.value })
            }
            token::Expression::Identifier(_i) => panic!("don't how to handle identifier"),
            token::Expression::PrefixExpression(pe) => {
                let right = eval(WN::E(pe.right));
                if is_error(&right) {
                    return right;
                }
                match pe.operator.as_ref() {
                    "!" => match right {
                        TRUE => FALSE,
                        FALSE => TRUE,
                        NIL => TRUE,
                        _ => FALSE,
                    },
                    "-" => match right {
                        object::Object::Integer(int_obj) => {
                            object::Object::Integer(object::Integer {
                                value: -int_obj.value,
                            })
                        }
                        _ => new_error(format!("unknown operator: -{}", right.object_type()))
                    },
                    _ => new_error(format!("unknown operator: {} {}", pe.operator, right.object_type()))
                }
            }
            token::Expression::InfixExpression(ie) => {
                let left = eval(WN::E(ie.left));
                if is_error(&left) {
                    return left;
                }
                let right = eval(WN::E(ie.right));
                if is_error(&right) {
                    return right;
                }
                if let (object::Object::Integer(left_obj), object::Object::Integer(right_obj)) =
                    (left.clone(), right.clone())
                {
                    match ie.operator.as_ref() {
                        "+" => object::Object::Integer(object::Integer {
                            value: left_obj.value + right_obj.value,
                        }),
                        "-" => object::Object::Integer(object::Integer {
                            value: left_obj.value - right_obj.value,
                        }),
                        "*" => object::Object::Integer(object::Integer {
                            value: left_obj.value * right_obj.value,
                        }),
                        "/" => object::Object::Integer(object::Integer {
                            value: left_obj.value / right_obj.value,
                        }),
                        "<" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value < right_obj.value
                        }),
                        ">" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value > right_obj.value
                        }),
                        "==" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value == right_obj.value
                        }),
                        "!=" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value != right_obj.value
                        }),
                        _ => new_error(format!("unknown operator: INTEGER {} INTEGER", ie.operator))
                    }
                    // what if not boolean?
                    // TODO: maybe I should cover every option every time?
                } else if let (object::Object::Boolean(left_obj), object::Object::Boolean(right_obj)) =
                    (left.clone(), right.clone()) {
                        match ie.operator.as_ref() {
                            "==" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value == right_obj.value,
                            }),
                            "!=" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value != right_obj.value,
                            }),
                            _ => new_error(format!("unknown operator: BOOLEAN {} BOOLEAN", ie.operator))
                        }
                } else {
                        new_error(format!("type mismatch: {} {} {}", left.object_type(), ie.operator, right.object_type()))
                }
            }
            token::Expression::Boolean(b) => {
                // TODO: Check possible perf optimization? Needed?
                // Reuse TRUE and FALSE I mean
                object::Object::Boolean(object::Boolean { value: b.value })
            }
            token::Expression::IfExpression(ie) => {
                let condition = eval(WN::E(ie.condition));
                if is_error(&condition) {
                    return condition;
                }

                if is_truthy(condition) {
                    eval(WN::B(ie.consequence))
                } else {
                    match ie.alternative {
                        Some(alt) => eval(WN::B(alt)),
                        None => NIL,
                    }
                }
            },
            token::Expression::FunctionLiteral(_fl) => {
                panic!("don't how to handle function literal")
            }
            token::Expression::CallExpression(_ce) => panic!("don't how to handle call expression"),
        },
    }
}

// ************************************************
// ************************************************
// *********   HELPER FUNCTIONS   *****************
// ************************************************
// ************************************************

fn is_truthy(cond: object::Object) -> bool {
    match cond {
        NIL => false,
        TRUE => true,
        FALSE => false,
        _ => true,
    }
}

fn is_error(potential_error: &object::Object) -> bool {
    match potential_error {
        object::Object::Error(_) => true,
        _ => false,
    }
}

fn new_error(formated_string: String) -> object::Object {
    object::Object::Error(object::Error {
        message: formated_string,
    })
}

pub fn eval_program(program: ast::Program) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let statements = program.statements;
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            object::Object::ReturnValue(ret_val) => break ret_val.value,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

pub fn eval_block_statement(statements: Vec<token::Statements>) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            // Do not unwrap return value. It will be unwraped at highest scope.
            val @ object::Object::ReturnValue(_) => break val,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

#[cfg(test)]
mod tests {
    use crate::evaluation;
    use crate::lexer;
    use crate::parser;
    use std::collections::HashMap;

    #[test]
    fn test_eval_integer_expression() {
        let pairs = vec![("1".to_string(), 1), ("2".to_string(), 2)];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_integer_object(evaluated, expected);
        })
    }

    #[test]
    fn test_eval_boolean_expression() {
        let pairs = vec![
            ("true".to_string(), true),
            ("false".to_string(), false),
            ("1 < 2".to_string(), true),
            ("1 > 2".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 == 1".to_string(), true),
            ("1 != 1".to_string(), false),
            ("1 == 2".to_string(), false),
            ("1 != 2".to_string(), true),
            ("true == true".to_string(), true),
            ("false == false".to_string(), true),
            ("true == false".to_string(), false),
        ];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_boolean_object(evaluated, expected);
        })
    }

    #[test]
    fn test_bang_operator() {
        let pairs = vec![
            ("!true".to_string(), false),
            ("!false".to_string(), true),
            ("!1".to_string(), false),
            ("!!true".to_string(), true),
            ("!!false".to_string(), false),
            ("!!1".to_string(), true),
        ];

        for (value, expected) in pairs {
            assert_boolean_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_prefix_negate_operator() {
        let pairs = vec![("-1".to_string(), -1), ("--1".to_string(), 1)];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_integer_arithmetic() {
        let pairs = vec![
            ("1 + 1 + 1".to_string(), 3),
            ("2 * 2 * 2".to_string(), 8),
            ("4 / 2 * 2".to_string(), 4),
            ("1 + 3 * 2".to_string(), 7),
        ];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_if_expression() {
        let pairs = vec![
            ("if (true) { 10 }".to_string(), Some(10)),
            ("if (false) { 10 }".to_string(), None),
            ("if (1) { 10 }".to_string(), Some(10)),
            ("if (1 < 2) { 10 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 }".to_string(), None),
            ("if (1 < 2) { 10 } else { 20 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 } else { 20 }".to_string(), Some(20)),
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match expected {
                Some(val) => assert_integer_object(evaluated, val),
                None => assert_eq!(evaluated, evaluation::evaluator::NIL),
            }
        }
    }

    #[test]
    fn test_return_statement() {
        let pairs = vec![
            ("return 10;".to_string(), 10),
            ("return 10;".to_string(), 10),
            ("return 2 * 2; 69;".to_string(), 4),
            ("4; return 2 * 3; 8".to_string(), 6),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return 999;
                 }

                 return 888;
               }
             "###.to_string(), 999)
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            assert_integer_object(evaluated, expected)
        }
    }

    #[test]
    fn test_error_handling() {
        let pairs = vec![
            ("2 + true;".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("2 + true; 999".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("-true;".to_string(), "unknown operator: -BOOLEAN".to_string()),
            ("false + true;".to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return true + false;
                 }

                 return 888;
               }
             "###.to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match evaluated {
                evaluation::object::Object::Error(err) => assert_eq!(err.message, expected),
                _ => panic!("expected error message, got {:?}", evaluated),
            }
        }
    }

    fn run_eval(source_code: String) -> evaluation::object::Object {
        let lexer = lexer::Lexer::new(source_code);
        let mut parser = parser::Parser::new(lexer);

        let mut lambda_parsers = parser::LambdaParsers {
            prefix_parse_fns: HashMap::new(),
            infix_parse_fns: HashMap::new(),
        };
        lambda_parsers.register_parsers();

        let program = parser.parse_program(&lambda_parsers);

        evaluation::evaluator::eval(evaluation::evaluator::WN::P(program))
    }

    fn assert_integer_object(object: evaluation::object::Object, expected: i32) {
        let integer = match object {
            evaluation::object::Object::Integer(int) => int,
            otherwise => panic!("expected integer, got {:?}", otherwise),
        };

        assert_eq!(integer.value, expected)
    }

    fn assert_boolean_object(object: evaluation::object::Object, expected: bool) {
        let bool = match object {
            evaluation::object::Object::Boolean(b) => b,
            otherwise => panic!("expected boolean, got {:?}", otherwise),
        };

        assert_eq!(bool.value, expected)
    }
}
<commit_msg>add test case and explanation for early return in evaler<commit_after>use crate::ast;
use crate::evaluation::object;
use crate::evaluation::object::ObjectT;
use crate::lexer;
use crate::parser;
use crate::token;

// TODO: Consider this to be a hack.
//
// We have 3 types of untis now.
// 1. Program
// 2. Statement
// 3. Expression
//
// To generalize eval API we would wrap program in a special
// wrapper enum, which knows how to handle every unit.

#[derive(Debug)]
pub enum WrappedNode {
    P(ast::Program),
    S(token::Statements),
    E(token::Expression),
    B(token::BlockStatement), // special case for block statement
}
pub type WN = WrappedNode; // Just alias to avoid typing :)

// Option number 2.
// Write 3 different functions to work with 3
// different types of values.
//
// It's super bad design decision, cause I'll have to
// use different evalers in my REPL implementation,
// but I don't see the design clearly for now.

// For now we'll stick with design approach with WrappedNode.
// Perhaps use From/Into modification?

// To save some typing in creating basic object
// we will use constants.

pub const NIL: object::Object = object::Object::Nil(object::Nil {});
pub const TRUE: object::Object = object::Object::Boolean(object::Boolean { value: true });
pub const FALSE: object::Object = object::Object::Boolean(object::Boolean { value: false });

pub fn eval(node: WN) -> object::Object {
    match node {
        WN::P(program) => eval_program(program),
        WN::S(statement) => match statement {
            token::Statements::ExpressionStatement(expr) => eval(WN::E(expr.expression)),
            token::Statements::LetStatement(_) => panic!("don't know how to handle let statement"),
            token::Statements::ReturnStatement(rs) => {
                let val = eval(WN::E(rs.return_value));
                // To see, why this early return is important look at the
                // test case `test_error_handling`.
                if is_error(&val) {
                    return val;
                }
                object::Object::ReturnValue(Box::new(object::ReturnValue { value: val }))
            }
        },
        WN::B(block) => eval_block_statement(block.statements),
        WN::E(expression) => match expression {
            token::Expression::IntegerLiteral(il) => {
                object::Object::Integer(object::Integer { value: il.value })
            }
            token::Expression::Identifier(_i) => panic!("don't how to handle identifier"),
            token::Expression::PrefixExpression(pe) => {
                let right = eval(WN::E(pe.right));
                if is_error(&right) {
                    return right;
                }
                match pe.operator.as_ref() {
                    "!" => match right {
                        TRUE => FALSE,
                        FALSE => TRUE,
                        NIL => TRUE,
                        _ => FALSE,
                    },
                    "-" => match right {
                        object::Object::Integer(int_obj) => {
                            object::Object::Integer(object::Integer {
                                value: -int_obj.value,
                            })
                        }
                        _ => new_error(format!("unknown operator: -{}", right.object_type()))
                    },
                    _ => new_error(format!("unknown operator: {} {}", pe.operator, right.object_type()))
                }
            }
            token::Expression::InfixExpression(ie) => {
                let left = eval(WN::E(ie.left));
                if is_error(&left) {
                    return left;
                }
                let right = eval(WN::E(ie.right));
                if is_error(&right) {
                    return right;
                }
                if let (object::Object::Integer(left_obj), object::Object::Integer(right_obj)) =
                    (left.clone(), right.clone())
                {
                    match ie.operator.as_ref() {
                        "+" => object::Object::Integer(object::Integer {
                            value: left_obj.value + right_obj.value,
                        }),
                        "-" => object::Object::Integer(object::Integer {
                            value: left_obj.value - right_obj.value,
                        }),
                        "*" => object::Object::Integer(object::Integer {
                            value: left_obj.value * right_obj.value,
                        }),
                        "/" => object::Object::Integer(object::Integer {
                            value: left_obj.value / right_obj.value,
                        }),
                        "<" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value < right_obj.value
                        }),
                        ">" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value > right_obj.value
                        }),
                        "==" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value == right_obj.value
                        }),
                        "!=" => object::Object::Boolean(object::Boolean {
                            value: left_obj.value != right_obj.value
                        }),
                        _ => new_error(format!("unknown operator: INTEGER {} INTEGER", ie.operator))
                    }
                    // what if not boolean?
                    // TODO: maybe I should cover every option every time?
                } else if let (object::Object::Boolean(left_obj), object::Object::Boolean(right_obj)) =
                    (left.clone(), right.clone()) {
                        match ie.operator.as_ref() {
                            "==" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value == right_obj.value,
                            }),
                            "!=" => object::Object::Boolean(object::Boolean {
                                value: left_obj.value != right_obj.value,
                            }),
                            _ => new_error(format!("unknown operator: BOOLEAN {} BOOLEAN", ie.operator))
                        }
                } else {
                        new_error(format!("type mismatch: {} {} {}", left.object_type(), ie.operator, right.object_type()))
                }
            }
            token::Expression::Boolean(b) => {
                // TODO: Check possible perf optimization? Needed?
                // Reuse TRUE and FALSE I mean
                object::Object::Boolean(object::Boolean { value: b.value })
            }
            token::Expression::IfExpression(ie) => {
                let condition = eval(WN::E(ie.condition));
                if is_error(&condition) {
                    return condition;
                }

                if is_truthy(condition) {
                    eval(WN::B(ie.consequence))
                } else {
                    match ie.alternative {
                        Some(alt) => eval(WN::B(alt)),
                        None => NIL,
                    }
                }
            },
            token::Expression::FunctionLiteral(_fl) => {
                panic!("don't how to handle function literal")
            }
            token::Expression::CallExpression(_ce) => panic!("don't how to handle call expression"),
        },
    }
}

// ************************************************
// ************************************************
// *********   HELPER FUNCTIONS   *****************
// ************************************************
// ************************************************

fn is_truthy(cond: object::Object) -> bool {
    match cond {
        NIL => false,
        TRUE => true,
        FALSE => false,
        _ => true,
    }
}

fn is_error(potential_error: &object::Object) -> bool {
    match potential_error {
        object::Object::Error(_) => true,
        _ => false,
    }
}

fn new_error(formated_string: String) -> object::Object {
    object::Object::Error(object::Error {
        message: formated_string,
    })
}

pub fn eval_program(program: ast::Program) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let statements = program.statements;
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            object::Object::ReturnValue(ret_val) => break ret_val.value,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

pub fn eval_block_statement(statements: Vec<token::Statements>) -> object::Object {
    // TODO: wow, impressive, I see your skill
    let mut statements = statements.into_iter();
    let mut size = statements.len();

    let result = loop {
        size = size - 1;
        let statement = match statements.next() {
            Some(statement) => statement,
            None => panic!("eval_statement is badly broken"),
        };

        let result = eval(WN::S(statement));

        // if statement is rendered into Return Value we have to
        // interupt the execution and return this value.
        match result.clone() {
            // Do not unwrap return value. It will be unwraped at highest scope.
            val @ object::Object::ReturnValue(_) => break val,
            err @ object::Object::Error(_) => break err,
            otherwise => (),
        };

        if size == 0 {
            break result;
        };
    };

    return result;
}

#[cfg(test)]
mod tests {
    use crate::evaluation;
    use crate::lexer;
    use crate::parser;
    use std::collections::HashMap;

    #[test]
    fn test_eval_integer_expression() {
        let pairs = vec![("1".to_string(), 1), ("2".to_string(), 2)];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_integer_object(evaluated, expected);
        })
    }

    #[test]
    fn test_eval_boolean_expression() {
        let pairs = vec![
            ("true".to_string(), true),
            ("false".to_string(), false),
            ("1 < 2".to_string(), true),
            ("1 > 2".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 < 1".to_string(), false),
            ("1 == 1".to_string(), true),
            ("1 != 1".to_string(), false),
            ("1 == 2".to_string(), false),
            ("1 != 2".to_string(), true),
            ("true == true".to_string(), true),
            ("false == false".to_string(), true),
            ("true == false".to_string(), false),
        ];

        pairs.into_iter().for_each(|(value, expected)| {
            let evaluated = run_eval(value);
            assert_boolean_object(evaluated, expected);
        })
    }

    #[test]
    fn test_bang_operator() {
        let pairs = vec![
            ("!true".to_string(), false),
            ("!false".to_string(), true),
            ("!1".to_string(), false),
            ("!!true".to_string(), true),
            ("!!false".to_string(), false),
            ("!!1".to_string(), true),
        ];

        for (value, expected) in pairs {
            assert_boolean_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_prefix_negate_operator() {
        let pairs = vec![("-1".to_string(), -1), ("--1".to_string(), 1)];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_integer_arithmetic() {
        let pairs = vec![
            ("1 + 1 + 1".to_string(), 3),
            ("2 * 2 * 2".to_string(), 8),
            ("4 / 2 * 2".to_string(), 4),
            ("1 + 3 * 2".to_string(), 7),
        ];

        for (value, expected) in pairs {
            assert_integer_object(run_eval(value), expected)
        }
    }

    #[test]
    fn test_if_expression() {
        let pairs = vec![
            ("if (true) { 10 }".to_string(), Some(10)),
            ("if (false) { 10 }".to_string(), None),
            ("if (1) { 10 }".to_string(), Some(10)),
            ("if (1 < 2) { 10 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 }".to_string(), None),
            ("if (1 < 2) { 10 } else { 20 }".to_string(), Some(10)),
            ("if (1 > 2) { 10 } else { 20 }".to_string(), Some(20)),
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match expected {
                Some(val) => assert_integer_object(evaluated, val),
                None => assert_eq!(evaluated, evaluation::evaluator::NIL),
            }
        }
    }

    #[test]
    fn test_return_statement() {
        let pairs = vec![
            ("return 10;".to_string(), 10),
            ("return 10;".to_string(), 10),
            ("return 2 * 2; 69;".to_string(), 4),
            ("4; return 2 * 3; 8".to_string(), 6),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return 999;
                 }

                 return 888;
               }
             "###.to_string(), 999)
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            assert_integer_object(evaluated, expected)
        }
    }

    #[test]
    fn test_error_handling() {
        let pairs = vec![
            ("2 + true;".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("2 + true; 999".to_string(), "type mismatch: INTEGER + BOOLEAN".to_string()),
            ("-true;".to_string(), "unknown operator: -BOOLEAN".to_string()),
            ("false + true;".to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
            (r###"
               if (2 > 1) {
                 if (2 > 1) {
                   return true + false;
                 }

                 return 888;
               }
             "###.to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string()),
            // This example checks if we stop evaling embedded path
            // and return first error.
            // In any compound AST component we have to stop evaluation
            // right after we encounter error.
            // In case we do not do this, we'll receive here an error like
            // `unknown operator: -BOOLEAN`, which is wrong.
            (r###"
               if (true + true) {
                 if (2 > 1) {
                   return -false;
                 }

                 return 888;
               }
             "###.to_string(), "unknown operator: BOOLEAN + BOOLEAN".to_string())
        ];

        for (expression, expected) in pairs {
            let evaluated = run_eval(expression);
            match evaluated {
                evaluation::object::Object::Error(err) => assert_eq!(err.message, expected),
                _ => panic!("expected error message, got {:?}", evaluated),
            }
        }
    }

    fn run_eval(source_code: String) -> evaluation::object::Object {
        let lexer = lexer::Lexer::new(source_code);
        let mut parser = parser::Parser::new(lexer);

        let mut lambda_parsers = parser::LambdaParsers {
            prefix_parse_fns: HashMap::new(),
            infix_parse_fns: HashMap::new(),
        };
        lambda_parsers.register_parsers();

        let program = parser.parse_program(&lambda_parsers);

        evaluation::evaluator::eval(evaluation::evaluator::WN::P(program))
    }

    fn assert_integer_object(object: evaluation::object::Object, expected: i32) {
        let integer = match object {
            evaluation::object::Object::Integer(int) => int,
            otherwise => panic!("expected integer, got {:?}", otherwise),
        };

        assert_eq!(integer.value, expected)
    }

    fn assert_boolean_object(object: evaluation::object::Object, expected: bool) {
        let bool = match object {
            evaluation::object::Object::Boolean(b) => b,
            otherwise => panic!("expected boolean, got {:?}", otherwise),
        };

        assert_eq!(bool.value, expected)
    }
}
<|endoftext|>