<commit_before>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

enum FilterMode {
    Bytes(usize),
    Lines(usize),
}

struct Settings {
    mode: FilterMode,
    sleep_msec: u32,
    beginning: bool,
    follow: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: FilterMode::Lines(10),
            sleep_msec: 1000,
            beginning: false,
            follow: false,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { settings.mode = FilterMode::Lines(n); args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    settings.follow = given_options.opt_present("f");
    if settings.follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { settings.sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                settings.beginning = true;
                slice = &slice[1..];
            }
            match parse_size(slice) {
                Some(m) => settings.mode = FilterMode::Lines(m),
                None => {
                    show_error!("invalid number of lines ({})", slice);
                    return 1;
                }
            }
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    settings.beginning = true;
                    slice = &slice[1..];
                }
                match parse_size(slice) {
                    Some(m) => settings.mode = FilterMode::Bytes(m),
                    None => {
                        show_error!("invalid number of bytes ({})", slice);
                        return 1;
                    }
                }
            }
            None => { }
        }
    };

    let files = given_options.free;

    if files.is_empty() {
        let mut buffer = BufReader::new(stdin());
        tail(&mut buffer, &settings);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in files.iter() {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            let mut buffer = BufReader::new(reader);
            tail(&mut buffer, &settings);
        }
    }

    0
}

fn parse_size(mut size_slice: &str) -> Option<usize> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000usize
        } else {
            1024usize
        };
    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' => 1usize,
                'M' => 2usize,
                'G' => 3usize,
                'T' => 4usize,
                'P' => 5usize,
                'E' => 6usize,
                'Z' => 7usize,
                'Y' => 8usize,
                'b' => {
                    base = 512usize;
                    1usize
                }
                _ => {
                    has_suffix = false;
                    0usize
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0usize
        };

    let mut multiplier = 1usize;
    for _ in 0usize .. exponent {
        multiplier *= base;
    }
    if base == 1000usize && exponent == 0usize {
        // sole B is not a valid suffix
        None
    } else {
        let value: Option<usize> = size_slice.parse().ok();
        match value {
            Some(v) => Some(multiplier * v),
            _ => None
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<usize> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

macro_rules! tail_impl (
    ($kind:ty, $kindfn:ident, $kindprint:ident, $reader:ident, $count:expr, $beginning:expr) => ({
        // read through each line and store them in a ringbuffer that always contains
        // count lines/chars. When reaching the end of file, output the data in the
        // ringbuf.
        let mut count = $count;
        let mut ringbuf: VecDeque<$kind> = VecDeque::new();
        let data = $reader.$kindfn().skip(
            if $beginning {
                let temp = count;
                count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            }
        );
        for io_datum in data {
            match io_datum {
                Ok(datum) => {
                    if count <= ringbuf.len() {
                        ringbuf.pop_front();
                    }
                    ringbuf.push_back(datum);
                }
                Err(err) => panic!(err)
            }
        }
        let mut stdout = stdout();
        for datum in ringbuf.iter() {
            $kindprint(&mut stdout, datum);
        }
    })
);

fn tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
    match settings.mode {
        FilterMode::Lines(count) => {
            tail_impl!(String, lines, print_string, reader, count, settings.beginning)
        },
        FilterMode::Bytes(count) => {
            tail_impl!(u8, bytes, print_byte, reader, count, settings.beginning)
        }
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while settings.follow {
        sleep(Duration::new(0, settings.sleep_msec*1000));
        for io_line in reader.lines() {
            match io_line {
                Ok(line) => print!("{}", line),
                Err(err) => panic!(err)
            }
        }
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &String) {
    print!("{}", s);
}

fn version() {
    println!("{} {}", NAME, VERSION);
}
<commit_msg>tail: use read_line/read instead of lines/bytes<commit_after>#![crate_name = "uu_tail"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Morten Olsen Lysgaard <morten@lysgaard.no>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */

extern crate getopts;

#[macro_use]
extern crate uucore;

use std::collections::VecDeque;
use std::fs::File;
use std::io::{BufRead, BufReader, Read, stdin, stdout, Write};
use std::path::Path;
use std::str::from_utf8;
use std::thread::sleep;
use std::time::Duration;

static NAME: &'static str = "tail";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

enum FilterMode {
    Bytes(usize),
    Lines(usize),
}

struct Settings {
    mode: FilterMode,
    sleep_msec: u32,
    beginning: bool,
    follow: bool,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: FilterMode::Lines(10),
            sleep_msec: 1000,
            beginning: false,
            follow: false,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();

    // handle obsolete -number syntax
    let options = match obsolete(&args[1..]) {
        (args, Some(n)) => { settings.mode = FilterMode::Lines(n); args },
        (args, None) => args
    };

    let args = options;

    let mut opts = getopts::Options::new();

    opts.optopt("c", "bytes", "Number of bytes to print", "k");
    opts.optopt("n", "lines", "Number of lines to print", "k");
    opts.optflag("f", "follow", "Print the file as it grows");
    opts.optopt("s", "sleep-interval", "Number or seconds to sleep between polling the file when running with -f", "n");
    opts.optflag("h", "help", "help");
    opts.optflag("V", "version", "version");

    let given_options = match opts.parse(&args) {
        Ok (m) => { m }
        Err(_) => {
            println!("{}", opts.usage(""));
            return 1;
        }
    };

    if given_options.opt_present("h") {
        println!("{}", opts.usage(""));
        return 0;
    }
    if given_options.opt_present("V") { version(); return 0 }

    settings.follow = given_options.opt_present("f");
    if settings.follow {
        match given_options.opt_str("s") {
            Some(n) => {
                let parsed: Option<u32> = n.parse().ok();
                match parsed {
                    Some(m) => { settings.sleep_msec = m * 1000 }
                    None => {}
                }
            }
            None => {}
        };
    }

    match given_options.opt_str("n") {
        Some(n) => {
            let mut slice: &str = n.as_ref();
            if slice.chars().next().unwrap_or('_') == '+' {
                settings.beginning = true;
                slice = &slice[1..];
            }
            match parse_size(slice) {
                Some(m) => settings.mode = FilterMode::Lines(m),
                None => {
                    show_error!("invalid number of lines ({})", slice);
                    return 1;
                }
            }
        }
        None => match given_options.opt_str("c") {
            Some(n) => {
                let mut slice: &str = n.as_ref();
                if slice.chars().next().unwrap_or('_') == '+' {
                    settings.beginning = true;
                    slice = &slice[1..];
                }
                match parse_size(slice) {
                    Some(m) => settings.mode = FilterMode::Bytes(m),
                    None => {
                        show_error!("invalid number of bytes ({})", slice);
                        return 1;
                    }
                }
            }
            None => { }
        }
    };

    let files = given_options.free;

    if files.is_empty() {
        let mut buffer = BufReader::new(stdin());
        tail(&mut buffer, &settings);
    } else {
        let mut multiple = false;
        let mut firstime = true;

        if files.len() > 1 {
            multiple = true;
        }

        for file in files.iter() {
            if multiple {
                if !firstime { println!(""); }
                println!("==> {} <==", file);
            }
            firstime = false;

            let path = Path::new(file);
            let reader = File::open(&path).unwrap();
            let mut buffer = BufReader::new(reader);
            tail(&mut buffer, &settings);
        }
    }

    0
}

fn parse_size(mut size_slice: &str) -> Option<usize> {
    let mut base =
        if size_slice.chars().last().unwrap_or('_') == 'B' {
            size_slice = &size_slice[..size_slice.len() - 1];
            1000usize
        } else {
            1024usize
        };
    let exponent =
        if size_slice.len() > 0 {
            let mut has_suffix = true;
            let exp = match size_slice.chars().last().unwrap_or('_') {
                'K' => 1usize,
                'M' => 2usize,
                'G' => 3usize,
                'T' => 4usize,
                'P' => 5usize,
                'E' => 6usize,
                'Z' => 7usize,
                'Y' => 8usize,
                'b' => {
                    base = 512usize;
                    1usize
                }
                _ => {
                    has_suffix = false;
                    0usize
                }
            };
            if has_suffix {
                size_slice = &size_slice[..size_slice.len() - 1];
            }
            exp
        } else {
            0usize
        };

    let mut multiplier = 1usize;
    for _ in 0usize .. exponent {
        multiplier *= base;
    }
    if base == 1000usize && exponent == 0usize {
        // sole B is not a valid suffix
        None
    } else {
        let value: Option<usize> = size_slice.parse().ok();
        match value {
            Some(v) => Some(multiplier * v),
            _ => None
        }
    }
}

// It searches for an option in the form of -123123
//
// In case is found, the options vector will get rid of that object so that
// getopts works correctly.
fn obsolete(options: &[String]) -> (Vec<String>, Option<usize>) {
    let mut options: Vec<String> = options.to_vec();
    let mut a = 0;
    let b = options.len();

    while a < b {
        let current = options[a].clone();
        let current = current.as_bytes();

        if current.len() > 1 && current[0] == '-' as u8 {
            let len = current.len();
            for pos in 1 .. len {
                // Ensure that the argument is only made out of digits
                if !(current[pos] as char).is_numeric() { break; }

                // If this is the last number
                if pos == len - 1 {
                    options.remove(a);
                    let number: Option<usize> = from_utf8(&current[1..len]).unwrap().parse().ok();
                    return (options, Some(number.unwrap()));
                }
            }
        }

        a += 1;
    };

    (options, None)
}

fn tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) {
    // Read through each line/char and store them in a ringbuffer that always
    // contains count lines/chars. When reaching the end of file, output the
    // data in the ringbuf.
    match settings.mode {
        FilterMode::Lines(mut count) => {
            let mut ringbuf: VecDeque<String> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = String::new();
                match reader.read_line(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in ringbuf.iter() {
                print_string(&mut stdout, datum);
            }
        },
        FilterMode::Bytes(mut count) => {
            let mut ringbuf: VecDeque<u8> = VecDeque::new();
            let mut skip = if settings.beginning {
                let temp = count;
                count = ::std::usize::MAX;
                temp - 1
            } else {
                0
            };
            loop {
                let mut datum = [0; 1];
                match reader.read(&mut datum) {
                    Ok(0) => break,
                    Ok(_) => {
                        if skip > 0 {
                            skip -= 1;
                        } else {
                            if count <= ringbuf.len() {
                                ringbuf.pop_front();
                            }
                            ringbuf.push_back(datum[0]);
                        }
                    },
                    Err(err) => panic!(err)
                }
            }
            let mut stdout = stdout();
            for datum in ringbuf.iter() {
                print_byte(&mut stdout, datum);
            }
        }
    }

    // if we follow the file, sleep a bit and print the rest if the file has grown.
    while settings.follow {
        sleep(Duration::new(0, settings.sleep_msec*1000));
        loop {
            let mut datum = String::new();
            match reader.read_line(&mut datum) {
                Ok(0) => break,
                Ok(_) => print!("{}", datum),
                Err(err) => panic!(err)
            }
        }
    }
}

#[inline]
fn print_byte<T: Write>(stdout: &mut T, ch: &u8) {
    if let Err(err) = stdout.write(&[*ch]) {
        crash!(1, "{}", err);
    }
}

#[inline]
fn print_string<T: Write>(_: &mut T, s: &String) {
    print!("{}", s);
}

fn version() {
    println!("{} {}", NAME, VERSION);
}
<|endoftext|>


<commit_before>use std::process::{Command, Stdio};

use clap::ArgMatches;
use rusoto_core::{ChainProvider, ProvideAwsCredentials};

use aws::credentials_provider;
use error::{KawsError, KawsResult};

pub struct Terraform<'a> {
    aws_credentials_provider: ChainProvider,
    cluster: &'a str,
    output: Option<&'a str>,
    terraform_args: Option<Vec<&'a str>>,
}

impl<'a> Terraform<'a> {
    pub fn new(matches: &'a ArgMatches) -> Terraform<'a> {
        Terraform {
            aws_credentials_provider: credentials_provider(
                matches.value_of("aws-credentials-path"),
                matches.value_of("aws-credentials-profile"),
            ),
            cluster: matches.value_of("cluster").expect("clap should have required cluster"),
            output: matches.value_of("output"),
            terraform_args: matches.values_of("terraform-args").map(|values| values.collect()),
        }
    }

    pub fn apply(&mut self) -> KawsResult {
        self.get()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "apply",
            "-backup=-",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        command.status()?;

        Ok(None)
    }

    pub fn destroy(&mut self) -> KawsResult {
        self.get()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "destroy",
            "-backup=-",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        let exit_status = command.status()?;

        if exit_status.success() {
            Ok(Some(format!(
                "Destroyed cluster \"{}\"! You should remove clusters/{} from Git.",
                self.cluster,
                self.cluster,
            )))
        } else {
            Err(KawsError::new(format!("Failed to destroy cluster!")))
        }
    }

    pub fn output(&mut self) -> KawsResult {
        self.get()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "output",
            "-module=kaws",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
        ]);

        if let Some(output) = self.output {
            command.arg(output);
        }

        command.status()?;

        Ok(None)
    }

    pub fn plan(&mut self) -> KawsResult {
        self.get()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "plan",
            "-module-depth=-1",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        command.status()?;

        Ok(None)
    }

    pub fn refresh(&mut self) -> KawsResult {
        self.get()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "refresh",
            "-backup=-",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        command.status()?;

        Ok(None)
    }

    fn get(&self) -> KawsResult {
        let exit_status = Command::new("terraform").args(&[
            "get",
            "terraform",
        ]).stdout(Stdio::null()).status()?;

        if exit_status.success() {
            Ok(None)
        } else {
            Err(KawsError::new(format!("Failed to download Terraform module!")))
        }
    }
}
<commit_msg>Use `terraform init` instead of `terraform get`.<commit_after>use std::process::{Command, Stdio};

use clap::ArgMatches;
use rusoto_core::{ChainProvider, ProvideAwsCredentials};

use aws::credentials_provider;
use error::{KawsError, KawsResult};

pub struct Terraform<'a> {
    aws_credentials_provider: ChainProvider,
    cluster: &'a str,
    output: Option<&'a str>,
    terraform_args: Option<Vec<&'a str>>,
}

impl<'a> Terraform<'a> {
    pub fn new(matches: &'a ArgMatches) -> Terraform<'a> {
        Terraform {
            aws_credentials_provider: credentials_provider(
                matches.value_of("aws-credentials-path"),
                matches.value_of("aws-credentials-profile"),
            ),
            cluster: matches.value_of("cluster").expect("clap should have required cluster"),
            output: matches.value_of("output"),
            terraform_args: matches.values_of("terraform-args").map(|values| values.collect()),
        }
    }

    pub fn apply(&mut self) -> KawsResult {
        self.init()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "apply",
            "-backup=-",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        command.status()?;

        Ok(None)
    }

    pub fn destroy(&mut self) -> KawsResult {
        self.init()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "destroy",
            "-backup=-",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        let exit_status = command.status()?;

        if exit_status.success() {
            Ok(Some(format!(
                "Destroyed cluster \"{}\"! You should remove clusters/{} from Git.",
                self.cluster,
                self.cluster,
            )))
        } else {
            Err(KawsError::new(format!("Failed to destroy cluster!")))
        }
    }

    pub fn output(&mut self) -> KawsResult {
        self.init()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "output",
            "-module=kaws",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
        ]);

        if let Some(output) = self.output {
            command.arg(output);
        }

        command.status()?;

        Ok(None)
    }

    pub fn plan(&mut self) -> KawsResult {
        self.init()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "plan",
            "-module-depth=-1",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        command.status()?;

        Ok(None)
    }

    pub fn refresh(&mut self) -> KawsResult {
        self.init()?;

        let mut command = Command::new("terraform");

        command.args(&[
            "refresh",
            "-backup=-",
            &format!("-state=clusters/{}/terraform.tfstate", self.cluster),
            &format!("-var-file=clusters/{}/terraform.tfvars", self.cluster),
        ]);

        if self.terraform_args.is_some() {
            command.args(self.terraform_args.as_ref().unwrap());
        }

        command.arg("terraform").env(
            "AWS_ACCESS_KEY_ID",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_access_key_id(),
        ).env(
            "AWS_SECRET_ACCESS_KEY",
            self.aws_credentials_provider.credentials().expect(
                "Failed to get AWS credentials"
            ).aws_secret_access_key(),
        );

        command.status()?;

        Ok(None)
    }

    fn init(&self) -> KawsResult {
        let exit_status = Command::new("terraform").args(&[
            "init",
            "terraform",
        ]).stdout(Stdio::null()).status()?;

        if exit_status.success() {
            Ok(None)
        } else {
            Err(KawsError::new("Failed to initialize Terraform!".to_string()))
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

use common::err::*;
use common::TypeClass;

use url::{Url, UrlParser, SchemeType, whatwg_scheme_type_mapper};

/// for HDFS URL scheme (i.e., hdfs://)
fn hdfs_scheme_handler(scheme: &str) -> SchemeType {
  match scheme {
    "file" => SchemeType::FileLike,
    "hdfs" => SchemeType::Relative(50070),
    _ => whatwg_scheme_type_mapper(scheme)
  }
}

/// storage handler mapper for StorageManager
pub fn default_space_handlers<'a>(url: Url) -> Box<TableSpace<'a>> {
	match url.scheme.as_str() {
		"file" => Box::new( LocalFS::new(&url.serialize()) ) ,
		_ => panic!("No supported: {}", url.serialize())
	}
}

pub trait TableSpace<'a> {
	fn url(&'a self) -> &'a str;

	fn format(&self) -> Void;

	fn create_table(&self) -> Void ;

	fn drop_table(&self, purge: bool) -> Void;

	fn rename_table(&self) -> Void;

	fn truncate_table(&self) -> Void;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;

	// fn new_scanner(&self, url: &str) -> TResult<Box<Executor>>;

	// fn new_appender(&self, url: &str) -> TResult<Box<Executor>>;
}

pub struct LocalFS {
	pub url: String
}

impl LocalFS {
	pub fn new(url: &str) -> LocalFS {
		LocalFS {url: url.to_owned()}
	}
}

impl<'a> TableSpace<'a> for LocalFS {
	fn url(&'a self) -> &'a str {
		self.url.as_str()
	}

	fn format(&self) -> Void {
		void_ok()
	}

	fn create_table(&self) -> Void {
		void_ok()
	}

	fn drop_table(&self, purge: bool) -> Void {
		void_ok()
	}

	fn rename_table(&self) -> Void {
		void_ok()
	}

	fn truncate_table(&self) -> Void {
		void_ok()
	}

	fn available_capacity(&self) -> u64 {
		0
	}

	fn total_capacity(&self) -> u64 {
		0
	}
}

pub trait DataFormat {
	fn name(&self) -> String;

	fn splittable() -> bool;

	fn compression_support() -> bool;

	fn random_accessable() -> bool;

	fn indexable() -> bool;

	fn supported_types() -> Vec<TypeClass>;
}

pub trait Storage {
	fn uri (&self) -> String;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;
}

pub trait BlockStorage: Storage {
	fn split(&self, url: String, size : u64);

	fn list_files(url : String);
}

/// Manages TableSpaces
pub struct StorageManager<'a> {
	space_handler: fn(uri: Url) -> Box<TableSpace<'a>>,
	space_map: UnsafeCell<HashMap<String, Box<TableSpace<'a>>>>,
	lock: Mutex<()>,
	url_parser: UrlParser<'a>,
	marker: PhantomData<&'a ()>
}

impl<'a> StorageManager<'a> {

	pub fn new() -> StorageManager<'a> {
		StorageManager::new_with_handler(default_space_handlers)
	}

	pub fn new_with_handler(space_handler: fn(uri: Url) -> Box<TableSpace<'a>>) -> StorageManager<'a> {

		let mut url_parser = UrlParser::new();
    url_parser.scheme_type_mapper(hdfs_scheme_handler);

		StorageManager {
			space_handler: space_handler,
			space_map: UnsafeCell::new(HashMap::new()),
			lock: Mutex::new(()),
			url_parser: url_parser,
			marker: PhantomData
		}
	}

	#[allow(unused_must_use)]
	pub fn get_space(&'a self, url: &str) -> TResult<&'a TableSpace> {
		self.lock.lock();

		unsafe {
			let key = url.to_owned();
			match (*self.space_map.get()).entry(key) {
				Entry::Vacant(entry) => {
					let res = self.url_parser.parse(url);
					if res.is_err() {
						panic!("Invalid space url: {}", url)
					}

					let parsed_url = res.unwrap();
					let handler:Box<TableSpace<'a>> = (self.space_handler)(parsed_url);
					Ok(&(**entry.insert(handler)))
				},
				Entry::Occupied(entry) => {
					let x: &Box<TableSpace<'a>> = entry.get();
					Err(Error::Unknown)
				}
			}
		}
	}
}<commit_msg>Change the return type of Space handler to TResult<Box<TableSpace>>.<commit_after>use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

use common::err::*;
use common::TypeClass;

use url::{Url, UrlParser, SchemeType, whatwg_scheme_type_mapper};

/// for HDFS URL scheme (i.e., hdfs://)
fn hdfs_scheme_handler(scheme: &str) -> SchemeType {
  match scheme {
    "file" => SchemeType::FileLike,
    "hdfs" => SchemeType::Relative(50070),
    _ => whatwg_scheme_type_mapper(scheme)
  }
}

/// storage handler mapper for StorageManager
pub fn default_space_handlers<'a>(url: Url) -> TResult<Box<TableSpace<'a>>> {
	match url.scheme.as_str() {
		"file" => Ok(Box::new(LocalFS::new(&url.serialize()))) ,
		_ => panic!("No supported: {}", url.serialize())
	}
}

pub trait TableSpace<'a> {
	fn url(&'a self) -> &'a str;

	fn format(&self) -> Void;

	fn create_table(&self) -> Void ;

	fn drop_table(&self, purge: bool) -> Void;

	fn rename_table(&self) -> Void;

	fn truncate_table(&self) -> Void;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;

	// fn new_scanner(&self, url: &str) -> TResult<Box<Executor>>;

	// fn new_appender(&self, url: &str) -> TResult<Box<Executor>>;
}

pub struct LocalFS {
	pub url: String
}

impl LocalFS {
	pub fn new(url: &str) -> LocalFS {
		LocalFS {url: url.to_owned()}
	}
}

impl<'a> TableSpace<'a> for LocalFS {
	fn url(&'a self) -> &'a str {
		self.url.as_str()
	}

	fn format(&self) -> Void {
		void_ok()
	}

	fn create_table(&self) -> Void {
		void_ok()
	}

	fn drop_table(&self, purge: bool) -> Void {
		void_ok()
	}

	fn rename_table(&self) -> Void {
		void_ok()
	}

	fn truncate_table(&self) -> Void {
		void_ok()
	}

	fn available_capacity(&self) -> u64 {
		0
	}

	fn total_capacity(&self) -> u64 {
		0
	}
}

pub trait DataFormat {
	fn name(&self) -> String;

	fn splittable() -> bool;

	fn compression_support() -> bool;

	fn random_accessable() -> bool;

	fn indexable() -> bool;

	fn supported_types() -> Vec<TypeClass>;
}

pub trait Storage {
	fn uri (&self) -> String;

	fn available_capacity(&self) -> u64;

	fn total_capacity(&self) -> u64;
}

pub trait BlockStorage: Storage {
	fn split(&self, url: String, size : u64);

	fn list_files(url : String);
}

/// Manages TableSpaces
pub struct StorageManager<'a> {
	space_handler: fn(uri: Url) -> TResult<Box<TableSpace<'a>>>,
	space_map: UnsafeCell<HashMap<String, Box<TableSpace<'a>>>>,
	lock: Mutex<()>,
	url_parser: UrlParser<'a>,
	marker: PhantomData<&'a ()>
}

impl<'a> StorageManager<'a> {

	pub fn new() -> StorageManager<'a> {
		StorageManager::new_with_handler(default_space_handlers)
	}

	pub fn new_with_handler(space_handler: fn(uri: Url) -> TResult<Box<TableSpace<'a>>>) -> StorageManager<'a> {

		let mut url_parser = UrlParser::new();
    url_parser.scheme_type_mapper(hdfs_scheme_handler);

		StorageManager {
			space_handler: space_handler,
			space_map: UnsafeCell::new(HashMap::new()),
			lock: Mutex::new(()),
			url_parser: url_parser,
			marker: PhantomData
		}
	}

	#[allow(unused_must_use)]
	pub fn get_space(&'a self, url: &str) -> TResult<&'a TableSpace> {
		self.lock.lock();

		unsafe {
			let key = url.to_owned();
			match (*self.space_map.get()).entry(key) {
				Entry::Vacant(entry) => {
					let res = self.url_parser.parse(url);
					if res.is_err() {
						panic!("Invalid space url: {}", url)
					}

					let parsed_url = res.unwrap();
					let handler = try!((self.space_handler)(parsed_url));
					Ok(&(**entry.insert(handler)))
				},
				Entry::Occupied(entry) => {
					let x: &Box<TableSpace<'a>> = entry.get();
					Err(Error::Unknown)
				}
			}
		}
	}
}<|endoftext|>


<commit_before>extern crate time;
extern crate serde_json;

use std::io::prelude::*;

use std::time::{SystemTime, UNIX_EPOCH};

use std::fs;
use std::path::Path;
use std::error::Error;
use std::fs::OpenOptions;

use std::process::Command;

#[derive(Serialize, Deserialize, Debug)]
pub enum Event {
    Pause { time: u64 },
    MetaPause { time: u64, reason: String },
    Proceed { time: u64 },
    Meta { time: u64, text: String },
    Commit { time: u64, hash: u64 },
    Branch { time: u64, name: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Session {
    start: u64,
    end: u64,
    events: Vec<Event>,
}

impl Session {
    fn new() -> Session {
        let seconds = get_seconds();
        Session {
            start: seconds,
            end: seconds - 1,
            events: Vec::<Event>::new(),
        }
    }

    fn in_progress(&self) -> bool {
        self.start == (self.end + 1)
    }

    fn finalize(&mut self) {
        self.end = get_seconds();
    }

    fn status(&self) -> String {
        format!("{:?}", self)
    }

    fn is_paused(&self) -> bool {
        match self.events.len() {
            0 => false,
            n => {
                match self.events[n - 1] {
                    Event::Pause { .. } |
                    Event::MetaPause { .. } => true,
                    _ => false,
                }
            }
        }
    }

    fn push_event(&mut self, event: Event) -> bool {
        /* Cannot push if session is already finalized! */
        if !self.in_progress() {
            println!("Already finalized, cannot push event");
            return false;
        }
        /* TODO: add logic */
        match event {
            Event::Pause { .. } |
            Event::MetaPause { .. } => {
                if !self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Already paused!");
                    false
                }
            }
            Event::Proceed { .. } => {
                if self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Currently not paused!");
                    false
                }
            }
            Event::Meta { .. } => {
                if self.is_paused() {
                    /* morph last pause into a pause_meta */
                } else {
                    self.events.push(event);
                };
                true
            }
            Event::Commit { .. } |
            Event::Branch { .. } => {
                if self.is_paused() {
                    let now = get_seconds();
                    self.push_event(Event::Proceed { time: now });
                }
                self.events.push(event);
                true
            }
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Timesheet {
    start: u64,
    end: u64,
    user: String,
    sessions: Vec<Session>,
}

impl Timesheet {
    /** Initializes the .trk/sessions.trk file which holds
     * the serialized timesheet
     * Returns Some(newTimesheet) if operation succeeded */
    pub fn init(author_name: Option<&str>) -> Option<Timesheet> {
        /* Check if file already exists (no init permitted) */
        if Timesheet::is_init() {
            None
        } else {
            /* File does not exist, initialize */
            let git_author_name = &git_author().unwrap_or("".to_string());
            let author_name = match author_name {
                Some(name) => name,
                None => git_author_name,
            };
            let sheet = Timesheet {
                start: get_seconds(),
                end: get_seconds() - 1,
                user: author_name.to_string(),
                sessions: Vec::<Session>::new(),
            };
            if sheet.save_to_file() {
                Some(sheet)
            } else {
                None
            }
        }
    }

    fn is_init() -> bool {
        if Path::new("./.trk/sessions.trk").exists() {
            match Timesheet::load_from_file() {
                Some(..) => true,
                /* else, loading failed */
                None => false,
            }
        } else {
            /* File doesn't even exist */
            false
        }
    }

    fn get_last_session(&mut self) -> Option<&mut Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&mut self.sessions[n - 1]),
        }
    }

    pub fn new_session(&mut self) -> bool {

        let nsessions = self.sessions.len();
        let pushed = match nsessions {
            0 => true,
            _ => {
                if !self.sessions[nsessions - 1].in_progress() {
                    true
                } else {
                    println!("Last session is still running!");
                    false
                }
            }
        };
        if pushed {
            self.sessions.push(Session::new());
            self.save_to_file();
        }
        pushed
    }

    pub fn end_session(&mut self) {
        match self.get_last_session() {
            Some(session) => session.finalize(),
            None => println!("No session to finalize!"),
        }
        self.save_to_file();
    }

    pub fn pause(&mut self, metatext: Option<String>) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                match metatext {
                    Some(reason) => {
                        session.push_event(Event::MetaPause {
                                               time: now,
                                               reason: reason.to_string(),
                                           });

                    }
                    None => {
                        session.push_event(Event::Pause { time: now });
                    }
                }
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn proceed(&mut self) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Proceed { time: now });
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn push_meta(&mut self, metatext: String) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Meta {
                                       time: now,
                                       text: metatext,
                                   });
            }
            None => println!("No session to add meta to!"),
        }
        self.save_to_file();
    }

    pub fn push_commit(&mut self, hash: u64) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Commit {
                                       time: now,
                                       hash: hash,
                                   });
            }
            None => println!("No session to add commit to!"),
        }
        self.save_to_file();
    }

    pub fn push_branch(&mut self, name: String) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Branch {
                                       time: now,
                                       name: name,
                                   });
            }
            None => println!("No session to change branch in!"),
        }
        self.save_to_file();
    }

    fn save_to_file(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */
        let path = Path::new("./.trk/sessions.trk");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                /* Convert the sheet to a JSON string. */
                let serialized =
                    serde_json::to_string(&self).expect("Could not write serialized time sheet!");
                file.write_all(serialized.as_bytes()).unwrap();
                /* save was successful */
                true
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    /** Return a Some(Timesheet) struct if a sessions.trk file is present and valid
     * in the .trk directory, and None otherwise.
     * TODO: improve error handling
     * */
    pub fn load_from_file() -> Option<Timesheet> {
        let path = Path::new("./.trk/sessions.trk");
        let file = OpenOptions::new().read(true).open(&path);
        match file {
            Ok(mut file) => {
                let mut serialized = String::new();
                match file.read_to_string(&mut serialized) {
                    Ok(..) => serde_json::from_str(&serialized).unwrap_or(None),
                    Err(..) => {
                        println!("Reading the string failed!");
                        None
                    }
                }
            }
            Err(..) => {
                // println!("{}", why.description());
                None
            }
        }
    }

    pub fn clear_sessions() {
        /* Try to get name */
        let sheet = Timesheet::load_from_file();
        let name: Option<String> = sheet.map(|s| s.user.clone());

        let path = Path::new("./.trk/sessions.trk");
        if path.exists() {
            match fs::remove_file(&path) {
                Ok(..) => {}
                Err(why) => println!("Could not remove sessions file: {}", why.description()),
            }
        }
        match name {
            Some(name) => {
                /* Will overwrite file */
                Timesheet::init(Some(&name));
            }
            None => {
                Timesheet::init(None);
            }
        }
    }

    pub fn timesheet_status(&self) -> String {
        format!("{:?}", self)
    }

    pub fn last_session_status(&self) -> Option<String> {
        let nsessions = self.sessions.len();
        match nsessions {
            0 => None,
            n => Some(self.sessions[n - 1].status()),
        }
    }
}

fn get_seconds() -> u64 {
    SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
}

fn git_author() -> Option<String> {
    if let Ok(output) = Command::new("git").arg("config").arg("user.name").output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            /* remove trailing newline character */
            let mut output = output.to_string();
            output.pop().expect("Empty name in git config!?!");
            Some(output)
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git config user.name failed! {}", output);
            None
        }
    } else {
        None
    }
}
<commit_msg>improve metapause handling - meta can now be added during a pause<commit_after>extern crate time;
extern crate serde_json;

use std::io::prelude::*;

use std::time::{SystemTime, UNIX_EPOCH};

use std::fs;
use std::path::Path;
use std::error::Error;
use std::fs::OpenOptions;

use std::process::Command;

#[derive(Serialize, Deserialize, Debug)]
pub enum Event {
    Pause { time: u64 },
    MetaPause { time: u64, reason: String },
    Proceed { time: u64 },
    Meta { time: u64, text: String },
    Commit { time: u64, hash: u64 },
    Branch { time: u64, name: String },
}

#[derive(Serialize, Deserialize, Debug)]
struct Session {
    start: u64,
    end: u64,
    events: Vec<Event>,
}

impl Session {
    fn new() -> Session {
        let seconds = get_seconds();
        Session {
            start: seconds,
            end: seconds - 1,
            events: Vec::<Event>::new(),
        }
    }

    fn in_progress(&self) -> bool {
        self.start == (self.end + 1)
    }

    fn finalize(&mut self) {
        self.end = get_seconds();
    }

    fn status(&self) -> String {
        format!("{:?}", self)
    }

    fn is_paused(&self) -> bool {
        match self.events.len() {
            0 => false,
            n => {
                match self.events[n - 1] {
                    Event::Pause { .. } |
                    Event::MetaPause { .. } => true,
                    _ => false,
                }
            }
        }
    }

    fn push_event(&mut self, event: Event) -> bool {
        /* Cannot push if session is already finalized! */
        if !self.in_progress() {
            println!("Already finalized, cannot push event");
            return false;
        }
        /* TODO: add logic */
        match event {
            Event::Pause { .. } |
            Event::MetaPause { .. } => {
                if !self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Already paused!");
                    false
                }
            }
            Event::Proceed { .. } => {
                if self.is_paused() {
                    self.events.push(event);
                    true
                } else {
                    println!("Currently not paused!");
                    false
                }
            }
            Event::Meta { time: ref metatime, text: ref metatext } => {
                if self.is_paused() {
                    /* morph last pause into a MetaPause */
                    let pause = self.events.pop().unwrap();
                    match pause {
                        Event::Pause {time: pausetime} => {
                            self.push_event(Event::MetaPause { time: pausetime, reason: metatext.to_string()})
                        }
                        Event::MetaPause { time: mp_time, reason } => {
                            /* Concat? */
                            let reason_concat = reason + metatext;
                            self.push_event(Event::MetaPause { time: mp_time, reason: reason_concat.to_string()})
                        }
                        _ => unreachable!(),
                    };
                } else {
                    self.events.push(Event::Meta {time: *metatime, text: metatext.clone()} )
                };
                true
            }
            Event::Commit { .. } |
            Event::Branch { .. } => {
                if self.is_paused() {
                    let now = get_seconds();
                    self.push_event(Event::Proceed { time: now });
                }
                self.events.push(event);
                true
            }
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Timesheet {
    start: u64,
    end: u64,
    user: String,
    sessions: Vec<Session>,
}

impl Timesheet {
    /** Initializes the .trk/sessions.trk file which holds
     * the serialized timesheet
     * Returns Some(newTimesheet) if operation succeeded */
    pub fn init(author_name: Option<&str>) -> Option<Timesheet> {
        /* Check if file already exists (no init permitted) */
        if Timesheet::is_init() {
            None
        } else {
            /* File does not exist, initialize */
            let git_author_name = &git_author().unwrap_or("".to_string());
            let author_name = match author_name {
                Some(name) => name,
                None => git_author_name,
            };
            let sheet = Timesheet {
                start: get_seconds(),
                end: get_seconds() - 1,
                user: author_name.to_string(),
                sessions: Vec::<Session>::new(),
            };
            if sheet.save_to_file() {
                Some(sheet)
            } else {
                None
            }
        }
    }

    fn is_init() -> bool {
        if Path::new("./.trk/sessions.trk").exists() {
            match Timesheet::load_from_file() {
                Some(..) => true,
                /* else, loading failed */
                None => false,
            }
        } else {
            /* File doesn't even exist */
            false
        }
    }

    fn get_last_session(&mut self) -> Option<&mut Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&mut self.sessions[n - 1]),
        }
    }

    pub fn new_session(&mut self) -> bool {

        let nsessions = self.sessions.len();
        let pushed = match nsessions {
            0 => true,
            _ => {
                if !self.sessions[nsessions - 1].in_progress() {
                    true
                } else {
                    println!("Last session is still running!");
                    false
                }
            }
        };
        if pushed {
            self.sessions.push(Session::new());
            self.save_to_file();
        }
        pushed
    }

    pub fn end_session(&mut self) {
        match self.get_last_session() {
            Some(session) => session.finalize(),
            None => println!("No session to finalize!"),
        }
        self.save_to_file();
    }

    pub fn pause(&mut self, metatext: Option<String>) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                match metatext {
                    Some(reason) => {
                        session.push_event(Event::MetaPause {
                                               time: now,
                                               reason: reason.to_string(),
                                           });

                    }
                    None => {
                        session.push_event(Event::Pause { time: now });
                    }
                }
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn proceed(&mut self) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Proceed { time: now });
            }
            None => println!("No session to pause!"),
        }
        self.save_to_file();
    }

    pub fn push_meta(&mut self, metatext: String) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Meta {
                                       time: now,
                                       text: metatext,
                                   });
            }
            None => println!("No session to add meta to!"),
        }
        self.save_to_file();
    }

    pub fn push_commit(&mut self, hash: u64) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Commit {
                                       time: now,
                                       hash: hash,
                                   });
            }
            None => println!("No session to add commit to!"),
        }
        self.save_to_file();
    }

    pub fn push_branch(&mut self, name: String) {
        match self.get_last_session() {
            Some(session) => {
                let now = get_seconds();
                session.push_event(Event::Branch {
                                       time: now,
                                       name: name,
                                   });
            }
            None => println!("No session to change branch in!"),
        }
        self.save_to_file();
    }

    fn save_to_file(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */
        let path = Path::new("./.trk/sessions.trk");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                /* Convert the sheet to a JSON string. */
                let serialized =
                    serde_json::to_string(&self).expect("Could not write serialized time sheet!");
                file.write_all(serialized.as_bytes()).unwrap();
                /* save was successful */
                true
            }
            Err(why) => {
                println!("{}", why.description());
                false
            }
        }
    }

    /** Return a Some(Timesheet) struct if a sessions.trk file is present and valid
     * in the .trk directory, and None otherwise.
     * TODO: improve error handling
     * */
    pub fn load_from_file() -> Option<Timesheet> {
        let path = Path::new("./.trk/sessions.trk");
        let file = OpenOptions::new().read(true).open(&path);
        match file {
            Ok(mut file) => {
                let mut serialized = String::new();
                match file.read_to_string(&mut serialized) {
                    Ok(..) => serde_json::from_str(&serialized).unwrap_or(None),
                    Err(..) => {
                        println!("Reading the string failed!");
                        None
                    }
                }
            }
            Err(..) => {
                // println!("{}", why.description());
                None
            }
        }
    }

    pub fn clear_sessions() {
        /* Try to get name */
        let sheet = Timesheet::load_from_file();
        let name: Option<String> = sheet.map(|s| s.user.clone());

        let path = Path::new("./.trk/sessions.trk");
        if path.exists() {
            match fs::remove_file(&path) {
                Ok(..) => {}
                Err(why) => println!("Could not remove sessions file: {}", why.description()),
            }
        }
        match name {
            Some(name) => {
                /* Will overwrite file */
                Timesheet::init(Some(&name));
            }
            None => {
                Timesheet::init(None);
            }
        }
    }

    pub fn timesheet_status(&self) -> String {
        format!("{:?}", self)
    }

    pub fn last_session_status(&self) -> Option<String> {
        let nsessions = self.sessions.len();
        match nsessions {
            0 => None,
            n => Some(self.sessions[n - 1].status()),
        }
    }
}

fn get_seconds() -> u64 {
    SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
}

fn git_author() -> Option<String> {
    if let Ok(output) = Command::new("git").arg("config").arg("user.name").output() {
        if output.status.success() {
            let output = String::from_utf8_lossy(&output.stdout);
            /* remove trailing newline character */
            let mut output = output.to_string();
            output.pop().expect("Empty name in git config!?!");
            Some(output)
        } else {
            let output = String::from_utf8_lossy(&output.stderr);
            println!("git config user.name failed! {}", output);
            None
        }
    } else {
        None
    }
}
<|endoftext|>


<commit_before>// 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.

#![allow(missing_docs)]

use std::error::Error;
use std::fmt;
use std::str;
use super::ast::*;

#[derive(Debug)]
pub struct ParseError {
    pub error_message: String,
}

impl ParseError {
    pub fn new(error_message: &str) -> Self {
        ParseError { error_message: String::from(error_message) }
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        &self.error_message
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

pub struct Parser<'a> {
    source: str::Chars<'a>,
    ch: Option<char>,
    pos: u16,
}

impl<'a> Parser<'a> {
    pub fn new(source: &'a str) -> Parser<'a> {
        Parser {
            source: source.chars(),
            ch: None,
            pos: 0,
        }
    }

    fn bump(&mut self) {
        self.ch = self.source.next();

        self.pos += 1;
    }

    fn ch_is(&self, ch: char) -> bool {
        self.ch == Some(ch)
    }

    fn get_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') {
            self.bump();
        }
    }

    fn get_line_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\t') {
            self.bump();
        }
    }

    pub fn parse(&mut self) -> Result<Vec<Entry>, ParseError> {
        let mut entries: Vec<Entry> = Vec::new();

        self.get_ws();

        self.bump();

        loop {
            if self.ch == None {
                break;
            }

            match self.get_entry() {
                Ok(entry) => entries.push(entry),
                Err(err) => return Err(err),
            }
            self.get_ws();
        }
        Ok(entries)
    }

    fn get_entry(&mut self) -> Result<Entry, ParseError> {
        self.get_entity()
    }

    fn get_entity(&mut self) -> Result<Entry, ParseError> {
        let id = try!(self.get_identifier());
        self.get_line_ws();

        if !self.ch_is('=') {
            return Err(ParseError::new("Expected '='"));
        }
        self.bump();

        self.get_line_ws();

        match self.get_pattern() {
            Ok(value) => {
                Ok(Entry::Entity {
                    id: id,
                    value: value,
                })
            }
            Err(err) => Err(err),
        }
    }

    fn get_identifier(&mut self) -> Result<Identifier, ParseError> {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => return Err(ParseError::new("Unexpected end of input.")),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Ok(Identifier { name: name }),
        }
        self.bump();

        loop {
            let ch = match self.ch {
                Some(c) => c,
                None => break,
            };

            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Ok(Identifier { name: name })
    }

    fn get_pattern(&mut self) -> Result<Value, ParseError> {
        let mut buffer = String::new();
        let mut source = String::new();
        let mut content = vec![];
        let mut quote_delimited: bool = false;
        let mut first_line = true;

        if self.ch_is('"') {
            quote_delimited = true;
        }

        loop {
            match self.ch {
                Some(c) if c == '\n' => {
                    if quote_delimited {
                        return Err(ParseError::new("Unclosed string"));
                    }
                    self.bump();
                    self.get_line_ws();

                    if !self.ch_is('|') {
                        break;
                    }
                    if first_line && buffer.len() != 0 {
                        return Err(ParseError::new("Multiline string should have the ID line \
                                                    empty"));
                    }
                    first_line = false;
                    self.bump();
                    if self.ch_is(' ') {
                        self.bump();
                    }
                    if buffer.len() != 0 {
                        buffer.push('\n');
                    }
                    continue;
                }
                Some(c) if c == '"' => {
                    self.bump();
                    quote_delimited = false;
                    break;
                }
                Some(c) => source.push(c),
                None => break,
            }
            match self.ch {
                Some(c) => buffer.push(c),
                None => continue,
            };
            self.bump();
        }

        if quote_delimited {
            return Err(ParseError::new("Unclosed string"));
        }

        if buffer.len() != 0 {
            // source.append(buffer);
            content.push(PatternElement::TextElement { value: source.clone() });
        }

        if content.len() == 0 {
            // return Value::Pattern(source: source, elements: content);
        }

        content.push(PatternElement::TextElement { value: source.clone() });

        Ok(Value::Pattern {
            source: source,
            elements: content,
        })
    }
}
<commit_msg>Start adding some tests to confirm what is working.<commit_after>// 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.

#![allow(missing_docs)]

use std::error::Error;
use std::fmt;
use std::str;
use super::ast::*;

#[derive(Debug)]
pub struct ParseError {
    pub error_message: String,
}

impl ParseError {
    pub fn new(error_message: &str) -> Self {
        ParseError { error_message: String::from(error_message) }
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        &self.error_message
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

pub struct Parser<'a> {
    source: str::Chars<'a>,
    ch: Option<char>,
    pos: u16,
}

impl<'a> Parser<'a> {
    pub fn new(source: &'a str) -> Parser<'a> {
        Parser {
            source: source.chars(),
            ch: None,
            pos: 0,
        }
    }

    fn bump(&mut self) {
        self.ch = self.source.next();

        self.pos += 1;
    }

    fn ch_is(&self, ch: char) -> bool {
        self.ch == Some(ch)
    }

    fn get_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') {
            self.bump();
        }
    }

    fn get_line_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\t') {
            self.bump();
        }
    }

    pub fn parse(&mut self) -> Result<Vec<Entry>, ParseError> {
        let mut entries: Vec<Entry> = Vec::new();

        self.get_ws();

        self.bump();

        loop {
            if self.ch == None {
                break;
            }

            match self.get_entry() {
                Ok(entry) => entries.push(entry),
                Err(err) => return Err(err),
            }
            self.get_ws();
        }
        Ok(entries)
    }

    fn get_entry(&mut self) -> Result<Entry, ParseError> {
        self.get_entity()
    }

    fn get_entity(&mut self) -> Result<Entry, ParseError> {
        let id = try!(self.get_identifier());
        self.get_line_ws();

        if !self.ch_is('=') {
            return Err(ParseError::new("Expected '='"));
        }
        self.bump();

        self.get_line_ws();

        match self.get_pattern() {
            Ok(value) => {
                Ok(Entry::Entity {
                    id: id,
                    value: value,
                })
            }
            Err(err) => Err(err),
        }
    }

    fn get_identifier(&mut self) -> Result<Identifier, ParseError> {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => return Err(ParseError::new("Unexpected end of input.")),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Ok(Identifier { name: name }),
        }
        self.bump();

        loop {
            let ch = match self.ch {
                Some(c) => c,
                None => break,
            };

            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Ok(Identifier { name: name })
    }

    fn get_pattern(&mut self) -> Result<Value, ParseError> {
        let mut buffer = String::new();
        let mut source = String::new();
        let mut content = vec![];
        let mut quote_delimited: bool = false;
        let mut first_line = true;

        if self.ch_is('"') {
            quote_delimited = true;
        }

        loop {
            match self.ch {
                Some(c) if c == '\n' => {
                    if quote_delimited {
                        return Err(ParseError::new("Unclosed string"));
                    }
                    self.bump();
                    self.get_line_ws();

                    if !self.ch_is('|') {
                        break;
                    }
                    if first_line && buffer.len() != 0 {
                        return Err(ParseError::new("Multiline string should have the ID line \
                                                    empty"));
                    }
                    first_line = false;
                    self.bump();
                    if self.ch_is(' ') {
                        self.bump();
                    }
                    if buffer.len() != 0 {
                        buffer.push('\n');
                    }
                    continue;
                }
                Some(c) if c == '"' => {
                    self.bump();
                    quote_delimited = false;
                    break;
                }
                Some(c) => source.push(c),
                None => break,
            }
            match self.ch {
                Some(c) => buffer.push(c),
                None => continue,
            };
            self.bump();
        }

        if quote_delimited {
            return Err(ParseError::new("Unclosed string"));
        }

        if buffer.len() != 0 {
            // source.append(buffer);
            content.push(PatternElement::TextElement { value: source.clone() });
        }

        if content.len() == 0 {
            // return Value::Pattern(source: source, elements: content);
        }

        content.push(PatternElement::TextElement { value: source.clone() });

        Ok(Value::Pattern {
            source: source,
            elements: content,
        })
    }
}

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

    fn expected_parse(name: &str, text: &str) {
        let mut p = Parser::new(text);
        match p.parse() {
            Err(e) => panic!("Parse failed: {}: {}", name, e),
            _ => {}
        }
    }

    fn expected_failure(name: &str, text: &str) {
        let mut p = Parser::new(text);
        match p.parse() {
            Ok(_) => panic!("Parse unexpectedly worked: {}", name),
            _ => {}
        }
    }

    #[test]
    fn it_works() {
        expected_parse("simple", "a = b");
        expected_parse("simple", "a=b");
        expected_parse("simple", "a   =     b");
        expected_parse("multiline",
                       "multi =\n\
                        | abc\n\
                       ");
        expected_failure("comment", "#comment");
        expected_failure("comment", "# comment");
        expected_failure("comment", "#  comment");
    }
}
<|endoftext|>


<commit_before>/*
Module: option

Represents the presence or absence of a value.

Every option<T> value can either be some(T) or none. Where in other languages
you might use a nullable type, in Rust you would use an option type.
*/

/*
Tag: t

The option type
*/
tag t<T> {
    /* Variant: none */
    none;
    /* Variant: some */
    some(T);
}

/* Section: Operations */

/*
Function: get

Gets the value out of an option

Failure:

Fails if the value equals `none`.
*/
fn get<T>(opt: t<T>) -> &T {
    alt opt { some(x) { ret x; } none. { fail "option none"; } }
}

/*
*/
fn map<T, U>(f: block(T) -> U, opt: t<T>) -> t<U> {
    alt opt { some(x) { some(f(x)) } none. { none } }
}

/*
Function: is_none

Returns true if the option equals none
*/
fn is_none<T>(opt: t<T>) -> bool {
    alt opt { none. { true } some(_) { false } }
}

/*
Function: is_some

Returns true if the option contains some value
*/
fn is_some<T>(opt: t<T>) -> bool { !is_none(opt) }

/*
Function: from_maybe

Returns the contained value or a default
*/
fn from_maybe<T>(def: T, opt: t<T>) -> T {
    alt opt { some(x) { x } none. { def } }
}

/*
Function: maybe

Applies a function to the contained value or returns a default
*/
fn maybe<T, U>(def: U, f: block(T) -> U, opt: t<T>) -> U {
    alt opt { none. { def } some(t) { f(t) } }
}

// FIXME: Can be defined in terms of the above when/if we have const bind.
/*
Function: may

Performs an operation on the contained value or does nothing
*/
fn may<T>(f: block(T), opt: t<T>) {
    alt opt { none. {/* nothing */ } some(t) { f(t); } }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Make option::is_some option::is_none pure<commit_after>/*
Module: option

Represents the presence or absence of a value.

Every option<T> value can either be some(T) or none. Where in other languages
you might use a nullable type, in Rust you would use an option type.
*/

/*
Tag: t

The option type
*/
tag t<T> {
    /* Variant: none */
    none;
    /* Variant: some */
    some(T);
}

/* Section: Operations */

/*
Function: get

Gets the value out of an option

Failure:

Fails if the value equals `none`.
*/
fn get<T>(opt: t<T>) -> &T {
    alt opt { some(x) { ret x; } none. { fail "option none"; } }
}

/*
*/
fn map<T, U>(f: block(T) -> U, opt: t<T>) -> t<U> {
    alt opt { some(x) { some(f(x)) } none. { none } }
}

/*
Function: is_none

Returns true if the option equals none
*/
pure fn is_none<T>(opt: t<T>) -> bool {
    alt opt { none. { true } some(_) { false } }
}

/*
Function: is_some

Returns true if the option contains some value
*/
pure fn is_some<T>(opt: t<T>) -> bool { !is_none(opt) }

/*
Function: from_maybe

Returns the contained value or a default
*/
fn from_maybe<T>(def: T, opt: t<T>) -> T {
    alt opt { some(x) { x } none. { def } }
}

/*
Function: maybe

Applies a function to the contained value or returns a default
*/
fn maybe<T, U>(def: U, f: block(T) -> U, opt: t<T>) -> U {
    alt opt { none. { def } some(t) { f(t) } }
}

// FIXME: Can be defined in terms of the above when/if we have const bind.
/*
Function: may

Performs an operation on the contained value or does nothing
*/
fn may<T>(f: block(T), opt: t<T>) {
    alt opt { none. {/* nothing */ } some(t) { f(t); } }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>use std::fmt;
use std::hash;

use num::{FromPrimitive};

/// please don't access VA.0 directly.
/// its provided so you can construct VA like:
/// ```
/// use lancelot::arch::VA;
/// let _ = VA(0x0);
/// ```
#[derive(Copy, Clone)]
pub struct VA(pub u64);

impl VA {
    /// Compute the VA given a delta RVA, like `self:va + other:rva`.
    /// Returns None on over/underflow.
    pub fn va(&self, other: RVA) -> Option<VA> {
        if other.0 < 0 {
            Some(VA(match self.0.checked_sub((-other.0) as u64) {
                None => return None,
                Some(v) => v,
            }))
        } else {
            Some(VA(match self.0.checked_add(other.0 as u64) {
                None => return None,
                Some(v) => v,
            }))
        }
    }

    /// Compute the delta RVA from `self:va - other:va`.
    /// Returns None on over/underflow.
    pub fn rva(&self, other: VA) -> Option<RVA> {
        if other.0 > self.0 {
            let v = match other.0.checked_sub(self.0) {
                None => return None,
                Some(v) => v,
            };
            if v > std::i64::MAX as u64{
                return None;
            }
            Some(RVA(-(v as i64)))
        } else {
            let v = match self.0.checked_sub(other.0) {
                None => return None,
                Some(v) => v
            };
            if v > std::i64::MAX as u64 {
                return None;
            }
            Some(RVA(v as i64))
        }
    }
}

impl fmt::Display for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::Debug for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::LowerHex for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:x}", self.0)
    }
}

impl fmt::UpperHex for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:X}", self.0)
    }
}

impl hash::Hash for VA {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl PartialEq for VA {
    fn eq(&self, other: &VA) -> bool {
        self.0 == other.0
    }
}

impl Eq for VA {}

impl std::convert::From<usize> for VA {
    fn from(v: usize) -> VA {
        // this should only fail when we get to 128-bit architectures
        VA(v as u64)
    }
}

impl std::convert::From<u32> for VA {
    fn from(v: u32) -> VA {
        VA(v as u64)
    }
}

impl std::convert::From<u64> for VA {
    fn from(v: u64) -> VA {
        VA(v)
    }
}

impl std::convert::Into<u64> for VA {
    fn into(self) -> u64 {
        self.0
    }
}

/// please don't access RVA.0 directly.
/// /// its provided so you can construct RVA like:
/// ```
/// use lancelot::arch::RVA;
/// let _ = RVA(0x0);
/// ```
#[derive(Copy, Clone)]
pub struct RVA(pub i64);

impl fmt::Display for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::Debug for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::LowerHex for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:x}", self.0)
    }
}

impl fmt::UpperHex for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:X}", self.0)
    }
}

impl hash::Hash for RVA {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl PartialEq for RVA {
    fn eq(&self, other: &RVA) -> bool {
        self.0 == other.0
    }
}

impl PartialOrd for RVA {
    fn partial_cmp(&self, other: &RVA) -> Option<std::cmp::Ordering> {
        Some(self.0.cmp(&other.0))
    }
}

impl Ord for RVA {
    fn cmp(&self, other: &RVA) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl Eq for RVA {}

impl std::convert::From<usize> for RVA {
    /// may panic on overflow
    fn from(v: usize) -> RVA {
        RVA(i64::from_usize(v).expect("usize too large for RVA"))
    }
}

impl std::convert::Into<usize> for RVA {
    /// may panic on over/underflow
    fn into(self) -> usize {
        if self.0 < 0 {
            panic!("usize underflow")
        }
        if self.0 as u64 > std::usize::MAX as u64 {
            panic!("usize overflow");
        }
        self.0 as usize
    }
}

impl std::convert::Into<u64> for RVA {
    fn into(self) -> u64 {
        self.0 as u64
    }
}

impl std::convert::Into<i64> for RVA {
    fn into(self) -> i64 {
        self.0
    }
}

impl std::convert::From<i32> for RVA {
    fn from(v: i32) -> RVA {
        RVA(v as i64)
    }
}

impl std::convert::From<i64> for RVA {
    fn from(v: i64) -> RVA {
        RVA(v)
    }
}

impl std::ops::Add<RVA> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: RVA) -> RVA {
        RVA(self.0.checked_add(rhs.0).expect("rva overflow"))
    }
}

impl std::ops::Add<usize> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: usize) -> RVA {
        self + RVA::from(rhs)
    }
}

impl std::ops::Add<u8> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: u8) -> RVA {
        self + RVA::from(rhs as i64)
    }
}

impl std::ops::Add<i32> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: i32) -> RVA {
        self + RVA::from(rhs as i64)
    }
}

impl std::ops::Sub<RVA> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn sub(self, rhs: RVA) -> RVA {
        RVA(self.0.checked_sub(rhs.0).expect("rva underflow"))
    }
}

#[derive(Copy, Clone)]
pub enum Arch {
    X32,
    X64
}

impl Arch {
    pub fn get_pointer_size(self) -> u8 {
        match self {
            Arch::X32 => 4,
            Arch::X64 => 8,
        }
    }
}

impl fmt::Display for Arch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Arch::X32 => write!(f, "x32"),
            Arch::X64 => write!(f, "x64"),
        }
    }
}

impl fmt::Debug for Arch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self{
            Arch::X32 => write!(f, "x32"),
            Arch::X64 => write!(f, "x64"),
        }
    }
}

<commit_msg>arch: rva: add From<u32><commit_after>use std::fmt;
use std::hash;

use num::{FromPrimitive};

/// please don't access VA.0 directly.
/// its provided so you can construct VA like:
/// ```
/// use lancelot::arch::VA;
/// let _ = VA(0x0);
/// ```
#[derive(Copy, Clone)]
pub struct VA(pub u64);

impl VA {
    /// Compute the VA given a delta RVA, like `self:va + other:rva`.
    /// Returns None on over/underflow.
    pub fn va(&self, other: RVA) -> Option<VA> {
        if other.0 < 0 {
            Some(VA(match self.0.checked_sub((-other.0) as u64) {
                None => return None,
                Some(v) => v,
            }))
        } else {
            Some(VA(match self.0.checked_add(other.0 as u64) {
                None => return None,
                Some(v) => v,
            }))
        }
    }

    /// Compute the delta RVA from `self:va - other:va`.
    /// Returns None on over/underflow.
    pub fn rva(&self, other: VA) -> Option<RVA> {
        if other.0 > self.0 {
            let v = match other.0.checked_sub(self.0) {
                None => return None,
                Some(v) => v,
            };
            if v > std::i64::MAX as u64{
                return None;
            }
            Some(RVA(-(v as i64)))
        } else {
            let v = match self.0.checked_sub(other.0) {
                None => return None,
                Some(v) => v
            };
            if v > std::i64::MAX as u64 {
                return None;
            }
            Some(RVA(v as i64))
        }
    }
}

impl fmt::Display for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::Debug for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::LowerHex for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:x}", self.0)
    }
}

impl fmt::UpperHex for VA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:X}", self.0)
    }
}

impl hash::Hash for VA {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl PartialEq for VA {
    fn eq(&self, other: &VA) -> bool {
        self.0 == other.0
    }
}

impl Eq for VA {}

impl std::convert::From<usize> for VA {
    fn from(v: usize) -> VA {
        // this should only fail when we get to 128-bit architectures
        VA(v as u64)
    }
}

impl std::convert::From<u32> for VA {
    fn from(v: u32) -> VA {
        VA(v as u64)
    }
}

impl std::convert::From<u64> for VA {
    fn from(v: u64) -> VA {
        VA(v)
    }
}

impl std::convert::Into<u64> for VA {
    fn into(self) -> u64 {
        self.0
    }
}

/// please don't access RVA.0 directly.
/// /// its provided so you can construct RVA like:
/// ```
/// use lancelot::arch::RVA;
/// let _ = RVA(0x0);
/// ```
#[derive(Copy, Clone)]
pub struct RVA(pub i64);

impl fmt::Display for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::Debug for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:#x}", self.0)
    }
}

impl fmt::LowerHex for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:x}", self.0)
    }
}

impl fmt::UpperHex for RVA {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:X}", self.0)
    }
}

impl hash::Hash for RVA {
    fn hash<H: hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl PartialEq for RVA {
    fn eq(&self, other: &RVA) -> bool {
        self.0 == other.0
    }
}

impl PartialOrd for RVA {
    fn partial_cmp(&self, other: &RVA) -> Option<std::cmp::Ordering> {
        Some(self.0.cmp(&other.0))
    }
}

impl Ord for RVA {
    fn cmp(&self, other: &RVA) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl Eq for RVA {}

impl std::convert::From<usize> for RVA {
    /// may panic on overflow
    fn from(v: usize) -> RVA {
        RVA(i64::from_usize(v).expect("usize too large for RVA"))
    }
}

impl std::convert::Into<usize> for RVA {
    /// may panic on over/underflow
    fn into(self) -> usize {
        if self.0 < 0 {
            panic!("usize underflow")
        }
        if self.0 as u64 > std::usize::MAX as u64 {
            panic!("usize overflow");
        }
        self.0 as usize
    }
}

impl std::convert::Into<u64> for RVA {
    fn into(self) -> u64 {
        self.0 as u64
    }
}

impl std::convert::Into<i64> for RVA {
    fn into(self) -> i64 {
        self.0
    }
}

impl std::convert::From<u32> for RVA {
    fn from(v: u32) -> RVA {
        RVA(v as i64)
    }
}

impl std::convert::From<i32> for RVA {
    fn from(v: i32) -> RVA {
        RVA(v as i64)
    }
}

impl std::convert::From<i64> for RVA {
    fn from(v: i64) -> RVA {
        RVA(v)
    }
}

impl std::ops::Add<RVA> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: RVA) -> RVA {
        RVA(self.0.checked_add(rhs.0).expect("rva overflow"))
    }
}

impl std::ops::Add<usize> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: usize) -> RVA {
        self + RVA::from(rhs)
    }
}

impl std::ops::Add<u8> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: u8) -> RVA {
        self + RVA::from(rhs as i64)
    }
}

impl std::ops::Add<i32> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn add(self, rhs: i32) -> RVA {
        self + RVA::from(rhs as i64)
    }
}

impl std::ops::Sub<RVA> for RVA {
    type Output = RVA;

    /// may panic on overflow
    fn sub(self, rhs: RVA) -> RVA {
        RVA(self.0.checked_sub(rhs.0).expect("rva underflow"))
    }
}

#[derive(Copy, Clone)]
pub enum Arch {
    X32,
    X64
}

impl Arch {
    pub fn get_pointer_size(self) -> u8 {
        match self {
            Arch::X32 => 4,
            Arch::X64 => 8,
        }
    }
}

impl fmt::Display for Arch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Arch::X32 => write!(f, "x32"),
            Arch::X64 => write!(f, "x64"),
        }
    }
}

impl fmt::Debug for Arch {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self{
            Arch::X32 => write!(f, "x32"),
            Arch::X64 => write!(f, "x64"),
        }
    }
}

<|endoftext|>