<commit_before>#[macro_use]
extern crate log;
extern crate loggerv;

#[macro_use]
extern crate clap;

extern crate serde_json;

extern crate time;

extern crate regex;

extern crate colored;

use clap::App;
use colored::*;
use log::LogLevel;
use regex::Regex;
use serde_json::Value;
use std::collections::BTreeMap as DataMap;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
use std::io::Write;
use std::process;
use time::get_time;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
struct MinionResult {
    command: Option<String>,
    retcode: Retcode,
    output: Option<String>,
    result: Option<String>,
    host: String,
}

type MinionResults = Vec<MinionResult>;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Retcode {
    Success,
    Failure,
}

impl Retcode {
    fn is_success(&self) -> bool {
        self == &Retcode::Success
    }
}

impl Default for Retcode {
    fn default() -> Retcode {
        Retcode::Failure
    }
}

impl From<u64> for Retcode {
    fn from(input: u64) -> Self {
        if input == 0 {
            Retcode::Success
        } else {
            Retcode::Failure
        }
    }
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml)
        .version(crate_version!())
        .get_matches();
    trace!("matches: {:?}", matches);

    {
        let loglevel: LogLevel = value_t!(matches, "loglevel", LogLevel)
            .expect("can not parse loglevel from args");
        loggerv::init_with_level(loglevel).expect("can not initialize logger with parsed loglevel");
    }

    let changed = matches.is_present("changed");
    let no_save_file = matches.is_present("no_save_file");

    let (host_data, no_return) = {
        let input = matches.value_of("input").expect("can not get input file from args");

        let input = if input == "-" {
            let mut buffer = String::new();
            io::stdin().read_to_string(&mut buffer).expect("can not read from stdin");
            buffer
        } else {
            let mut file = File::open(input).expect("can not open input file");
            let mut input = String::new();
            file.read_to_string(&mut input).expect("can not read input file to string");
            input
        };

        // match all hosts that have not returned as they are not in the json data
        // format is normally like "Minion pricesearch did not respond. No job will be sent."
        let catch_not_returned_minions =
            Regex::new(r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$")
                .expect("regex for catching not returned minions is not valid");

        let mut no_return = Vec::new();
        for host in catch_not_returned_minions.captures_iter(input.as_str()) {
            no_return.push(host[1].to_string());
        }

        // clean up hosts that have not returned from the json data
        (catch_not_returned_minions.replace_all(input.as_str(), "").into_owned(), no_return)
    };

    let value: Value = serde_json::from_str(host_data.as_str())
        .expect("can not convert input data to value. have you run the salt command with \
                 --static?");

    let results = match get_results(&value, no_return) {
        Ok(r) => r,
        Err(e) => {
            error!("can not get results from serde value: {}", e);
            if !no_save_file {
                write_save_file(host_data);
            }
            process::exit(1)
        }
    };

    trace!("results: {:#?}", results);

    let compressed = get_compressed(results);
    trace!("compressed: {:#?}", compressed);

    print_compressed(compressed, changed);
}

#[derive(Debug)]
enum ResultError {
    ConvertDiffToString,
    ConvertValueToString,
    ReturnCodeNotNumber,
    RetValueIsNone,
    RetValueIsNull,
    RetValueIsNumber,
    ValueNotAnObject,
}

impl fmt::Display for ResultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultError::ConvertDiffToString => write!(f, "can not convert diff to string"),
            ResultError::ConvertValueToString => write!(f, "can not convert value to string"),
            ResultError::ReturnCodeNotNumber => write!(f, "returncode is not a number"),
            ResultError::RetValueIsNone => write!(f, "ret value is none"),
            ResultError::RetValueIsNull => write!(f, "ret value is null"),
            ResultError::RetValueIsNumber => write!(f, "ret value is number"),
            ResultError::ValueNotAnObject => write!(f, "value it not an object"),
        }
    }
}

fn get_results(value: &Value, no_return: Vec<String>) -> Result<MinionResults, ResultError> {
    if !value.is_object() {
        return Err(ResultError::ValueNotAnObject);
    }

    let mut results: MinionResults = Vec::new();

    for (host, values) in value.as_object().unwrap().iter() {
        trace!("host: {:#?}", host);
        trace!("values: {:#?}", values);

        let retcode: Retcode = match values.get("retcode") {
            Some(o) => {
                match o.as_u64() {
                    Some(v) => v.into(),
                    None => return Err(ResultError::ReturnCodeNotNumber),
                }
            }
            None => {
                warn!("host {} does not have a return code", host);
                Retcode::Failure
            }
        };

        let ret = match values.get("ret") {
            None => return Err(ResultError::RetValueIsNone),
            Some(r) => r,
        };

        match *ret {
            Value::Null => return Err(ResultError::RetValueIsNull),
            Value::Bool(r) => {
                let (command, result, output) = if r {
                    (None, Some("true".to_string()), None)
                } else {
                    (None, Some("false".to_string()), None)
                };

                results.push(MinionResult {
                    command: command,
                    host: host.clone(),
                    output: output,
                    result: result,
                    retcode: retcode,
                });
            }
            Value::Number(_) => return Err(ResultError::RetValueIsNumber),
            Value::String(ref r) => {
                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(r.clone()),
                    retcode: retcode,
                    ..MinionResult::default()
                });
            }

            Value::Array(ref r) => {
                let values: Vec<_> = r.iter()
                    .map(|v| v.as_str().expect("can not convert the array value to a string"))
                    .collect();

                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(values.join("\n").to_string()),
                    retcode: retcode.clone(),
                    ..MinionResult::default()
                });
            }
            Value::Object(ref r) => {
                if r.is_empty() {
                    results.push(MinionResult {
                        host: host.clone(),
                        retcode: retcode.clone(),
                        ..MinionResult::default()
                    });
                }

                for (command, command_result) in r.iter() {
                    trace!("command: {:#?}", command);
                    trace!("command_result: {:#?}", command_result);

                    let result = match command_result.get("comment") {
                        Some(r) => {
                            match r.as_str() {
                                Some(s) => Some(s.to_string()),
                                None => return Err(ResultError::ConvertValueToString),
                            }
                        }
                        None => None,
                    };

                    let output = match command_result.get("changes") {
                        Some(r) => {
                            match r.get("diff") {
                                Some(d) => {
                                    match d.as_str() {
                                        Some(i) => Some(i.to_string()),
                                        None => return Err(ResultError::ConvertDiffToString),
                                    }
                                }
                                None => None,
                            }
                        }
                        None => None,
                    };

                    results.push(MinionResult {
                        command: Some(command.to_string()),
                        host: host.clone(),
                        output: output,
                        result: result,
                        retcode: retcode.clone(),
                    });
                }
            }
        };
    }

    for host in no_return {
        results.push(MinionResult {
            host: host,
            retcode: Retcode::Failure,
            output: Some("Minion did not respond. No job will be sent.".to_string()),
            ..MinionResult::default()
        });
    }

    Ok(results)
}

fn get_compressed(results: MinionResults) -> DataMap<MinionResult, Vec<String>> {
    // compress output by changeing the hostname to the same value for all results and then just
    // adding all hosts with that value to the map.
    let mut compressed: DataMap<MinionResult, Vec<String>> = DataMap::new();
    for result in results {
        let mut result_no_host = result.clone();
        result_no_host.host = String::new();

        compressed.entry(result_no_host).or_insert_with(Vec::new).push(result.host);
    }

    compressed
}

fn print_compressed(compressed: DataMap<MinionResult, Vec<String>>, changed: bool) {
    let mut unchanged = 0;
    for (result, hosts) in compressed {
        // continue if we only want to print out changes and there are none and the command was a
        // success
        // TODO: make this a filter of the map
        if changed && !result.output.is_some() && result.retcode.is_success() {
            unchanged = unchanged + 1;
            continue;
        }

        println!("");
        println!("{}", "----------".bold());
        println!("");

        // state, command info
        {
            if result.command.is_some() {
                println!("{}", "------".purple());

                if result.command.is_some() {
                    println!("{}",
                             format!("COMMAND: {}", result.command.clone().unwrap()).purple());
                }

                println!("{}\n", "------".purple());
            }
        }

        // hosts
        {
            println!("{}", "------".cyan());
            println!("{}{}", "HOSTS: ".cyan(), hosts.join(", ").as_str());
            println!("{}\n", "------".cyan());
        }

        // output
        {
            println!("{}", "------".yellow());

            match result.retcode {
                Retcode::Success => println!("{}{}", "RETURN CODE: ".yellow(), "Success".green()),
                Retcode::Failure => println!("{}{}", "RETURN CODE: ".yellow(), "Failure".red()),
            }

            if result.result.is_some() {
                println!("{}", "RESULT:".yellow());
                println!("{}\n", result.result.unwrap());
            }

            println!("{}", "OUTPUT:".yellow());
            if result.output.is_some() {
                for line in result.output.unwrap().lines() {
                    if line.starts_with('-') {
                        println!("{}", line.red());
                        continue;
                    }

                    if line.starts_with('+') {
                        println!("{}", line.green());
                        continue;
                    }

                    println!("{}", line);
                }
            } else {
                println!("No changes");
            }
            println!("{}", "------".yellow());
        }
    }

    if changed {
        println!("");
        info!("{} unchanged states", unchanged);
    }
}

fn write_save_file(host_data: String) {
    let save_filename = format!("/tmp/salt-compressor_{}.json", get_time().sec);
    let mut save_file = File::create(save_filename.clone()).expect("can not create save_file");
    save_file.write_all(host_data.as_bytes()).expect("can not write host data to save_file");
    info!("please send me the save file under {} which contains the json \
           data from salt",
          save_filename);
}
<commit_msg>Ran clippy linter.<commit_after>#[macro_use]
extern crate log;
extern crate loggerv;

#[macro_use]
extern crate clap;

extern crate serde_json;

extern crate time;

extern crate regex;

extern crate colored;

use clap::App;
use colored::*;
use log::LogLevel;
use regex::Regex;
use serde_json::Value;
use std::collections::BTreeMap as DataMap;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
use std::io::Write;
use std::process;
use time::get_time;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
struct MinionResult {
    command: Option<String>,
    retcode: Retcode,
    output: Option<String>,
    result: Option<String>,
    host: String,
}

type MinionResults = Vec<MinionResult>;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Retcode {
    Success,
    Failure,
}

impl Retcode {
    fn is_success(&self) -> bool {
        self == &Retcode::Success
    }
}

impl Default for Retcode {
    fn default() -> Retcode {
        Retcode::Failure
    }
}

impl From<u64> for Retcode {
    fn from(input: u64) -> Self {
        if input == 0 {
            Retcode::Success
        } else {
            Retcode::Failure
        }
    }
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml)
        .version(crate_version!())
        .get_matches();
    trace!("matches: {:?}", matches);

    {
        let loglevel: LogLevel = value_t!(matches, "loglevel", LogLevel)
            .expect("can not parse loglevel from args");
        loggerv::init_with_level(loglevel).expect("can not initialize logger with parsed loglevel");
    }

    let changed = matches.is_present("changed");
    let no_save_file = matches.is_present("no_save_file");

    let (host_data, no_return) = {
        let input = matches.value_of("input").expect("can not get input file from args");

        let input = if input == "-" {
            let mut buffer = String::new();
            io::stdin().read_to_string(&mut buffer).expect("can not read from stdin");
            buffer
        } else {
            let mut file = File::open(input).expect("can not open input file");
            let mut input = String::new();
            file.read_to_string(&mut input).expect("can not read input file to string");
            input
        };

        // match all hosts that have not returned as they are not in the json data
        // format is normally like "Minion pricesearch did not respond. No job will be sent."
        let catch_not_returned_minions =
            Regex::new(r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$")
                .expect("regex for catching not returned minions is not valid");

        let mut no_return = Vec::new();
        for host in catch_not_returned_minions.captures_iter(input.as_str()) {
            no_return.push(host[1].to_string());
        }

        // clean up hosts that have not returned from the json data
        (catch_not_returned_minions.replace_all(input.as_str(), "").into_owned(), no_return)
    };

    let value: Value = serde_json::from_str(host_data.as_str())
        .expect("can not convert input data to value. have you run the salt command with \
                 --static?");

    let results = match get_results(&value, no_return) {
        Ok(r) => r,
        Err(e) => {
            error!("can not get results from serde value: {}", e);
            if !no_save_file {
                write_save_file(host_data.as_str());
            }
            process::exit(1)
        }
    };

    trace!("results: {:#?}", results);

    let compressed = get_compressed(results);
    trace!("compressed: {:#?}", compressed);

    print_compressed(compressed, changed);
}

#[derive(Debug)]
enum ResultError {
    ConvertDiffToString,
    ConvertValueToString,
    ReturnCodeNotNumber,
    RetValueIsNone,
    RetValueIsNull,
    RetValueIsNumber,
    ValueNotAnObject,
}

impl fmt::Display for ResultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultError::ConvertDiffToString => write!(f, "can not convert diff to string"),
            ResultError::ConvertValueToString => write!(f, "can not convert value to string"),
            ResultError::ReturnCodeNotNumber => write!(f, "returncode is not a number"),
            ResultError::RetValueIsNone => write!(f, "ret value is none"),
            ResultError::RetValueIsNull => write!(f, "ret value is null"),
            ResultError::RetValueIsNumber => write!(f, "ret value is number"),
            ResultError::ValueNotAnObject => write!(f, "value it not an object"),
        }
    }
}

fn get_results(value: &Value, no_return: Vec<String>) -> Result<MinionResults, ResultError> {
    if !value.is_object() {
        return Err(ResultError::ValueNotAnObject);
    }

    let mut results: MinionResults = Vec::new();

    for (host, values) in value.as_object().unwrap().iter() {
        trace!("host: {:#?}", host);
        trace!("values: {:#?}", values);

        let retcode: Retcode = match values.get("retcode") {
            Some(o) => {
                match o.as_u64() {
                    Some(v) => v.into(),
                    None => return Err(ResultError::ReturnCodeNotNumber),
                }
            }
            None => {
                warn!("host {} does not have a return code", host);
                Retcode::Failure
            }
        };

        let ret = match values.get("ret") {
            None => return Err(ResultError::RetValueIsNone),
            Some(r) => r,
        };

        match *ret {
            Value::Null => return Err(ResultError::RetValueIsNull),
            Value::Bool(r) => {
                let (command, result, output) = if r {
                    (None, Some("true".to_string()), None)
                } else {
                    (None, Some("false".to_string()), None)
                };

                results.push(MinionResult {
                    command: command,
                    host: host.clone(),
                    output: output,
                    result: result,
                    retcode: retcode,
                });
            }
            Value::Number(_) => return Err(ResultError::RetValueIsNumber),
            Value::String(ref r) => {
                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(r.clone()),
                    retcode: retcode,
                    ..MinionResult::default()
                });
            }

            Value::Array(ref r) => {
                let values: Vec<_> = r.iter()
                    .map(|v| v.as_str().expect("can not convert the array value to a string"))
                    .collect();

                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(values.join("\n").to_string()),
                    retcode: retcode.clone(),
                    ..MinionResult::default()
                });
            }
            Value::Object(ref r) => {
                if r.is_empty() {
                    results.push(MinionResult {
                        host: host.clone(),
                        retcode: retcode.clone(),
                        ..MinionResult::default()
                    });
                }

                for (command, command_result) in r.iter() {
                    trace!("command: {:#?}", command);
                    trace!("command_result: {:#?}", command_result);

                    let result = match command_result.get("comment") {
                        Some(r) => {
                            match r.as_str() {
                                Some(s) => Some(s.to_string()),
                                None => return Err(ResultError::ConvertValueToString),
                            }
                        }
                        None => None,
                    };

                    let output = match command_result.get("changes") {
                        Some(r) => {
                            match r.get("diff") {
                                Some(d) => {
                                    match d.as_str() {
                                        Some(i) => Some(i.to_string()),
                                        None => return Err(ResultError::ConvertDiffToString),
                                    }
                                }
                                None => None,
                            }
                        }
                        None => None,
                    };

                    results.push(MinionResult {
                        command: Some(command.to_string()),
                        host: host.clone(),
                        output: output,
                        result: result,
                        retcode: retcode.clone(),
                    });
                }
            }
        };
    }

    for host in no_return {
        results.push(MinionResult {
            host: host,
            retcode: Retcode::Failure,
            output: Some("Minion did not respond. No job will be sent.".to_string()),
            ..MinionResult::default()
        });
    }

    Ok(results)
}

fn get_compressed(results: MinionResults) -> DataMap<MinionResult, Vec<String>> {
    // compress output by changeing the hostname to the same value for all results and then just
    // adding all hosts with that value to the map.
    let mut compressed: DataMap<MinionResult, Vec<String>> = DataMap::new();
    for result in results {
        let mut result_no_host = result.clone();
        result_no_host.host = String::new();

        compressed.entry(result_no_host).or_insert_with(Vec::new).push(result.host);
    }

    compressed
}

fn print_compressed(compressed: DataMap<MinionResult, Vec<String>>, changed: bool) {
    let mut unchanged = 0;
    for (result, hosts) in compressed {
        // continue if we only want to print out changes and there are none and the command was a
        // success
        // TODO: make this a filter of the map
        if changed && !result.output.is_some() && result.retcode.is_success() {
            unchanged += 1;
            continue;
        }

        println!("");
        println!("{}", "----------".bold());
        println!("");

        // state, command info
        {
            if result.command.is_some() {
                println!("{}", "------".purple());

                if result.command.is_some() {
                    println!("{}",
                             format!("COMMAND: {}", result.command.clone().unwrap()).purple());
                }

                println!("{}\n", "------".purple());
            }
        }

        // hosts
        {
            println!("{}", "------".cyan());
            println!("{}{}", "HOSTS: ".cyan(), hosts.join(", ").as_str());
            println!("{}\n", "------".cyan());
        }

        // output
        {
            println!("{}", "------".yellow());

            match result.retcode {
                Retcode::Success => println!("{}{}", "RETURN CODE: ".yellow(), "Success".green()),
                Retcode::Failure => println!("{}{}", "RETURN CODE: ".yellow(), "Failure".red()),
            }

            if result.result.is_some() {
                println!("{}", "RESULT:".yellow());
                println!("{}\n", result.result.unwrap());
            }

            println!("{}", "OUTPUT:".yellow());
            if result.output.is_some() {
                for line in result.output.unwrap().lines() {
                    if line.starts_with('-') {
                        println!("{}", line.red());
                        continue;
                    }

                    if line.starts_with('+') {
                        println!("{}", line.green());
                        continue;
                    }

                    println!("{}", line);
                }
            } else {
                println!("No changes");
            }
            println!("{}", "------".yellow());
        }
    }

    if changed {
        println!("");
        info!("{} unchanged states", unchanged);
    }
}

fn write_save_file(host_data: &str) {
    let save_filename = format!("/tmp/salt-compressor_{}.json", get_time().sec);
    let mut save_file = File::create(save_filename.clone()).expect("can not create save_file");
    save_file.write_all(host_data.as_bytes()).expect("can not write host data to save_file");
    info!("please send me the save file under {} which contains the json \
           data from salt",
          save_filename);
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::env;
use std::fs::File;
use std::io::{self, Read};
use handlebars::{Handlebars, to_json};

extern crate handlebars;

struct Col {
    pub buffers: Vec<String>
}
impl Col {
    fn new() -> Col {
        Col { buffers: Vec::new() }
    }
    fn add_buf(&mut self) {
        self.buffers.push(String::new());
    }
    fn append(&mut self, chr: char) {
        let mut len = self.buffers.len();

        if len == 0 {
            self.add_buf();
            len = 1;
        }

        let ref mut buffer = self.buffers[len-1];
        buffer.push(chr);
    }
    fn append_str(&mut self, str: &str) {
        for chr in str.chars() {
            self.append(chr);
        }
    }
    fn out(&self, template: &Template) -> String {
        let mut out: String = String::new();

        out.push_str(template.col_start.as_str());
        for ref buf in self.buffers.iter() {
            out.push_str(format!("{}{}{}",
                    template.segment_start.as_str(),
                    buf,
                    template.segment_end.as_str()
                ).as_str());
        }
        out.push_str(template.col_end.as_str());

        out
    }
}

struct Row {
    pub cols: Vec<Col>
}
impl Row {
    fn add_col(&mut self, col: Col) {
        self.cols.push(col);
    }
    fn get_col(&mut self, index: u8) -> &mut Col {
        self.cols.get_mut(index as usize).unwrap()
    }
    fn col_count(&self) -> usize {
        self.cols.len()
    }
    fn new() -> Row {
        let mut row: Row = Row { cols: Vec::new() };
        row.add_col(Col::new());
        row
    }
    fn out(&self, template: &Template) -> String {
        let mut out: String = String::new();

        out.push_str(template.row_start.as_str());
        for ref col in self.cols.iter() {
            let formatted = format!("{}", col.out(template));
            out.push_str(formatted.as_str());
        }
        out.push_str(template.row_end.as_str());

        out
    }
}

struct Template {
    container_start: String,
    container_end: String,
    row_start: String,
    row_end: String,
    col_start: String,
    col_end: String,
    segment_start: String,
    segment_end: String,
    label: String,
    text: String,
    checkbox: String,
    radio: String,
    textarea: String,
    hr: String,
    button: String,
    select: String
}
impl Template {
    fn from_vec(vec: &Vec<&str>) -> Template {
        Template {
            container_start: vec[0].to_string().trim().to_string(),
            row_start: vec[1].to_string().trim().to_string(),
            col_start: vec[2].to_string().trim().to_string(),
            segment_start: vec[3].to_string().trim().to_string(),
            segment_end: vec[4].to_string().trim().to_string(),
            col_end: vec[5].to_string().trim().to_string(),
            row_end: vec[6].to_string().trim().to_string(),
            container_end: vec[7].to_string().trim().to_string(),
            label: vec[8].to_string().trim().to_string(),
            text: vec[9].to_string().trim().to_string(),
            checkbox: vec[10].to_string().trim().to_string(),
            radio: vec[11].to_string().trim().to_string(),
            textarea: vec[12].to_string().trim().to_string(),
            button: vec[13].to_string().trim().to_string(),
            select: vec[14].to_string().trim().to_string(),
            hr: vec[15].to_string().trim().to_string()
        }
    }
}

#[derive(PartialEq)]
enum InputType {
    Text,
    Radio,
    Checkbox,
    Textarea
}

#[derive(PartialEq)]
enum Type {
    Unknown,
    Input(InputType),
    Button,
    Label,
    Hr,
    Select
}

struct Working {
    str: String,
    until: char,
    work_type: Type
}
impl Working {
    fn new(until: char, work_type: Type) -> Working {
        Working {
            str: String::new(),
            until: until,
            work_type: work_type
        }
    }
    fn is_working(&self) -> bool {
        self.until != '\0'
    }
    fn append(&mut self, chr: char) {
        self.str.push(chr);
    }
    fn compile(&mut self, template: &Template) -> String {
        // Convert type to template
        let ref template = match self.work_type {
            Type::Button => template.button.as_str(),
            Type::Label => template.label.as_str(),
            Type::Hr => template.hr.as_str(),
            Type::Select => template.select.as_str(),
            Type::Input(ref t) => match t {
                &InputType::Text => template.text.as_str(),
                &InputType::Radio => template.radio.as_str(),
                &InputType::Checkbox => template.checkbox.as_str(),
                &InputType::Textarea => template.textarea.as_str()
            },
            _ => ""
        };

        // No actual template available
        if template.is_empty() {
            return String::new();
        }

        // Compile template
        let mut handlebars = Handlebars::new();
        assert!(handlebars.register_template_string("tmpl", template)
            .is_ok());

        // Bind data to template
        let mut data = BTreeMap::new();
        let val = match self.work_type {
            Type::Select => {
                let options: Vec<&str> = self.str.split(",").collect();
                //let v: Vec<&str> = self.str.split(",").collect();
                //let options: Vec<_> = v.iter().map(|o| ).collect();
                to_json(&options)
            },
            _ => to_json(&self.str)
        };
        data.insert("value".to_string(), val);
        handlebars.render("tmpl", &data).unwrap()
    }
}

/**
 * An empty line implies a new "row"
 * A line starting with a | implies a columnar form section
 * [value] is an input text field
 * {value} is an input textarea field
 * @ is a radio
 * * is a checkbox
 * --- is an hr
 *
 */
fn main() {
    // Read in standard input
    let mut buffer = String::new();
    io::stdin().read_to_string(&mut buffer)
        .expect("Error reading from stdin");

    // Split input by line
    let v: Vec<&str> = buffer.split("\n").collect();

    // Keep track of rows in output
    let mut rows: Vec<Row> = Vec::new();
    let mut row: Row = Row::new();

    // Check arg for template
    let template = match env::args().nth(1) {
        Some(file) => {
            let mut in_file = match File::open(&file) {
                Err(_) => panic!("Unable to open template file"),
                Ok(file) => file,
            };

            let mut content = String::new();
            in_file.read_to_string(&mut content)
                .expect("Unable to process template file");

            let sp: Vec<&str> = content.split("\n").collect();

            Template::from_vec(&sp)
        },
        _ => match env::var("template") {
            Ok(val) => {
                let tv: Vec<&str> = val.split(",").collect();
                Template::from_vec(&tv)
            },
            _ => Template {
                container_start: "".to_string(),
                container_end: "".to_string(),
                row_start: "
".to_string(),
                row_end: "
".to_string(),
                col_start: "".to_string(),
                col_end: "".to_string(),
                segment_start: "".to_string(),
                segment_end: "
".to_string(),
                label: "{[value}}".to_string(),
                text: "<input type=\"text\" value=\"{{value}}\"/>".to_string(),
                checkbox: "<input type=\"checkbox\"/>".to_string(),
                radio: "<input type=\"radio\"/>".to_string(),
                textarea: "<textarea>{{value}}</textarea>".to_string(),
                hr: "
".to_string(),
                button: "<button>{{value}}</button>".to_string(),
                select: "<select>{{#each value}}<option>{{this}}</option>{{/each}}</select>".to_string()
            }
        }
    };

    // Iterate over lines in input
    for line in &v {
        // New row by way of empty line
        if line.chars().count() == 0 {
            rows.push(row);
            row = Row::new();
            continue;
        }

        let mut form_mode = false; // If user toggled form mode
        let mut col_index: u8 = 1; // Keeping tracking of column index
        let mut dashes: u8 = 0; // Keep track of dash count
        let mut working = Working::new('\0', Type::Unknown);

        // Iterate over characters in string
        for (i, c) in line.chars().enumerate() {
            // Match c against known tokens
            match c {
                '-' => {
                    dashes += 1;
                    if dashes == 3 {
                        row.get_col(col_index-1).append_str(
                            Working::new('\0', Type::Hr)
                                .compile(&template).as_str()
                            );
                        break;
                    }
                },

                // Controlling form modes
                '|' => {
                    if i == 0 {
                        form_mode = true;
                        row.get_col(col_index-1).add_buf()
                    } else if form_mode {
                        // Check if we have enough columns in row
                        // if not add a new column
                        if (row.col_count() as u8) < col_index + 1 {
                            row.add_col(Col::new());
                        }

                        // Increment index
                        col_index += 1;

                        row.get_col(col_index-1).add_buf();
                    } else {
                        row.get_col(col_index-1).append(c);
                    }
                },

                // Handle items that have a counterpart
                c if c == '[' && form_mode
                    => working = Working::new(']', Type::Unknown),
                c if c == '{' && form_mode
                    => working = Working::new('}', Type::Label),
                c if c == '(' && form_mode
                    => working = Working::new(')', Type::Button),
                c if c == '<' && form_mode
                    => working = Working::new('>', Type::Select),

                // Handle input type and we are waiting for mode qualifier
                c if c != working.until
                    && working.until != '\0'
                    && working.work_type == Type::Unknown =>
                        match c {
                            'o' => working.work_type = Type::Input(
                                InputType::Radio),
                            '/' => working.work_type = Type::Input(
                                InputType::Checkbox),
                            '+' => working.work_type = Type::Input(
                                InputType::Textarea),
                            c => {
                                working.work_type = Type::Input(
                                    InputType::Text);
                                working.append(c);
                            },
                        },

                // Handle the case we are working towards the end goal of an
                // incoming char
                c if c == working.until => {
                    row.get_col(col_index-1)
                        .append_str(working.compile(&template).as_str());
                    working = Working::new('\0', Type::Unknown);
                },

                // In any other scenario we are building strings
                _ => {
                    if working.is_working() {
                        working.append(c);
                    } else {
                        row.get_col(col_index-1).append(c)
                    }
                }
            }
        }
    }

    rows.push(row);

    print!("{}", template.container_start);
    for row in rows {
        print!("{}", row.out(&template));
    }
    println!("{}", template.container_end);
}
<commit_msg>Adding support for pages<commit_after>use std::collections::BTreeMap;
use std::env;
use std::fs::File;
use std::io::{self, Read};
use handlebars::{Handlebars, to_json};

extern crate handlebars;

struct Col {
    pub buffers: Vec<String>
}
impl Col {
    fn new() -> Col {
        Col { buffers: Vec::new() }
    }
    fn add_buf(&mut self) {
        self.buffers.push(String::new());
    }
    fn append(&mut self, chr: char) {
        let mut len = self.buffers.len();

        if len == 0 {
            self.add_buf();
            len = 1;
        }

        let ref mut buffer = self.buffers[len-1];
        buffer.push(chr);
    }
    fn append_str(&mut self, str: &str) {
        for chr in str.chars() {
            self.append(chr);
        }
    }
    fn out(&self, template: &Template) -> String {
        let mut out: String = String::new();

        out.push_str(template.col_start.as_str());
        for ref buf in self.buffers.iter() {
            out.push_str(format!("{}{}{}",
                    template.segment_start.as_str(),
                    buf,
                    template.segment_end.as_str()
                ).as_str());
        }
        out.push_str(template.col_end.as_str());

        out
    }
}

struct Row {
    cols: Vec<Col>
}
impl Row {
    fn add_col(&mut self, col: Col) {
        self.cols.push(col);
    }
    fn get_col(&mut self, index: u8) -> &mut Col {
        self.cols.get_mut(index as usize).unwrap()
    }
    fn col_count(&self) -> usize {
        self.cols.len()
    }
    fn new() -> Row {
        let mut row: Row = Row { cols: Vec::new() };
        row.add_col(Col::new());
        row
    }
    fn out(&self, template: &Template) -> String {
        let mut out: String = String::new();

        out.push_str("\n");
        out.push_str(template.row_start.as_str());
        for ref col in self.cols.iter() {
            let formatted = format!("{}", col.out(template));
            out.push_str(formatted.as_str());
        }
        out.push_str(template.row_end.as_str());

        out
    }
}

struct Page {
    name: String,
    rows: Vec<Row>
}
impl Page {
    fn new(name: String) -> Page {
        let mut page: Page = Page { name: name, rows: Vec::new() };
        page.add_row(Row::new());
        page
    }
    fn add_row(&mut self, row: Row) {
        self.rows.push(row);
    }
    fn get_current_row(&mut self) -> &mut Row {
        let len = self.rows.len();
        self.rows.get_mut(len-1).unwrap()
    }
    fn out(&self, template: &Template) -> String {
        let mut out: String = String::new();

        out.push_str(render_template(template.container_start.as_str(), &Type::Page, &self.name).as_str());

        for ref row in self.rows.iter() {
            let formatted = format!("{}", row.out(template));
            out.push_str(formatted.as_str());
        }

        out.push_str(render_template(template.container_end.as_str(), &Type::Page, &self.name).as_str());

        out
    }
}

struct Template {
    head: String,
    foot: String,
    container_start: String,
    container_end: String,
    row_start: String,
    row_end: String,
    col_start: String,
    col_end: String,
    segment_start: String,
    segment_end: String,
    label: String,
    text: String,
    checkbox: String,
    radio: String,
    textarea: String,
    hr: String,
    button: String,
    select: String
}
impl Template {
    fn from_vec(vec: &Vec<&str>) -> Template {
        Template {
            head: vec[0].to_string().trim().to_string(),
            foot: vec[1].to_string().trim().to_string(),
            container_start: vec[2].to_string().trim().to_string(),
            container_end: vec[3].to_string().trim().to_string(),
            row_start: vec[4].to_string().trim().to_string(),
            row_end: vec[5].to_string().trim().to_string(),
            col_start: vec[6].to_string().trim().to_string(),
            col_end: vec[7].to_string().trim().to_string(),
            segment_start: vec[8].to_string().trim().to_string(),
            segment_end: vec[9].to_string().trim().to_string(),
            label: vec[10].to_string().trim().to_string(),
            text: vec[11].to_string().trim().to_string(),
            checkbox: vec[12].to_string().trim().to_string(),
            radio: vec[13].to_string().trim().to_string(),
            textarea: vec[14].to_string().trim().to_string(),
            button: vec[15].to_string().trim().to_string(),
            select: vec[16].to_string().trim().to_string(),
            hr: vec[17].to_string().trim().to_string()
        }
    }
}

#[derive(PartialEq)]
enum InputType {
    Text,
    Radio,
    Checkbox,
    Textarea
}

#[derive(PartialEq)]
enum Type {
    Unknown,
    Page,
    Input(InputType),
    Button,
    Label,
    Hr,
    Select
}

struct Working {
    str: String,
    until: char,
    work_type: Type
}
impl Working {
    fn new(until: char, work_type: Type) -> Working {
        Working {
            str: String::new(),
            until: until,
            work_type: work_type
        }
    }
    fn is_working(&self) -> bool {
        self.until != '\0'
    }
    fn append(&mut self, chr: char) {
        self.str.push(chr);
    }
    fn compile(&mut self, template: &Template) -> String {
        // Convert type to template
        let ref template = match self.work_type {
            Type::Page => template.container_start.as_str(),
            Type::Button => template.button.as_str(),
            Type::Label => template.label.as_str(),
            Type::Hr => template.hr.as_str(),
            Type::Select => template.select.as_str(),
            Type::Input(ref t) => match t {
                &InputType::Text => template.text.as_str(),
                &InputType::Radio => template.radio.as_str(),
                &InputType::Checkbox => template.checkbox.as_str(),
                &InputType::Textarea => template.textarea.as_str()
            },
            _ => ""
        };

        // No actual template available
        if template.is_empty() {
            return String::new();
        }

        render_template(template, &self.work_type, &self.str)
    }
}

fn render_template(template: &str, work_type: &Type, str: &String) -> String {
    let val = match *work_type {
        Type::Select => {
            let options: Vec<&str> = str.split(",").collect();
            //let v: Vec<&str> = self.str.split(",").collect();
            //let options: Vec<_> = v.iter().map(|o| ).collect();
            to_json(&options)
        },
        _ => to_json(&str)
    };

    // Compile template
    let mut handlebars = Handlebars::new();
    assert!(handlebars.register_template_string("tmpl", template)
        .is_ok());

    // Bind data to template
    let mut data = BTreeMap::new();
    data.insert("value".to_string(), val);
    handlebars.render("tmpl", &data).unwrap()
}

/*
fn compile(rows: &mut Vec<Row>, pages: &mut HashMap<String, Box<String>>, template: &Template) {
    if rows.len() == 0 {
        return;
    }

    let mut content = String::new();
    let mut page = String::new();

    while rows.len() > 0 {
        let row = rows.pop().unwrap();
        content.push_str(format!("{}", row.out(&template)).as_str());
        page = row.page.clone();
    }

    println!("New page {}", page);
    pages.insert(page.to_string(), Box::new(content));
}
*/

/**
 * An empty line implies a new "row"
 * A line starting with a | implies a columnar form section
 * [value] is an input text field
 * {value} is an input textarea field
 * @ is a radio
 * * is a checkbox
 * --- is an hr
 *
 */
fn main() {
    // Read in standard input
    let mut buffer = String::new();
    io::stdin().read_to_string(&mut buffer)
        .expect("Error reading from stdin");

    // Split input by line
    let v: Vec<&str> = buffer.split("\n").collect();

    // Keep track of rows in output

    let mut pages: Vec<Page> = Vec::new();

    // Check arg for template
    let template = match env::args().nth(1) {
        Some(file) => {
            let mut in_file = match File::open(&file) {
                Err(_) => panic!("Unable to open template file"),
                Ok(file) => file,
            };

            let mut content = String::new();
            in_file.read_to_string(&mut content)
                .expect("Unable to process template file");

            let sp: Vec<&str> = content.split("\n").collect();

            Template::from_vec(&sp)
        },
        _ => match env::var("template") {
            Ok(val) => {
                let tv: Vec<&str> = val.split(",").collect();
                Template::from_vec(&tv)
            },
            _ => Template {
                head: "".to_string(),
                foot: "".to_string(),
                container_start: "
".to_string(),
                container_end: "
".to_string(),
                row_start: "
".to_string(),
                row_end: "
".to_string(),
                col_start: "".to_string(),
                col_end: "".to_string(),
                segment_start: "".to_string(),
                segment_end: "
".to_string(),
                label: "{[value}}".to_string(),
                text: "<input type=\"text\" value=\"{{value}}\"/>".to_string(),
                checkbox: "<input type=\"checkbox\"/>".to_string(),
                radio: "<input type=\"radio\"/>".to_string(),
                textarea: "<textarea>{{value}}</textarea>".to_string(),
                hr: "
".to_string(),
                button: "<button>{{value}}</button>".to_string(),
                select: "<select>{{#each value}}<option>{{this}}</option>{{/each}}</select>".to_string()
            }
        }
    };

    let mut page: Page = Page::new("default".to_string());

    // Iterate over lines in input
    for line in &v {
        // New row by way of empty line
        if line.chars().count() == 0 {
            page.add_row(Row::new());
            continue;
        }

        let mut form_mode = false; // If user toggled form mode
        let mut col_index: u8 = 1; // Keeping tracking of column index
        let mut dashes: u8 = 0; // Keep track of dash count
        let mut working = Working::new('\0', Type::Unknown);

        // Iterate over characters in string
        for (i, c) in line.chars().enumerate() {
            // Match c against known tokens
            match c {
                '-' => {
                    dashes += 1;
                    if dashes == 3 {
                        page.get_current_row().get_col(col_index-1).append_str(
                            Working::new('\0', Type::Hr)
                                .compile(&template).as_str()
                            );
                        break;
                    }
                },

                // New page
                '#' if i == 0 => {
                    pages.push(page);
                    page = Page::new(line[1..].to_string());
                    break;
                },
                /*{

                    row.get_col(col_index-1).append_str(
                        Working::new('\n', Type::Page)
                            .compile(&template).as_str()
                        );
                    //compile(&mut rows, &mut pages, &template);
                    //page = &line[1..];
                    //row = Row::new(page);
                    break;
                },
                */

                // Controlling form modes
                '|' => {
                    let mut row = page.get_current_row();
                    if i == 0 {
                        form_mode = true;
                        row.get_col(col_index-1).add_buf()
                    } else if form_mode {
                        // Check if we have enough columns in row
                        // if not add a new column
                        if (row.col_count() as u8) < col_index + 1 {
                            row.add_col(Col::new());
                        }

                        // Increment index
                        col_index += 1;

                        row.get_col(col_index-1).add_buf();
                    } else {
                        row.get_col(col_index-1).append(c);
                    }
                },

                // Handle items that have a counterpart
                c if c == '[' && form_mode
                    => working = Working::new(']', Type::Unknown),
                c if c == '{' && form_mode
                    => working = Working::new('}', Type::Label),
                c if c == '(' && form_mode
                    => working = Working::new(')', Type::Button),
                c if c == '<' && form_mode
                    => working = Working::new('>', Type::Select),

                // Handle input type and we are waiting for mode qualifier
                c if c != working.until
                    && working.until != '\0'
                    && working.work_type == Type::Unknown =>
                        match c {
                            'o' => working.work_type = Type::Input(
                                InputType::Radio),
                            '/' => working.work_type = Type::Input(
                                InputType::Checkbox),
                            '+' => working.work_type = Type::Input(
                                InputType::Textarea),
                            c => {
                                working.work_type = Type::Input(
                                    InputType::Text);
                                working.append(c);
                            },
                        },

                // Handle the case we are working towards the end goal of an
                // incoming char
                c if c == working.until => {
                    page.get_current_row().get_col(col_index-1)
                        .append_str(working.compile(&template).as_str());
                    working = Working::new('\0', Type::Unknown);
                },

                // In any other scenario we are building strings
                _ => {
                    if working.is_working() {
                        working.append(c);
                    } else {
                        page.get_current_row().get_col(col_index-1).append(c)
                    }
                }
            }
        }
    }

    pages.push(page);

    print!("{}", &template.head);

    for page in pages {
        print!("{}", page.out(&template));
    }

    println!("{}", &template.foot);
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate tempdir;
extern crate tuf as _tuf;
extern crate url;

use clap::{App, AppSettings, SubCommand, Arg, ArgMatches};
use std::path::PathBuf;
use _tuf::{Tuf, Config, Error};
use url::Url;

// TODO logging

fn main() {
    let matches = parser().get_matches();
    env_logger::init().unwrap();

    match run_main(matches) {
        Ok(()) => std::process::exit(0),
        Err(e) => {
            error!("{:?}", e);
            std::process::exit(1);
        }
    }
}

fn run_main(matches: ArgMatches) -> Result<(), Error> {
    let config = Config::build()
        .url(Url::parse(matches.value_of("url").unwrap()).unwrap())
        .local_path(PathBuf::from(matches.value_of("path").unwrap()))
        .finish()
        .expect("bad config"); // TODO don't use expect

    if let Some(_) = matches.subcommand_matches("init") {
        let path = PathBuf::from(matches.value_of("path").unwrap());
        cmd_init(&path)
    } else if let Some(_) = matches.subcommand_matches("list") {
        let mut tuf = Tuf::new(config).unwrap(); // TODO unwrap
        cmd_list(&mut tuf)
    } else if let Some(matches) = matches.subcommand_matches("verify") {
        let mut tuf = Tuf::new(config).unwrap(); // TODO unwrap
        cmd_verify(&mut tuf, matches.value_of("target").unwrap())
    } else {
        unreachable!() // because of AppSettings::SubcommandRequiredElseHelp
    }
}

fn url_validator(url: String) -> Result<(), String> {
    Url::parse(&url)
        .map(|_| ())
        .map_err(|_| "URL was not valid".into())
}

fn parser<'a, 'b>() -> App<'a, 'b> {
    App::new("tuf")
        .version(env!("CARGO_PKG_VERSION"))
        .about("CLI tool for verifying TUF metadata and downloading targets")
        .settings(&[AppSettings::SubcommandRequiredElseHelp])
        .arg(Arg::with_name("verbose")
            .short("v")
            .long("verbose")
            .multiple(true)
            .help("Increase the verbosity of output to stderr"))
        .arg(Arg::with_name("url")
            .short("U")
            .long("url")
            .takes_value(true)
            .required(true)
            .validator(url_validator)
            .help("URL of the TUF repo (local or remote)"))
        .arg(Arg::with_name("path")
            .short("p")
            .long("path")
            .takes_value(true)
            .required(true)
            .help("Local path the TUF repo"))
        .subcommand(SubCommand::with_name("init").about("Initializes a new TUF repo"))
        .subcommand(SubCommand::with_name("list").about("Lists available targets"))
        .subcommand(SubCommand::with_name("verify")
            .about("Verifies a target")
            .arg(Arg::with_name("target")
                .takes_value(true)
                .required(true)
                .help("The full (non-local) path of the target to verify")))
}

fn cmd_init(local_path: &PathBuf) -> Result<(), Error> {
    Tuf::initialize(local_path)
}

fn cmd_list(tuf: &mut Tuf) -> Result<(), Error> {
    let mut targets = tuf.list_targets();
    targets.sort();

    for target in targets.iter() {
        println!("{}", target);
    }

    Ok(())
}

fn cmd_verify(tuf: &mut Tuf, target: &str) -> Result<(), Error> {
    tuf.verify_target(target)
}

#[cfg(test)]
mod test {
    use super::*;
    use std::fs::{self, DirBuilder};
    use std::path::Path;
    use tempdir::TempDir;

    #[test]
    fn test_clap() {
        let _ = parser();
    }

    fn init_temp(temp: &Path) {
        for dir in vec!["metadata/latest", "metadata/archive", "targets"].iter() {
            DirBuilder::new()
                .recursive(true)
                .create(temp.join(dir))
                .expect(&format!("couldn't create path {}:", dir));
        }

        for file in vec!["metadata/latest/root.json",
                         "metadata/latest/targets.json",
                         "metadata/latest/timestamp.json",
                         "metadata/latest/snapshot.json",
                         "targets/big-file.txt",
                         "targets/hack-eryone.sh"]
            .iter() {
            fs::copy(format!("./tests/repo-1/{}", file), temp.join(file))
                .expect(&format!("copy failed: {}", file));
        }
    }

    #[ignore]
    fn run_verify() {
        let temp = TempDir::new("rust-tuf").expect("couldn't make temp dir");
        init_temp(temp.path());

        let matches = parser()
            .get_matches_from_safe(vec!["tuf",
                                        "--url",
                                        "file:///tmp",
                                        "--path",
                                        temp.path().to_str().expect("path not utf-8"),
                                        "verify",
                                        "big-file.txt"])
            .expect("parse error");

        assert_eq!(run_main(matches), Ok(()));
    }
}
<commit_msg>fixed cli test<commit_after>extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate log;
#[cfg(test)]
extern crate tempdir;
extern crate tuf as _tuf;
extern crate url;

use clap::{App, AppSettings, SubCommand, Arg, ArgMatches};
use std::path::PathBuf;
use _tuf::{Tuf, Config, Error};
use url::Url;

// TODO logging

fn main() {
    let matches = parser().get_matches();
    env_logger::init().unwrap();

    match run_main(matches) {
        Ok(()) => std::process::exit(0),
        Err(e) => {
            error!("{:?}", e);
            std::process::exit(1);
        }
    }
}

fn run_main(matches: ArgMatches) -> Result<(), Error> {
    let config = Config::build()
        .url(Url::parse(matches.value_of("url").unwrap()).unwrap())
        .local_path(PathBuf::from(matches.value_of("path").unwrap()))
        .finish()
        .expect("bad config"); // TODO don't use expect

    if let Some(_) = matches.subcommand_matches("init") {
        let path = PathBuf::from(matches.value_of("path").unwrap());
        cmd_init(&path)
    } else if let Some(_) = matches.subcommand_matches("list") {
        let mut tuf = Tuf::new(config).unwrap(); // TODO unwrap
        cmd_list(&mut tuf)
    } else if let Some(matches) = matches.subcommand_matches("verify") {
        let mut tuf = Tuf::new(config).unwrap(); // TODO unwrap
        cmd_verify(&mut tuf, matches.value_of("target").unwrap())
    } else {
        unreachable!() // because of AppSettings::SubcommandRequiredElseHelp
    }
}

fn url_validator(url: String) -> Result<(), String> {
    Url::parse(&url)
        .map(|_| ())
        .map_err(|_| "URL was not valid".into())
}

fn parser<'a, 'b>() -> App<'a, 'b> {
    App::new("tuf")
        .version(env!("CARGO_PKG_VERSION"))
        .about("CLI tool for verifying TUF metadata and downloading targets")
        .settings(&[AppSettings::SubcommandRequiredElseHelp])
        .arg(Arg::with_name("verbose")
            .short("v")
            .long("verbose")
            .multiple(true)
            .help("Increase the verbosity of output to stderr"))
        .arg(Arg::with_name("url")
            .short("U")
            .long("url")
            .takes_value(true)
            .required(true)
            .validator(url_validator)
            .help("URL of the TUF repo (local or remote)"))
        .arg(Arg::with_name("path")
            .short("p")
            .long("path")
            .takes_value(true)
            .required(true)
            .help("Local path the TUF repo"))
        .subcommand(SubCommand::with_name("init").about("Initializes a new TUF repo"))
        .subcommand(SubCommand::with_name("list").about("Lists available targets"))
        .subcommand(SubCommand::with_name("verify")
            .about("Verifies a target")
            .arg(Arg::with_name("target")
                .takes_value(true)
                .required(true)
                .help("The full (non-local) path of the target to verify")))
}

fn cmd_init(local_path: &PathBuf) -> Result<(), Error> {
    Tuf::initialize(local_path)
}

fn cmd_list(tuf: &mut Tuf) -> Result<(), Error> {
    let mut targets = tuf.list_targets();
    targets.sort();

    for target in targets.iter() {
        println!("{}", target);
    }

    Ok(())
}

fn cmd_verify(tuf: &mut Tuf, target: &str) -> Result<(), Error> {
    tuf.verify_target(target)
}

#[cfg(test)]
mod test {
    use super::*;
    use std::fs::{self, DirBuilder};
    use std::path::Path;
    use tempdir::TempDir;

    #[test]
    fn test_clap() {
        let _ = parser();
    }

    fn init_temp(temp: &Path) {
        let vector_path = "./tests/tuf-test-vectors/vectors/001";

        for dir in vec!["metadata/latest", "metadata/archive", "targets"].iter() {
            DirBuilder::new()
                .recursive(true)
                .create(temp.join(dir))
                .expect(&format!("couldn't create path {}:", dir));
        }

        for file in vec!["root.json",
                         "targets.json",
                         "timestamp.json",
                         "snapshot.json"]
            .iter() {
            fs::copy(format!("{}/repo/{}", vector_path, file),
                     temp.join("metadata").join("latest").join(file))
                .expect(&format!("copy failed: {}", file));
        }

        fs::copy(format!("{}/repo/targets/file.txt", vector_path),
                 temp.join("targets").join("file.txt"))
                .expect(&format!("copy failed for target"));
    }

    #[test]
    fn run_verify() {
        let temp = TempDir::new("rust-tuf").expect("couldn't make temp dir");
        init_temp(temp.path());

        let matches = parser()
            .get_matches_from_safe(vec!["tuf",
                                        "--url",
                                        "file:///tmp",
                                        "--path",
                                        temp.path().to_str().expect("path not utf-8"),
                                        "verify",
                                        "targets/file.txt"])
            .expect("parse error");

        assert_eq!(run_main(matches), Ok(()));
    }
}
<|endoftext|>


<commit_before>// TODO: many items from tokio-core::io have been deprecated in favour of tokio-io
#![allow(deprecated)]

#[cfg(debug_assertions)]
#[macro_use] extern crate log;
#[cfg(debug_assertions)]
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_signal;

#[cfg(debug_assertions)]
use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
#[cfg(debug_assertions)]
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_core::io::IoStream;
use std::mem;

use librespot::spirc::{Spirc, SpircTask};
use librespot::authentication::{get_credentials, Credentials};
#[cfg(not(target_os="windows"))]
use librespot::authentication::discovery::{discovery, DiscoveryStream};
use librespot::audio_backend;
use librespot::cache::Cache;
use librespot::player::Player;
use librespot::session::{Bitrate, Config, Session};
use librespot::mixer;
use librespot::util::SpotifyId;

const VERSION: &'static str = concat!(env!("CARGO_PKG_NAME"), " v", env!("CARGO_PKG_VERSION"));

fn usage(program: &str, opts: &getopts::Options) -> String {
	println!("{}", VERSION.to_string());

	let brief = format!("Usage: {} [options]", program);
	opts.usage(&brief)
}

#[cfg(debug_assertions)]
fn setup_logging(verbose: bool) {
	let mut builder = LogBuilder::new();
	match env::var("RUST_LOG") {
		Ok(config) => {
			builder.parse(&config);
			builder.init().unwrap();

			if verbose {
				warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
			}
		}
		Err(_) => {
			if verbose {
				builder.parse("mdns=info,librespot=trace");
			} else {
				builder.parse("mdns=error,librespot=warn");
			}
			builder.init().unwrap();
		}
	}
}

#[derive(Clone)]
struct Setup {
	name: String,
	cache: Option<Cache>,
	config: Config,
	credentials: Option<Credentials>,
	authenticate: bool,
	enable_discovery: bool,

	get_token: bool,
	client_id: Option<String>,
	scope: Option<String>,

	single_track: Option<String>,
	start_position: u32,
}

fn setup(args: &[String]) -> Setup {
	let mut opts = getopts::Options::new();
	opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
		.optflag("", "enable-audio-cache", "Enable caching of the audio data.")
		.optflag("", "disable-audio-cache", "(Only here fore compatibility with librespot - audio cache is disabled by default).")
		.reqopt("n", "name", "Device name", "NAME")
        .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
		.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
		.optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
		.optopt("", "onchange", "Run PROGRAM when playback changes (new track, seeking etc.).", "PROGRAM")
		.optopt("", "single-track", "Play a single track ID and exit.", "ID")
		.optopt("", "start-position", "Position (in ms) where playback should be started. Only valid with the --single-track option.", "STARTPOSITION")
		.optopt("u", "username", "Username to sign in with", "USERNAME")
		.optopt("p", "password", "Password", "PASSWORD")
		.optflag("a", "authenticate", "Authenticate given username and password. Make sure you define a cache folder to store credentials.")
		.optflag("", "disable-discovery", "Disable discovery mode")
		.optflag("t", "get-token", "Get oauth token to be used with the web API etc.")
		.optopt("i", "client-id", "A Spotify client_id to be used to get the oauth token. Required with the --get-token request.", "CLIENT_ID")
		.optopt("", "scope", "The scopes you want to have access to with the oauth token.", "SCOPE")
		.optflag("x", "check", "Run quick internal check");

	#[cfg(debug_assertions)]
	opts.optflag("v", "verbose", "Enable verbose output");

	let matches = match opts.parse(&args[1..]) {
		Ok(m) => m,
		Err(f) => {
			writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
			exit(1);
		}
	};

	if matches.opt_present("check") {
		println!("ok {}", VERSION.to_string());
		exit(1);
	}

	#[cfg(debug_assertions)]
	{
		let verbose = matches.opt_present("verbose");
		setup_logging(verbose);
	}

    let bitrate = matches.opt_str("b").as_ref()
        .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
        .unwrap_or(Bitrate::Bitrate320);

	let name = matches.opt_str("name").unwrap();
	let device_id = librespot::session::device_id(&name);

	let use_audio_cache = matches.opt_present("enable-audio-cache") && !matches.opt_present("disable-audio-cache");

	let cache = matches.opt_str("c").map(|cache_location| {
		Cache::new(PathBuf::from(cache_location), use_audio_cache)
	});

	let cached_credentials = cache.as_ref().and_then(Cache::credentials);

	let credentials = get_credentials(matches.opt_str("username"), matches.opt_str("password"), cached_credentials);

	let authenticate = matches.opt_present("authenticate");

#[cfg(not(target_os="windows"))]
	let enable_discovery = !matches.opt_present("disable-discovery");
#[cfg(target_os="windows")]
	let enable_discovery = false;

	let start_position = matches.opt_str("start-position")
		.unwrap_or("0".to_string())
		.parse().unwrap_or(0.0);

	let config = Config {
		user_agent: VERSION.to_string(),
		device_id: device_id,
		bitrate: bitrate,
		onstart: matches.opt_str("onstart"),
		onstop: matches.opt_str("onstop"),
		onchange: matches.opt_str("onchange"),
	};

	Setup {
		name: name,
		cache: cache,
		config: config,
		credentials: credentials,
		authenticate: authenticate,
		enable_discovery: enable_discovery,

		get_token: matches.opt_present("get-token"),
		client_id: matches.opt_str("client-id"),
		scope: matches.opt_str("scope"),

		single_track: matches.opt_str("single-track"),
		start_position: (start_position * 1000.0) as u32,
	}
}

struct Main {
	name: String,
	cache: Option<Cache>,
	config: Config,
	handle: Handle,

#[cfg(not(target_os="windows"))]
	discovery: Option<DiscoveryStream>,
#[cfg(target_os="windows")]
	discovery: Option<String>,
	signal: IoStream<()>,

	spirc: Option<Spirc>,
	spirc_task: Option<SpircTask>,
	connect: Box<Future<Item=Session, Error=io::Error>>,

	player: Option<Player>,

	shutdown: bool,
	authenticate: bool,
}

impl Main {
	fn new(
		handle: Handle,
		name: String,
		config: Config,
		cache: Option<Cache>,
		authenticate: bool,
	) -> Main
	{
		Main {
			handle: handle.clone(),
			name: name,
			cache: cache,
			config: config,

			connect: Box::new(futures::future::empty()),
			discovery: None,
			spirc: None,
			spirc_task: None,

			player: None,

			shutdown: false,
			authenticate: authenticate,
			signal: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
		}
	}

#[cfg(not(target_os="windows"))]
	fn discovery(&mut self) {
		let device_id = self.config.device_id.clone();
		let name = self.name.clone();

		self.discovery = Some(discovery(&self.handle, name, device_id).unwrap());
	}

	fn credentials(&mut self, credentials: Credentials) {
		let config = self.config.clone();
		let handle = self.handle.clone();

		let connection = Session::connect(config, credentials, self.cache.clone(), handle);

		self.connect = connection;
		self.spirc = None;
		let task = mem::replace(&mut self.spirc_task, None);
		if let Some(task) = task {
			self.handle.spawn(task);
		}
	}
}

impl Future for Main {
	type Item = ();
	type Error = ();

	fn poll(&mut self) -> Poll<(), ()> {
		loop {
			let mut progress = false;

#[cfg(not(target_os="windows"))] {
			if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
				if let Some(ref spirc) = self.spirc {
					spirc.shutdown();
				}
				self.credentials(creds);

				progress = true;
			}
}

			if let Async::Ready(session) = self.connect.poll().unwrap() {
				if self.authenticate {
					if !self.shutdown {
						if let Some(ref spirc) = self.spirc {
							spirc.shutdown();
						}
						self.shutdown = true;
					} else {
						return Ok(Async::Ready(()));
					}
				}
				else {
					self.connect = Box::new(futures::future::empty());

					let mixer = (mixer::find(Some("softvol")).unwrap())();

					let audio_filter = mixer.get_audio_filter();
					let backend = audio_backend::find(None).unwrap();
					let player = Player::new(session.clone(), audio_filter, move || {
						(backend)(None)
					});

					self.player = Some(player.clone());

					let (spirc, spirc_task) = Spirc::new(self.name.clone(), session, player, mixer);
					self.spirc = Some(spirc);
					self.spirc_task = Some(spirc_task);
				}

				progress = true;
			}

			if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
				if !self.shutdown {
					if let Some(ref spirc) = self.spirc {
						spirc.shutdown();
					}
					self.shutdown = true;
				} else {
					return Ok(Async::Ready(()));
				}

				progress = true;
			}

			if let Some(ref mut spirc_task) = self.spirc_task {
				if let Async::Ready(()) = spirc_task.poll().unwrap() {
					if self.shutdown {
						return Ok(Async::Ready(()));
					} else {
						panic!("Spirc shut down unexpectedly");
					}
				}
			}

			if !progress {
				return Ok(Async::NotReady);
			}
		}
	}
}

fn main() {
	let mut core = Core::new().unwrap();
	let handle = core.handle();

	let args: Vec<String> = std::env::args().collect();
	let Setup { name, config, cache, enable_discovery, credentials, authenticate, get_token, client_id, scope, single_track, start_position } = setup(&args);

	if let Some(ref track_id) = single_track {
		match credentials {
			Some(credentials) => {
				let backend = audio_backend::find(None).unwrap();

				let track = SpotifyId::from_base62(
									track_id.replace("spotty://", "")
									.replace("spotify://", "")
									.replace("spotify:", "")
									.replace("track:", "")
									.as_str());

				let session = core.run(Session::connect(config, credentials, cache.clone(), handle)).unwrap();

				let player = Player::new(session.clone(), None, move || (backend)(None));

				core.run(player.load(track, true, start_position)).unwrap();
			}
			None => {
				println!("Missing credentials");
			}
		}
	}
	else if authenticate && !enable_discovery {
		core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
		println!("authorized");
	}
	else if get_token {
		if let Some(client_id) = client_id {
			let session = core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
			let scope = scope.unwrap_or("user-read-private,playlist-read-private,playlist-read-collaborative,playlist-modify-public,playlist-modify-private,user-follow-modify,user-follow-read,user-library-read,user-library-modify,user-top-read,user-read-recently-played".to_string());
			let url = format!("hm://keymaster/token/authenticated?client_id={}&scope={}", client_id, scope);
			core.run(session.mercury().get(url).map(move |response| {
				let data = response.payload.first().expect("Empty payload");
				let token = String::from_utf8(data.clone()).unwrap();
				println!("{}", token);
			}).boxed()).unwrap();
		}
		else {
			println!("Use --client-id to provide a CLIENT_ID");
		}
	}
	else {
		let mut task = Main::new(handle, name, config, cache, authenticate);
		if enable_discovery {
#[cfg(not(target_os="windows"))]
			task.discovery();
		}
		if let Some(credentials) = credentials {
			task.credentials(credentials);
		}

		core.run(task).unwrap()
	}
}

<commit_msg>Fix bitrate description<commit_after>// TODO: many items from tokio-core::io have been deprecated in favour of tokio-io
#![allow(deprecated)]

#[cfg(debug_assertions)]
#[macro_use] extern crate log;
#[cfg(debug_assertions)]
extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate librespot;
extern crate tokio_core;
extern crate tokio_signal;

#[cfg(debug_assertions)]
use env_logger::LogBuilder;
use futures::{Future, Async, Poll, Stream};
#[cfg(debug_assertions)]
use std::env;
use std::io::{self, stderr, Write};
use std::path::PathBuf;
use std::process::exit;
use std::str::FromStr;
use tokio_core::reactor::{Handle, Core};
use tokio_core::io::IoStream;
use std::mem;

use librespot::spirc::{Spirc, SpircTask};
use librespot::authentication::{get_credentials, Credentials};
#[cfg(not(target_os="windows"))]
use librespot::authentication::discovery::{discovery, DiscoveryStream};
use librespot::audio_backend;
use librespot::cache::Cache;
use librespot::player::Player;
use librespot::session::{Bitrate, Config, Session};
use librespot::mixer;
use librespot::util::SpotifyId;

const VERSION: &'static str = concat!(env!("CARGO_PKG_NAME"), " v", env!("CARGO_PKG_VERSION"));

fn usage(program: &str, opts: &getopts::Options) -> String {
	println!("{}", VERSION.to_string());

	let brief = format!("Usage: {} [options]", program);
	opts.usage(&brief)
}

#[cfg(debug_assertions)]
fn setup_logging(verbose: bool) {
	let mut builder = LogBuilder::new();
	match env::var("RUST_LOG") {
		Ok(config) => {
			builder.parse(&config);
			builder.init().unwrap();

			if verbose {
				warn!("`--verbose` flag overidden by `RUST_LOG` environment variable");
			}
		}
		Err(_) => {
			if verbose {
				builder.parse("mdns=info,librespot=trace");
			} else {
				builder.parse("mdns=error,librespot=warn");
			}
			builder.init().unwrap();
		}
	}
}

#[derive(Clone)]
struct Setup {
	name: String,
	cache: Option<Cache>,
	config: Config,
	credentials: Option<Credentials>,
	authenticate: bool,
	enable_discovery: bool,

	get_token: bool,
	client_id: Option<String>,
	scope: Option<String>,

	single_track: Option<String>,
	start_position: u32,
}

fn setup(args: &[String]) -> Setup {
	let mut opts = getopts::Options::new();
	opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
		.optflag("", "enable-audio-cache", "Enable caching of the audio data.")
		.optflag("", "disable-audio-cache", "(Only here fore compatibility with librespot - audio cache is disabled by default).")
		.reqopt("n", "name", "Device name", "NAME")
        .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 320.", "BITRATE")
		.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
		.optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
		.optopt("", "onchange", "Run PROGRAM when playback changes (new track, seeking etc.).", "PROGRAM")
		.optopt("", "single-track", "Play a single track ID and exit.", "ID")
		.optopt("", "start-position", "Position (in ms) where playback should be started. Only valid with the --single-track option.", "STARTPOSITION")
		.optopt("u", "username", "Username to sign in with", "USERNAME")
		.optopt("p", "password", "Password", "PASSWORD")
		.optflag("a", "authenticate", "Authenticate given username and password. Make sure you define a cache folder to store credentials.")
		.optflag("", "disable-discovery", "Disable discovery mode")
		.optflag("t", "get-token", "Get oauth token to be used with the web API etc.")
		.optopt("i", "client-id", "A Spotify client_id to be used to get the oauth token. Required with the --get-token request.", "CLIENT_ID")
		.optopt("", "scope", "The scopes you want to have access to with the oauth token.", "SCOPE")
		.optflag("x", "check", "Run quick internal check");

	#[cfg(debug_assertions)]
	opts.optflag("v", "verbose", "Enable verbose output");

	let matches = match opts.parse(&args[1..]) {
		Ok(m) => m,
		Err(f) => {
			writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap();
			exit(1);
		}
	};

	if matches.opt_present("check") {
		println!("ok {}", VERSION.to_string());
		exit(1);
	}

	#[cfg(debug_assertions)]
	{
		let verbose = matches.opt_present("verbose");
		setup_logging(verbose);
	}

    let bitrate = matches.opt_str("b").as_ref()
        .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
        .unwrap_or(Bitrate::Bitrate320);

	let name = matches.opt_str("name").unwrap();
	let device_id = librespot::session::device_id(&name);

	let use_audio_cache = matches.opt_present("enable-audio-cache") && !matches.opt_present("disable-audio-cache");

	let cache = matches.opt_str("c").map(|cache_location| {
		Cache::new(PathBuf::from(cache_location), use_audio_cache)
	});

	let cached_credentials = cache.as_ref().and_then(Cache::credentials);

	let credentials = get_credentials(matches.opt_str("username"), matches.opt_str("password"), cached_credentials);

	let authenticate = matches.opt_present("authenticate");

#[cfg(not(target_os="windows"))]
	let enable_discovery = !matches.opt_present("disable-discovery");
#[cfg(target_os="windows")]
	let enable_discovery = false;

	let start_position = matches.opt_str("start-position")
		.unwrap_or("0".to_string())
		.parse().unwrap_or(0.0);

	let config = Config {
		user_agent: VERSION.to_string(),
		device_id: device_id,
		bitrate: bitrate,
		onstart: matches.opt_str("onstart"),
		onstop: matches.opt_str("onstop"),
		onchange: matches.opt_str("onchange"),
	};

	Setup {
		name: name,
		cache: cache,
		config: config,
		credentials: credentials,
		authenticate: authenticate,
		enable_discovery: enable_discovery,

		get_token: matches.opt_present("get-token"),
		client_id: matches.opt_str("client-id"),
		scope: matches.opt_str("scope"),

		single_track: matches.opt_str("single-track"),
		start_position: (start_position * 1000.0) as u32,
	}
}

struct Main {
	name: String,
	cache: Option<Cache>,
	config: Config,
	handle: Handle,

#[cfg(not(target_os="windows"))]
	discovery: Option<DiscoveryStream>,
#[cfg(target_os="windows")]
	discovery: Option<String>,
	signal: IoStream<()>,

	spirc: Option<Spirc>,
	spirc_task: Option<SpircTask>,
	connect: Box<Future<Item=Session, Error=io::Error>>,

	player: Option<Player>,

	shutdown: bool,
	authenticate: bool,
}

impl Main {
	fn new(
		handle: Handle,
		name: String,
		config: Config,
		cache: Option<Cache>,
		authenticate: bool,
	) -> Main
	{
		Main {
			handle: handle.clone(),
			name: name,
			cache: cache,
			config: config,

			connect: Box::new(futures::future::empty()),
			discovery: None,
			spirc: None,
			spirc_task: None,

			player: None,

			shutdown: false,
			authenticate: authenticate,
			signal: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
		}
	}

#[cfg(not(target_os="windows"))]
	fn discovery(&mut self) {
		let device_id = self.config.device_id.clone();
		let name = self.name.clone();

		self.discovery = Some(discovery(&self.handle, name, device_id).unwrap());
	}

	fn credentials(&mut self, credentials: Credentials) {
		let config = self.config.clone();
		let handle = self.handle.clone();

		let connection = Session::connect(config, credentials, self.cache.clone(), handle);

		self.connect = connection;
		self.spirc = None;
		let task = mem::replace(&mut self.spirc_task, None);
		if let Some(task) = task {
			self.handle.spawn(task);
		}
	}
}

impl Future for Main {
	type Item = ();
	type Error = ();

	fn poll(&mut self) -> Poll<(), ()> {
		loop {
			let mut progress = false;

#[cfg(not(target_os="windows"))] {
			if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) {
				if let Some(ref spirc) = self.spirc {
					spirc.shutdown();
				}
				self.credentials(creds);

				progress = true;
			}
}

			if let Async::Ready(session) = self.connect.poll().unwrap() {
				if self.authenticate {
					if !self.shutdown {
						if let Some(ref spirc) = self.spirc {
							spirc.shutdown();
						}
						self.shutdown = true;
					} else {
						return Ok(Async::Ready(()));
					}
				}
				else {
					self.connect = Box::new(futures::future::empty());

					let mixer = (mixer::find(Some("softvol")).unwrap())();

					let audio_filter = mixer.get_audio_filter();
					let backend = audio_backend::find(None).unwrap();
					let player = Player::new(session.clone(), audio_filter, move || {
						(backend)(None)
					});

					self.player = Some(player.clone());

					let (spirc, spirc_task) = Spirc::new(self.name.clone(), session, player, mixer);
					self.spirc = Some(spirc);
					self.spirc_task = Some(spirc_task);
				}

				progress = true;
			}

			if let Async::Ready(Some(())) = self.signal.poll().unwrap() {
				if !self.shutdown {
					if let Some(ref spirc) = self.spirc {
						spirc.shutdown();
					}
					self.shutdown = true;
				} else {
					return Ok(Async::Ready(()));
				}

				progress = true;
			}

			if let Some(ref mut spirc_task) = self.spirc_task {
				if let Async::Ready(()) = spirc_task.poll().unwrap() {
					if self.shutdown {
						return Ok(Async::Ready(()));
					} else {
						panic!("Spirc shut down unexpectedly");
					}
				}
			}

			if !progress {
				return Ok(Async::NotReady);
			}
		}
	}
}

fn main() {
	let mut core = Core::new().unwrap();
	let handle = core.handle();

	let args: Vec<String> = std::env::args().collect();
	let Setup { name, config, cache, enable_discovery, credentials, authenticate, get_token, client_id, scope, single_track, start_position } = setup(&args);

	if let Some(ref track_id) = single_track {
		match credentials {
			Some(credentials) => {
				let backend = audio_backend::find(None).unwrap();

				let track = SpotifyId::from_base62(
									track_id.replace("spotty://", "")
									.replace("spotify://", "")
									.replace("spotify:", "")
									.replace("track:", "")
									.as_str());

				let session = core.run(Session::connect(config, credentials, cache.clone(), handle)).unwrap();

				let player = Player::new(session.clone(), None, move || (backend)(None));

				core.run(player.load(track, true, start_position)).unwrap();
			}
			None => {
				println!("Missing credentials");
			}
		}
	}
	else if authenticate && !enable_discovery {
		core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
		println!("authorized");
	}
	else if get_token {
		if let Some(client_id) = client_id {
			let session = core.run(Session::connect(config, credentials.unwrap(), cache.clone(), handle)).unwrap();
			let scope = scope.unwrap_or("user-read-private,playlist-read-private,playlist-read-collaborative,playlist-modify-public,playlist-modify-private,user-follow-modify,user-follow-read,user-library-read,user-library-modify,user-top-read,user-read-recently-played".to_string());
			let url = format!("hm://keymaster/token/authenticated?client_id={}&scope={}", client_id, scope);
			core.run(session.mercury().get(url).map(move |response| {
				let data = response.payload.first().expect("Empty payload");
				let token = String::from_utf8(data.clone()).unwrap();
				println!("{}", token);
			}).boxed()).unwrap();
		}
		else {
			println!("Use --client-id to provide a CLIENT_ID");
		}
	}
	else {
		let mut task = Main::new(handle, name, config, cache, authenticate);
		if enable_discovery {
#[cfg(not(target_os="windows"))]
			task.discovery();
		}
		if let Some(credentials) = credentials {
			task.credentials(credentials);
		}

		core.run(task).unwrap()
	}
}

<|endoftext|>


<commit_before>#![feature(scheme)]
#![feature(compile)]
#![feature(convert)]
extern crate rustc_serialize;
extern crate docopt;

extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

use docopt::Docopt;
use std::io;
use std::io::{Read, BufRead};

static USAGE: &'static str = "
Usage:
    seax repl [-vd]
    seax [-vd] <bin>

Options:
    -v, --verbose   Enable verbose mode
    -d, --debug     Enable debug mode
";

#[derive(RustcDecodable)]
struct Args {
    cmd_repl: bool,
    arg_bin: String,
    flag_verbose: bool,
    flag_debug: bool,
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                .and_then(|d| d.decode())
                .unwrap_or_else(|e| e.exit());
    if args.cmd_repl {
        let mut console = io::stdin();
        let lock = console.lock();
        for line in lock.lines() {
            println!(">");
            match line {
                Ok(line) => { scheme::compile(line.as_ref())
                                .and_then(  |p| Ok(svm::eval_program(p, args.flag_debug)) )
                                .map(       |r| println!("{}", r) )
                                .map_err(   |e| println!("{}", e) ); },
                Err(why) => println!("{}", why)

            }
        }
    }
}
<commit_msg>Added prompt to REPL<commit_after>#![feature(scheme)]
#![feature(compile)]
extern crate rustc_serialize;
extern crate docopt;

extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

use docopt::Docopt;
use std::io;
use std::io::{Write, Read, BufRead,BufReader};

static USAGE: &'static str = "
Usage:
    seax repl [-vd]
    seax [-vd] <bin>

Options:
    -v, --verbose   Enable verbose mode
    -d, --debug     Enable debug mode
";

#[derive(RustcDecodable)]
struct Args {
    cmd_repl: bool,
    arg_bin: String,
    flag_verbose: bool,
    flag_debug: bool,
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                .and_then(|d| d.decode())
                .unwrap_or_else(|e| e.exit());
    if args.cmd_repl {
        let mut stdin = BufReader::new(io::stdin());
        let mut stdout = io::stdout();
        print!("scheme> ");
        stdout.flush();
        for line in stdin.lines() {

            match line {
                Ok(line) => { scheme::compile(line.as_ref())
                                .and_then(  |p| Ok(svm::eval_program(p, args.flag_debug)) )
                                .map(       |r| println!("{}", r) )
                                .map_err(   |e| println!("{}", e) ); },
                Err(why) => println!("{}", why)
            }

            print!("scheme> ");
            stdout.flush();
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    move_up(&origin.y); // borrowing the value
    move_up(&origin.y); // so that I can change it

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // Here I get the value and store it
    y_cord.set(new_val + 1); // Here I modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

#[test]
fn it_can_move_up_one_pixel() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    assert_eq!(0, origin.y.get());

    move_up(&origin.y);

    assert_eq!(1, origin.y.get());

    move_up(&origin.y);
    move_up(&origin.y);
    move_up(&origin.y);

    assert_eq!(4, origin.y.get())
}
<commit_msg>changed test function name<commit_after>use std::cell::Cell;

struct Coordinates {
    x: Cell<i32>,
    y: Cell<i32>,
}

fn main() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    move_up(&origin.y); // borrowing the value
    move_up(&origin.y); // so that I can change it

    println!("X is {:?} and Y is {:?}", origin.x, origin.y);
}

fn move_up(y_cord: &Cell<i32>) -> &Cell<i32> {
    let new_val = y_cord.get(); // Here I get the value and store it
    y_cord.set(new_val + 1); // Here I modify the stored value and set the incoming param
    y_cord // no semicolon here because it is the return value
}

#[test]
fn it_can_move_up() {
    let origin = Coordinates { x: Cell::new(0), y: Cell::new(0) };

    assert_eq!(0, origin.y.get());

    move_up(&origin.y);

    assert_eq!(1, origin.y.get());

    move_up(&origin.y);
    move_up(&origin.y);
    move_up(&origin.y);

    assert_eq!(4, origin.y.get())
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate serde_json;

#[macro_use]
extern crate error_type;

#[macro_use]
extern crate lazy_static;

use std::fs::File;
use std::io::Read;
use std::io::{Write, stderr};
use std::process::{Command, exit};
use std::env::args;
use std::path::{Path, PathBuf};
use serde_json::Value;
use ct_result::{CtResult, CtError};

mod ct_result;
mod dirs;
mod cache;

fn main() {
    execute().unwrap_or_else(|err| {
        let _ = writeln!(&mut stderr(), "{}", err);
        exit(1);
    });
}

fn execute() -> CtResult<()> {
    let mut args = std::env::args();
    args.next();

    let cpp_file = {
        let file = match args.next() {
            Some(arg) => PathBuf::from(arg),
            None      => return Err(CtError::from("Missing C++ source file argument!"))
        };

        if file.is_relative() {
            return Err(CtError::from("C++ source file has to have an absolute path!"));
        }

        file
    };

    let db_files = {
        let mut files = Vec::<PathBuf>::new();
        for arg in args {
            files.push(PathBuf::from(arg));
        }

        if files.is_empty() {
            return Err(CtError::from("Missing clang compilation database argument!"));
        }

        files
    };

    let cmd_str = try!(get_command_str(&cpp_file, &db_files));
    let mut cmd = try!(build_command(&cmd_str));

    try!(cmd.spawn()
       .map_err(|e| CtError::from(format!("Command execution failed: {}, because: {}", cmd_str, e))));

    Ok(())
}

fn get_command_str(cpp_file: &Path, db_files: &[PathBuf]) -> CtResult<String> {
    if let Some(cmd_str) = try!(cache::find_command_str(cpp_file)) {
        return Ok(cmd_str);
    }

    let mut file_buffer = String::new();

    for db_file in db_files {
        let mut file = try!(File::open(db_file));
        file_buffer.clear();
        try!(file.read_to_string(&mut file_buffer));

        let json_value: Value = try!(serde_json::from_str(&file_buffer));

        let objs = try!(json_value.as_array()
            .ok_or(CtError::from(format!("Expected a json array but got: '{}'", json_value))));

        for obj in objs {
            let obj = try!(obj.as_object()
                .ok_or(CtError::from(format!("Expected a json object but got: '{}'", obj))));

            let file = {
                let f = PathBuf::from(try!(try!(obj.get("file")
                    .ok_or(CtError::from(format!("Couldn't find entry 'file' in json object: '{:?}'", obj))))
                    .as_str()
                    .ok_or(CtError::from(format!("Couldn't get entry 'file' as str from json object: '{:?}'", obj)))));

                if f.is_relative() {
                    let dir = PathBuf::from(try!(try!(obj.get("directory")
                        .ok_or(CtError::from(format!("Couldn't find entry 'directory' in json object: '{:?}'", obj))))
                        .as_str()
                        .ok_or(CtError::from(format!("Couldn't get entry 'directory' as str from json object: '{:?}'", obj)))));

                    dir.join(f)
                } else {
                    f
                }
            };

            if cpp_file == file {
                let cmd_str = String::from(try!(try!(obj.get("command")
                    .ok_or(CtError::from(format!("Couldn't find entry 'command' in json object: '{:?}'", obj))))
                    .as_str()
                    .ok_or(CtError::from(format!("Couldn't get entry 'command' as str from json object: '{:?}'", obj)))));

                try!(cache::write_command_str(cpp_file, &cmd_str));
                return Ok(cmd_str);
            }
        }
    }

    Err(CtError::from(format!("Couldn't find C++ source file '{:?}' in compilation database!", cpp_file)))
}

fn build_command(cmd_str: &str) -> CtResult<Command> {
    if cmd_str.is_empty() {
        return Err(CtError::from("Unexpected empty command string!"));
    }

    let mut parts = cmd_str.split(" ");
    let compiler = try!(parts.next()
        .ok_or(CtError::from("Unexpected empty parts after command string split!")));

    if ! compiler.contains("gcc")
        && ! compiler.contains("g++")
        && ! compiler.contains("clang")
        && ! compiler.contains("clang++") {
        return Err(CtError::from(format!(
            "Unsupported compiler for typecheck: '{}'! Currently supported are 'gcc/g++' and 'clang/clang++'!", compiler)));
    }

    let mut cmd = Command::new(compiler);
    for p in parts {
        if p.is_empty() {
            continue;
        }

        cmd.arg(p.replace("\\", ""));
    }

    cmd.arg("-fsyntax-only");
    Ok(cmd)
}
<commit_msg>Fix escapes in cache<commit_after>#![allow(dead_code)]

extern crate serde_json;

#[macro_use]
extern crate error_type;

#[macro_use]
extern crate lazy_static;

use std::fs::File;
use std::io::Read;
use std::io::{Write, stderr};
use std::process::{Command, exit};
use std::env::args;
use std::path::{Path, PathBuf};
use serde_json::Value;
use ct_result::{CtResult, CtError};

mod ct_result;
mod dirs;
mod cache;

fn main() {
    execute().unwrap_or_else(|err| {
        let _ = writeln!(&mut stderr(), "{}", err);
        exit(1);
    });
}

fn execute() -> CtResult<()> {
    let mut args = std::env::args();
    args.next();

    let cpp_file = {
        let file = match args.next() {
            Some(arg) => PathBuf::from(arg),
            None      => return Err(CtError::from("Missing C++ source file argument!"))
        };

        if file.is_relative() {
            return Err(CtError::from("C++ source file has to have an absolute path!"));
        }

        file
    };

    let db_files = {
        let mut files = Vec::<PathBuf>::new();
        for arg in args {
            files.push(PathBuf::from(arg));
        }

        if files.is_empty() {
            return Err(CtError::from("Missing clang compilation database argument!"));
        }

        files
    };

    let cmd_str = try!(get_command_str(&cpp_file, &db_files));
    let mut cmd = try!(build_command(&cmd_str));

    try!(cmd.spawn()
       .map_err(|e| CtError::from(format!("Command execution failed: {}, because: {}", cmd_str, e))));

    Ok(())
}

fn get_command_str(cpp_file: &Path, db_files: &[PathBuf]) -> CtResult<String> {
    if let Some(cmd_str) = try!(cache::find_command_str(cpp_file)) {
        return Ok(cmd_str);
    }

    let mut file_buffer = String::new();

    for db_file in db_files {
        let mut file = try!(File::open(db_file));
        file_buffer.clear();
        try!(file.read_to_string(&mut file_buffer));

        let json_value: Value = try!(serde_json::from_str(&file_buffer));

        let objs = try!(json_value.as_array()
            .ok_or(CtError::from(format!("Expected a json array but got: '{}'", json_value))));

        for obj in objs {
            let obj = try!(obj.as_object()
                .ok_or(CtError::from(format!("Expected a json object but got: '{}'", obj))));

            let file = {
                let f = PathBuf::from(try!(try!(obj.get("file")
                    .ok_or(CtError::from(format!("Couldn't find entry 'file' in json object: '{:?}'", obj))))
                    .as_str()
                    .ok_or(CtError::from(format!("Couldn't get entry 'file' as str from json object: '{:?}'", obj)))));

                if f.is_relative() {
                    let dir = PathBuf::from(try!(try!(obj.get("directory")
                        .ok_or(CtError::from(format!("Couldn't find entry 'directory' in json object: '{:?}'", obj))))
                        .as_str()
                        .ok_or(CtError::from(format!("Couldn't get entry 'directory' as str from json object: '{:?}'", obj)))));

                    dir.join(f)
                } else {
                    f
                }
            };

            if cpp_file == file {
                let cmd_str = String::from(try!(try!(obj.get("command")
                    .ok_or(CtError::from(format!("Couldn't find entry 'command' in json object: '{:?}'", obj))))
                    .as_str()
                    .ok_or(CtError::from(format!("Couldn't get entry 'command' as str from json object: '{:?}'", obj)))))
                    .replace("\\", "");

                try!(cache::write_command_str(cpp_file, &cmd_str));
                return Ok(cmd_str);
            }
        }
    }

    Err(CtError::from(format!("Couldn't find C++ source file '{:?}' in compilation database!", cpp_file)))
}

fn build_command(cmd_str: &str) -> CtResult<Command> {
    if cmd_str.is_empty() {
        return Err(CtError::from("Unexpected empty command string!"));
    }

    let mut parts = cmd_str.split(" ");
    let compiler = try!(parts.next()
        .ok_or(CtError::from("Unexpected empty parts after command string split!")));

    if ! compiler.contains("gcc")
        && ! compiler.contains("g++")
        && ! compiler.contains("clang")
        && ! compiler.contains("clang++") {
        return Err(CtError::from(format!(
            "Unsupported compiler for typecheck: '{}'! Currently supported are 'gcc/g++' and 'clang/clang++'!", compiler)));
    }

    let mut cmd = Command::new(compiler);

    for p in parts {
        if p.is_empty() {
            continue;
        }

        cmd.arg(p);
    }

    cmd.arg("-fsyntax-only");
    Ok(cmd)
}
<|endoftext|>