<commit_before>//! Translation skeletton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it.
use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder};
use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section,
                          parse_function_section, parse_export_section, parse_memory_section,
                          parse_global_section, parse_table_section, parse_elements_section,
                          parse_data_section};
use translation_utils::{type_to_type, Import, SignatureIndex, FunctionIndex, invert_hashmaps};
use cretonne::ir::{Function, Type, FuncRef, SigRef};
use code_translator::translate_function_body;
use cton_frontend::ILBuilder;
use std::collections::HashMap;
use runtime::WasmRuntime;

/// Output of the [`translate_module`](fn.translate_module.html) function.
pub struct TranslationResult {
    /// The translated functions.
    pub functions: Vec<FunctionTranslation>,
    /// When present, the index of the function defined as `start` of the module.
    pub start_index: Option<FunctionIndex>,
}

/// A function in a WebAssembly module can be either imported, or defined inside it.
#[derive(Clone)]
pub enum FunctionTranslation {
    /// A function defined inside the WebAssembly module.
    Code {
        /// The translation in Cretonne IL.
        il: Function,
        /// The mappings between Cretonne imports and indexes in the function index space.
        imports: ImportMappings,
    },
    /// An imported function.
    Import(),
}

#[derive(Clone,Debug)]
/// Mappings describing the relations between imports of the Cretonne IL functions and the
/// functions in the WebAssembly module.
pub struct ImportMappings {
    /// Find the index of a function in the WebAssembly module thanks to a `FuncRef`.
    pub functions: HashMap<FuncRef, FunctionIndex>,
    /// Find the index of a signature in the WebAssembly module thanks to a `SigRef`.
    pub signatures: HashMap<SigRef, SignatureIndex>,
}

impl ImportMappings {
    /// Create a new empty `ImportMappings`.
    pub fn new() -> ImportMappings {
        ImportMappings {
            functions: HashMap::new(),
            signatures: HashMap::new(),
        }
    }
}

/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IL
/// [`Function`](../cretonne/ir/function/struct.Function.html).
/// Returns the functions and also the mappings for imported functions and signature between the
/// indexes in the wasm module and the indexes inside each functions.
pub fn translate_module(data: &Vec<u8>,
                        runtime: &mut WasmRuntime)
                        -> Result<TranslationResult, String> {
    let mut parser = Parser::new(data.as_slice());
    match *parser.read() {
        ParserState::BeginWasm { .. } => {}
        ref s @ _ => panic!("modules should begin properly: {:?}", s),
    }
    let mut signatures = None;
    let mut functions: Option<Vec<SignatureIndex>> = None;
    let mut globals = Vec::new();
    let mut exports: Option<HashMap<FunctionIndex, String>> = None;
    let mut next_input = ParserInput::Default;
    let mut function_index: FunctionIndex = 0;
    let mut function_imports_count = 0;
    let mut start_index: Option<FunctionIndex> = None;
    loop {
        match *parser.read_with_input(next_input) {
            ParserState::BeginSection { code: SectionCode::Type, .. } => {
                match parse_function_signatures(&mut parser) {
                    Ok(sigs) => signatures = Some(sigs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the type section: {}", s))
                    }
                };
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Import, .. } => {
                match parse_import_section(&mut parser) {
                    Ok(imps) => {
                        for import in imps {
                            match import {
                                Import::Function { sig_index } => {
                                    functions = match functions {
                                        None => Some(vec![sig_index as SignatureIndex]),
                                        Some(mut funcs) => {
                                            funcs.push(sig_index as SignatureIndex);
                                            Some(funcs)
                                        }
                                    };
                                    function_index += 1;
                                }
                                Import::Memory(mem) => {
                                    runtime.declare_memory(mem);
                                }
                                Import::Global(glob) => {
                                    runtime.declare_global(glob.clone());
                                    globals.push(glob);
                                }
                                Import::Table(tab) => {
                                    runtime.declare_table(tab);
                                }
                            }
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the import section: {}", s))
                    }
                }
                function_imports_count = function_index;
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Function, .. } => {
                match parse_function_section(&mut parser) {
                    Ok(funcs) => {
                        match functions {
                            None => functions = Some(funcs),
                            Some(ref mut imps) => imps.extend(funcs),
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the function section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Table, .. } => {
                match parse_table_section(&mut parser, runtime) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the table section: {}", s))
                    }
                }
            }
            ParserState::BeginSection { code: SectionCode::Memory, .. } => {
                match parse_memory_section(&mut parser) {
                    Ok(mems) => {
                        for mem in mems {
                            runtime.declare_memory(mem);
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the memory section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Global, .. } => {
                match parse_global_section(&mut parser, runtime) {
                    Ok(mut globs) => globals.append(&mut globs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the global section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Export, .. } => {
                match parse_export_section(&mut parser) {
                    Ok(exps) => exports = Some(exps),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the export section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Start, .. } => {
                match *parser.read() {
                    ParserState::StartSectionEntry(index) => {
                        start_index = Some(index as FunctionIndex)
                    }
                    _ => return Err(String::from("wrong content in the start section")),
                }
                match *parser.read() {
                    ParserState::EndSection => {}
                    _ => return Err(String::from("wrong content in the start section")),
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Element, .. } => {
                match parse_elements_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the element section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Code, .. } => {
                // The code section begins
                break;
            }
            ParserState::EndSection => {
                next_input = ParserInput::Default;
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                              functions: Vec::new(),
                              start_index: None,
                          })
            }
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            _ => return Err(String::from("wrong content in the preamble")),
        };
    }
    // At this point we've entered the code section
    // First we check that we have all that is necessary to translate a function.
    let signatures = match signatures {
        None => Vec::new(),
        Some(sigs) => sigs,
    };
    let functions = match functions {
        None => return Err(String::from("missing a function section")),
        Some(functions) => functions,
    };
    let mut il_functions: Vec<FunctionTranslation> = Vec::new();
    il_functions.resize(function_imports_count, FunctionTranslation::Import());
    let mut il_builder = ILBuilder::new();
    runtime.begin_translation();
    loop {
        let locals: Vec<(usize, Type)> = match *parser.read() {
            ParserState::BeginFunctionBody { ref locals, .. } => {
                locals
                    .iter()
                    .map(|&(index, ref ty)| {
                             (index as usize,
                              match type_to_type(ty) {
                                  Ok(ty) => ty,
                                  Err(()) => panic!("unsupported type for local variable"),
                              })
                         })
                    .collect()
            }
            ParserState::EndSection => break,
            _ => return Err(String::from(format!("wrong content in code section"))),
        };
        let signature = signatures[functions[function_index as usize] as usize].clone();
        match translate_function_body(&mut parser,
                                      function_index,
                                      signature,
                                      &locals,
                                      &exports,
                                      &signatures,
                                      &functions,
                                      &mut il_builder,
                                      runtime) {
            Ok((il_func, imports)) => {
                il_functions.push(FunctionTranslation::Code {
                                      il: il_func,
                                      imports: invert_hashmaps(imports),
                                  })
            }
            Err(s) => return Err(s),
        }
        function_index += 1;
    }
    loop {
        match *parser.read() {
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                              functions: il_functions,
                              start_index,
                          })
            }
            _ => (),
        }
    }
}
<commit_msg>Handle wasmparser errors gracefully.<commit_after>//! Translation skeletton that traverses the whole WebAssembly module and call helper functions
//! to deal with each part of it.
use wasmparser::{ParserState, SectionCode, ParserInput, Parser, WasmDecoder, BinaryReaderError};
use sections_translator::{SectionParsingError, parse_function_signatures, parse_import_section,
                          parse_function_section, parse_export_section, parse_memory_section,
                          parse_global_section, parse_table_section, parse_elements_section,
                          parse_data_section};
use translation_utils::{type_to_type, Import, SignatureIndex, FunctionIndex, invert_hashmaps};
use cretonne::ir::{Function, Type, FuncRef, SigRef};
use code_translator::translate_function_body;
use cton_frontend::ILBuilder;
use std::collections::HashMap;
use runtime::WasmRuntime;

/// Output of the [`translate_module`](fn.translate_module.html) function.
pub struct TranslationResult {
    /// The translated functions.
    pub functions: Vec<FunctionTranslation>,
    /// When present, the index of the function defined as `start` of the module.
    pub start_index: Option<FunctionIndex>,
}

/// A function in a WebAssembly module can be either imported, or defined inside it.
#[derive(Clone)]
pub enum FunctionTranslation {
    /// A function defined inside the WebAssembly module.
    Code {
        /// The translation in Cretonne IL.
        il: Function,
        /// The mappings between Cretonne imports and indexes in the function index space.
        imports: ImportMappings,
    },
    /// An imported function.
    Import(),
}

#[derive(Clone,Debug)]
/// Mappings describing the relations between imports of the Cretonne IL functions and the
/// functions in the WebAssembly module.
pub struct ImportMappings {
    /// Find the index of a function in the WebAssembly module thanks to a `FuncRef`.
    pub functions: HashMap<FuncRef, FunctionIndex>,
    /// Find the index of a signature in the WebAssembly module thanks to a `SigRef`.
    pub signatures: HashMap<SigRef, SignatureIndex>,
}

impl ImportMappings {
    /// Create a new empty `ImportMappings`.
    pub fn new() -> ImportMappings {
        ImportMappings {
            functions: HashMap::new(),
            signatures: HashMap::new(),
        }
    }
}

/// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cretonne IL
/// [`Function`](../cretonne/ir/function/struct.Function.html).
/// Returns the functions and also the mappings for imported functions and signature between the
/// indexes in the wasm module and the indexes inside each functions.
pub fn translate_module(data: &Vec<u8>,
                        runtime: &mut WasmRuntime)
                        -> Result<TranslationResult, String> {
    let mut parser = Parser::new(data.as_slice());
    match *parser.read() {
        ParserState::BeginWasm { .. } => {}
        ParserState::Error(BinaryReaderError { message, offset }) => {
            return Err(format!("at offset {}: {}", offset, message));
        }
        ref s @ _ => panic!("modules should begin properly: {:?}", s),
    }
    let mut signatures = None;
    let mut functions: Option<Vec<SignatureIndex>> = None;
    let mut globals = Vec::new();
    let mut exports: Option<HashMap<FunctionIndex, String>> = None;
    let mut next_input = ParserInput::Default;
    let mut function_index: FunctionIndex = 0;
    let mut function_imports_count = 0;
    let mut start_index: Option<FunctionIndex> = None;
    loop {
        match *parser.read_with_input(next_input) {
            ParserState::BeginSection { code: SectionCode::Type, .. } => {
                match parse_function_signatures(&mut parser) {
                    Ok(sigs) => signatures = Some(sigs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the type section: {}", s))
                    }
                };
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Import, .. } => {
                match parse_import_section(&mut parser) {
                    Ok(imps) => {
                        for import in imps {
                            match import {
                                Import::Function { sig_index } => {
                                    functions = match functions {
                                        None => Some(vec![sig_index as SignatureIndex]),
                                        Some(mut funcs) => {
                                            funcs.push(sig_index as SignatureIndex);
                                            Some(funcs)
                                        }
                                    };
                                    function_index += 1;
                                }
                                Import::Memory(mem) => {
                                    runtime.declare_memory(mem);
                                }
                                Import::Global(glob) => {
                                    runtime.declare_global(glob.clone());
                                    globals.push(glob);
                                }
                                Import::Table(tab) => {
                                    runtime.declare_table(tab);
                                }
                            }
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the import section: {}", s))
                    }
                }
                function_imports_count = function_index;
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Function, .. } => {
                match parse_function_section(&mut parser) {
                    Ok(funcs) => {
                        match functions {
                            None => functions = Some(funcs),
                            Some(ref mut imps) => imps.extend(funcs),
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the function section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Table, .. } => {
                match parse_table_section(&mut parser, runtime) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the table section: {}", s))
                    }
                }
            }
            ParserState::BeginSection { code: SectionCode::Memory, .. } => {
                match parse_memory_section(&mut parser) {
                    Ok(mems) => {
                        for mem in mems {
                            runtime.declare_memory(mem);
                        }
                    }
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the memory section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Global, .. } => {
                match parse_global_section(&mut parser, runtime) {
                    Ok(mut globs) => globals.append(&mut globs),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the global section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Export, .. } => {
                match parse_export_section(&mut parser) {
                    Ok(exps) => exports = Some(exps),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the export section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Start, .. } => {
                match *parser.read() {
                    ParserState::StartSectionEntry(index) => {
                        start_index = Some(index as FunctionIndex)
                    }
                    _ => return Err(String::from("wrong content in the start section")),
                }
                match *parser.read() {
                    ParserState::EndSection => {}
                    _ => return Err(String::from("wrong content in the start section")),
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Element, .. } => {
                match parse_elements_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the element section: {}", s))
                    }
                }
                next_input = ParserInput::Default;
            }
            ParserState::BeginSection { code: SectionCode::Code, .. } => {
                // The code section begins
                break;
            }
            ParserState::EndSection => {
                next_input = ParserInput::Default;
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                              functions: Vec::new(),
                              start_index: None,
                          })
            }
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            _ => return Err(String::from("wrong content in the preamble")),
        };
    }
    // At this point we've entered the code section
    // First we check that we have all that is necessary to translate a function.
    let signatures = match signatures {
        None => Vec::new(),
        Some(sigs) => sigs,
    };
    let functions = match functions {
        None => return Err(String::from("missing a function section")),
        Some(functions) => functions,
    };
    let mut il_functions: Vec<FunctionTranslation> = Vec::new();
    il_functions.resize(function_imports_count, FunctionTranslation::Import());
    let mut il_builder = ILBuilder::new();
    runtime.begin_translation();
    loop {
        let locals: Vec<(usize, Type)> = match *parser.read() {
            ParserState::BeginFunctionBody { ref locals, .. } => {
                locals
                    .iter()
                    .map(|&(index, ref ty)| {
                             (index as usize,
                              match type_to_type(ty) {
                                  Ok(ty) => ty,
                                  Err(()) => panic!("unsupported type for local variable"),
                              })
                         })
                    .collect()
            }
            ParserState::EndSection => break,
            _ => return Err(String::from(format!("wrong content in code section"))),
        };
        let signature = signatures[functions[function_index as usize] as usize].clone();
        match translate_function_body(&mut parser,
                                      function_index,
                                      signature,
                                      &locals,
                                      &exports,
                                      &signatures,
                                      &functions,
                                      &mut il_builder,
                                      runtime) {
            Ok((il_func, imports)) => {
                il_functions.push(FunctionTranslation::Code {
                                      il: il_func,
                                      imports: invert_hashmaps(imports),
                                  })
            }
            Err(s) => return Err(s),
        }
        function_index += 1;
    }
    loop {
        match *parser.read() {
            ParserState::BeginSection { code: SectionCode::Data, .. } => {
                match parse_data_section(&mut parser, runtime, &globals) {
                    Ok(()) => (),
                    Err(SectionParsingError::WrongSectionContent(s)) => {
                        return Err(format!("wrong content in the data section: {}", s))
                    }
                }
            }
            ParserState::EndWasm => {
                return Ok(TranslationResult {
                              functions: il_functions,
                              start_index,
                          })
            }
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use std::{env, fs, io};

use log::{error, info, warn};
use serde::Deserialize;
use serde_yaml::mapping::Mapping;
use serde_yaml::Value;

use alacritty_terminal::config::{Config as TermConfig, LOG_TARGET_CONFIG};

pub mod debug;
pub mod font;
pub mod monitor;
pub mod serde_utils;
pub mod ui_config;
pub mod window;

mod bindings;
mod mouse;

use crate::cli::Options;
pub use crate::config::bindings::{Action, Binding, Key, ViAction};
#[cfg(test)]
pub use crate::config::mouse::{ClickHandler, Mouse};
use crate::config::ui_config::UIConfig;

/// Maximum number of depth for the configuration file imports.
const IMPORT_RECURSION_LIMIT: usize = 5;

pub type Config = TermConfig<UIConfig>;

/// Result from config loading.
pub type Result<T> = std::result::Result<T, Error>;

/// Errors occurring during config loading.
#[derive(Debug)]
pub enum Error {
    /// Config file not found.
    NotFound,

    /// Couldn't read $HOME environment variable.
    ReadingEnvHome(env::VarError),

    /// io error reading file.
    Io(io::Error),

    /// Not valid yaml or missing parameters.
    Yaml(serde_yaml::Error),
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::NotFound => None,
            Error::ReadingEnvHome(err) => err.source(),
            Error::Io(err) => err.source(),
            Error::Yaml(err) => err.source(),
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Error::NotFound => write!(f, "Unable to locate config file"),
            Error::ReadingEnvHome(err) => {
                write!(f, "Unable to read $HOME environment variable: {}", err)
            },
            Error::Io(err) => write!(f, "Error reading config file: {}", err),
            Error::Yaml(err) => write!(f, "Problem with config: {}", err),
        }
    }
}

impl From<env::VarError> for Error {
    fn from(val: env::VarError) -> Self {
        Error::ReadingEnvHome(val)
    }
}

impl From<io::Error> for Error {
    fn from(val: io::Error) -> Self {
        if val.kind() == io::ErrorKind::NotFound {
            Error::NotFound
        } else {
            Error::Io(val)
        }
    }
}

impl From<serde_yaml::Error> for Error {
    fn from(val: serde_yaml::Error) -> Self {
        Error::Yaml(val)
    }
}

/// Load the configuration file.
pub fn load(options: &Options) -> Config {
    // Get config path.
    let config_path = match options.config_path().or_else(installed_config) {
        Some(path) => path,
        None => {
            info!(target: LOG_TARGET_CONFIG, "No config file found; using default");
            return Config::default();
        },
    };

    // Load config, falling back to the default on error.
    let config_options = options.config_options().clone();
    let mut config = load_from(&config_path, config_options).unwrap_or_default();

    // Override config with CLI options.
    options.override_config(&mut config);

    config
}

/// Attempt to reload the configuration file.
pub fn reload(config_path: &PathBuf, options: &Options) -> Result<Config> {
    // Load config, propagating errors.
    let config_options = options.config_options().clone();
    let mut config = load_from(&config_path, config_options)?;

    // Override config with CLI options.
    options.override_config(&mut config);

    Ok(config)
}

/// Load configuration file and log errors.
fn load_from(path: &PathBuf, cli_config: Value) -> Result<Config> {
    match read_config(path, cli_config) {
        Ok(config) => Ok(config),
        Err(err) => {
            error!(target: LOG_TARGET_CONFIG, "Unable to load config {:?}: {}", path, err);
            Err(err)
        },
    }
}

/// Deserialize configuration file from path.
fn read_config(path: &PathBuf, cli_config: Value) -> Result<Config> {
    let mut config_paths = Vec::new();
    let mut config_value = parse_config(&path, &mut config_paths, IMPORT_RECURSION_LIMIT)?;

    // Override config with CLI options.
    config_value = serde_utils::merge(config_value, cli_config);

    // Deserialize to concrete type.
    let mut config = Config::deserialize(config_value)?;
    config.ui_config.config_paths = config_paths;

    print_deprecation_warnings(&config);

    Ok(config)
}

/// Deserialize all configuration files as generic Value.
fn parse_config(
    path: &PathBuf,
    config_paths: &mut Vec<PathBuf>,
    recursion_limit: usize,
) -> Result<Value> {
    config_paths.push(path.to_owned());

    let mut contents = fs::read_to_string(path)?;

    // Remove UTF-8 BOM.
    if contents.starts_with('\u{FEFF}') {
        contents = contents.split_off(3);
    }

    // Load configuration file as Value.
    let config: Value = match serde_yaml::from_str(&contents) {
        Ok(config) => config,
        Err(error) => {
            // Prevent parsing error with an empty string and commented out file.
            if error.to_string() == "EOF while parsing a value" {
                Value::Mapping(Mapping::new())
            } else {
                return Err(Error::Yaml(error));
            }
        },
    };

    // Merge config with imports.
    let imports = load_imports(&config, config_paths, recursion_limit);
    Ok(serde_utils::merge(imports, config))
}

/// Load all referenced configuration files.
fn load_imports(config: &Value, config_paths: &mut Vec<PathBuf>, recursion_limit: usize) -> Value {
    let imports = match config.get("import") {
        Some(Value::Sequence(imports)) => imports,
        Some(_) => {
            error!(target: LOG_TARGET_CONFIG, "Invalid import type: expected a sequence");
            return Value::Null;
        },
        None => return Value::Null,
    };

    // Limit recursion to prevent infinite loops.
    if !imports.is_empty() && recursion_limit == 0 {
        error!(target: LOG_TARGET_CONFIG, "Exceeded maximum configuration import depth");
        return Value::Null;
    }

    let mut merged = Value::Null;

    for import in imports {
        let path = match import {
            Value::String(path) => PathBuf::from(path),
            _ => {
                error!(
                    target: LOG_TARGET_CONFIG,
                    "Invalid import element type: expected path string"
                );
                continue;
            },
        };

        match parse_config(&path, config_paths, recursion_limit - 1) {
            Ok(config) => merged = serde_utils::merge(merged, config),
            Err(err) => {
                error!(target: LOG_TARGET_CONFIG, "Unable to import config {:?}: {}", path, err)
            },
        }
    }

    merged
}

/// Get the location of the first found default config file paths
/// according to the following order:
///
/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml
/// 2. $XDG_CONFIG_HOME/alacritty.yml
/// 3. $HOME/.config/alacritty/alacritty.yml
/// 4. $HOME/.alacritty.yml
#[cfg(not(windows))]
fn installed_config() -> Option<PathBuf> {
    // Try using XDG location by default.
    xdg::BaseDirectories::with_prefix("alacritty")
        .ok()
        .and_then(|xdg| xdg.find_config_file("alacritty.yml"))
        .or_else(|| {
            xdg::BaseDirectories::new()
                .ok()
                .and_then(|fallback| fallback.find_config_file("alacritty.yml"))
        })
        .or_else(|| {
            if let Ok(home) = env::var("HOME") {
                // Fallback path: $HOME/.config/alacritty/alacritty.yml.
                let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml");
                if fallback.exists() {
                    return Some(fallback);
                }
                // Fallback path: $HOME/.alacritty.yml.
                let fallback = PathBuf::from(&home).join(".alacritty.yml");
                if fallback.exists() {
                    return Some(fallback);
                }
            }
            None
        })
}

#[cfg(windows)]
fn installed_config() -> Option<PathBuf> {
    dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists())
}

fn print_deprecation_warnings(config: &Config) {
    if config.scrolling.faux_multiplier().is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config scrolling.faux_multiplier is deprecated; the alternate scroll escape can now \
             be used to disable it and `scrolling.multiplier` controls the number of scrolled \
             lines"
        );
    }

    if config.scrolling.auto_scroll.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config scrolling.auto_scroll has been removed and is now always disabled, it can be \
             safely removed from the config"
        );
    }

    if config.tabspaces.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config tabspaces has been removed and is now always 8, it can be safely removed from \
             the config"
        );
    }

    if config.visual_bell.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config visual_bell has been deprecated; please use bell instead"
        )
    }

    if config.ui_config.dynamic_title.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config dynamic_title is deprecated; please use window.dynamic_title instead",
        )
    }
}

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

    static DEFAULT_ALACRITTY_CONFIG: &str =
        concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml");

    #[test]
    fn config_read_eof() {
        let config_path: PathBuf = DEFAULT_ALACRITTY_CONFIG.into();
        let mut config = read_config(&config_path, Value::Null).unwrap();
        config.ui_config.config_paths = Vec::new();
        assert_eq!(config, Config::default());
    }
}
<commit_msg>Fix CLI arguments without config file<commit_after>use std::fmt::{self, Display, Formatter};
use std::path::PathBuf;
use std::{env, fs, io};

use log::{error, info, warn};
use serde::Deserialize;
use serde_yaml::mapping::Mapping;
use serde_yaml::Value;

use alacritty_terminal::config::{Config as TermConfig, LOG_TARGET_CONFIG};

pub mod debug;
pub mod font;
pub mod monitor;
pub mod serde_utils;
pub mod ui_config;
pub mod window;

mod bindings;
mod mouse;

use crate::cli::Options;
pub use crate::config::bindings::{Action, Binding, Key, ViAction};
#[cfg(test)]
pub use crate::config::mouse::{ClickHandler, Mouse};
use crate::config::ui_config::UIConfig;

/// Maximum number of depth for the configuration file imports.
const IMPORT_RECURSION_LIMIT: usize = 5;

pub type Config = TermConfig<UIConfig>;

/// Result from config loading.
pub type Result<T> = std::result::Result<T, Error>;

/// Errors occurring during config loading.
#[derive(Debug)]
pub enum Error {
    /// Config file not found.
    NotFound,

    /// Couldn't read $HOME environment variable.
    ReadingEnvHome(env::VarError),

    /// io error reading file.
    Io(io::Error),

    /// Not valid yaml or missing parameters.
    Yaml(serde_yaml::Error),
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::NotFound => None,
            Error::ReadingEnvHome(err) => err.source(),
            Error::Io(err) => err.source(),
            Error::Yaml(err) => err.source(),
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Error::NotFound => write!(f, "Unable to locate config file"),
            Error::ReadingEnvHome(err) => {
                write!(f, "Unable to read $HOME environment variable: {}", err)
            },
            Error::Io(err) => write!(f, "Error reading config file: {}", err),
            Error::Yaml(err) => write!(f, "Problem with config: {}", err),
        }
    }
}

impl From<env::VarError> for Error {
    fn from(val: env::VarError) -> Self {
        Error::ReadingEnvHome(val)
    }
}

impl From<io::Error> for Error {
    fn from(val: io::Error) -> Self {
        if val.kind() == io::ErrorKind::NotFound {
            Error::NotFound
        } else {
            Error::Io(val)
        }
    }
}

impl From<serde_yaml::Error> for Error {
    fn from(val: serde_yaml::Error) -> Self {
        Error::Yaml(val)
    }
}

/// Load the configuration file.
pub fn load(options: &Options) -> Config {
    let config_options = options.config_options().clone();
    let config_path = options.config_path().or_else(installed_config);

    if config_path.is_none() {
        info!(target: LOG_TARGET_CONFIG, "No config file found; using default");
    }

    // Load the config using the following fallback behavior:
    //  - Config path + CLI overrides
    //  - CLI overrides
    //  - Default
    let mut config = config_path
        .and_then(|config_path| load_from(&config_path, config_options.clone()).ok())
        .unwrap_or_else(|| Config::deserialize(config_options).unwrap_or_default());

    // Override config with CLI options.
    options.override_config(&mut config);

    config
}

/// Attempt to reload the configuration file.
pub fn reload(config_path: &PathBuf, options: &Options) -> Result<Config> {
    // Load config, propagating errors.
    let config_options = options.config_options().clone();
    let mut config = load_from(&config_path, config_options)?;

    // Override config with CLI options.
    options.override_config(&mut config);

    Ok(config)
}

/// Load configuration file and log errors.
fn load_from(path: &PathBuf, cli_config: Value) -> Result<Config> {
    match read_config(path, cli_config) {
        Ok(config) => Ok(config),
        Err(err) => {
            error!(target: LOG_TARGET_CONFIG, "Unable to load config {:?}: {}", path, err);
            Err(err)
        },
    }
}

/// Deserialize configuration file from path.
fn read_config(path: &PathBuf, cli_config: Value) -> Result<Config> {
    let mut config_paths = Vec::new();
    let mut config_value = parse_config(&path, &mut config_paths, IMPORT_RECURSION_LIMIT)?;

    // Override config with CLI options.
    config_value = serde_utils::merge(config_value, cli_config);

    // Deserialize to concrete type.
    let mut config = Config::deserialize(config_value)?;
    config.ui_config.config_paths = config_paths;

    print_deprecation_warnings(&config);

    Ok(config)
}

/// Deserialize all configuration files as generic Value.
fn parse_config(
    path: &PathBuf,
    config_paths: &mut Vec<PathBuf>,
    recursion_limit: usize,
) -> Result<Value> {
    config_paths.push(path.to_owned());

    let mut contents = fs::read_to_string(path)?;

    // Remove UTF-8 BOM.
    if contents.starts_with('\u{FEFF}') {
        contents = contents.split_off(3);
    }

    // Load configuration file as Value.
    let config: Value = match serde_yaml::from_str(&contents) {
        Ok(config) => config,
        Err(error) => {
            // Prevent parsing error with an empty string and commented out file.
            if error.to_string() == "EOF while parsing a value" {
                Value::Mapping(Mapping::new())
            } else {
                return Err(Error::Yaml(error));
            }
        },
    };

    // Merge config with imports.
    let imports = load_imports(&config, config_paths, recursion_limit);
    Ok(serde_utils::merge(imports, config))
}

/// Load all referenced configuration files.
fn load_imports(config: &Value, config_paths: &mut Vec<PathBuf>, recursion_limit: usize) -> Value {
    let imports = match config.get("import") {
        Some(Value::Sequence(imports)) => imports,
        Some(_) => {
            error!(target: LOG_TARGET_CONFIG, "Invalid import type: expected a sequence");
            return Value::Null;
        },
        None => return Value::Null,
    };

    // Limit recursion to prevent infinite loops.
    if !imports.is_empty() && recursion_limit == 0 {
        error!(target: LOG_TARGET_CONFIG, "Exceeded maximum configuration import depth");
        return Value::Null;
    }

    let mut merged = Value::Null;

    for import in imports {
        let path = match import {
            Value::String(path) => PathBuf::from(path),
            _ => {
                error!(
                    target: LOG_TARGET_CONFIG,
                    "Invalid import element type: expected path string"
                );
                continue;
            },
        };

        match parse_config(&path, config_paths, recursion_limit - 1) {
            Ok(config) => merged = serde_utils::merge(merged, config),
            Err(err) => {
                error!(target: LOG_TARGET_CONFIG, "Unable to import config {:?}: {}", path, err)
            },
        }
    }

    merged
}

/// Get the location of the first found default config file paths
/// according to the following order:
///
/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml
/// 2. $XDG_CONFIG_HOME/alacritty.yml
/// 3. $HOME/.config/alacritty/alacritty.yml
/// 4. $HOME/.alacritty.yml
#[cfg(not(windows))]
fn installed_config() -> Option<PathBuf> {
    // Try using XDG location by default.
    xdg::BaseDirectories::with_prefix("alacritty")
        .ok()
        .and_then(|xdg| xdg.find_config_file("alacritty.yml"))
        .or_else(|| {
            xdg::BaseDirectories::new()
                .ok()
                .and_then(|fallback| fallback.find_config_file("alacritty.yml"))
        })
        .or_else(|| {
            if let Ok(home) = env::var("HOME") {
                // Fallback path: $HOME/.config/alacritty/alacritty.yml.
                let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml");
                if fallback.exists() {
                    return Some(fallback);
                }
                // Fallback path: $HOME/.alacritty.yml.
                let fallback = PathBuf::from(&home).join(".alacritty.yml");
                if fallback.exists() {
                    return Some(fallback);
                }
            }
            None
        })
}

#[cfg(windows)]
fn installed_config() -> Option<PathBuf> {
    dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists())
}

fn print_deprecation_warnings(config: &Config) {
    if config.scrolling.faux_multiplier().is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config scrolling.faux_multiplier is deprecated; the alternate scroll escape can now \
             be used to disable it and `scrolling.multiplier` controls the number of scrolled \
             lines"
        );
    }

    if config.scrolling.auto_scroll.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config scrolling.auto_scroll has been removed and is now always disabled, it can be \
             safely removed from the config"
        );
    }

    if config.tabspaces.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config tabspaces has been removed and is now always 8, it can be safely removed from \
             the config"
        );
    }

    if config.visual_bell.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config visual_bell has been deprecated; please use bell instead"
        )
    }

    if config.ui_config.dynamic_title.is_some() {
        warn!(
            target: LOG_TARGET_CONFIG,
            "Config dynamic_title is deprecated; please use window.dynamic_title instead",
        )
    }
}

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

    static DEFAULT_ALACRITTY_CONFIG: &str =
        concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml");

    #[test]
    fn config_read_eof() {
        let config_path: PathBuf = DEFAULT_ALACRITTY_CONFIG.into();
        let mut config = read_config(&config_path, Value::Null).unwrap();
        config.ui_config.config_paths = Vec::new();
        assert_eq!(config, Config::default());
    }
}
<|endoftext|>


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

use evcxr;

use colored::*;
use evcxr::{CommandContext, CompilationError, Error};
use rustyline::{error::ReadlineError, At, Cmd, Editor, KeyPress, Movement, Word};
use std::fs;
use std::io;
use std::sync::mpsc;
use structopt::StructOpt;

use evcxr_repl::EvcxrRustylineHelper;

const PROMPT: &str = ">> ";

struct Repl {
    command_context: CommandContext,
    ide_mode: bool,
}

fn send_output<T: io::Write + Send + 'static>(
    channel: mpsc::Receiver<String>,
    mut output: T,
    color: Option<Color>,
) {
    std::thread::spawn(move || {
        while let Ok(line) = channel.recv() {
            let status = if let Some(color) = color {
                writeln!(output, "{}", line.color(color))
            } else {
                writeln!(output, "{}", line)
            };
            if status.is_err() {
                break;
            }
        }
    });
}

impl Repl {
    fn new(ide_mode: bool) -> Result<Repl, Error> {
        let (command_context, outputs) = CommandContext::new()?;
        send_output(outputs.stdout, io::stdout(), None);
        send_output(outputs.stderr, io::stderr(), Some(Color::BrightRed));
        let mut repl = Repl {
            command_context,
            ide_mode,
        };
        repl.execute(":load_config");
        Ok(repl)
    }

    fn execute(&mut self, to_run: &str) {
        let success = match self.command_context.execute(to_run) {
            Ok(output) => {
                if let Some(text) = output.get("text/plain") {
                    println!("{}", text);
                }
                if let Some(duration) = output.timing {
                    println!("{}", format!("Took {}ms", duration.as_millis()).blue());

                    for phase in output.phases {
                        println!(
                            "{}",
                            format!("  {}: {}ms", phase.name, phase.duration.as_millis()).blue()
                        );
                    }
                }
                true
            }
            Err(evcxr::Error::CompilationErrors(errors)) => {
                self.display_errors(errors);
                false
            }
            Err(err) => {
                eprintln!("{}", format!("{}", err).bright_red());
                false
            }
        };

        if self.ide_mode {
            let success_marker = if success { "\u{0091}" } else { "\u{0092}" };
            print!("{}", success_marker);
        }
    }

    fn display_errors(&mut self, errors: Vec<CompilationError>) {
        for error in errors {
            if error.is_from_user_code() {
                for spanned_message in error.spanned_messages() {
                    if let Some(span) = &spanned_message.span {
                        for _ in 1..span.start_column + PROMPT.len() {
                            print!(" ");
                        }
                        let mut carrots = String::new();
                        for _ in span.start_column..span.end_column {
                            carrots.push('^');
                        }
                        print!("{}", carrots.bright_red());
                        println!(" {}", spanned_message.label.bright_blue());
                    } else {
                        // Our error originates from both user-code and generated
                        // code.
                        println!("{}", spanned_message.label.bright_blue());
                    }
                }
                println!("{}", error.message().bright_red());
                for help in error.help() {
                    println!("{} {}", "help:".bold(), help);
                }
                if let Some(extra_hint) = error.evcxr_extra_hint() {
                    println!("{}", extra_hint);
                }
            } else {
                println!(
                    "A compilation error was found in code we generated.\n\
                     Ideally this shouldn't happen. Type :last_error_json to see details.\n{}",
                    error.rendered()
                );
            }
        }
    }
}

fn readline_direct(prompt: &str) -> rustyline::Result<String> {
    use std::io::Write;

    // Write prompt and flush it to stdout
    let mut stdout = io::stdout();
    stdout.write_all(prompt.as_bytes())?;
    stdout.flush()?;

    let mut line = String::new();
    if io::stdin().read_line(&mut line)? > 0 {
        line = line.replace('\u{2028}', "\n");
        Ok(line)
    } else {
        Err(rustyline::error::ReadlineError::Eof)
    }
}

#[derive(StructOpt, Debug)]
#[structopt(name = "evcxr")]
struct Options {
    #[structopt(long)]
    disable_readline: bool,
    #[structopt(long)]
    ide_mode: bool,
    /// Optimization level (0, 1 or 2)
    #[structopt(long, default_value = "")]
    opt: String,
}

fn main() {
    evcxr::runtime_hook();

    let options = Options::from_args();

    #[cfg(windows)]
    colored::control::set_virtual_terminal(true).ok();

    println!("Welcome to evcxr. For help, type :help");
    let mut repl = match Repl::new(options.ide_mode) {
        Ok(c) => c,
        Err(error) => {
            eprintln!("{}", error);
            return;
        }
    };

    repl.command_context.set_opt_level(&options.opt).ok();
    let mut editor = Editor::<EvcxrRustylineHelper>::new();
    editor.bind_sequence(
        KeyPress::ControlLeft,
        Cmd::Move(Movement::BackwardWord(1, Word::Big)),
    );
    editor.bind_sequence(
        KeyPress::ControlRight,
        Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Big)),
    );
    editor.set_helper(Some(EvcxrRustylineHelper::default()));
    let mut opt_history_file = None;
    let config_dir = evcxr::config_dir();
    if let Some(config_dir) = &config_dir {
        fs::create_dir_all(config_dir).ok();
        let history_file = config_dir.join("history.txt");
        editor.load_history(&history_file).ok();
        opt_history_file = Some(history_file);
    }
    loop {
        let prompt = format!("{}", PROMPT.yellow());
        let readline = if options.disable_readline {
            readline_direct(&prompt)
        } else {
            editor.readline(PROMPT)
        };
        match readline {
            Ok(line) => {
                editor.add_history_entry(line.clone());
                repl.execute(&line);
            }
            Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break,
            Err(err) => {
                eprintln!("Error: {:?}", err);
                break;
            }
        }
    }
    if let Some(history_file) = &opt_history_file {
        editor.save_history(&history_file).ok();
    }
}
<commit_msg>display_errors() does not work properly with multi line inputs. (#127)<commit_after>// Copyright 2020 The Evcxr Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use evcxr;

use colored::*;
use evcxr::{CommandContext, CompilationError, Error};
use rustyline::{error::ReadlineError, At, Cmd, Editor, KeyPress, Movement, Word};
use std::fs;
use std::io;
use std::sync::mpsc;
use structopt::StructOpt;

use evcxr_repl::EvcxrRustylineHelper;

const PROMPT: &str = ">> ";

struct Repl {
    command_context: CommandContext,
    ide_mode: bool,
}

fn send_output<T: io::Write + Send + 'static>(
    channel: mpsc::Receiver<String>,
    mut output: T,
    color: Option<Color>,
) {
    std::thread::spawn(move || {
        while let Ok(line) = channel.recv() {
            let status = if let Some(color) = color {
                writeln!(output, "{}", line.color(color))
            } else {
                writeln!(output, "{}", line)
            };
            if status.is_err() {
                break;
            }
        }
    });
}

impl Repl {
    fn new(ide_mode: bool) -> Result<Repl, Error> {
        let (command_context, outputs) = CommandContext::new()?;
        send_output(outputs.stdout, io::stdout(), None);
        send_output(outputs.stderr, io::stderr(), Some(Color::BrightRed));
        let mut repl = Repl {
            command_context,
            ide_mode,
        };
        repl.execute(":load_config");
        Ok(repl)
    }

    fn execute(&mut self, to_run: &str) {
        let success = match self.command_context.execute(to_run) {
            Ok(output) => {
                if let Some(text) = output.get("text/plain") {
                    println!("{}", text);
                }
                if let Some(duration) = output.timing {
                    println!("{}", format!("Took {}ms", duration.as_millis()).blue());

                    for phase in output.phases {
                        println!(
                            "{}",
                            format!("  {}: {}ms", phase.name, phase.duration.as_millis()).blue()
                        );
                    }
                }
                true
            }
            Err(evcxr::Error::CompilationErrors(errors)) => {
                self.display_errors(to_run, errors);
                false
            }
            Err(err) => {
                eprintln!("{}", format!("{}", err).bright_red());
                false
            }
        };

        if self.ide_mode {
            let success_marker = if success { "\u{0091}" } else { "\u{0092}" };
            print!("{}", success_marker);
        }
    }

    fn display_errors(&mut self, source: &str, errors: Vec<CompilationError>) {
        let source_lines : Vec<&str> = source.lines().collect();
        let mut last_span_lines : &Vec<String> = &vec![];
        for error in &errors {
            if error.is_from_user_code() {
                for spanned_message in error.spanned_messages() {
                    if let Some(span) = &spanned_message.span {
                        if source_lines.len() > 1 {
                            // for multi line source code, print the lines
                            if last_span_lines != &spanned_message.lines {
                                for line in &spanned_message.lines {
                                    println!("{}", line);
                                }
                            }
                            last_span_lines = &spanned_message.lines;
                        } else {
                            print!("{}", " ".repeat(PROMPT.len()));
                        }
                        print!("{}", " ".repeat(span.start_column - 1));

                        let carrots = "^".repeat(span.end_column - span.start_column);
                        print!("{}", carrots.bright_red());
                        println!(" {}", spanned_message.label.bright_blue());
                    } else {
                        // Our error originates from both user-code and generated
                        // code.
                        println!("{}", spanned_message.label.bright_blue());
                    }
                }
                println!("{}", error.message().bright_red());
                for help in error.help() {
                    println!("{} {}", "help:".bold(), help);
                }
                if let Some(extra_hint) = error.evcxr_extra_hint() {
                    println!("{}", extra_hint);
                }
            } else {
                println!(
                    "A compilation error was found in code we generated.\n\
                     Ideally this shouldn't happen. Type :last_error_json to see details.\n{}",
                    error.rendered()
                );
            }
        }
    }
}

fn readline_direct(prompt: &str) -> rustyline::Result<String> {
    use std::io::Write;

    // Write prompt and flush it to stdout
    let mut stdout = io::stdout();
    stdout.write_all(prompt.as_bytes())?;
    stdout.flush()?;

    let mut line = String::new();
    if io::stdin().read_line(&mut line)? > 0 {
        line = line.replace('\u{2028}', "\n");
        Ok(line)
    } else {
        Err(rustyline::error::ReadlineError::Eof)
    }
}

#[derive(StructOpt, Debug)]
#[structopt(name = "evcxr")]
struct Options {
    #[structopt(long)]
    disable_readline: bool,
    #[structopt(long)]
    ide_mode: bool,
    /// Optimization level (0, 1 or 2)
    #[structopt(long, default_value = "")]
    opt: String,
}

fn main() {
    evcxr::runtime_hook();

    let options = Options::from_args();

    #[cfg(windows)]
    colored::control::set_virtual_terminal(true).ok();

    println!("Welcome to evcxr. For help, type :help");
    let mut repl = match Repl::new(options.ide_mode) {
        Ok(c) => c,
        Err(error) => {
            eprintln!("{}", error);
            return;
        }
    };

    repl.command_context.set_opt_level(&options.opt).ok();
    let mut editor = Editor::<EvcxrRustylineHelper>::new();
    editor.bind_sequence(
        KeyPress::ControlLeft,
        Cmd::Move(Movement::BackwardWord(1, Word::Big)),
    );
    editor.bind_sequence(
        KeyPress::ControlRight,
        Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Big)),
    );
    editor.set_helper(Some(EvcxrRustylineHelper::default()));
    let mut opt_history_file = None;
    let config_dir = evcxr::config_dir();
    if let Some(config_dir) = &config_dir {
        fs::create_dir_all(config_dir).ok();
        let history_file = config_dir.join("history.txt");
        editor.load_history(&history_file).ok();
        opt_history_file = Some(history_file);
    }
    loop {
        let prompt = format!("{}", PROMPT.yellow());
        let readline = if options.disable_readline {
            readline_direct(&prompt)
        } else {
            editor.readline(PROMPT)
        };
        match readline {
            Ok(line) => {
                editor.add_history_entry(line.clone());
                repl.execute(&line);
            }
            Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => break,
            Err(err) => {
                eprintln!("Error: {:?}", err);
                break;
            }
        }
    }
    if let Some(history_file) = &opt_history_file {
        editor.save_history(&history_file).ok();
    }
}
<|endoftext|>


<commit_before>#[derive(Debug)]
pub enum Command {
    Quit,
    DisplayAvailable,
    Block,
    Logoff,
    Unrecognized
}

impl PartialEq for Command {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Command::Quit, &Command::Quit) => true,
            (&Command::DisplayAvailable, &Command::DisplayAvailable) => true,
            (&Command::Block, &Command::Block) => true,
            (&Command::Logoff, &Command::Logoff) => true,
            (&Command::Unrecognized, &Command::Unrecognized) => true,
            _ => false
        }
    }
}

pub fn parse_command(cmd: String) -> Command {
    if cmd == "/q" || cmd == "/quit" {
        Command::Quit
    } else if cmd == "/list" {
        Command::DisplayAvailable
    } else if cmd == "/block" {
        Command::Block
    } else if cmd == "/logoff" {
        Command::Logoff
    } else {
        Command::Unrecognized
    }
}

#[cfg(test)]
mod command_tests {

    use super::{Command, parse_command};

    #[test]
    fn parse_command_test_q() {
        let cmd = "/q".to_string();
        assert_eq!(Command::Quit, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_quit() {
        let cmd = "/quit".to_string();
        assert_eq!(Command::Quit, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_list() {
        let cmd = "/list".to_string();
        assert_eq!(Command::DisplayAvailable, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_block() {
        let cmd = "/block".to_string();
        assert_eq!(Command::Block, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_logoff() {
        let cmd = "/logoff".to_string();
        assert_eq!(Command::Logoff, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_unrecognized() {
        let cmd = "/hello".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "/blah".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "/a".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "/".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "quit".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "list".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "block".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "logoff".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
    }
}<commit_msg>Case insensitive commands. Do we want case insensitive usernames?<commit_after>#[derive(Debug)]
pub enum Command {
    Quit,
    DisplayAvailable,
    Block,
    Logoff,
    Unrecognized
}

impl PartialEq for Command {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Command::Quit, &Command::Quit) => true,
            (&Command::DisplayAvailable, &Command::DisplayAvailable) => true,
            (&Command::Block, &Command::Block) => true,
            (&Command::Logoff, &Command::Logoff) => true,
            (&Command::Unrecognized, &Command::Unrecognized) => true,
            _ => false
        }
    }
}

pub fn parse_command(cmd: String) -> Command {
    let cmd = cmd.to_lowercase();
    if cmd == "/q" || cmd == "/quit" {
        Command::Quit
    } else if cmd == "/list" {
        Command::DisplayAvailable
    } else if cmd == "/block" {
        Command::Block
    } else if cmd == "/logoff" {
        Command::Logoff
    } else {
        Command::Unrecognized
    }
}

#[cfg(test)]
mod command_tests {

    use super::{Command, parse_command};

    #[test]
    fn parse_command_test_q() {
        let cmd = "/q".to_string();
        assert_eq!(Command::Quit, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_quit() {
        let cmd = "/quit".to_string();
        assert_eq!(Command::Quit, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_list() {
        let cmd = "/list".to_string();
        assert_eq!(Command::DisplayAvailable, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_block() {
        let cmd = "/block".to_string();
        assert_eq!(Command::Block, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_logoff() {
        let cmd = "/logoff".to_string();
        assert_eq!(Command::Logoff, parse_command(cmd));
    }

    #[test]
    fn parse_command_test_unrecognized() {
        let cmd = "/hello".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "/blah".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "/a".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "/".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "quit".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "list".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "block".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
        let cmd = "logoff".to_string();
        assert_eq!(Command::Unrecognized, parse_command(cmd));
    }
}<|endoftext|>


<commit_before>#[derive(Debug,Clone)]
pub enum Command {
    Output,
    Input,
    Increment,
    Decrement,
    RightShift,
    LeftShift
}
<commit_msg>Add Jump commands to Command enum<commit_after>#[derive(Debug,Clone)]
pub enum Command {
    JumpForward,
    JumpBackward,
    Output,
    Input,
    Increment,
    Decrement,
    RightShift,
    LeftShift
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, edef <edef@edef.eu>
// See the LICENSE file included in this distribution.
use core::prelude::*;
use core::marker::PhantomData;
use arch::Registers;
use stack;
use debug::StackId;

#[derive(Debug)]
pub struct Context<'a, Stack: stack::Stack> {
  regs: Registers,
  _stack_id: StackId,
  stack: Stack,
  _ref: PhantomData<&'a ()>
}

unsafe impl<'a, Stack> Send for Context<'a, Stack>
  where Stack: stack::Stack + Send {}

impl<'a, Stack> Context<'a, Stack> where Stack: stack::Stack {
  #[inline]
  pub unsafe fn new<F>(mut stack: Stack, f: F) -> Context<'a, Stack>
    where F: FnOnce() + Send + 'a {
    let stack_id = StackId::register(&mut stack);
    let regs = Registers::new(&mut stack, f);
    Context {
      regs: regs,
      _stack_id: stack_id,
      stack: stack,
      _ref: PhantomData
    }
  }

  #[inline(always)]
  pub unsafe fn swap(&mut self) {
    self.regs.swap()
  }

  #[inline]
  pub unsafe fn destroy(self) -> Stack {
    self.stack
  }
}
<commit_msg>rename Context::destroy to Context::unwrap, in line with lang conventions<commit_after>// Copyright (c) 2015, edef <edef@edef.eu>
// See the LICENSE file included in this distribution.
use core::prelude::*;
use core::marker::PhantomData;
use arch::Registers;
use stack;
use debug::StackId;

#[derive(Debug)]
pub struct Context<'a, Stack: stack::Stack> {
  regs: Registers,
  _stack_id: StackId,
  stack: Stack,
  _ref: PhantomData<&'a ()>
}

unsafe impl<'a, Stack> Send for Context<'a, Stack>
  where Stack: stack::Stack + Send {}

impl<'a, Stack> Context<'a, Stack> where Stack: stack::Stack {
  #[inline]
  pub unsafe fn new<F>(mut stack: Stack, f: F) -> Context<'a, Stack>
    where F: FnOnce() + Send + 'a {
    let stack_id = StackId::register(&mut stack);
    let regs = Registers::new(&mut stack, f);
    Context {
      regs: regs,
      _stack_id: stack_id,
      stack: stack,
      _ref: PhantomData
    }
  }

  #[inline(always)]
  pub unsafe fn swap(&mut self) {
    self.regs.swap()
  }

  #[inline]
  pub unsafe fn unwrap(self) -> Stack {
    self.stack
  }
}
<|endoftext|>