<commit_before>extern crate argparse;
extern crate libc;
extern crate libmount;
extern crate lithos;
extern crate nix;
extern crate quire;
extern crate signal;
extern crate syslog;
extern crate time;
extern crate unshare;
#[macro_use] extern crate log;

use std::env;
use std::str::FromStr;
use std::io::{stderr, Write};
use std::fs::OpenOptions;
use std::path::{Path};
use std::time::{Instant, Duration};
use std::thread::sleep;
use std::process::exit;

use libmount::BindMount;
use quire::{parse_config, Options as COptions};
use signal::trap::Trap;
use unshare::{Command, Stdio, reap_zombies};
use nix::sys::signal::Signal;
use nix::sys::signal::{SIGINT, SIGTERM, SIGCHLD};

use lithos::cgroup;
use lithos::utils::{check_mapping, in_mapping, change_root};
use lithos::range::in_range;
use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::container_config::{ContainerConfig, Variables};
use lithos::container_config::ContainerKind::Daemon;
use lithos::setup::{setup_filesystem, read_local_config, prepare_state_dir};
use lithos::setup::{init_logging};
use lithos::mount::{unmount, mount_private, mount_ro_recursive};
use lithos::limits::{set_fileno_limit};
use lithos_knot_options::Options;

mod lithos_knot_options;

struct SignalIter<'a> {
    trap: &'a mut Trap,
    interrupt: bool,
}

impl<'a> SignalIter<'a> {
    fn new(trap: &mut Trap) -> SignalIter {
        SignalIter {
            trap: trap,
            interrupt: false,
        }
    }
    fn interrupt(&mut self) {
        self.interrupt = true;
    }
}

impl<'a> Iterator for SignalIter<'a> {
    type Item = Signal;
    fn next(&mut self) -> Option<Signal> {
        if self.interrupt {
            return self.trap.wait(Instant::now());
        } else {
            return self.trap.next();
        }
    }
}

fn run(options: Options) -> Result<i32, String>
{
    let master: MasterConfig = try!(parse_config(&options.master_config,
        &MasterConfig::validator(), &COptions::default())
        .map_err(|e| format!("Error reading master config: {}", e)));
    let sandbox_name = options.name[..].splitn(2, '/').next().unwrap();
    let sandbox: SandboxConfig = try!(parse_config(
        &options.master_config.parent().unwrap()
         .join(&master.sandboxes_dir).join(sandbox_name.to_string() + ".yaml"),
        &SandboxConfig::validator(), &COptions::default())
        .map_err(|e| format!("Error reading sandbox config: {}", e)));

    let log_file;
    if let Some(ref fname) = sandbox.log_file {
        log_file = master.default_log_dir.join(fname);
    } else {
        log_file = master.default_log_dir.join(format!("{}.log", sandbox_name));
    }
    try!(init_logging(&master, &log_file,
        &format!("{}-{}", master.syslog_app_name, sandbox_name),
        options.log_stderr,
        options.log_level
            .or(sandbox.log_level.as_ref()
                .and_then(|x| FromStr::from_str(&x).ok()))
            .or_else(|| FromStr::from_str(&master.log_level).ok())
            .unwrap_or(log::LogLevel::Warn)));

    let stderr_path = master.stdio_log_dir
        .join(format!("{}.log", sandbox_name));
    let stderr_file = try!(OpenOptions::new()
                .create(true).append(true).write(true).open(&stderr_path)
                .map_err(|e| format!(
                    "Error opening stderr file {:?}: {}", stderr_path, e)));

    try!(mount_private(&Path::new("/")));
    let image_path = sandbox.image_dir.join(&options.config.image);
    let mount_dir = master.runtime_dir.join(&master.mount_dir);
    try!(BindMount::new(&image_path, &mount_dir).mount()
        .map_err(|e| e.to_string()));
    try!(mount_ro_recursive(&mount_dir));

    let container: ContainerConfig;
    container = try!(read_local_config(&mount_dir, &options.config));
    if !container.kind.matches(options.config.kind) {
        return Err(format!("Container type mismatch {:?} != {:?}",
              container.kind, options.config.kind));
    }
    let local = container.instantiate(&Variables {
        user_vars: &options.config.variables,
        lithos_name: &options.name,
        lithos_config_filename: &options.config.config,
    }).map_err(|e| format!("Variable substitution error: {}", e.join("; ")))?;

    if local.uid_map.len() > 0 {
        if !in_mapping(&local.uid_map, local.user_id) {
            return Err(format!("User is not in mapped range (uid: {})",
                local.user_id));
        }
    } else {
        if !in_range(&sandbox.allow_users, local.user_id) {
            return Err(format!("User is not in allowed range (uid: {})",
                local.user_id));
        }
    }
    if local.gid_map.len() > 0 {
        if !in_mapping(&local.gid_map, local.group_id) {
            return Err(format!("Group is not in mapped range (gid: {})",
                local.user_id));
        }
    } else {
        if !in_range(&sandbox.allow_groups, local.group_id) {
            return Err(format!("Group is not in allowed range (gid: {})",
                local.group_id));
        }
    }
    if !check_mapping(&sandbox.allow_users, &local.uid_map) {
        return Err("Bad uid mapping (probably doesn't match allow_users)"
            .to_string());
    }
    if !check_mapping(&sandbox.allow_groups, &local.gid_map) {
        return Err("Bad gid mapping (probably doesn't match allow_groups)"
            .to_string());
    }

    info!("[{}] Starting container", options.name);

    let state_dir = &master.runtime_dir.join(&master.state_dir)
        .join(&options.name);
    try!(prepare_state_dir(state_dir, &local, &sandbox));
    try!(setup_filesystem(&master, &sandbox, &local, state_dir));
    if let Some(cgroup_parent) = master.cgroup_name {
        // Warning setting cgroup relative to it's own cgroup may not work
        // if we ever want to restart lithos_knot in-place
        let cgroups = try!(cgroup::ensure_in_group(
            &(cgroup_parent + "/" +
              &options.name.replace("/", ":") + ".scope"),
            &master.cgroup_controllers));
        cgroups.set_value(cgroup::Controller::Memory,
            "memory.limit_in_bytes",
            &format!("{}", local.memory_limit))
            .map_err(|e| error!("Error setting cgroup limit: {}", e)).ok();
        cgroups.set_value(cgroup::Controller::Cpu,
                "cpu.shares",
                &format!("{}", local.cpu_shares))
            .map_err(|e| error!("Error setting cgroup limit: {}", e)).ok();
    }

    let mount_dir = master.runtime_dir.join(&master.mount_dir);
    try!(change_root(&mount_dir, &mount_dir.join("tmp")));
    try!(unmount(Path::new("/tmp")));

    try!(set_fileno_limit(local.fileno_limit)
        .map_err(|e| format!("Error setting file limit: {}", e)));

    let mut cmd = Command::new(&local.executable);
    cmd.uid(local.user_id);
    cmd.gid(local.group_id);
    cmd.current_dir(&local.workdir);

    // Should we propagate TERM?
    cmd.env_clear();
    cmd.env("TERM", env::var("TERM").unwrap_or("dumb".to_string()));
    for (k, v) in local.environ.iter() {
        cmd.env(k, v);
    }
    cmd.env("LITHOS_NAME", &options.name);
    cmd.env("LITHOS_CONFIG", &options.config.config);

    cmd.args(&local.arguments);
    cmd.args(&options.args);
    if sandbox.uid_map.len() > 0 || sandbox.gid_map.len() > 0 {
        cmd.set_id_maps(
            sandbox.uid_map.iter().map(|u| unshare::UidMap {
                inside_uid: u.inside,
                outside_uid: u.outside,
                count: u.count,
            }).collect(),
            sandbox.gid_map.iter().map(|g| unshare::GidMap {
                inside_gid: g.inside,
                outside_gid: g.outside,
                count: g.count,
            }).collect());
    } else if local.uid_map.len() > 0 || local.gid_map.len() > 0 {
        cmd.set_id_maps(
            local.uid_map.iter().map(|u| unshare::UidMap {
                inside_uid: u.inside,
                outside_uid: u.outside,
                count: u.count,
            }).collect(),
            local.gid_map.iter().map(|g| unshare::GidMap {
                inside_gid: g.inside,
                outside_gid: g.outside,
                count: g.count,
            }).collect());
    }
    let rtimeo = Duration::from_millis((local.restart_timeout*1000.0) as u64);

    let mut trap = Trap::trap(&[SIGINT, SIGTERM, SIGCHLD]);
    let mut should_exit = local.kind != Daemon || !local.restart_process_only;
    // only successful code on SIGTERM
    let mut exit_code = 2;
    loop {
        let start = Instant::now();

        if !local.interactive {
            if let Some(ref path) = local.stdout_stderr_file {
                // Reopen file at each start
                let f = try!(OpenOptions::new()
                    .create(true).append(true).write(true).open(path)
                    .map_err(|e| format!(
                        "Error opening output file {:?}: {}", path, e)));
                cmd.stdout(try!(Stdio::dup_file(&f)
                    .map_err(|e| format!(
                        "Duplicating file descriptor: {}", e))));
                cmd.stderr(Stdio::from_file(f));
            } else {
                // Can't reopen, because file is outside of container
                cmd.stdout(try!(Stdio::dup_file(&stderr_file)
                    .map_err(|e| format!(
                        "Duplicating file descriptor: {}", e))));
                cmd.stderr(try!(Stdio::dup_file(&stderr_file)
                    .map_err(|e| format!(
                        "Duplicating file descriptor: {}", e))));
            };
        }

        let child = try!(cmd.spawn().map_err(|e|
            format!("Error running {:?}: {}", options.name, e)));

        let mut iter = SignalIter::new(&mut trap);
        while let Some(signal) = iter.next() {
            match signal {
                SIGINT => {
                    // SIGINT is usually a Ctrl+C so it's sent to whole
                    // process group, so we don't need to do anything special
                    debug!("Received SIGINT. Waiting process to stop..");
                    should_exit = true;
                }
                SIGTERM => {
                    // SIGTERM is usually sent to a specific process so we
                    // forward it to children
                    debug!("Received SIGTERM signal, propagating");
                    should_exit = true;
                    exit_code = 0;
                    child.signal(SIGTERM).ok();
                }
                SIGCHLD => {
                    for (pid, status) in reap_zombies() {
                        if pid == child.pid() {
                            if status.signal() == Some(SIGTERM as i32) {
                                exit_code = 0;
                            }
                            error!("Process {:?} {}", options.name, status);
                            iter.interrupt();
                        }
                    }
                }
                _ => unreachable!(),
            }
        }

        if should_exit {
            break;
        }
        let left = rtimeo - (Instant::now() - start);
        if left > Duration::new(0, 0) {
            sleep(left);
        }
    }

    Ok(exit_code)
}

fn main() {

    let options = match Options::parse_args() {
        Ok(options) => options,
        Err(x) => {
            exit(x);
        }
    };
    match run(options)
    {
        Ok(code) => {
            exit(code);
        }
        Err(e) => {
            write!(&mut stderr(), "Fatal error: {}\n", e).ok();
            error!("Fatal error: {}", e);
            exit(1);
        }
    }
}
<commit_msg>Print uptime on process exit<commit_after>extern crate argparse;
extern crate libc;
extern crate libmount;
extern crate lithos;
extern crate nix;
extern crate quire;
extern crate signal;
extern crate syslog;
extern crate time;
extern crate unshare;
#[macro_use] extern crate log;

use std::env;
use std::str::FromStr;
use std::io::{stderr, Write};
use std::fs::OpenOptions;
use std::path::{Path};
use std::time::{Instant, Duration};
use std::thread::sleep;
use std::process::exit;

use libmount::BindMount;
use quire::{parse_config, Options as COptions};
use signal::trap::Trap;
use unshare::{Command, Stdio, reap_zombies};
use nix::sys::signal::Signal;
use nix::sys::signal::{SIGINT, SIGTERM, SIGCHLD};

use lithos::cgroup;
use lithos::utils::{check_mapping, in_mapping, change_root};
use lithos::range::in_range;
use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::container_config::{ContainerConfig, Variables};
use lithos::container_config::ContainerKind::Daemon;
use lithos::setup::{setup_filesystem, read_local_config, prepare_state_dir};
use lithos::setup::{init_logging};
use lithos::mount::{unmount, mount_private, mount_ro_recursive};
use lithos::limits::{set_fileno_limit};
use lithos_knot_options::Options;

mod lithos_knot_options;

struct SignalIter<'a> {
    trap: &'a mut Trap,
    interrupt: bool,
}

impl<'a> SignalIter<'a> {
    fn new(trap: &mut Trap) -> SignalIter {
        SignalIter {
            trap: trap,
            interrupt: false,
        }
    }
    fn interrupt(&mut self) {
        self.interrupt = true;
    }
}

impl<'a> Iterator for SignalIter<'a> {
    type Item = Signal;
    fn next(&mut self) -> Option<Signal> {
        if self.interrupt {
            return self.trap.wait(Instant::now());
        } else {
            return self.trap.next();
        }
    }
}

fn run(options: Options) -> Result<i32, String>
{
    let master: MasterConfig = try!(parse_config(&options.master_config,
        &MasterConfig::validator(), &COptions::default())
        .map_err(|e| format!("Error reading master config: {}", e)));
    let sandbox_name = options.name[..].splitn(2, '/').next().unwrap();
    let sandbox: SandboxConfig = try!(parse_config(
        &options.master_config.parent().unwrap()
         .join(&master.sandboxes_dir).join(sandbox_name.to_string() + ".yaml"),
        &SandboxConfig::validator(), &COptions::default())
        .map_err(|e| format!("Error reading sandbox config: {}", e)));

    let log_file;
    if let Some(ref fname) = sandbox.log_file {
        log_file = master.default_log_dir.join(fname);
    } else {
        log_file = master.default_log_dir.join(format!("{}.log", sandbox_name));
    }
    try!(init_logging(&master, &log_file,
        &format!("{}-{}", master.syslog_app_name, sandbox_name),
        options.log_stderr,
        options.log_level
            .or(sandbox.log_level.as_ref()
                .and_then(|x| FromStr::from_str(&x).ok()))
            .or_else(|| FromStr::from_str(&master.log_level).ok())
            .unwrap_or(log::LogLevel::Warn)));

    let stderr_path = master.stdio_log_dir
        .join(format!("{}.log", sandbox_name));
    let stderr_file = try!(OpenOptions::new()
                .create(true).append(true).write(true).open(&stderr_path)
                .map_err(|e| format!(
                    "Error opening stderr file {:?}: {}", stderr_path, e)));

    try!(mount_private(&Path::new("/")));
    let image_path = sandbox.image_dir.join(&options.config.image);
    let mount_dir = master.runtime_dir.join(&master.mount_dir);
    try!(BindMount::new(&image_path, &mount_dir).mount()
        .map_err(|e| e.to_string()));
    try!(mount_ro_recursive(&mount_dir));

    let container: ContainerConfig;
    container = try!(read_local_config(&mount_dir, &options.config));
    if !container.kind.matches(options.config.kind) {
        return Err(format!("Container type mismatch {:?} != {:?}",
              container.kind, options.config.kind));
    }
    let local = container.instantiate(&Variables {
        user_vars: &options.config.variables,
        lithos_name: &options.name,
        lithos_config_filename: &options.config.config,
    }).map_err(|e| format!("Variable substitution error: {}", e.join("; ")))?;

    if local.uid_map.len() > 0 {
        if !in_mapping(&local.uid_map, local.user_id) {
            return Err(format!("User is not in mapped range (uid: {})",
                local.user_id));
        }
    } else {
        if !in_range(&sandbox.allow_users, local.user_id) {
            return Err(format!("User is not in allowed range (uid: {})",
                local.user_id));
        }
    }
    if local.gid_map.len() > 0 {
        if !in_mapping(&local.gid_map, local.group_id) {
            return Err(format!("Group is not in mapped range (gid: {})",
                local.user_id));
        }
    } else {
        if !in_range(&sandbox.allow_groups, local.group_id) {
            return Err(format!("Group is not in allowed range (gid: {})",
                local.group_id));
        }
    }
    if !check_mapping(&sandbox.allow_users, &local.uid_map) {
        return Err("Bad uid mapping (probably doesn't match allow_users)"
            .to_string());
    }
    if !check_mapping(&sandbox.allow_groups, &local.gid_map) {
        return Err("Bad gid mapping (probably doesn't match allow_groups)"
            .to_string());
    }

    info!("[{}] Starting container", options.name);

    let state_dir = &master.runtime_dir.join(&master.state_dir)
        .join(&options.name);
    try!(prepare_state_dir(state_dir, &local, &sandbox));
    try!(setup_filesystem(&master, &sandbox, &local, state_dir));
    if let Some(cgroup_parent) = master.cgroup_name {
        // Warning setting cgroup relative to it's own cgroup may not work
        // if we ever want to restart lithos_knot in-place
        let cgroups = try!(cgroup::ensure_in_group(
            &(cgroup_parent + "/" +
              &options.name.replace("/", ":") + ".scope"),
            &master.cgroup_controllers));
        cgroups.set_value(cgroup::Controller::Memory,
            "memory.limit_in_bytes",
            &format!("{}", local.memory_limit))
            .map_err(|e| error!("Error setting cgroup limit: {}", e)).ok();
        cgroups.set_value(cgroup::Controller::Cpu,
                "cpu.shares",
                &format!("{}", local.cpu_shares))
            .map_err(|e| error!("Error setting cgroup limit: {}", e)).ok();
    }

    let mount_dir = master.runtime_dir.join(&master.mount_dir);
    try!(change_root(&mount_dir, &mount_dir.join("tmp")));
    try!(unmount(Path::new("/tmp")));

    try!(set_fileno_limit(local.fileno_limit)
        .map_err(|e| format!("Error setting file limit: {}", e)));

    let mut cmd = Command::new(&local.executable);
    cmd.uid(local.user_id);
    cmd.gid(local.group_id);
    cmd.current_dir(&local.workdir);

    // Should we propagate TERM?
    cmd.env_clear();
    cmd.env("TERM", env::var("TERM").unwrap_or("dumb".to_string()));
    for (k, v) in local.environ.iter() {
        cmd.env(k, v);
    }
    cmd.env("LITHOS_NAME", &options.name);
    cmd.env("LITHOS_CONFIG", &options.config.config);

    cmd.args(&local.arguments);
    cmd.args(&options.args);
    if sandbox.uid_map.len() > 0 || sandbox.gid_map.len() > 0 {
        cmd.set_id_maps(
            sandbox.uid_map.iter().map(|u| unshare::UidMap {
                inside_uid: u.inside,
                outside_uid: u.outside,
                count: u.count,
            }).collect(),
            sandbox.gid_map.iter().map(|g| unshare::GidMap {
                inside_gid: g.inside,
                outside_gid: g.outside,
                count: g.count,
            }).collect());
    } else if local.uid_map.len() > 0 || local.gid_map.len() > 0 {
        cmd.set_id_maps(
            local.uid_map.iter().map(|u| unshare::UidMap {
                inside_uid: u.inside,
                outside_uid: u.outside,
                count: u.count,
            }).collect(),
            local.gid_map.iter().map(|g| unshare::GidMap {
                inside_gid: g.inside,
                outside_gid: g.outside,
                count: g.count,
            }).collect());
    }
    let rtimeo = Duration::from_millis((local.restart_timeout*1000.0) as u64);

    let mut trap = Trap::trap(&[SIGINT, SIGTERM, SIGCHLD]);
    let mut should_exit = local.kind != Daemon || !local.restart_process_only;
    // only successful code on SIGTERM
    let mut exit_code = 2;
    loop {
        let start = Instant::now();

        if !local.interactive {
            if let Some(ref path) = local.stdout_stderr_file {
                // Reopen file at each start
                let f = try!(OpenOptions::new()
                    .create(true).append(true).write(true).open(path)
                    .map_err(|e| format!(
                        "Error opening output file {:?}: {}", path, e)));
                cmd.stdout(try!(Stdio::dup_file(&f)
                    .map_err(|e| format!(
                        "Duplicating file descriptor: {}", e))));
                cmd.stderr(Stdio::from_file(f));
            } else {
                // Can't reopen, because file is outside of container
                cmd.stdout(try!(Stdio::dup_file(&stderr_file)
                    .map_err(|e| format!(
                        "Duplicating file descriptor: {}", e))));
                cmd.stderr(try!(Stdio::dup_file(&stderr_file)
                    .map_err(|e| format!(
                        "Duplicating file descriptor: {}", e))));
            };
        }

        let child = try!(cmd.spawn().map_err(|e|
            format!("Error running {:?}: {}", options.name, e)));

        let mut iter = SignalIter::new(&mut trap);
        while let Some(signal) = iter.next() {
            match signal {
                SIGINT => {
                    // SIGINT is usually a Ctrl+C so it's sent to whole
                    // process group, so we don't need to do anything special
                    debug!("Received SIGINT. Waiting process to stop..");
                    should_exit = true;
                }
                SIGTERM => {
                    // SIGTERM is usually sent to a specific process so we
                    // forward it to children
                    debug!("Received SIGTERM signal, propagating");
                    should_exit = true;
                    exit_code = 0;
                    child.signal(SIGTERM).ok();
                }
                SIGCHLD => {
                    for (pid, status) in reap_zombies() {
                        if pid == child.pid() {
                            if status.signal() == Some(SIGTERM as i32) {
                                exit_code = 0;
                            }
                            let uptime = Instant::now() - start;
                            error!("Process {:?} {}, uptime {}s",
                                options.name, status, uptime.as_secs());
                            iter.interrupt();
                        }
                    }
                }
                _ => unreachable!(),
            }
        }

        if should_exit {
            break;
        }
        let left = rtimeo - (Instant::now() - start);
        if left > Duration::new(0, 0) {
            sleep(left);
        }
    }

    Ok(exit_code)
}

fn main() {

    let options = match Options::parse_args() {
        Ok(options) => options,
        Err(x) => {
            exit(x);
        }
    };
    match run(options)
    {
        Ok(code) => {
            exit(code);
        }
        Err(e) => {
            write!(&mut stderr(), "Fatal error: {}\n", e).ok();
            error!("Fatal error: {}", e);
            exit(1);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Support open source version of vscode as well<commit_after><|endoftext|>


<commit_before>use std::os;
use std::fs;
use std::fs::PathExt;
use std::io;
use std::io::{Read,Write};
use std::process;
use std::path;
use std::env;
use std::borrow::ToOwned;
use std::ffi::AsOsStr;

use atomicwrites::{AtomicFile,AllowOverwrite};

use utils;

macro_rules! main_try {
    ($result: expr, $errmsg: expr) => (
        match $result {
            Ok(m) => m,
            Err(e) => {
                if e.description().len() > 0 {
                    writeln!(&mut io::stderr(), "{}: {}", $errmsg, e).unwrap();
                };
                env::set_exit_status(1);
                return;
            }
        }
    )
}

fn get_pwd() -> path::PathBuf {
    match os::getcwd() {
        Ok(x) => path::PathBuf::new(x.as_os_str()),
        Err(e) => panic!(format!("Failed to get current working directory: {}", e))
    }
}

fn get_envvar(key: &str) -> Option<String> {
    match env::var(key) {
        Ok(x) => Some(x),
        Err(e) => match e {
            env::VarError::NotPresent => None,
            env::VarError::NotUnicode(_) => panic!(format!("{} is not unicode.", key))
        }
    }
}

fn build_index(outfile: &path::Path, dir: &path::Path) -> io::Result<()> {
    if !dir.is_dir() {
        return Err(io::Error::new(
            io::ErrorKind::MismatchedFileTypeForOperation,
            "MATES_DIR must be a directory.",
            None
        ));
    };

    let af = AtomicFile::new(&outfile, AllowOverwrite);
    let mut errors = false;

    try!(af.write(|outf| {
        for entry in try!(fs::read_dir(dir)) {
            let entry = match entry {
                Ok(x) => x,
                Err(e) => {
                    println!("Error while listing directory: {}", e);
                    errors = true;
                    continue;
                }
            };

            let pathbuf = entry.path();

            if !(*pathbuf).is_file() || match pathbuf.extension() {
                Some(x) => x.to_str().unwrap_or("") != "vcf",
                None => false
            } {
                continue;
            }

            let contact = match utils::Contact::from_file(&pathbuf) {
                Ok(x) => x,
                Err(e) => {
                    println!("Error while reading {}: {}", pathbuf.display(), e);
                    errors = true;
                    continue
                }
            };

            match utils::index_item_from_contact(&contact) {
                Ok(index_string) => {
                    try!(outf.write_all(index_string.as_bytes()));
                },
                Err(e) => {
                    println!("Error while indexing {}: {}", pathbuf.display(), e);
                    errors = true;
                    continue
                }
            };
        };
        Ok(())
    }));

    if errors {
        Err(io::Error::new(
            io::ErrorKind::Other,
            "Several errors happened while generating the index.",
            None
        ))
    } else {
        Ok(())
    }
}

pub fn cli_main() {
    let mut args = env::args();
    let program = args.next().unwrap_or("mates".to_string());

    let help = format!("Usage: {} COMMAND
Commands:
    index:
        Rewrite/create the index.
    mutt-query <query>:
        Search for contact, output is usable for mutt's query_command.
    file-query <query>:
        Search for contact, return just the filename.
    email-query <query>:
        Search for contact, return \"name <email>\".
    add:
        Take mail from stdin, add sender to contacts. Print filename.
    edit <file-or-query>:
        Open contact (given by filepath or search-string) in $MATES_EDITOR. If the file is cleared,
        the contact is removed. As a further convenience it also clears stdin, which is necessary
        for editors and most interactive programs to not act weird when piped to.",
        program);

    let print_help = || {
        println!("{}", help);
    };

    let command = match args.next() {
        Some(x) => x,
        None => {
            print_help();
            env::set_exit_status(1);
            return;
        }
    };

    if command == "--help" || command == "help" || command == "-h" {
        print_help();
        return;
    }

    let config = match Configuration::new() {
        Ok(x) => x,
        Err(e) => {
            println!("Error while reading configuration: {}", e);
            env::set_exit_status(1);
            return;
        }
    };

    match command.as_slice() {
        "index" => {
            println!("Rebuilding index file \"{}\"...", config.index_path.display());
            main_try!(build_index(&config.index_path, &config.vdir_path), "Failed to build index");
        },
        "mutt-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(mutt_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "file-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(file_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "email-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(email_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "add" => {
            let stdin = io::stdin();
            let mut email = String::new();
            main_try!(stdin.lock().read_to_string(&mut email), "Failed to read email");
            let contact = main_try!(utils::add_contact_from_email(
                &config.vdir_path,
                email.as_slice()
            ), "Failed to add contact");
            println!("{}", contact.path.display());

            let mut index_fp = main_try!(
                fs::OpenOptions::new()
                .append(true)
                .write(true)
                .open(&config.index_path),
                "Failed to open index"
            );

            let index_entry = main_try!(utils::index_item_from_contact(&contact), "Failed to generate index");
            main_try!(index_fp.write_all(index_entry.as_bytes()), "Failed to write to index");
        },
        "edit" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(edit_contact(&config, query.as_slice()), "Failed to edit contact");
        },
        _ => {
            println!("Invalid command: {}", command);
            print_help();
            env::set_exit_status(1);
        }
    };
}

fn edit_contact(config: &Configuration, query: &str) -> io::Result<()> {
    let results = if get_pwd().join(query).is_file() {
        vec![path::PathBuf::new(query)]
    } else {
        try!(utils::file_query(config, query)).into_iter().collect()
    };

    if results.len() < 1 {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "No such contact.",
            None
        ))
    } else if results.len() > 1 {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "Ambiguous query.",
            None
        ))
    }

    let fpath = &results[0];
    let mut process = try!(process::Command::new("sh")
        .arg("-c")
        // clear stdin, http://unix.stackexchange.com/a/77593
        .arg(format!("$0 -- \"$1\" < $2").as_slice())
        .arg(config.editor_cmd.as_slice())
        .arg(fpath.as_os_str())
        .arg("/dev/tty")
        .stdin(process::Stdio::inherit())
        .stdout(process::Stdio::inherit())
        .stderr(process::Stdio::inherit())
        .spawn());

    try!(utils::handle_process(&mut process));

    let fcontent = {
        let mut fcontent = String::new();
        let mut file = try!(fs::File::open(fpath));
        try!(file.read_to_string(&mut fcontent));
        fcontent
    };

    if fcontent.as_slice().trim().len() == 0 {
        try!(fs::remove_file(fpath));
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "Contact emptied, file removed.",
            None
        ));
    };

    Ok(())
}

fn mutt_query<'a>(config: &Configuration, query: &str) -> io::Result<()> {
    println!("");  // For some reason mutt requires an empty line
    for item in try!(utils::index_query(config, query)) {
        if item.email.len() > 0 && item.name.len() > 0 {
            println!("{}\t{}", item.email, item.name);
        };
    };
    Ok(())
}

fn file_query<'a>(config: &Configuration, query: &str) -> io::Result<()> {
    for path in try!(utils::file_query(config, query)).iter() {
        println!("{}", path.display());
    };
    Ok(())
}

fn email_query<'a>(config: &Configuration, query: &str) -> io::Result<()> {
    for item in try!(utils::index_query(config, query)) {
        if item.name.len() > 0 && item.email.len() > 0 {
            println!("{} <{}>", item.name, item.email);
        };
    };
    Ok(())
}

pub struct Configuration {
    pub index_path: path::PathBuf,
    pub vdir_path: path::PathBuf,
    pub editor_cmd: String,
    pub grep_cmd: String
}

impl Configuration {
    pub fn new() -> Result<Configuration, String> {
        Ok(Configuration {
            index_path: match get_envvar("MATES_INDEX") {
                Some(x) => path::PathBuf::new(&x),
                None => match get_envvar("HOME") {
                    Some(home) => get_pwd().join(&home).join(".mates_index"),
                    None => return Err("Unable to determine user's home directory.".to_owned())
                }
            },
            vdir_path: match get_envvar("MATES_DIR") {
                Some(x) => path::PathBuf::new(&x),
                None => return Err("MATES_DIR must be set to your vdir path (directory of vcf-files).".to_owned())
            },
            editor_cmd: match get_envvar("MATES_EDITOR") {
                Some(x) => x,
                None => match get_envvar("EDITOR") {
                    Some(x) => x,
                    None => return Err("MATES_EDITOR or EDITOR must be set.".to_owned())
                }
            },
            grep_cmd: match get_envvar("MATES_GREP") {
                Some(x) => x,
                None => "grep -i".to_owned()
            }
        })
    }
}
<commit_msg>Remove useless stuff from editor invocation<commit_after>use std::os;
use std::fs;
use std::fs::PathExt;
use std::io;
use std::io::{Read,Write};
use std::process;
use std::path;
use std::env;
use std::borrow::ToOwned;
use std::ffi::AsOsStr;

use atomicwrites::{AtomicFile,AllowOverwrite};

use utils;

macro_rules! main_try {
    ($result: expr, $errmsg: expr) => (
        match $result {
            Ok(m) => m,
            Err(e) => {
                if e.description().len() > 0 {
                    writeln!(&mut io::stderr(), "{}: {}", $errmsg, e).unwrap();
                };
                env::set_exit_status(1);
                return;
            }
        }
    )
}

fn get_pwd() -> path::PathBuf {
    match os::getcwd() {
        Ok(x) => path::PathBuf::new(x.as_os_str()),
        Err(e) => panic!(format!("Failed to get current working directory: {}", e))
    }
}

fn get_envvar(key: &str) -> Option<String> {
    match env::var(key) {
        Ok(x) => Some(x),
        Err(e) => match e {
            env::VarError::NotPresent => None,
            env::VarError::NotUnicode(_) => panic!(format!("{} is not unicode.", key))
        }
    }
}

fn build_index(outfile: &path::Path, dir: &path::Path) -> io::Result<()> {
    if !dir.is_dir() {
        return Err(io::Error::new(
            io::ErrorKind::MismatchedFileTypeForOperation,
            "MATES_DIR must be a directory.",
            None
        ));
    };

    let af = AtomicFile::new(&outfile, AllowOverwrite);
    let mut errors = false;

    try!(af.write(|outf| {
        for entry in try!(fs::read_dir(dir)) {
            let entry = match entry {
                Ok(x) => x,
                Err(e) => {
                    println!("Error while listing directory: {}", e);
                    errors = true;
                    continue;
                }
            };

            let pathbuf = entry.path();

            if !(*pathbuf).is_file() || match pathbuf.extension() {
                Some(x) => x.to_str().unwrap_or("") != "vcf",
                None => false
            } {
                continue;
            }

            let contact = match utils::Contact::from_file(&pathbuf) {
                Ok(x) => x,
                Err(e) => {
                    println!("Error while reading {}: {}", pathbuf.display(), e);
                    errors = true;
                    continue
                }
            };

            match utils::index_item_from_contact(&contact) {
                Ok(index_string) => {
                    try!(outf.write_all(index_string.as_bytes()));
                },
                Err(e) => {
                    println!("Error while indexing {}: {}", pathbuf.display(), e);
                    errors = true;
                    continue
                }
            };
        };
        Ok(())
    }));

    if errors {
        Err(io::Error::new(
            io::ErrorKind::Other,
            "Several errors happened while generating the index.",
            None
        ))
    } else {
        Ok(())
    }
}

pub fn cli_main() {
    let mut args = env::args();
    let program = args.next().unwrap_or("mates".to_string());

    let help = format!("Usage: {} COMMAND
Commands:
    index:
        Rewrite/create the index.
    mutt-query <query>:
        Search for contact, output is usable for mutt's query_command.
    file-query <query>:
        Search for contact, return just the filename.
    email-query <query>:
        Search for contact, return \"name <email>\".
    add:
        Take mail from stdin, add sender to contacts. Print filename.
    edit <file-or-query>:
        Open contact (given by filepath or search-string) in $MATES_EDITOR. If the file is cleared,
        the contact is removed. As a further convenience it also clears stdin, which is necessary
        for editors and most interactive programs to not act weird when piped to.",
        program);

    let print_help = || {
        println!("{}", help);
    };

    let command = match args.next() {
        Some(x) => x,
        None => {
            print_help();
            env::set_exit_status(1);
            return;
        }
    };

    if command == "--help" || command == "help" || command == "-h" {
        print_help();
        return;
    }

    let config = match Configuration::new() {
        Ok(x) => x,
        Err(e) => {
            println!("Error while reading configuration: {}", e);
            env::set_exit_status(1);
            return;
        }
    };

    match command.as_slice() {
        "index" => {
            println!("Rebuilding index file \"{}\"...", config.index_path.display());
            main_try!(build_index(&config.index_path, &config.vdir_path), "Failed to build index");
        },
        "mutt-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(mutt_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "file-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(file_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "email-query" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(email_query(&config, query.as_slice()), "Failed to execute grep");
        },
        "add" => {
            let stdin = io::stdin();
            let mut email = String::new();
            main_try!(stdin.lock().read_to_string(&mut email), "Failed to read email");
            let contact = main_try!(utils::add_contact_from_email(
                &config.vdir_path,
                email.as_slice()
            ), "Failed to add contact");
            println!("{}", contact.path.display());

            let mut index_fp = main_try!(
                fs::OpenOptions::new()
                .append(true)
                .write(true)
                .open(&config.index_path),
                "Failed to open index"
            );

            let index_entry = main_try!(utils::index_item_from_contact(&contact), "Failed to generate index");
            main_try!(index_fp.write_all(index_entry.as_bytes()), "Failed to write to index");
        },
        "edit" => {
            let query = args.next().unwrap_or("".to_string());
            main_try!(edit_contact(&config, query.as_slice()), "Failed to edit contact");
        },
        _ => {
            println!("Invalid command: {}", command);
            print_help();
            env::set_exit_status(1);
        }
    };
}

fn edit_contact(config: &Configuration, query: &str) -> io::Result<()> {
    let results = if get_pwd().join(query).is_file() {
        vec![path::PathBuf::new(query)]
    } else {
        try!(utils::file_query(config, query)).into_iter().collect()
    };

    if results.len() < 1 {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "No such contact.",
            None
        ))
    } else if results.len() > 1 {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "Ambiguous query.",
            None
        ))
    }

    let fpath = &results[0];
    let mut process = try!(process::Command::new("sh")
        .arg("-c")
        // clear stdin, http://unix.stackexchange.com/a/77593
        .arg("$0 \"$1\" < $2")
        .arg(config.editor_cmd.as_slice())
        .arg(fpath.as_os_str())
        .arg("/dev/tty")
        .stdin(process::Stdio::inherit())
        .stdout(process::Stdio::inherit())
        .stderr(process::Stdio::inherit())
        .spawn());

    try!(utils::handle_process(&mut process));

    let fcontent = {
        let mut fcontent = String::new();
        let mut file = try!(fs::File::open(fpath));
        try!(file.read_to_string(&mut fcontent));
        fcontent
    };

    if fcontent.as_slice().trim().len() == 0 {
        try!(fs::remove_file(fpath));
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "Contact emptied, file removed.",
            None
        ));
    };

    Ok(())
}

fn mutt_query<'a>(config: &Configuration, query: &str) -> io::Result<()> {
    println!("");  // For some reason mutt requires an empty line
    for item in try!(utils::index_query(config, query)) {
        if item.email.len() > 0 && item.name.len() > 0 {
            println!("{}\t{}", item.email, item.name);
        };
    };
    Ok(())
}

fn file_query<'a>(config: &Configuration, query: &str) -> io::Result<()> {
    for path in try!(utils::file_query(config, query)).iter() {
        println!("{}", path.display());
    };
    Ok(())
}

fn email_query<'a>(config: &Configuration, query: &str) -> io::Result<()> {
    for item in try!(utils::index_query(config, query)) {
        if item.name.len() > 0 && item.email.len() > 0 {
            println!("{} <{}>", item.name, item.email);
        };
    };
    Ok(())
}

pub struct Configuration {
    pub index_path: path::PathBuf,
    pub vdir_path: path::PathBuf,
    pub editor_cmd: String,
    pub grep_cmd: String
}

impl Configuration {
    pub fn new() -> Result<Configuration, String> {
        Ok(Configuration {
            index_path: match get_envvar("MATES_INDEX") {
                Some(x) => path::PathBuf::new(&x),
                None => match get_envvar("HOME") {
                    Some(home) => get_pwd().join(&home).join(".mates_index"),
                    None => return Err("Unable to determine user's home directory.".to_owned())
                }
            },
            vdir_path: match get_envvar("MATES_DIR") {
                Some(x) => path::PathBuf::new(&x),
                None => return Err("MATES_DIR must be set to your vdir path (directory of vcf-files).".to_owned())
            },
            editor_cmd: match get_envvar("MATES_EDITOR") {
                Some(x) => x,
                None => match get_envvar("EDITOR") {
                    Some(x) => x,
                    None => return Err("MATES_EDITOR or EDITOR must be set.".to_owned())
                }
            },
            grep_cmd: match get_envvar("MATES_GREP") {
                Some(x) => x,
                None => "grep -i".to_owned()
            }
        })
    }
}
<|endoftext|>


<commit_before>use rand::{Rng};
use std::cmp::min;
use std::slice;
use super::{BoundedRand, Bounds, Collision, Particle, Space, SpaceVec, Vector};

#[derive(Debug, Clone)]
pub struct SpaceBox {
  space_vec: SpaceVec,
  bounds: Bounds
}

impl SpaceBox {
  pub fn new(ps: Vec<Particle>, bottom_left: Vector, top_right: Vector) -> SpaceBox {
    let space_vec = SpaceVec::new(ps);
    let bounds = Bounds::new(top_right, bottom_left);
    assert!(
      space_vec.particles().all(|p| bounds.within(p)),
      "bounds must include all particles"
    );
    SpaceBox { space_vec: space_vec, bounds: bounds }
  }

  pub fn new_random<R: Rng>(rng: &mut R, count: usize, min: Particle, max: Particle) -> SpaceBox {
    let mut particles = Vec::with_capacity(count);

    for _ in 0..count {
      let mut new_p: Particle;
      loop {
        new_p = BoundedRand::rand(rng, &min, &max);
        if ! particles.iter().any(|p: &Particle| p.overlaps(&new_p)) { break; }
      }
      particles.push(new_p);
    }

    SpaceBox::new(particles, min.x, max.x)
  }
}

impl Space for SpaceBox {

  fn particles(&self) -> slice::Iter<Particle> {
    self.space_vec.particles()
  }

  fn next_collision(&self) -> Collision {
    let inter_particle_coll = self.space_vec.next_collision();
    let wall_coll = self.particles()
      .map(|p| self.bounds.next_collision(p))
      .min()
      .unwrap_or(Collision::Free);

    min(inter_particle_coll, wall_coll)
  }

  fn update(&self, collision: Collision) -> Option<Self> {
    let space_vec_opt = match collision {
      Collision::Wall { t, ref prev, ref next } => {
        let new_vec: Vec<_> = self.particles().map( |p: &Particle|
          if p == prev { next.clone() }
          else { p.evolve(t) }
        ).collect();

        Some(SpaceVec::new(new_vec))
      },
      _ => self.space_vec
        .update(collision)
    };

    space_vec_opt.map(|sv| SpaceBox { space_vec: sv, bounds: self.bounds.clone() } )
  }
}
<commit_msg>ensure particles aren't embedded in walls for random initial conditions<commit_after>use rand::{Rng};
use std::cmp::min;
use std::slice;
use super::{BoundedRand, Bounds, Collision, Particle, Space, SpaceVec, Vector};

#[derive(Debug, Clone)]
pub struct SpaceBox {
  space_vec: SpaceVec,
  bounds: Bounds
}

impl SpaceBox {
  pub fn new(ps: Vec<Particle>, bottom_left: Vector, top_right: Vector) -> SpaceBox {
    let space_vec = SpaceVec::new(ps);
    let bounds = Bounds::new(top_right, bottom_left);
    assert!(
      space_vec.particles().all(|p| bounds.within(p)),
      "bounds must include all particles"
    );
    SpaceBox { space_vec: space_vec, bounds: bounds }
  }

  pub fn new_random<R: Rng>(rng: &mut R, count: usize, min: Particle, max: Particle) -> SpaceBox {
    let mut particles = Vec::with_capacity(count);

    for _ in 0..count {
      let mut new_p: Particle;
      loop {
        new_p = BoundedRand::rand(rng, &min, &max);
        if ! particles.iter().any(|p: &Particle| p.overlaps(&new_p)) { break; }
      }
      particles.push(new_p);
    }

    let r_vec = Vector((max.r, max.r));
    SpaceBox::new(particles, &min.x - &r_vec, &max.x + &r_vec)
  }
}

impl Space for SpaceBox {

  fn particles(&self) -> slice::Iter<Particle> {
    self.space_vec.particles()
  }

  fn next_collision(&self) -> Collision {
    let inter_particle_coll = self.space_vec.next_collision();
    let wall_coll = self.particles()
      .map(|p| self.bounds.next_collision(p))
      .min()
      .unwrap_or(Collision::Free);

    min(inter_particle_coll, wall_coll)
  }

  fn update(&self, collision: Collision) -> Option<Self> {
    let space_vec_opt = match collision {
      Collision::Wall { t, ref prev, ref next } => {
        let new_vec: Vec<_> = self.particles().map( |p: &Particle|
          if p == prev { next.clone() }
          else { p.evolve(t) }
        ).collect();

        Some(SpaceVec::new(new_vec))
      },
      _ => self.space_vec
        .update(collision)
    };

    space_vec_opt.map(|sv| SpaceBox { space_vec: sv, bounds: self.bounds.clone() } )
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(not(stage1))]
#[phase(syntax)]
extern crate regex_macros;

// Dirty hack: During stage1, test dynamic regexs. For stage2, we test
// native regexs.
#[cfg(stage1)]
macro_rules! regex(
    ($re:expr) => (
        match ::regex::Regex::new($re) {
            Ok(re) => re,
            Err(err) => fail!("{}", err),
        }
    );
)

mod bench;
mod tests;

<commit_msg>auto merge of #13766 : BurntSushi/rust/regex-dynamic-tests, r=alexcrichton<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(not(stage1))]
#[phase(syntax)]
extern crate regex_macros;

#[cfg(not(stage1))]
#[path = "bench.rs"]
mod native_bench;

#[cfg(not(stage1))]
#[path = "tests.rs"]
mod native_tests;

// Due to macro scoping rules, this definition only applies for the modules
// defined below. Effectively, it allows us to use the same tests for both
// native and dynamic regexes.
macro_rules! regex(
    ($re:expr) => (
        match ::regex::Regex::new($re) {
            Ok(re) => re,
            Err(err) => fail!("{}", err),
        }
    );
)

#[path = "bench.rs"]
mod dynamic_bench;
#[path = "tests.rs"]
mod dynamic_tests;

<|endoftext|>


<commit_before>// Copyright (c) 2015 by Stacy Prowell.  All rights reserved.
//
// Licensed under the BSD 2-Clause license.  See the file LICENSE
// that is part of this distribution.  This file may not be copied,
// modified, or distributed except according to those terms.extern

//! Provide basic definitions of terms.

use std::collections::BitVec;
use std::rc::Rc;

/// Represents root terms.
struct Root ( () );
struct Any ( () );
struct ENone ( () );

#[derive(Debug)]
struct EString( String ) ;
impl EString{
    fn new(val: &str) -> Self { EString(val.to_string()) }
}

struct Symbol( String );
#[derive(Debug)]
struct Integer( isize );
impl Integer{
    fn new(val: isize) -> Self { Integer(val) }
}

struct Float( f64 );
struct BitString( BitVec );
struct Boolean( bool );

//For simplicity while experimenting, I'm not using Rc<T>
//Can't derive Debug automatically for Term
//#[derive(Debug)]
struct Lambda{
    parameter: Box<Term>,
    guard    : Box<Term>,
    body     : Box<Term>,
    //type,
    index    : usize,
    stuff     : (),
}
impl Lambda{
    fn new(p: Box<Term>, g: Box<Term>, b: Box<Term>) -> Self{
        Lambda{parameter: p, guard: g, body: b, index: 0, stuff: ()}
    }
}

trait RootType<T>{
    fn native(&self) -> &T;
}
impl RootType<String> for EString{
    fn native(&self) -> &String { &self.0 }
}
impl RootType<isize> for Integer{
    fn native(&self) -> &isize { &self.0 }
}

trait Term{
    fn to_string(&self) -> String { "This is a Term.".to_string() }
}

impl Term for EString{
    fn to_string(&self) -> String { "This is an EString".to_string() }
}

impl Term for Integer{
    fn to_string(&self) -> String {"This is an Integer".to_string() }
}

impl Term for Lambda{
    fn to_string(&self) -> String {"This is a Lambda.".to_string() }
}

#[test]
fn term_type_check_test() -> (){
    let estring = EString("Test".to_string());
    let integer = Integer::new(3);
    let i2 = Integer::new(5);
    let lambda = Lambda::new(Box::new(estring), Box::new(integer),
                 Box::new(i2));
    // Uncomment the following line to see the term type checker assist in action
    // stringterm = integerterm;

    panic!("Intentionial panic. {:?}", lambda.to_string() );
}

<commit_msg>Added a usage of native() to the test.<commit_after>// Copyright (c) 2015 by Stacy Prowell.  All rights reserved.
//
// Licensed under the BSD 2-Clause license.  See the file LICENSE
// that is part of this distribution.  This file may not be copied,
// modified, or distributed except according to those terms.extern

//! Provide basic definitions of terms.

use std::collections::BitVec;
use std::rc::Rc;

/// Represents root terms.
struct Root ( () );
struct Any ( () );
struct ENone ( () );

#[derive(Debug)]
struct EString( String ) ;
impl EString{
    fn new(val: &str) -> Self { EString(val.to_string()) }
}

struct Symbol( String );
#[derive(Debug)]
struct Integer( isize );
impl Integer{
    fn new(val: isize) -> Self { Integer(val) }
}

struct Float( f64 );
struct BitString( BitVec );
struct Boolean( bool );

//For simplicity while experimenting, I'm not using Rc<T>
//Can't derive Debug automatically for Term
//#[derive(Debug)]
struct Lambda{
    parameter: Box<Term>,
    guard    : Box<Term>,
    body     : Box<Term>,
    product  : (),
    index    : usize,
    stuff    : (),
}
impl Lambda{
    fn new(p: Box<Term>, g: Box<Term>, b: Box<Term>) -> Self{
        Lambda{parameter: p, guard: g, body: b, product: (), index: 0, stuff: ()}
    }
}

trait RootType<T>{
    fn native(&self) -> &T;
}
impl RootType<String> for EString{
    fn native(&self) -> &String { &self.0 }
}
impl RootType<isize> for Integer{
    fn native(&self) -> &isize { &self.0 }
}

trait Term{
    fn to_string(&self) -> String { "This is a Term.".to_string() }
}

impl Term for EString{
    fn to_string(&self) -> String { "This is an EString".to_string() }
}

impl Term for Integer{
    fn to_string(&self) -> String {"This is an Integer".to_string() }
}

impl Term for Lambda{
    fn to_string(&self) -> String {"This is a Lambda.".to_string() }
}

#[test]
fn term_type_check_test() -> (){
    let estring = EString("Test Lambda Body".to_string());
    let estring2 = EString("Test native".to_string());
    let integer = Integer::new(3);
    let i2 = Integer::new(5);
    let lambda = Lambda::new(Box::new(estring), Box::new(integer),
                 Box::new(i2));
    // Uncomment the following line to see the term type checker assist in action
    // stringterm = integerterm;

    panic!("Intentionial panic. {:?}", estring2.native().to_string() );
}

<|endoftext|>


<commit_before>// Copyright 2014-2015 The GeoRust Developers
//
// 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 std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub type PeekableTokens = Peekable<Token, Tokens>;

pub struct Tokens {
    text: String
}

impl Tokens {
    pub fn from_str(input: &str) -> Self {
        Tokens {text: String::from_str(input)}
    }
}

impl Iterator for Tokens {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return None,
        };

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace();
                number = number.trim_left_matches('+').to_string();
                match number.parse::<f64>() {
                    Some(parsed_num) => Some(Token::Number(parsed_num)),
                    None => panic!("Could not parse number: {}", number),
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokens {
    fn pop_front(&mut self) -> Option<char> {
        match self.text.is_empty() {
            true => None,
            false => Some(self.text.remove(0))
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return "".to_string(),
        };

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + &self.read_until_whitespace(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<commit_msg>Update parse() result<commit_after>// Copyright 2014-2015 The GeoRust Developers
//
// 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 std::iter::Peekable;

#[derive(PartialEq, Debug)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub type PeekableTokens = Peekable<Token, Tokens>;

pub struct Tokens {
    text: String
}

impl Tokens {
    pub fn from_str(input: &str) -> Self {
        Tokens {text: String::from_str(input)}
    }
}

impl Iterator for Tokens {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return None,
        };

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + &self.read_until_whitespace();
                number = number.trim_left_matches('+').to_string();
                match number.parse::<f64>() {
                    Ok(parsed_num) => Some(Token::Number(parsed_num)),
                    Err(e) => panic!("Could not parse number: {}", e),
                }
            }
            c => {
                let word = c.to_string() + &self.read_until_whitespace();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokens {
    fn pop_front(&mut self) -> Option<char> {
        match self.text.is_empty() {
            true => None,
            false => Some(self.text.remove(0))
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let next_char = match self.pop_front() {
            Some(c) => c,
            None => return "".to_string(),
        };

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + &self.read_until_whitespace(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = Tokens::from_str(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<|endoftext|>


<commit_before>use std::str;
use std::str::Bytes;
use std::iter::{ Peekable, Iterator };
use lexicon::Token;
use lexicon::Token::*;
use lexicon::ReservedKind::*;
use grammar::OperatorType::*;
use grammar::VariableDeclarationKind::*;
use grammar::LiteralValue;
use grammar::LiteralValue::*;

macro_rules! match_operators {
    { $tokenizer:ident $ch:ident
        $(
            $prime:pat => $tok:ident $cascade:tt
        )*
    } => ({
        match $ch {
            $(
                $prime => match_descend!( $tokenizer $tok $cascade )
            ),*
            ,
            _ => {}
        }
    })
}

macro_rules! match_descend {
    ( $tokenizer:ident $matched:ident { $(
        $secondary:pat => $tok:ident $cascade:tt
    )* } ) => ({
        match $tokenizer.source.peek() {
            $(
                Some(&$secondary) => {
                    $tokenizer.source.next();
                    match_descend!( $tokenizer $tok $cascade )
                },
            )*
            _ => return Some(Operator($matched))
        }
    });
    ( $tokenizer:ident return $matched:expr ) => {
        return Some($matched)
    };
    ( $tokenizer:ident $matched:expr , ) => {
        return Some(Operator($matched))
    }
}

pub struct Tokenizer<'a> {
    source: Peekable<Bytes<'a>>,
    buffer: Vec<u8>,
}

impl<'a> Tokenizer<'a> {
    pub fn new(source: &'a str) -> Self {
        Tokenizer {
            source: source.bytes().peekable(),
            buffer: Vec::with_capacity(128),
        }
    }

    fn read_string(&mut self, first: u8) -> String {
        self.buffer.clear();
        let mut escape = false;

        while let Some(ch) = self.source.next() {
            if ch == first && escape == false {
                break;
            }
            match ch {
                b'\\' => {
                    if escape {
                        escape = false;
                        self.buffer.push(ch);
                    } else {
                        escape = true;
                    }
                },
                _ => {
                    self.buffer.push(ch);
                    escape = false;
                },
            }
        }

        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }.to_owned()
    }

    fn read_binary(&mut self) -> LiteralValue {
        let mut value = 0;

        while let Some(&peek) = self.source.peek() {
            match peek {
                b'0' | b'1' => {
                    value <<= 1;
                    if peek == b'1' {
                        value += 1;
                    }
                    self.source.next();
                },
                _ => return LiteralInteger(value),
            }
        }

        return LiteralInteger(value);
    }

    fn read_octal(&mut self) -> LiteralValue {
        let mut value = 0;

        while let Some(&peek) = self.source.peek() {
            let digit = match peek {
                b'0' => 0,
                b'1' => 1,
                b'2' => 2,
                b'3' => 3,
                b'4' => 4,
                b'5' => 5,
                b'6' => 6,
                b'7' => 7,
                _    => -1,
            };
            if digit == -1 {
                return LiteralInteger(value);
            } else {
                value <<= 3;
                value += digit;
                self.source.next();
            }
        }

        return LiteralInteger(value);
    }

    fn read_hexadec(&mut self) -> LiteralValue {
        let mut value = 0;

        while let Some(&peek) = self.source.peek() {
            let digit = match peek {
                b'0'        => 0,
                b'1'        => 1,
                b'2'        => 2,
                b'3'        => 3,
                b'4'        => 4,
                b'5'        => 5,
                b'6'        => 6,
                b'7'        => 7,
                b'8'        => 8,
                b'9'        => 9,
                b'a' | b'A' => 10,
                b'b' | b'B' => 11,
                b'c' | b'C' => 12,
                b'd' | b'D' => 13,
                b'e' | b'E' => 14,
                b'f' | b'F' => 15,
                _           => -1,
            };
            if digit == -1 {
                return LiteralInteger(value);
            } else {
                value <<= 4;
                value += digit;
                self.source.next();
            }
        }

        return LiteralInteger(value);
    }

    fn read_number(&mut self, first: u8) -> LiteralValue {
        self.buffer.clear();
        self.buffer.push(first);
        let mut period = false;

        if first == b'.' {
            period = true;
        } else if first == b'0' {
            if let Some(&peek) = self.source.peek() {
                if peek == b'b' {
                    self.source.next();
                    return self.read_binary();
                } else if peek == b'o' {
                    self.source.next();
                    return self.read_octal();
                } else if peek == b'x' {
                    self.source.next();
                    return self.read_hexadec();
                }
            }
        }

        while let Some(&ch) = self.source.peek() {
            match ch {
                b'0'...b'9' => {
                    self.buffer.push(ch);
                    self.source.next();
                },
                b'.' => {
                    if !period {
                        period = true;
                        self.buffer.push(ch);
                        self.source.next();
                    } else {
                        return LiteralValue::float_from_string(&self.buffer);
                    }
                },
                _ => return LiteralValue::float_from_string(&self.buffer),
            }
        }

        return LiteralValue::float_from_string(&self.buffer);
    }

    fn read_comment(&mut self) {
        while let Some(&ch) = self.source.peek() {
            if ch == b'\n' {
                return;
            }
            self.source.next();
        }
    }

    fn read_block_comment(&mut self) {
        let mut asterisk = false;

        while let Some(ch) = self.source.next() {
            if ch == b'/' && asterisk {
                return;
            }
            if ch == b'*' {
                asterisk = true;
            }
        }
    }

    fn read_label(&mut self, first: u8) -> Token {
        self.buffer.clear();
        self.buffer.push(first);

        while let Some(&ch) = self.source.peek() {
            match ch {
                b'a'...b'z' |
                b'A'...b'Z' |
                b'0'...b'9' |
                b'_' | b'$' => {
                    self.buffer.push(ch);
                    self.source.next();
                }
                _ => break
            }
        }

        let slice: &[u8] = &self.buffer;

        match slice {
            b"new"        => Operator(New),
            b"typeof"     => Operator(Typeof),
            b"delete"     => Operator(Delete),
            b"void"       => Operator(Void),
            b"in"         => Operator(In),
            b"instanceof" => Operator(Instanceof),
            b"var"        => Declaration(Var),
            b"let"        => Declaration(Let),
            b"const"      => Declaration(Const),
            b"break"      => Break,
            b"do"         => Do,
            b"case"       => Case,
            b"else"       => Else,
            b"catch"      => Catch,
            b"export"     => Export,
            b"class"      => Class,
            b"extends"    => Extends,
            b"return"     => Return,
            b"while"      => While,
            b"finally"    => Finally,
            b"super"      => Super,
            b"with"       => With,
            b"continue"   => Continue,
            b"for"        => For,
            b"switch"     => Switch,
            b"yield"      => Yield,
            b"debugger"   => Debugger,
            b"function"   => Function,
            b"this"       => This,
            b"default"    => Default,
            b"if"         => If,
            b"throw"      => Throw,
            b"import"     => Import,
            b"try"        => Try,
            b"await"      => Await,
            b"static"     => Static,
            b"true"       => Literal(LiteralTrue),
            b"false"      => Literal(LiteralFalse),
            b"undefined"  => Literal(LiteralUndefined),
            b"null"       => Literal(LiteralNull),
            b"enum"       => Reserved(Enum),
            b"implements" => Reserved(Implements),
            b"package"    => Reserved(Package),
            b"protected"  => Reserved(Protected),
            b"interface"  => Reserved(Interface),
            b"private"    => Reserved(Private),
            b"public"     => Reserved(Public),
            _             => Identifier(unsafe { str::from_utf8_unchecked(slice) }.to_owned()),
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        'lex: while let Some(ch) = self.source.next() {

            match_operators! { self ch
                b'=' => Assign {
                    b'=' => Equality {
                        b'=' => StrictEquality,
                    }
                    b'>' => FatArrow,
                }
                b'!' => LogicalNot {
                    b'=' => Inequality {
                        b'=' => StrictInequality,
                    }
                }
                b'<' => Lesser {
                    b'<' => BitShiftLeft {
                        b'=' => BSLAssign,
                    }
                    b'=' => LesserEquals,
                }
                b'>' => Greater {
                    b'>' => BitShiftRight {
                        b'>' => UBitShiftRight {
                            b'=' => UBSRAssign,
                        }
                        b'=' => BSRAssign,
                    }
                    b'=' => GreaterEquals,
                }
                b'?' => Conditional,
                b'~' => BitwiseNot,
                b'^' => BitwiseXor {
                    b'=' => BitXorAssign,
                }
                b'&' => BitwiseAnd {
                    b'&' => LogicalAnd,
                    b'=' => BitAndAssign,
                }
                b'|' => BitwiseOr {
                    b'|' => LogicalOr,
                    b'=' => BitOrAssign,
                }
                b'+' => Addition {
                    b'+' => Increment,
                    b'=' => AddAssign,
                }
                b'-' => Substraction {
                    b'-' => Decrement,
                    b'=' => SubstractAssign,
                }
                b'*' => Multiplication {
                    b'*' => Exponent {
                        b'=' => ExponentAssign,
                    }
                    b'=' => MultiplyAssign,
                }
                b'%' => Remainder {
                    b'=' => RemainderAssign,
                }
            }

            return Some(match ch {
                b'\n' => LineTermination,
                b';' => Semicolon,
                b',' => Comma,
                b':' => Colon,
                b'(' => ParenOn,
                b')' => ParenOff,
                b'[' => BracketOn,
                b']' => BracketOff,
                b'{' => BraceOn,
                b'}' => BraceOff,
                b'"' | b'\'' => {
                    Literal(LiteralString( self.read_string(ch) ))
                },
                b'/' => {
                    match self.source.peek() {
                        Some(&b'/') => {
                            self.source.next();
                            self.read_comment();
                            continue 'lex
                        },
                        Some(&b'*') => {
                            self.source.next();
                            self.read_block_comment();
                            continue 'lex
                        },
                        Some(&b'=') => {
                            self.source.next();
                            Operator(DivideAssign)
                        },
                        _ => Operator(Division)
                    }
                },
                b'.' => {
                    match self.source.peek() {
                        Some(&b'0'...b'9') => {
                            Literal(self.read_number(b'.'))
                        },
                        Some(&b'.') => {
                            self.source.next();
                            match self.source.next() {
                                Some(b'.') => Operator(Spread),
                                ch        => {
                                    panic!("Invalid character `{:?}`", ch);
                                }
                            }
                        },
                        _ => Operator(Accessor)
                    }
                },
                b'0'...b'9' => Literal(self.read_number(ch)),
                _ => {
                    match ch {
                        b'a'...b'z' |
                        b'A'...b'Z' |
                        b'$' | b'_' => {
                            self.read_label(ch)
                        },

                        b' ' | b'\t' => continue 'lex,

                        _ => {
                            panic!("Invalid character `{:?}`", ch);
                        }
                    }
                }
            });
        }
        return None;
    }
}
<commit_msg>Fast labels<commit_after>use std::str;
use std::iter::Iterator;
use lexicon::Token;
use lexicon::Token::*;
use lexicon::ReservedKind::*;
use grammar::OperatorType::*;
use grammar::VariableDeclarationKind::*;
use grammar::LiteralValue;
use grammar::LiteralValue::*;

macro_rules! match_operators {
    { $tokenizer:ident $ch:ident
        $(
            $prime:pat => $tok:ident $cascade:tt
        )*
    } => ({
        match $ch {
            $(
                $prime => match_descend!( $tokenizer $tok $cascade )
            ),*
            ,
            _ => {}
        }
    })
}

macro_rules! match_descend {
    ( $tokenizer:ident $matched:ident { $(
        $secondary:pat => $tok:ident $cascade:tt
    )* } ) => ({
        if $tokenizer.is_eof() {
            return Some(Operator($matched));
        }
        match $tokenizer.read_byte() {
            $(
                $secondary => {
                    $tokenizer.bump();
                    match_descend!( $tokenizer $tok $cascade )
                },
            )*
            _ => return Some(Operator($matched))
        }
    });
    ( $tokenizer:ident return $matched:expr ) => {
        return Some($matched)
    };
    ( $tokenizer:ident $matched:expr , ) => {
        return Some(Operator($matched))
    }
}

pub struct Tokenizer<'a> {
    // Helper buffer for parsing strings that can't be just memcopied from
    // the original source (escaped characters)
    buffer: Vec<u8>,

    // String slice to parse
    source: &'a str,

    // Byte pointer to the slice above
    byte_ptr: *const u8,

    // Current index
    index: usize,

    // Lenght of the source
    length: usize,

    // Auxiliary index informing when last token begun
    pub token_start: usize,
}

impl<'a> Tokenizer<'a> {
    pub fn new(source: &'a str) -> Self {
        Tokenizer {
            buffer: Vec::with_capacity(30),
            source: source,
            byte_ptr: source.as_ptr(),
            index: 0,
            token_start: 0,
            length: source.len(),
        }
    }

    // Check if we are at the end of the source.
    #[inline]
    fn is_eof(&mut self) -> bool {
        self.index == self.length
    }

    // Read a byte from the source. Note that this does not increment
    // the index. In few cases (all of them related to number parsing)
    // we want to peek at the byte before doing anything. This will,
    // very very rarely, lead to a situation where the same byte is read
    // twice, but since this operation is using a raw pointer, the cost
    // is virtually irrelevant.
    #[inline]
    fn read_byte(&self) -> u8 {
        unsafe { *self.byte_ptr.offset(self.index as isize) }
    }

    // Manually increment the index. Calling `read_byte` and then `bump`
    // is equivalent to consuming a byte on an iterator.
    #[inline]
    fn bump(&mut self) {
        self.index += 1;
    }

    #[inline]
    fn expect_byte(&mut self) -> u8 {
        if self.is_eof() {
            panic!("Unexpected end of source");
        }

        let ch = self.read_byte();
        self.bump();
        ch
    }

    fn read_string(&mut self, first: u8) -> String {
        self.buffer.clear();
        let mut escape = false;

        while !self.is_eof() {
            let ch = self.expect_byte();
            if ch == first && escape == false {
                break;
            }
            match ch {
                b'\\' => {
                    if escape {
                        escape = false;
                        self.buffer.push(ch);
                    } else {
                        escape = true;
                    }
                },
                _ => {
                    self.buffer.push(ch);
                    escape = false;
                },
            }
        }

        unsafe { str::from_utf8_unchecked(self.buffer.as_ref()) }.to_owned()
    }

    fn read_binary(&mut self) -> LiteralValue {
        let mut value = 0;

        while !self.is_eof() {
            let peek = self.read_byte();
            match peek {
                b'0' | b'1' => {
                    value <<= 1;
                    if peek == b'1' {
                        value += 1;
                    }
                    self.bump();
                },
                _ => return LiteralInteger(value),
            }
        }

        return LiteralInteger(value);
    }

    fn read_octal(&mut self) -> LiteralValue {
        let mut value = 0;

        while !self.is_eof() {
            let peek = self.read_byte();

            let digit = match peek {
                b'0' => 0,
                b'1' => 1,
                b'2' => 2,
                b'3' => 3,
                b'4' => 4,
                b'5' => 5,
                b'6' => 6,
                b'7' => 7,
                _    => -1,
            };
            if digit == -1 {
                return LiteralInteger(value);
            } else {
                value <<= 3;
                value += digit;
                self.bump();
            }
        }

        return LiteralInteger(value);
    }

    fn read_hexadec(&mut self) -> LiteralValue {
        let mut value = 0;

        while !self.is_eof() {
            let peek = self.read_byte();
            let digit = match peek {
                b'0'        => 0,
                b'1'        => 1,
                b'2'        => 2,
                b'3'        => 3,
                b'4'        => 4,
                b'5'        => 5,
                b'6'        => 6,
                b'7'        => 7,
                b'8'        => 8,
                b'9'        => 9,
                b'a' | b'A' => 10,
                b'b' | b'B' => 11,
                b'c' | b'C' => 12,
                b'd' | b'D' => 13,
                b'e' | b'E' => 14,
                b'f' | b'F' => 15,
                _           => -1,
            };
            if digit == -1 {
                return LiteralInteger(value);
            } else {
                value <<= 4;
                value += digit;
                self.bump();
            }
        }

        return LiteralInteger(value);
    }

    fn read_number(&mut self, first: u8) -> LiteralValue {
        self.buffer.clear();
        self.buffer.push(first);
        let mut period = false;

        if first == b'.' {
            period = true;
        } else if first == b'0' {
            if !self.is_eof() {
                match self.read_byte() {
                    b'b' => {
                        self.bump();
                        return self.read_binary();
                    },
                    b'o' => {
                        self.bump();
                        return self.read_octal();
                    },
                    b'x' => {
                        self.bump();
                        return self.read_hexadec();
                    },
                    _ => {}
                }
            }
        }

        while !self.is_eof() {
            let ch = self.read_byte();
            match ch {
                b'0'...b'9' => {
                    self.buffer.push(ch);
                    self.bump();
                },
                b'.' => {
                    if !period {
                        period = true;
                        self.buffer.push(ch);
                        self.bump();
                    } else {
                        return LiteralValue::float_from_string(&self.buffer);
                    }
                },
                _ => return LiteralValue::float_from_string(&self.buffer),
            }
        }

        return LiteralValue::float_from_string(&self.buffer);
    }

    fn read_comment(&mut self) {
        while !self.is_eof() {
            if self.read_byte() == b'\n' {
                return;
            }
            self.bump();
        }
    }

    fn read_block_comment(&mut self) {
        loop {
            if self.expect_byte() == b'*' && self.expect_byte() == b'/' {
                return;
            }
        }
    }

    fn read_label(&mut self) -> Token {
        let start = self.index - 1;

        while !self.is_eof() {
            match self.read_byte() {
                b'a'...b'z' |
                b'A'...b'Z' |
                b'0'...b'9' |
                b'_' | b'$' => self.bump(),
                _ => break
            }
        }

        let slice = &self.source[start..self.index];

        match slice {
            "new"        => Operator(New),
            "typeof"     => Operator(Typeof),
            "delete"     => Operator(Delete),
            "void"       => Operator(Void),
            "in"         => Operator(In),
            "instanceof" => Operator(Instanceof),
            "var"        => Declaration(Var),
            "let"        => Declaration(Let),
            "const"      => Declaration(Const),
            "break"      => Break,
            "do"         => Do,
            "case"       => Case,
            "else"       => Else,
            "catch"      => Catch,
            "export"     => Export,
            "class"      => Class,
            "extends"    => Extends,
            "return"     => Return,
            "while"      => While,
            "finally"    => Finally,
            "super"      => Super,
            "with"       => With,
            "continue"   => Continue,
            "for"        => For,
            "switch"     => Switch,
            "yield"      => Yield,
            "debugger"   => Debugger,
            "function"   => Function,
            "this"       => This,
            "default"    => Default,
            "if"         => If,
            "throw"      => Throw,
            "import"     => Import,
            "try"        => Try,
            "await"      => Await,
            "static"     => Static,
            "true"       => Literal(LiteralTrue),
            "false"      => Literal(LiteralFalse),
            "undefined"  => Literal(LiteralUndefined),
            "null"       => Literal(LiteralNull),
            "enum"       => Reserved(Enum),
            "implements" => Reserved(Implements),
            "package"    => Reserved(Package),
            "protected"  => Reserved(Protected),
            "interface"  => Reserved(Interface),
            "private"    => Reserved(Private),
            "public"     => Reserved(Public),
            _            => Identifier(slice.to_owned()),
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        while !self.is_eof() {
            self.token_start = self.index;

            let ch = self.expect_byte();

            match_operators! { self ch
                b'=' => Assign {
                    b'=' => Equality {
                        b'=' => StrictEquality,
                    }
                    b'>' => FatArrow,
                }
                b'!' => LogicalNot {
                    b'=' => Inequality {
                        b'=' => StrictInequality,
                    }
                }
                b'<' => Lesser {
                    b'<' => BitShiftLeft {
                        b'=' => BSLAssign,
                    }
                    b'=' => LesserEquals,
                }
                b'>' => Greater {
                    b'>' => BitShiftRight {
                        b'>' => UBitShiftRight {
                            b'=' => UBSRAssign,
                        }
                        b'=' => BSRAssign,
                    }
                    b'=' => GreaterEquals,
                }
                b'?' => Conditional,
                b'~' => BitwiseNot,
                b'^' => BitwiseXor {
                    b'=' => BitXorAssign,
                }
                b'&' => BitwiseAnd {
                    b'&' => LogicalAnd,
                    b'=' => BitAndAssign,
                }
                b'|' => BitwiseOr {
                    b'|' => LogicalOr,
                    b'=' => BitOrAssign,
                }
                b'+' => Addition {
                    b'+' => Increment,
                    b'=' => AddAssign,
                }
                b'-' => Substraction {
                    b'-' => Decrement,
                    b'=' => SubstractAssign,
                }
                b'*' => Multiplication {
                    b'*' => Exponent {
                        b'=' => ExponentAssign,
                    }
                    b'=' => MultiplyAssign,
                }
                b'%' => Remainder {
                    b'=' => RemainderAssign,
                }
            }

            return Some(match ch {
                b'\n' => LineTermination,
                b';' => Semicolon,
                b',' => Comma,
                b':' => Colon,
                b'(' => ParenOn,
                b')' => ParenOff,
                b'[' => BracketOn,
                b']' => BracketOff,
                b'{' => BraceOn,
                b'}' => BraceOff,
                b'"' | b'\'' => {
                    Literal(LiteralString( self.read_string(ch) ))
                },
                b'/' => {
                    if self.is_eof() {
                        Operator(Division)
                    } else {
                        match self.read_byte() {
                            b'/' => {
                                self.bump();
                                self.read_comment();
                                continue;
                            },
                            b'*' => {
                                self.bump();
                                self.read_block_comment();
                                continue;
                            },
                            b'=' => {
                                self.bump();
                                Operator(DivideAssign)
                            },
                            _ => Operator(Division)
                        }
                    }
                },
                b'.' => {
                    if self.is_eof() {
                        Operator(Accessor)
                    } else {
                        match self.read_byte() {
                            b'0'...b'9' => {
                                Literal(self.read_number(b'.'))
                            },
                            b'.' => {
                                self.bump();
                                match self.expect_byte() {
                                    b'.' => Operator(Spread),
                                    ch   => {
                                        panic!("Invalid character `{:?}`", ch);
                                    }
                                }
                            },
                            _ => Operator(Accessor)
                        }
                    }
                },
                b'0'...b'9' => Literal(self.read_number(ch)),
                b'a'...b'z' |
                b'A'...b'Z' |
                b'$' | b'_' => self.read_label(),

                b' ' | b'\t' => continue,

                _ => {
                    panic!("Invalid character `{:?}`", ch);
                }
            });
        }
        return None;
    }
}
<|endoftext|>


<commit_before>use toml::Parser;
use regex::Regex;

pub fn read_version(file: String) -> Option<String> {
   let file_map = Parser::new(&file).parse().unwrap();
   println!("{:?}", file_map);
   let package = file_map.get("package").unwrap();
   let version = package.as_table()
       .unwrap()
       .get("version");
   match version {
       Some(v) => Some(v.as_str().unwrap().into()),
       None => None
   }
}

pub fn file_with_new_version(file: String, new_version: &str) -> String {
    let re = Regex::new(r#"version\s=\s"\d+\.\d+\.\d+""#).unwrap();
    let new_version = format!("version = \"{}\"", new_version);
    re.replace(&file, &new_version[..])
}

#[cfg(test)]
mod tests {
    extern crate toml;
    extern crate regex;
    use super::*;

    fn example_file() -> String {
        "[package]
    name = \"semantic-rs\"
    version = \"0.1.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    fn example_file_without_version() -> String {
        "[package]
    name = \"semantic-rs\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    #[test]
    fn read_version_number() {
        let version_str = read_version(example_file());
        assert_eq!(version_str, Some("0.1.0".into()));
    }

    #[test]
    fn read_file_without_version_number() {
        let version_str = read_version(example_file_without_version());
        assert_eq!(version_str, None);
    }

    #[test]
    fn write_new_version_number() {
        let new_toml_file = file_with_new_version(example_file(), "0.2.0".into());
        let expected_file =
            "[package]
    name = \"semantic-rs\"
    version = \"0.2.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string();
        assert_eq!(new_toml_file, expected_file);
    }
}
<commit_msg>feat(toml_file): add read and write functions for Cargo.toml<commit_after>use toml::Parser;
use regex::Regex;
use std::io::prelude::*;
use std::fs::File;
use std::io::Error;

pub enum TomlError {
    parseError(&'static str),
    ioError(Error)
}

pub fn read_version(file: String) -> Option<String> {
   let file_map = Parser::new(&file).parse().unwrap();
   println!("{:?}", file_map);
   let package = file_map.get("package").unwrap();
   let version = package.as_table()
       .unwrap()
       .get("version");
   match version {
       Some(v) => Some(v.as_str().unwrap().into()),
       None => None
   }
}

pub fn file_with_new_version(file: String, new_version: &str) -> String {
    let re = Regex::new(r#"version\s=\s"\d+\.\d+\.\d+""#).unwrap();
    let new_version = format!("version = \"{}\"", new_version);
    re.replace(&file, &new_version[..])
}

pub fn read_from_file() -> Result<String, TomlError> {
    let cargo_file = match read_cargo_toml() {
        Ok(buffer) => buffer,
        Err(err) => return Err(TomlError::ioError(err))
    };

    match read_version(cargo_file) {
        Some(version) => Ok(version),
        None => Err(TomlError::parseError("No version field found"))
    }
}

pub fn write_new_version(new_version: String) -> Result<(), Error> {
    let cargo_toml = match read_cargo_toml() {
        Ok(file) => file,
        Err(err) => return Err(err)
    };
    let new_cargo_toml = file_with_new_version(cargo_toml, &new_version);

    let mut handle = match File::open("Cargo.toml") {
        Ok(handle) => handle,
        Err(err) => {
            return Err(err)
        }
    };

    match handle.write_all(new_cargo_toml.as_bytes()) {
        Ok(_) => Ok(()),
        Err(err) => Err(err)
    }
}

fn read_cargo_toml() -> Result<String, Error> {
    let mut handle = match File::open("Cargo.toml") {
        Ok(handle) => handle,
        Err(err) => {
            return Err(err)
        }
    };

    let mut buffer = String::new();
    match handle.read_to_string(&mut buffer) {
        Ok(_) => Ok(buffer),
        Err(err) => Err(err)
    }
}

#[cfg(test)]
mod tests {
    extern crate toml;
    extern crate regex;
    use super::*;

    fn example_file() -> String {
        "[package]
    name = \"semantic-rs\"
    version = \"0.1.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    fn example_file_without_version() -> String {
        "[package]
    name = \"semantic-rs\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string()
    }

    #[test]
    fn read_version_number() {
        let version_str = read_version(example_file());
        assert_eq!(version_str, Some("0.1.0".into()));
    }

    #[test]
    fn read_file_without_version_number() {
        let version_str = read_version(example_file_without_version());
        assert_eq!(version_str, None);
    }

    #[test]
    fn write_new_version_number() {
        let new_toml_file = file_with_new_version(example_file(), "0.2.0".into());
        let expected_file =
            "[package]
    name = \"semantic-rs\"
    version = \"0.2.0\"
    authors = [\"Jan Schulte <hello@unexpected-co.de>\"]
    [dependencies]
    term = \"0.2\"
    toml = \"0.1\"".to_string();
        assert_eq!(new_toml_file, expected_file);
    }
}
<|endoftext|>