<commit_before>#![allow(unused_must_use)]
extern crate pad;
#[macro_use]
extern crate quicli;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate term;

use pad::PadStr;
use reqwest::Url;

use std::process;
use std::str;

use quicli::prelude::*;

#[macro_use]
mod macros;

#[derive(Debug, StructOpt)]
#[structopt(name = "cargo")]
enum Cli {
    #[structopt(name = "ssearch", about = "cargo search on steroids")]
    Ssearch {
        /// how many packages to display
        #[structopt(long = "limit", short = "l", default_value = "10")]
        limit: usize,
        /// the crates.io search result page to display
        #[structopt(long = "page", default_value = "1")]
        page: usize,
        /// quiet output, display only crate, version and downloads
        #[structopt(long = "quiet", short = "q")]
        quiet: bool,
        /// sort by recent downloads instead of overall downloads
        #[structopt(long = "recent", short = "r")]
        recent: bool,
        /// query string for crates.io
        query: String,
    },
}

#[derive(Debug, Deserialize)]
struct Args {
    flag_info: bool,
    arg_query: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Meta {
    total: i32,
}

#[derive(Debug, Serialize, Deserialize)]
struct Response {
    crates: Vec<EncodableCrate>,
    meta: Meta,
}

// structs from crates.io backend

#[derive(Debug, Serialize, Deserialize)]
struct EncodableCrate {
    id: String,
    name: String,
    updated_at: String,
    versions: Option<Vec<i32>>,
    created_at: String,
    downloads: i32,
    max_version: String,
    description: Option<String>,
    homepage: Option<String>,
    documentation: Option<String>,
    keywords: Option<Vec<String>>,
    license: Option<String>,
    repository: Option<String>,
    links: CrateLinks,
}

#[derive(Debug, Serialize, Deserialize)]
struct CrateLinks {
    version_downloads: String,
    versions: Option<String>,
    owners: Option<String>,
    reverse_dependencies: String,
}

fn query_crates_io(
    query: &str,
    page: usize,
    per_page: usize,
    recent: bool,
) -> Result<(i32, Vec<EncodableCrate>)> {
    let sort = if recent {
        "recent-downloads"
    } else {
        "downloads"
    };
    let url = Url::parse_with_params(
        "https://crates.io/api/v1/crates",
        &[
            ("q", query),
            ("page", &page.to_string()),
            ("per_page", &per_page.to_string()),
            ("sort", &sort),
        ],
    )?;
    let body = reqwest::get(url)?.text()?;
    let data: Response = serde_json::from_str(&body)?;
    Ok((data.meta.total, data.crates))
}

fn show_crate(t: &mut Box<term::StdoutTerminal>, cr: &EncodableCrate, quiet: bool, max_len: usize) {
    p_green!(t, "{}", cr.name.pad_to_width(max_len));
    p_white!(
        t,
        " = \"{}\"    \t(downloads: {})\n",
        cr.max_version,
        cr.downloads
    );

    if !quiet {
        cr.description
            .as_ref()
            .map(|description| p_yellow!(t, " -> {}\n", description.clone().trim()));
        cr.documentation
            .as_ref()
            .map(|documentation| p_white!(t, "    docs: {}\n", documentation));
        cr.homepage
            .as_ref()
            .map(|homepage| p_white!(t, "    home: {}\n", homepage));
        p_white!(t, "\n");
    }
}

main!(|args: Cli| {
    let Cli::Ssearch {
        query,
        page,
        limit,
        quiet,
        recent,
    } = args;

    let mut t = term::stdout().unwrap();

    // TODO: Add decoding of updated_at and allow to use it for sorting
    let (total, crates) = query_crates_io(&query, page, limit, recent).unwrap_or_else(|e| {
        p_red!(t, "[error]: {}.\n", e);
        t.reset().unwrap();
        process::exit(1)
    });

    p_white!(
        t,
        "Displaying {} crates from page {} out of the {} found.\n\n",
        crates.len(),
        page,
        total,
    );
    let max_len = (&crates).iter().map(|ref cr| cr.name.len()).max().unwrap();
    for cr in &crates {
        show_crate(&mut t, &cr, quiet, max_len);
    }

    t.reset().unwrap();
});
<commit_msg>exit gracefully if no match was found<commit_after>#![allow(unused_must_use)]
extern crate pad;
#[macro_use]
extern crate quicli;
extern crate reqwest;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate term;

use pad::PadStr;
use reqwest::Url;

use std::process;
use std::str;

use quicli::prelude::*;

#[macro_use]
mod macros;

#[derive(Debug, StructOpt)]
#[structopt(name = "cargo")]
enum Cli {
    #[structopt(name = "ssearch", about = "cargo search on steroids")]
    Ssearch {
        /// how many packages to display
        #[structopt(long = "limit", short = "l", default_value = "10")]
        limit: usize,
        /// the crates.io search result page to display
        #[structopt(long = "page", default_value = "1")]
        page: usize,
        /// quiet output, display only crate, version and downloads
        #[structopt(long = "quiet", short = "q")]
        quiet: bool,
        /// sort by recent downloads instead of overall downloads
        #[structopt(long = "recent", short = "r")]
        recent: bool,
        /// query string for crates.io
        query: String,
    },
}

#[derive(Debug, Deserialize)]
struct Args {
    flag_info: bool,
    arg_query: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Meta {
    total: i32,
}

#[derive(Debug, Serialize, Deserialize)]
struct Response {
    crates: Vec<EncodableCrate>,
    meta: Meta,
}

// structs from crates.io backend

#[derive(Debug, Serialize, Deserialize)]
struct EncodableCrate {
    id: String,
    name: String,
    updated_at: String,
    versions: Option<Vec<i32>>,
    created_at: String,
    downloads: i32,
    max_version: String,
    description: Option<String>,
    homepage: Option<String>,
    documentation: Option<String>,
    keywords: Option<Vec<String>>,
    license: Option<String>,
    repository: Option<String>,
    links: CrateLinks,
}

#[derive(Debug, Serialize, Deserialize)]
struct CrateLinks {
    version_downloads: String,
    versions: Option<String>,
    owners: Option<String>,
    reverse_dependencies: String,
}

fn query_crates_io(
    query: &str,
    page: usize,
    per_page: usize,
    recent: bool,
) -> Result<(i32, Vec<EncodableCrate>)> {
    let sort = if recent {
        "recent-downloads"
    } else {
        "downloads"
    };
    let url = Url::parse_with_params(
        "https://crates.io/api/v1/crates",
        &[
            ("q", query),
            ("page", &page.to_string()),
            ("per_page", &per_page.to_string()),
            ("sort", &sort),
        ],
    )?;
    let body = reqwest::get(url)?.text()?;
    let data: Response = serde_json::from_str(&body)?;
    Ok((data.meta.total, data.crates))
}

fn show_crate(t: &mut Box<term::StdoutTerminal>, cr: &EncodableCrate, quiet: bool, max_len: usize) {
    p_green!(t, "{}", cr.name.pad_to_width(max_len));
    p_white!(
        t,
        " = \"{}\"    \t(downloads: {})\n",
        cr.max_version,
        cr.downloads
    );

    if !quiet {
        cr.description
            .as_ref()
            .map(|description| p_yellow!(t, " -> {}\n", description.clone().trim()));
        cr.documentation
            .as_ref()
            .map(|documentation| p_white!(t, "    docs: {}\n", documentation));
        cr.homepage
            .as_ref()
            .map(|homepage| p_white!(t, "    home: {}\n", homepage));
        p_white!(t, "\n");
    }
}

main!(|args: Cli| {
    let Cli::Ssearch {
        query,
        page,
        limit,
        quiet,
        recent,
    } = args;

    let mut t = term::stdout().unwrap();

    // TODO: Add decoding of updated_at and allow to use it for sorting
    let (total, crates) = query_crates_io(&query, page, limit, recent).unwrap_or_else(|e| {
        p_red!(t, "[error]: {}.\n", e);
        t.reset().unwrap();
        process::exit(1)
    });

    if total == 0 {
        p_white!(t, "No crate matching \"{}\" has been found.\n", query);
        t.reset().unwrap();
        process::exit(0);
    }

    p_white!(
        t,
        "Displaying {} crates from page {} out of the {} found.\n\n",
        crates.len(),
        page,
        total,
    );
    let max_len = (&crates).iter().map(|ref cr| cr.name.len()).max().unwrap();
    for cr in &crates {
        show_crate(&mut t, &cr, quiet, max_len);
    }

    t.reset().unwrap();
});
<|endoftext|>


<commit_before>use std::collections::{HashSet, HashMap};
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

fn print_arg_error() {
    println!("incorrect arguments passed");
}

struct CsvDescriptor<'a> {
    file_path: &'a  Path,
    delimiter:      char,
    quote:          Option<char>,
}

impl<'a> std::fmt::Display for CsvDescriptor<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{} {} {:?}", self.file_path.display(), self.delimiter, self.quote)
    }
}

fn parse_args<'a>(path_arg:         &'a String,
                  delimiter_arg:    &'a String,
                  quote_arg:        &'a String) -> Result<CsvDescriptor<'a>, &'static str>
{

    let csv_file_path = Path::new(path_arg);

    let csv_delimiter = match delimiter_arg.chars().next() {
                            Some(result) => result,
                            None         => return Err("incorrect delimiter"),
                        };

    let csv_quote     = quote_arg.chars().next();

    Ok(CsvDescriptor {file_path: &csv_file_path, delimiter: csv_delimiter, quote: csv_quote})
}

fn get_csv_cols(csv_desc: &CsvDescriptor) -> Result<Vec<String>, String> {

    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);

    let mut csv_line_iter = csv_reader.lines();

    let csv_header: String = match csv_line_iter.next() {
        Some(result) => match result {
                            Err(why) => return Err(format!("error getting csv header: {}", why)),
                            Ok(header) => header,
                        },
        None         => return Err("csv header reading failed".to_string()),
    };

    let csv_cols: Vec<String> = {
        let cols_iter = csv_header.split(csv_desc.delimiter);
        match csv_desc.quote {
            Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
            None    => cols_iter.map(|s| {s.to_string()}).collect(),
        }
    };

    Ok(csv_cols)
}

fn build_index(csv_desc: &CsvDescriptor) -> Result<HashMap<String, usize>, String> {
    // TODO it would probably be better to keep a File in the csv descriptor instead of a Path
    let mut csv_index = HashMap::new();
    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);

    let mut csv_line_iter = csv_reader.lines().skip(1);

    let mut line_index = 0;
    loop {

        let csv_row: String = match csv_line_iter.next() {
            Some(result) => match result {
                                Err(why) => return Err(format!("error getting csv row: {}", why)),
                                Ok(header) => header,
                            },
            None         => break,
        };

        let csv_cols: Vec<String> = {
            let cols_iter = csv_row.split(csv_desc.delimiter);
            match csv_desc.quote {
                Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
                None    => cols_iter.map(|s| {s.to_string()}).collect(),
            }
        };

        // TODO: check if all lines have the same number of columns
        let key = format!("{}{}", csv_cols[0], csv_cols[1]);
        csv_index.insert(key, line_index);

        line_index += 1;
    }

    Ok(csv_index)
}

fn get_csv_row(csv_desc: &CsvDescriptor, line_num: usize) -> Result<Vec<String>, String> {

    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);

    let mut csv_line_iter = csv_reader.lines().skip(line_num + 1);

    let csv_row: String = match csv_line_iter.next() {
        Some(result) => match result {
                            Err(why) => return Err(format!("error getting csv row: {}", why)),
                            Ok(row) => row,
                        },
        None         => return Err("csv row reading failed".to_string()),
    };

    let result: Vec<String> = {
        let cols_iter = csv_row.split(csv_desc.delimiter);
        match csv_desc.quote {
            Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
            None    => cols_iter.map(|s| {s.to_string()}).collect(),
        }
    };

    Ok(result)
}

fn main() {

/*

1. Parse arguments
2. Open CSV files
3. Get columns (cols_N)
4. Get intersection of those two sets of columns(cols_to_compare)
5. Create {column name : column index in cols_N} dicts
6. Create {CSV_col_value : CSV row index in file} dicts, where CSV_col_value is a unique key made of the value of several CSV columns.
   For example, {Alex38 : 76}. In this example a name and age form a unique key for the 76th CSV row.
7. Get intersection of key sets of dicts from step 6 (row_keys_to_compare)
8. Loop through row_keys_to_compare, use dicts from step 6 to get line numbers for CSV files
    8.1 Loop through cols_to_compare, use dicts from step 5 to extract column values from CSV rows(step 8)
    8.2 Compare values

Input parameters: CSV paths, delimiters, quotes

For example, ./main file_1.csv "," "'" file_2.csv " " ""

*/

    /*** 1 ***/
    let args: Vec<String> = env::args().collect();
    if args.len() != 7 {
        print_arg_error();
        return;
    };

    let csv_desc_1: CsvDescriptor = match parse_args(&args[1], &args[2], &args[3]) {
        Err(why)    => panic!("error parsing arguments for CSV #1: {}", why),
        Ok(result)  => result,
    };

    let csv_desc_2: CsvDescriptor = match parse_args(&args[4], &args[5], &args[6]) {
        Err(why)    => panic!("error parsing arguments for CSV #2: {}", why),
        Ok(result)  => result,
    };

    /*** 2&3 ***/
    let csv_cols_1: Vec<String> = match get_csv_cols(&csv_desc_1) {
        Err(why) => panic!("couldn't get columns: {}", why),
        Ok(cols) => cols,
    };

    let csv_cols_2: Vec<String> = match get_csv_cols(&csv_desc_2) {
        Err(why) => panic!("couldn't get columns: {}", why),
        Ok(cols) => cols,
    };

    /*** 5 ***/
    let mut csv_col_index_1 = HashMap::new();
    for i in 0..csv_cols_1.len() {
        let key = csv_cols_1[i].clone();
        if csv_col_index_1.contains_key(&key) {
            panic!("duplicate column found in CSV #1: {}", key);
        };
        csv_col_index_1.insert(key, i);
    }
    println!("{:?}", csv_col_index_1);

    let mut csv_col_index_2 = HashMap::new();
    for i in 0..csv_cols_2.len() {
        let key = csv_cols_2[i].clone();
        if csv_col_index_2.contains_key(&key) {
            panic!("duplicate column found in CSV #1: {}", key);
        };
        csv_col_index_2.insert(key, i);
    }
    println!("{:?}", csv_col_index_2);

    /*** 4 ***/
    let mut cols_to_compare = HashSet::new();
    for col_1 in csv_col_index_1.keys() {
        if csv_col_index_2.contains_key(col_1) {
            cols_to_compare.insert(col_1);
        };
    }
    println!("{:?}", cols_to_compare);

    /*** 6 ***/
    // let's assume that the unique key is (col_0 + col_1)
    let csv_index_1 = match build_index(&csv_desc_1) {
        Err(why)  => panic!("failed building index #1: {}", why),
        Ok(index) => index,
    };

    let csv_index_2 = match build_index(&csv_desc_2) {
        Err(why)  => panic!("failed building index #2: {}", why),
        Ok(index) => index,
    };

    /*** 7 ***/
    let mut row_keys_to_compare = HashSet::new();
    for key_1 in csv_index_1.keys() {
        if csv_index_2.contains_key(key_1) {
            row_keys_to_compare.insert(key_1);
        };
    }
    println!("{:?}", row_keys_to_compare);

    /*** 8 ***/
    for row_key in row_keys_to_compare {

        let index_1 = *csv_index_1.get(row_key).unwrap(); // TODO: handle me
        let index_2 = *csv_index_2.get(row_key).unwrap();

        let row_1 = get_csv_row(&csv_desc_1, index_1).unwrap(); // TODO: handle me
        let row_2 = get_csv_row(&csv_desc_2, index_2).unwrap();

        println!("comparing {}:", row_key);
        println!("line #1: {:?}", row_1);
        println!("line #2: {:?}", row_2);

        for col in &cols_to_compare {

            let col_index_1 = *csv_col_index_1.get(*col).unwrap(); // TODO: handle me
            let col_index_2 = *csv_col_index_2.get(*col).unwrap();

            println!("column {}, index_1={}, index_2={}", col, col_index_1, col_index_2);

            if row_1[col_index_1] != row_2[col_index_2] {
                println!("found a difference for {}, {}: {} / {}", row_key, col, row_1[col_index_1], row_2[col_index_2]);
            }
        }

    }

}
<commit_msg>Get the row offsets first and use them to get the csv rows with O(1)<commit_after>use std::collections::{HashSet, HashMap};
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Seek, SeekFrom};
use std::path::Path;

fn print_arg_error() {
    println!("incorrect arguments passed");
}

struct CsvDescriptor<'a> {
    file_path: &'a  Path,
    delimiter:      char,
    quote:          Option<char>,
}

impl<'a> std::fmt::Display for CsvDescriptor<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{} {} {:?}", self.file_path.display(), self.delimiter, self.quote)
    }
}

fn parse_args<'a>(path_arg:         &'a String,
                  delimiter_arg:    &'a String,
                  quote_arg:        &'a String) -> Result<CsvDescriptor<'a>, &'static str>
{

    let csv_file_path = Path::new(path_arg);

    let csv_delimiter = match delimiter_arg.chars().next() {
                            Some(result) => result,
                            None         => return Err("incorrect delimiter"),
                        };

    let csv_quote     = quote_arg.chars().next();

    Ok(CsvDescriptor {file_path: &csv_file_path, delimiter: csv_delimiter, quote: csv_quote})
}

fn get_csv_cols(csv_desc: &CsvDescriptor) -> Result<Vec<String>, String> {

    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);

    let mut csv_line_iter = csv_reader.lines();

    let csv_header: String = match csv_line_iter.next() {
        Some(result) => match result {
                            Err(why) => return Err(format!("error getting csv header: {}", why)),
                            Ok(header) => header,
                        },
        None         => return Err("csv header reading failed".to_string()),
    };

    let csv_cols: Vec<String> = {
        let cols_iter = csv_header.split(csv_desc.delimiter);
        match csv_desc.quote {
            Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
            None    => cols_iter.map(|s| {s.to_string()}).collect(),
        }
    };

    Ok(csv_cols)
}

fn build_index(csv_desc: &CsvDescriptor) -> Result<HashMap<String, usize>, String> {
    // TODO it would probably be better to keep a File in the csv descriptor instead of a Path
    let mut csv_index = HashMap::new();
    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);

    let mut csv_line_iter = csv_reader.lines().skip(1);

    let mut line_index = 0;
    loop {

        let csv_row: String = match csv_line_iter.next() {
            Some(result) => match result {
                                Err(why) => return Err(format!("error getting csv row: {}", why)),
                                Ok(header) => header,
                            },
            None         => break,
        };

        let csv_cols: Vec<String> = {
            let cols_iter = csv_row.split(csv_desc.delimiter);
            match csv_desc.quote {
                Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
                None    => cols_iter.map(|s| {s.to_string()}).collect(),
            }
        };

        // TODO: check if all lines have the same number of columns
        let key = format!("{}{}", csv_cols[0], csv_cols[1]);
        csv_index.insert(key, line_index);

        line_index += 1;
    }

    Ok(csv_index)
}

fn build_index_2(csv_desc: &CsvDescriptor) -> Result<HashMap<String, u64>, String> {
    // TODO it would probably be better to keep a File in the csv descriptor instead of a Path
    let mut csv_index = HashMap::new();
    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);
    let mut csv_line_iter = csv_reader.lines();

    let mut offset_in_file: u64 = 0;
    loop {

        let csv_row: String = match csv_line_iter.next() {
            Some(result) => match result {
                                Err(why) => return Err(format!("error getting csv row: {}", why)),
                                Ok(header) => header,
                            },
            None         => break,
        };

        let csv_cols: Vec<String> = {
            let cols_iter = csv_row.split(csv_desc.delimiter);
            match csv_desc.quote {
                Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
                None    => cols_iter.map(|s| {s.to_string()}).collect(),
            }
        };

        // TODO: check if all lines have the same number of columns
        let key = format!("{}{}", csv_cols[0], csv_cols[1]);

        csv_index.insert(key, offset_in_file);
        offset_in_file += (csv_row.len() + 1) as u64;
    }

    Ok(csv_index)
}

fn get_csv_row(csv_desc: &CsvDescriptor, line_num: usize) -> Result<Vec<String>, String> {

    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);

    let mut csv_line_iter = csv_reader.lines().skip(line_num + 1);

    let csv_row: String = match csv_line_iter.next() {
        Some(result) => match result {
                            Err(why) => return Err(format!("error getting csv row: {}", why)),
                            Ok(row) => row,
                        },
        None         => return Err("csv row reading failed".to_string()),
    };

    let result: Vec<String> = {
        let cols_iter = csv_row.split(csv_desc.delimiter);
        match csv_desc.quote {
            Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
            None    => cols_iter.map(|s| {s.to_string()}).collect(),
        }
    };

    Ok(result)
}

fn get_csv_row_2(csv_desc: &CsvDescriptor, line_offset: u64) -> Result<Vec<String>, String> {

    let mut csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    csv_file.seek(SeekFrom::Start(line_offset)).unwrap(); // TODO: handle me

    let mut csv_reader = BufReader::new(csv_file);
    let mut row_buff = String::new();

    match csv_reader.read_line(&mut row_buff) {
        Ok(_n)  => {
            if row_buff.ends_with("\n") {
                row_buff.pop();
            }
        }
        Err(e) => return Err(format!("error gettig csv row: {}", e)),
    };

    let result: Vec<String> = {
        let cols_iter = row_buff.split(csv_desc.delimiter);
        match csv_desc.quote {
            Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
            None    => cols_iter.map(|s| {s.to_string()}).collect(),
        }
    };

    Ok(result)
}

fn main() {

/*

1. Parse arguments
2. Open CSV files
3. Get columns (cols_N)
4. Get intersection of those two sets of columns(cols_to_compare)
5. Create {column name : column index in cols_N} dicts
6. Create {CSV_col_value : CSV row index in file} dicts, where CSV_col_value is a unique key made of the value of several CSV columns.
   For example, {Alex38 : 76}. In this example a name and age form a unique key for the 76th CSV row.
7. Get intersection of key sets of dicts from step 6 (row_keys_to_compare)
8. Loop through row_keys_to_compare, use dicts from step 6 to get line numbers for CSV files
    8.1 Loop through cols_to_compare, use dicts from step 5 to extract column values from CSV rows(step 8)
    8.2 Compare values

Input parameters: CSV paths, delimiters, quotes

For example, ./main file_1.csv "," "'" file_2.csv " " ""

*/

    /*** 1 ***/
    let args: Vec<String> = env::args().collect();
    if args.len() != 7 {
        print_arg_error();
        return;
    };

    let csv_desc_1: CsvDescriptor = match parse_args(&args[1], &args[2], &args[3]) {
        Err(why)    => panic!("error parsing arguments for CSV #1: {}", why),
        Ok(result)  => result,
    };

    let csv_desc_2: CsvDescriptor = match parse_args(&args[4], &args[5], &args[6]) {
        Err(why)    => panic!("error parsing arguments for CSV #2: {}", why),
        Ok(result)  => result,
    };

    /*** 2&3 ***/
    let csv_cols_1: Vec<String> = match get_csv_cols(&csv_desc_1) {
        Err(why) => panic!("couldn't get columns: {}", why),
        Ok(cols) => cols,
    };

    let csv_cols_2: Vec<String> = match get_csv_cols(&csv_desc_2) {
        Err(why) => panic!("couldn't get columns: {}", why),
        Ok(cols) => cols,
    };

    /*** 5 ***/
    let mut csv_col_index_1 = HashMap::new();
    for i in 0..csv_cols_1.len() {
        let key = csv_cols_1[i].clone();
        if csv_col_index_1.contains_key(&key) {
            panic!("duplicate column found in CSV #1: {}", key);
        };
        csv_col_index_1.insert(key, i);
    }
    println!("{:?}", csv_col_index_1);

    let mut csv_col_index_2 = HashMap::new();
    for i in 0..csv_cols_2.len() {
        let key = csv_cols_2[i].clone();
        if csv_col_index_2.contains_key(&key) {
            panic!("duplicate column found in CSV #1: {}", key);
        };
        csv_col_index_2.insert(key, i);
    }
    println!("{:?}", csv_col_index_2);

    /*** 4 ***/
    let mut cols_to_compare = HashSet::new();
    for col_1 in csv_col_index_1.keys() {
        if csv_col_index_2.contains_key(col_1) {
            cols_to_compare.insert(col_1);
        };
    }
    println!("{:?}", cols_to_compare);

    /*** 6 ***/
    // let's assume that the unique key is (col_0 + col_1)
    let csv_index_1 = match build_index_2(&csv_desc_1) {
        Err(why)  => panic!("failed building index #1: {}", why),
        Ok(index) => index,
    };

    let csv_index_2 = match build_index_2(&csv_desc_2) {
        Err(why)  => panic!("failed building index #2: {}", why),
        Ok(index) => index,
    };

    /*** 7 ***/
    let mut row_keys_to_compare = HashSet::new();
    for key_1 in csv_index_1.keys() {
        if csv_index_2.contains_key(key_1) {
            row_keys_to_compare.insert(key_1);
        };
    }
    println!("{:?}", row_keys_to_compare);

    /*** 8 ***/
    for row_key in row_keys_to_compare {

        let index_1 = *csv_index_1.get(row_key).unwrap(); // TODO: handle me
        let index_2 = *csv_index_2.get(row_key).unwrap();

        let row_1 = get_csv_row_2(&csv_desc_1, index_1).unwrap(); // TODO: handle me
        let row_2 = get_csv_row_2(&csv_desc_2, index_2).unwrap();

        println!("comparing {}:", row_key);
        println!("line #1: {:?}", row_1);
        println!("line #2: {:?}", row_2);

        for col in &cols_to_compare {

            let col_index_1 = *csv_col_index_1.get(*col).unwrap(); // TODO: handle me
            let col_index_2 = *csv_col_index_2.get(*col).unwrap();

            println!("column {}, index_1={}, index_2={}", col, col_index_1, col_index_2);

            if row_1[col_index_1] != row_2[col_index_2] {
                println!("found a difference for {}, {}: {} / {}", row_key, col, row_1[col_index_1], row_2[col_index_2]);
            }
        }

    }

}
<|endoftext|>


<commit_before>//!                             The Unlicense
//! This is free and unencumbered software released into the public domain.
//!
//! Anyone is free to copy, modify, publish, use, compile, sell, or
//! distribute this software, either in source code form or as a compiled
//! binary, for any purpose, commercial or non-commercial, and by any
//! means.
//!
//! In jurisdictions that recognize copyright laws, the author or authors
//! of this software dedicate any and all copyright interest in the
//! software to the public domain. We make this dedication for the benefit
//! of the public at large and to the detriment of our heirs and
//! successors. We intend this dedication to be an overt act of
//! relinquishment in perpetuity of all present and future rights to this
//! software under copyright law.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//! IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
//! OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//! OTHER DEALINGS IN THE SOFTWARE.
//!
//! For more information, please refer to <http://unlicense.org>
//!

extern crate iron;
extern crate router;
extern crate hyper;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper_rustls;
extern crate urlencoded;
extern crate persistent;
extern crate lru_cache;
extern crate time;
extern crate crypto_hash;

use std::vec::Vec;
use hyper::Client as HyperClient;
use hyper::client::Response as HyperResponse;
use hyper::net::HttpsConnector;
use router::{Router, NoRoute};
use iron::prelude::*;
use iron::status;
use iron::AfterMiddleware;
use regex::Regex;
use std::io::Read;
use hyper_rustls::TlsClient as RustlsClient;
use urlencoded::{UrlEncodedQuery, UrlDecodingError};
use std::collections::HashMap;
use std::option::Option;
use iron::headers::{ContentType, ContentDisposition, CacheControl, ContentLength, LastModified,
                    EntityTag, HttpDate, CacheDirective};
use lru_cache::LruCache;
use persistent::Write;
use iron::typemap::Key;
use std::result::Result;
use crypto_hash::{Algorithm as HashAlgorithm, hex_digest};
use std::ops::Deref;

fn verify_md5(md5: &str) -> bool {
    lazy_static! {
        static ref MD5_RE: Regex = Regex::new("^[0-9a-f]{32}$").unwrap();
    }

    MD5_RE.is_match(md5)
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct CacheControlKey {
    email_md5: String,
    size: Option<i64>,
    default: Option<String>,
}

impl CacheControlKey {
    fn from_request(req: &mut Request) -> Result<CacheControlKey, Response> {
        let email_md5 = req.extensions
            .get::<Router>()
            .unwrap()
            .find("email_md5")
            .unwrap_or("")
            .to_lowercase();

        if !verify_md5(email_md5.as_str()) {
            return Err(Response::with((status::NotFound)));
        }

        let maybe_query = req.get::<UrlEncodedQuery>()
            .map(|val| Some(val))
            .unwrap_or_else(|err: UrlDecodingError| {
                match err {
                    UrlDecodingError::EmptyQuery => Some(HashMap::new()),
                    _ => None,
                }
            });

        let query = match maybe_query {
            Some(m) => m,
            None => return Err(Response::with((status::BadRequest))),
        };

        let size_str = query.get("s")
            .map(|s_vec| s_vec.clone())
            .map(|s_vec| { if s_vec.len() > 0 { Some(s_vec) } else { None } })
            .unwrap_or(None)
            .map(|s_vec| s_vec[0].clone());

        let default = query.get("d")
            .map(|d_vec| d_vec.clone())
            .map(|d_vec| { if d_vec.len() > 0 { Some(d_vec) } else { None } })
            .unwrap_or(None)
            .map(|d_vec| d_vec[0].clone());

        let size: Option<i64> = match size_str {
            Some(m) => {
                match m.parse::<i64>() {
                    Ok(m) => Some(m),
                    Err(_) => return Err(Response::with((status::BadRequest))),
                }
            }
            None => None,
        };

        Ok(CacheControlKey {
            email_md5: email_md5,
            size: size,
            default: default,
        })
    }
}

struct CacheControlData {
    pub etag: EntityTag,
    pub last_modified: HttpDate,
    pub content_type: ContentType,
    pub content_length: u64,
    pub content_disposition: Option<ContentDisposition>,
    pub cache_control: CacheControl,
    pub expires: HttpDate,
}

impl CacheControlData {
    fn from_response(res: &HyperResponse, res_body: &[u8]) -> AvatarResult<CacheControlData> {
        let etag = EntityTag::strong(hex_digest(HashAlgorithm::SHA256, res_body.to_vec()));
        let last_modified = res.headers
            .get::<LastModified>()
            .map(|m| m.deref().clone())
            .unwrap_or_else(|| HttpDate(time::now()));

        let content_type = try!(res.headers
            .get::<ContentType>()
            .map(|m| m.clone())
            .ok_or_else(|| Response::with((status::BadGateway))));

        let content_length = try!(res.headers
            .get::<ContentLength>()
            .map(|m| m.deref().clone() as u64)
            .ok_or_else(|| Response::with((status::BadGateway))));

        let content_disposition = res.headers.get::<ContentDisposition>().map(|m| m.clone());
        let cache_control = res.headers
            .get::<CacheControl>()
            .map(|m| m.clone())
            .unwrap_or_else(|| CacheControl(vec![CacheDirective::MaxAge(600 as u32)]));
        let expires = HttpDate(time::now() + time::Duration::minutes(10));

        Ok(CacheControlData {
            etag: etag,
            last_modified: last_modified,
            content_type: content_type,
            content_length: content_length,
            content_disposition: content_disposition,
            cache_control: cache_control,
            expires: expires,
        })
    }
}

#[derive(Copy, Clone)]
struct Cache;
impl Key for Cache {
    type Value = LruCache<CacheControlKey, CacheControlData>;
}

struct Avatar {
    pub buf: Vec<u8>,
    pub cc_key: CacheControlKey,
    pub cc_data: CacheControlData,
}

type AvatarResult<T> = Result<T, Response>;

impl Avatar {
    fn make_origin_url(cc_key: &CacheControlKey) -> String {
        let mut origin_url = format!("https://secure.gravatar.com/avatar/{}", cc_key.email_md5);

        let mut params: Vec<String> = Vec::new();

        if let Some(ref m) = cc_key.size {
            params.push(format!("s={}", m));
        }

        if let Some(ref m) = cc_key.default {
            params.push(format!("d={}", m));
        }

        if params.len() > 0 {
            origin_url.push_str("?");
            origin_url.push_str(params.join("&").as_str());
        }

        origin_url
    }

    pub fn from_response(res: &mut HyperResponse, cc_key: CacheControlKey) -> AvatarResult<Avatar> {
        if res.status != hyper::Ok {
            return Err(Response::with((status::BadGateway)));
        }

        let mut avatar_body: Vec<u8> = Vec::new();

        try!(res.read_to_end(&mut avatar_body)
            .map_err(|_| Response::with((status::BadGateway))));

        let cc_data = try!(CacheControlData::from_response(res, avatar_body.as_slice()));

        Ok(Avatar {
            buf: avatar_body,
            cc_key: cc_key,
            cc_data: cc_data,
        })

    }

    pub fn fetch(cc_key: CacheControlKey) -> AvatarResult<Avatar> {
        lazy_static! {
            static ref HYPER_CLIENT: HyperClient = HyperClient::with_connector(HttpsConnector::new(RustlsClient::new()));
        }

        let origin_url = Avatar::make_origin_url(&cc_key);

        let mut origin_res = try!(HYPER_CLIENT.get(origin_url.as_str())
            .send()
            .map_err(|_| Response::with((status::BadGateway))));

        Avatar::from_response(&mut origin_res, cc_key)
    }
}

fn handler(mut req: &mut Request) -> IronResult<Response> {
    let cache_mutex = req.get::<Write<Cache>>().unwrap();

    let cc_key = match CacheControlKey::from_request(&mut req) {
        Ok(v) => v,
        Err(res) => return Ok(res),
    };

    {
        let mut cache = cache_mutex.lock().unwrap();

        if cache.contains_key(&cc_key) {
            panic!();
        }
    }

    let avatar = match Avatar::fetch(cc_key) {
        Ok(v) => v,
        Err(_) => return Ok(Response::with((status::BadGateway))),
    };

    Ok(Response::with(((*avatar.cc_data.content_type).clone(), status::Ok, avatar.buf.clone())))
}

struct CustomErrorMsg;

impl CustomErrorMsg {
    fn alter_response(&self, res: Response) -> Response {
        if res.status == Some(status::NotFound) {
            Response::with((status::NotFound, "HTTP 404: Not Found."))

        } else if res.status == Some(status::BadRequest) {
            Response::with((status::BadRequest, "HTTP 400: Bad Request."))

        } else if res.status == Some(status::BadGateway) {
            Response::with((status::BadGateway, "HTTP 502: Bad Gateway."))

        } else {
            res

        }
    }
}

impl AfterMiddleware for CustomErrorMsg {
    fn after(&self, _: &mut Request, res: Response) -> IronResult<Response> {
        Ok(self.alter_response(res))
    }

    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        let res = err.response;
        let res = self.alter_response(res);

        if let Some(_) = err.error.downcast::<NoRoute>() {
            return Ok(res);
        }

        Err(IronError {
            error: err.error,
            response: res,
        })
    }
}

struct XPoweredBy;

impl XPoweredBy {
    fn add_header(&self, res: &mut Response) {
        res.headers.set_raw("X-Powered-By", vec![b"Rust".to_vec()]);
    }
}

impl AfterMiddleware for XPoweredBy {
    fn after(&self, _: &mut Request, mut res: Response) -> IronResult<Response> {
        self.add_header(&mut res);

        Ok(res)
    }

    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        let mut res = err.response;
        let inner_err = err.error;
        self.add_header(&mut res);

        Err(IronError {
            error: inner_err,
            response: res,
        })
    }
}

fn main() {
    let mut router = Router::new();

    router.get("/avatar/:email_md5", handler, "email_md5");

    let mut chain = Chain::new(router);
    chain.link_after(CustomErrorMsg);
    chain.link_after(XPoweredBy);

    chain.link(Write::<Cache>::both(LruCache::new(102400)));

    Iron::new(chain).http("localhost:3000").unwrap();
}
<commit_msg>Finish Cache Control.<commit_after>//!                             The Unlicense
//! This is free and unencumbered software released into the public domain.
//!
//! Anyone is free to copy, modify, publish, use, compile, sell, or
//! distribute this software, either in source code form or as a compiled
//! binary, for any purpose, commercial or non-commercial, and by any
//! means.
//!
//! In jurisdictions that recognize copyright laws, the author or authors
//! of this software dedicate any and all copyright interest in the
//! software to the public domain. We make this dedication for the benefit
//! of the public at large and to the detriment of our heirs and
//! successors. We intend this dedication to be an overt act of
//! relinquishment in perpetuity of all present and future rights to this
//! software under copyright law.
//!
//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
//! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
//! IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
//! OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//! OTHER DEALINGS IN THE SOFTWARE.
//!
//! For more information, please refer to <http://unlicense.org>
//!

extern crate iron;
extern crate router;
extern crate hyper;
#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper_rustls;
extern crate urlencoded;
extern crate persistent;
extern crate lru_cache;
extern crate time;
extern crate crypto_hash;

use std::vec::Vec;
use hyper::Client as HyperClient;
use hyper::client::Response as HyperResponse;
use hyper::net::HttpsConnector;
use router::{Router, NoRoute};
use iron::prelude::*;
use iron::status;
use iron::{AfterMiddleware, Handler};
use regex::Regex;
use std::io::Read;
use hyper_rustls::TlsClient as RustlsClient;
use urlencoded::{UrlEncodedQuery, UrlDecodingError};
use std::collections::HashMap;
use std::option::Option;
use iron::headers::{ContentType, ContentDisposition, CacheControl, LastModified, ETag, Expires,
                    IfModifiedSince, IfNoneMatch, EntityTag, HttpDate, CacheDirective};
use lru_cache::LruCache;
use persistent::Write;
use iron::typemap::Key;
use std::result::Result;
use crypto_hash::{Algorithm as HashAlgorithm, hex_digest};
use std::ops::Deref;
use std::sync::{Arc, Mutex};

fn verify_md5(md5: &str) -> bool {
    lazy_static! {
        static ref MD5_RE: Regex = Regex::new("^[0-9a-f]{32}$").unwrap();
    }

    MD5_RE.is_match(md5)
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct CacheControlKey {
    email_md5: String,
    size: Option<i64>,
    default: Option<String>,
}

impl CacheControlKey {
    fn from_request(req: &mut Request) -> Result<CacheControlKey, Response> {
        let email_md5 = req.extensions
            .get::<Router>()
            .unwrap()
            .find("email_md5")
            .unwrap_or("")
            .to_lowercase();

        if !verify_md5(email_md5.as_str()) {
            return Err(Response::with((status::NotFound)));
        }

        let maybe_query = req.get::<UrlEncodedQuery>()
            .map(|val| Some(val))
            .unwrap_or_else(|err: UrlDecodingError| {
                match err {
                    UrlDecodingError::EmptyQuery => Some(HashMap::new()),
                    _ => None,
                }
            });

        let query = match maybe_query {
            Some(m) => m,
            None => return Err(Response::with((status::BadRequest))),
        };

        let size_str = query.get("s")
            .map(|s_vec| s_vec.clone())
            .map(|s_vec| { if s_vec.len() > 0 { Some(s_vec) } else { None } })
            .unwrap_or(None)
            .map(|s_vec| s_vec[0].clone());

        let default = query.get("d")
            .map(|d_vec| d_vec.clone())
            .map(|d_vec| { if d_vec.len() > 0 { Some(d_vec) } else { None } })
            .unwrap_or(None)
            .map(|d_vec| d_vec[0].clone());

        let size: Option<i64> = match size_str {
            Some(m) => {
                match m.parse::<i64>() {
                    Ok(m) => Some(m),
                    Err(_) => return Err(Response::with((status::BadRequest))),
                }
            }
            None => None,
        };

        Ok(CacheControlKey {
            email_md5: email_md5,
            size: size,
            default: default,
        })
    }
}

#[derive(Clone)]
struct CacheControlData {
    pub etag: EntityTag,
    pub last_modified: HttpDate,
    pub content_type: ContentType,
    pub content_disposition: Option<ContentDisposition>,
    pub cache_control: CacheControl,
    pub expires: HttpDate,
}

impl CacheControlData {
    fn from_response(res: &HyperResponse, res_body: &[u8]) -> AvatarResult<CacheControlData> {
        let etag = EntityTag::strong(hex_digest(HashAlgorithm::SHA256, res_body.to_vec()));
        let last_modified = res.headers
            .get::<LastModified>()
            .map(|m| m.deref().clone())
            .unwrap_or_else(|| HttpDate(time::now()));

        let content_type = try!(res.headers
            .get::<ContentType>()
            .map(|m| m.clone())
            .ok_or_else(|| Response::with((status::BadGateway))));

        let content_disposition = res.headers.get::<ContentDisposition>().map(|m| m.clone());
        let cache_control = CacheControl(vec![CacheDirective::MaxAge(600 as u32)]);
        let expires = HttpDate(time::now() + time::Duration::minutes(10));

        Ok(CacheControlData {
            etag: etag,
            last_modified: last_modified,
            content_type: content_type,
            content_disposition: content_disposition,
            cache_control: cache_control,
            expires: expires,
        })
    }

    fn set_cache_headers(&self, mut res: &mut Response) {
        res.headers.set(ETag(self.etag.clone()));
        res.headers.set(LastModified(self.last_modified.clone()));
        if let Some(m) = self.content_disposition.clone() {
            res.headers.set(m);
        }
        res.headers.set(self.cache_control.clone());
        res.headers.set(Expires(self.expires.clone()));

    }
}

#[derive(Clone)]
struct Cache;
impl Key for Cache {
    type Value = LruCache<CacheControlKey, CacheControlData>;
}

struct Avatar {
    pub buf: Vec<u8>,
    pub cc_key: CacheControlKey,
    pub cc_data: CacheControlData,
}

type AvatarResult<T> = Result<T, Response>;

impl Avatar {
    fn make_origin_url(cc_key: &CacheControlKey) -> String {
        let mut origin_url = format!("https://secure.gravatar.com/avatar/{}", cc_key.email_md5);

        let mut params: Vec<String> = Vec::new();

        if let Some(ref m) = cc_key.size {
            params.push(format!("s={}", m));
        }

        if let Some(ref m) = cc_key.default {
            params.push(format!("d={}", m));
        }

        if params.len() > 0 {
            origin_url.push_str("?");
            origin_url.push_str(params.join("&").as_str());
        }

        origin_url
    }

    pub fn from_response(res: &mut HyperResponse, cc_key: CacheControlKey) -> AvatarResult<Avatar> {
        if res.status != hyper::Ok {
            return Err(Response::with((status::BadGateway)));
        }

        let mut avatar_body: Vec<u8> = Vec::new();

        try!(res.read_to_end(&mut avatar_body)
            .map_err(|_| Response::with((status::BadGateway))));

        let cc_data = try!(CacheControlData::from_response(res, avatar_body.as_slice()));

        Ok(Avatar {
            buf: avatar_body,
            cc_key: cc_key,
            cc_data: cc_data,
        })

    }

    pub fn fetch(cc_key: CacheControlKey) -> AvatarResult<Avatar> {
        lazy_static! {
            static ref HYPER_CLIENT: HyperClient = HyperClient::with_connector(HttpsConnector::new(RustlsClient::new()));
        }

        let origin_url = Avatar::make_origin_url(&cc_key);

        let mut origin_res = try!(HYPER_CLIENT.get(origin_url.as_str())
            .send()
            .map_err(|_| Response::with((status::BadGateway))));

        Avatar::from_response(&mut origin_res, cc_key)
    }
}

struct MainHandler;

impl MainHandler {
    fn read_from_cache(&self,
                       cc_key: &CacheControlKey,
                       cache_mutex: &Arc<Mutex<LruCache<CacheControlKey, CacheControlData>>>)
                       -> Option<CacheControlData> {
        let mut cache = cache_mutex.lock().unwrap();

        let cc_data = match cache.get_mut(cc_key).map(|m| m.clone()) {
            Some(m) => m,
            None => return None,
        };

        if cc_data.expires > HttpDate(time::now()) {
            Some(cc_data)
        } else {
            cache.remove(cc_key);
            None
        }
    }

    fn response_with_200(&self, avatar: &Avatar) -> IronResult<Response> {
        let mut res = Response::with((avatar.cc_data.content_type.deref().clone(),
                                      status::Ok,
                                      avatar.buf.clone()));

        avatar.cc_data.set_cache_headers(&mut res);
        Ok(res)

    }

    fn response_with_304(&self, cc_data: &CacheControlData) -> IronResult<Response> {
        let mut res = Response::with((cc_data.content_type.deref().clone(), status::NotModified));

        cc_data.set_cache_headers(&mut res);

        Ok(res)
    }
}

impl Handler for MainHandler {
    fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
        let cache_mutex = req.get::<Write<Cache>>().unwrap();

        let cc_key = match CacheControlKey::from_request(&mut req) {
            Ok(v) => v,
            Err(res) => return Ok(res),
        };

        let maybe_if_modified_since = req.headers.get::<IfModifiedSince>();
        let maybe_if_none_match = req.headers.get::<IfNoneMatch>();

        if let Some(if_none_match) = maybe_if_none_match {
            // Prefer ETag.
            if let Some(cc_data) = self.read_from_cache(&cc_key, &cache_mutex) {
                if let &IfNoneMatch::Items(ref items) = if_none_match {
                    if items.as_slice().contains(&cc_data.etag) {
                        return self.response_with_304(&cc_data);
                    }
                }
            }
        } else if let Some(if_modified_since) = maybe_if_modified_since {
            if let Some(cc_data) = self.read_from_cache(&cc_key, &cache_mutex) {
                if if_modified_since.deref() == &cc_data.last_modified {
                    return self.response_with_304(&cc_data);
                }
            }
        }

        let avatar = match Avatar::fetch(cc_key) {
            Ok(v) => v,
            Err(res) => return Ok(res),
        };

        let res_result = self.response_with_200(&avatar);
        {
            let mut cache = cache_mutex.lock().unwrap();

            cache.insert(avatar.cc_key, avatar.cc_data);
        }
        res_result
    }
}

struct CustomErrorMsg;

impl CustomErrorMsg {
    fn alter_response(&self, res: Response) -> Response {
        if res.status == Some(status::NotFound) {
            Response::with((status::NotFound, "HTTP 404: Not Found."))

        } else if res.status == Some(status::BadRequest) {
            Response::with((status::BadRequest, "HTTP 400: Bad Request."))

        } else if res.status == Some(status::BadGateway) {
            Response::with((status::BadGateway, "HTTP 502: Bad Gateway."))

        } else {
            res

        }
    }
}

impl AfterMiddleware for CustomErrorMsg {
    fn after(&self, _: &mut Request, res: Response) -> IronResult<Response> {
        Ok(self.alter_response(res))
    }

    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        let res = err.response;
        let res = self.alter_response(res);

        if let Some(_) = err.error.downcast::<NoRoute>() {
            return Ok(res);
        }

        Err(IronError {
            error: err.error,
            response: res,
        })
    }
}

struct XPoweredBy;

impl XPoweredBy {
    fn add_header(&self, res: &mut Response) {
        res.headers.set_raw("X-Powered-By", vec![b"Rust".to_vec()]);
    }
}

impl AfterMiddleware for XPoweredBy {
    fn after(&self, _: &mut Request, mut res: Response) -> IronResult<Response> {
        self.add_header(&mut res);

        Ok(res)
    }

    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        let mut res = err.response;
        let inner_err = err.error;
        self.add_header(&mut res);

        Err(IronError {
            error: inner_err,
            response: res,
        })
    }
}

fn main() {
    let mut router = Router::new();

    router.get("/avatar/:email_md5", MainHandler, "email_md5");

    let mut chain = Chain::new(router);
    chain.link_after(CustomErrorMsg);
    chain.link_after(XPoweredBy);

    chain.link(Write::<Cache>::both(LruCache::new(102400)));

    Iron::new(chain).http("localhost:3000").unwrap();
}
<|endoftext|>


<commit_before>extern crate pnet;
extern crate clap;

pub mod cli;
pub mod config;

use std::thread;
use std::time::Duration;
use std::net::Ipv4Addr;

use pnet::datalink::{self, NetworkInterface};
use pnet::datalink::Channel;
use pnet::packet::ethernet::MutableEthernetPacket;
use pnet::packet::arp::MutableArpPacket;
use pnet::util::MacAddr;
use pnet::packet::ethernet::EtherTypes;
use pnet::packet::MutablePacket;
use pnet::packet::arp::{ArpHardwareTypes, ArpOperations, ArpOperation};

use config::Config;
use cli::cli_main;

fn send_arp_packet(interface: NetworkInterface, source_ip: Ipv4Addr, source_mac: MacAddr, target_ip: Ipv4Addr, target_mac: MacAddr, arp_operation: ArpOperation) {
    let(mut tx, _) = match datalink::channel(&interface, Default::default()) {
        Ok(Channel::Ethernet(tx, rx)) => (tx, rx),
        Ok(_) => panic!("Unknown channel type"),
        Err(e) => panic!("Error happened {}", e),
    };

    /// ethernet_packet = Ethernet {
    ///     destination: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
    ///     source: [0x28, 0xef, 0xf9, 0x5f, 0x8e, 0x2b],
    ///     ethertype: [0x08, 0x06], // Arp(0x0806)
    ///     payload: arp_packet
    /// }
    ///
    /// arp_packet = Arp {
    ///     hardware_type: [0x00, 0x01],
    ///     protocol_type: [0x08, 0x00], // Ipv4(0x0800)
    ///     hw_addr_len: [0x06],
    ///     proto_addr_len: [0x04],
    ///     operation: [0x00, 0x02], // Reply(0x0002)
    ///     sender_hw_addr: [0x28, 0xef, 0xf9, 0x5f, 0x8e, 0x2b],
    ///     sender_proto_addr: [0xc0, 0xa8, 0x00, 0x66], // Ipv4(192.168.0.102)
    ///     target_hw_addr: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff], // Broadcast
    ///     target_proto_addr: [0xc0, 0xa8, 0x00, 0x65], // Ipv4(192.168.0.101)
    ///     payload: [],
    /// }

    let mut ethernet_buffer: Vec<u8> = (0..42).map(|_| 0x00).collect::<Vec<u8>>();

    let mut ethernet_packet = MutableEthernetPacket::new(&mut ethernet_buffer).unwrap();
    ethernet_packet.set_destination(target_mac);
    ethernet_packet.set_source(source_mac);
    ethernet_packet.set_ethertype(EtherTypes::Arp);

    let mut arp_buffer: Vec<u8> = (0..28).map(|_| 0x00).collect::<Vec<u8>>();

    let mut arp_packet = MutableArpPacket::new(&mut arp_buffer).unwrap();

    arp_packet.set_hardware_type(ArpHardwareTypes::Ethernet);
    arp_packet.set_protocol_type(EtherTypes::Ipv4);
    arp_packet.set_hw_addr_len(6);
    arp_packet.set_proto_addr_len(4);
    arp_packet.set_operation(arp_operation);
    arp_packet.set_sender_hw_addr(source_mac);
    arp_packet.set_sender_proto_addr(source_ip);
    arp_packet.set_target_hw_addr(target_mac);
    arp_packet.set_target_proto_addr(target_ip);

    ethernet_packet.set_payload(arp_packet.packet_mut());

    tx.send_to(&ethernet_packet.to_immutable(), Some(interface));
}

fn main() {
    let config: Config = cli_main();
    let mut packet_count: i32 = 0;

    loop {
        let interfaces = datalink::interfaces();

        // Get en1 interface in osx.
        let interfaces_name_match = |iface: &NetworkInterface| iface.name == config.interface;
        let interface = interfaces.into_iter().filter(interfaces_name_match).next().unwrap();

        send_arp_packet(interface, config.source_ip, config.source_mac, config.target_ip, config.target_mac, config.arp_operation);

        packet_count += 1;
        let arp_operation = match config.arp_operation {
            ArpOperations::Request => "Request",
            ArpOperations::Reply => "Reply",
            ArpOperation(_) => panic!("Unknown operation")
        };
        println!("Sent {} ARP {} packets.", packet_count, arp_operation);
        thread::sleep(Duration::new(5, 0));
    }
}
<commit_msg>Don't use vectors instead use arrays<commit_after>extern crate pnet;
extern crate clap;

pub mod cli;
pub mod config;

use std::thread;
use std::time::Duration;
use std::net::Ipv4Addr;

use pnet::datalink::{self, NetworkInterface};
use pnet::datalink::Channel;
use pnet::packet::ethernet::MutableEthernetPacket;
use pnet::packet::arp::MutableArpPacket;
use pnet::util::MacAddr;
use pnet::packet::ethernet::EtherTypes;
use pnet::packet::MutablePacket;
use pnet::packet::arp::{ArpHardwareTypes, ArpOperations, ArpOperation};

use config::Config;
use cli::cli_main;

fn send_arp_packet(interface: NetworkInterface, source_ip: Ipv4Addr, source_mac: MacAddr, target_ip: Ipv4Addr, target_mac: MacAddr, arp_operation: ArpOperation) {
    let(mut tx, _) = match datalink::channel(&interface, Default::default()) {
        Ok(Channel::Ethernet(tx, rx)) => (tx, rx),
        Ok(_) => panic!("Unknown channel type"),
        Err(e) => panic!("Error happened {}", e),
    };

    /// ethernet_packet = Ethernet {
    ///     destination: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
    ///     source: [0x28, 0xef, 0xf9, 0x5f, 0x8e, 0x2b],
    ///     ethertype: [0x08, 0x06], // Arp(0x0806)
    ///     payload: arp_packet
    /// }
    ///
    /// arp_packet = Arp {
    ///     hardware_type: [0x00, 0x01],
    ///     protocol_type: [0x08, 0x00], // Ipv4(0x0800)
    ///     hw_addr_len: [0x06],
    ///     proto_addr_len: [0x04],
    ///     operation: [0x00, 0x02], // Reply(0x0002)
    ///     sender_hw_addr: [0x28, 0xef, 0xf9, 0x5f, 0x8e, 0x2b],
    ///     sender_proto_addr: [0xc0, 0xa8, 0x00, 0x66], // Ipv4(192.168.0.102)
    ///     target_hw_addr: [0xff, 0xff, 0xff, 0xff, 0xff, 0xff], // Broadcast
    ///     target_proto_addr: [0xc0, 0xa8, 0x00, 0x65], // Ipv4(192.168.0.101)
    ///     payload: [],
    /// }

    let mut ethernet_buffer = [0u8; 42];
    let mut ethernet_packet = MutableEthernetPacket::new(&mut ethernet_buffer).unwrap();

    ethernet_packet.set_destination(target_mac);
    ethernet_packet.set_source(source_mac);
    ethernet_packet.set_ethertype(EtherTypes::Arp);

    let mut arp_buffer = [0u8; 28];
    let mut arp_packet = MutableArpPacket::new(&mut arp_buffer).unwrap();

    arp_packet.set_hardware_type(ArpHardwareTypes::Ethernet);
    arp_packet.set_protocol_type(EtherTypes::Ipv4);
    arp_packet.set_hw_addr_len(6);
    arp_packet.set_proto_addr_len(4);
    arp_packet.set_operation(arp_operation);
    arp_packet.set_sender_hw_addr(source_mac);
    arp_packet.set_sender_proto_addr(source_ip);
    arp_packet.set_target_hw_addr(target_mac);
    arp_packet.set_target_proto_addr(target_ip);

    ethernet_packet.set_payload(arp_packet.packet_mut());

    tx.send_to(&ethernet_packet.to_immutable(), Some(interface));
}

fn main() {
    let config: Config = cli_main();
    let mut packet_count: i32 = 0;

    loop {
        let interfaces = datalink::interfaces();

        let interfaces_name_match = |iface: &NetworkInterface| iface.name == config.interface;
        let interface = interfaces.into_iter().filter(interfaces_name_match).next().unwrap();

        send_arp_packet(interface, config.source_ip, config.source_mac, config.target_ip, config.target_mac, config.arp_operation);

        packet_count += 1;
        let arp_operation = match config.arp_operation {
            ArpOperations::Request => "Request",
            ArpOperations::Reply => "Reply",
            ArpOperation(_) => panic!("Unknown operation")
        };
        println!("Sent {} ARP {} packets.", packet_count, arp_operation);
        thread::sleep(Duration::new(5, 0));
    }
}
<|endoftext|>


<commit_before>extern crate cargo;
extern crate tabwriter;

extern crate serde;
#[macro_use]
extern crate serde_derive;

use cargo::core::Package;
use cargo::core::Workspace;
use cargo::ops;
use cargo::util::Config;

use std::env;
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::path;

#[derive(Deserialize)]
struct Options {
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_frozen: bool,
    flag_locked: bool,
    flag_unstable: Vec<String>,
}

#[derive(Debug)]
enum Licenses {
    Licenses(Vec<String>),
    File(String),
    Missing,
}

impl fmt::Display for Licenses {
    fn fmt(self: &Self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Licenses::File(_) => Ok(write!(f, "Specified in license file")?),
            Licenses::Missing => Ok(write!(f, "Missing")?),
            Licenses::Licenses(ref lic_names) => Ok(write!(f, "{}", lic_names.join(", "))?),
        }
    }
}

const USAGE_STR: &str = r#"
Produce Bill of Materials from Cargo project's depencies
Usage:
    cargo bom [options]
Options:
    -h, --help               Print this message
    -V, --version            Print version information
    -v, --verbose ...        Use verbose output
    --frozen                 Require Cargo.lock and cache are up to date
    --locked                 Require Cargo.lock is up to date
    --color WHEN             Coloring: auto, always, never
This cargo subcommand will produce Bill of Materials (BOM) from crates the
project depends on.
"#;

fn main() {
    let mut config = Config::default().expect("cargo config");
    let args: Vec<String> = env::args().collect();

    let res = cargo::call_main_without_stdin(real_main, &mut config, USAGE_STR, &args, false);
    if let Err(e) = res {
        cargo::exit_with_error(e, &mut *config.shell());
    }
}

fn real_main(options: Options, config: &mut Config) -> cargo::CliResult {
    config.configure(
        options.flag_verbose,
        options.flag_quiet,
        &options.flag_color,
        options.flag_frozen,
        options.flag_locked,
        &options.flag_unstable,
    )?;

    let manifest = config.cwd().join("Cargo.toml");
    let ws = Workspace::new(&manifest, config)?;
    let members: Vec<&Package> = ws.members().collect();
    let (package_ids, resolve) = ops::resolve_ws(&ws)?;

    let mut packages = Vec::new();
    for package_id in resolve.iter() {
        let package = package_ids.get(package_id)?;
        if members.contains(&package) {
            // Skip listing our own packages in our workspace
            continue;
        }
        let name = package.name().to_owned();
        let version = format!("{}", package.version());
        let licenses = format!("{}", package_licenses(package));
        let license_files = package_license_files(package);
        packages.push((name, version, licenses, license_files));
    }

    packages.sort();

    let mut tw = tabwriter::TabWriter::new(io::stdout());

    writeln!(tw, "Name\t| Version\t| Licenses").expect("write");

    for (name, version, licenses, _) in packages.clone() {
        writeln!(tw, "{}\t| {}\t| {}", &name, &version, &licenses).expect("write");
    }

    tw.flush().expect("tw.flush"); // TabWriter flush() makes the actual write to stdout.

    println!();

    for (name, version, _, license_files) in packages {
        if license_files.is_empty() {
            continue;
        }

        println!("-----BEGIN {} {} LICENSES-----", name, version);

        let mut buf = String::new();
        for file in license_files {
            let mut fs = std::fs::File::open(file).expect("File::open");
            fs.read_to_string(&mut buf).expect("read_to_string");
            println!("{}", buf);
            buf.clear();
        }

        println!("-----END {} {} LICENSES-----", name, version);
        println!();
    }

    Ok(())
}

fn package_licenses(package: &Package) -> Licenses {
    let metadata = package.manifest().metadata();

    if let Some(ref license_str) = metadata.license {
        let mut licenses: Vec<String> = license_str
            .split('/')
            .map(|s| s.trim().to_owned())
            .collect();
        licenses.sort();
        licenses.dedup();
        return Licenses::Licenses(licenses);
    }

    if let Some(ref license_file) = metadata.license_file {
        return Licenses::File(license_file.to_owned());
    }

    Licenses::Missing
}

fn package_license_files(package: &Package) -> Vec<path::PathBuf> {
    let mut result = Vec::new();

    if let Some(path) = package.manifest_path().parent() {
        for entry in path.read_dir().expect("read_dir call failed") {
            if let Ok(entry) = entry {
                if let Ok(name) = entry.file_name().into_string() {
                    if name.starts_with("LICENSE") {
                        result.push(entry.path())
                    }
                }
            }
        }
    }

    result
}
<commit_msg>Fix Clippy nag<commit_after>extern crate cargo;
extern crate tabwriter;

extern crate serde;
#[macro_use]
extern crate serde_derive;

use cargo::core::Package;
use cargo::core::Workspace;
use cargo::ops;
use cargo::util::Config;

use std::env;
use std::fmt;
use std::io;
use std::io::prelude::*;
use std::path;

#[derive(Deserialize)]
struct Options {
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_frozen: bool,
    flag_locked: bool,
    flag_unstable: Vec<String>,
}

#[derive(Debug)]
enum Licenses {
    Licenses(Vec<String>),
    File(String),
    Missing,
}

impl fmt::Display for Licenses {
    fn fmt(self: &Self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Licenses::File(_) => write!(f, "Specified in license file")?,
            Licenses::Missing => write!(f, "Missing")?,
            Licenses::Licenses(ref lic_names) => write!(f, "{}", lic_names.join(", "))?,
        }
        Ok(())
    }
}

const USAGE_STR: &str = r#"
Produce Bill of Materials from Cargo project's depencies
Usage:
    cargo bom [options]
Options:
    -h, --help               Print this message
    -V, --version            Print version information
    -v, --verbose ...        Use verbose output
    --frozen                 Require Cargo.lock and cache are up to date
    --locked                 Require Cargo.lock is up to date
    --color WHEN             Coloring: auto, always, never
This cargo subcommand will produce Bill of Materials (BOM) from crates the
project depends on.
"#;

fn main() {
    let mut config = Config::default().expect("cargo config");
    let args: Vec<String> = env::args().collect();

    let res = cargo::call_main_without_stdin(real_main, &mut config, USAGE_STR, &args, false);
    if let Err(e) = res {
        cargo::exit_with_error(e, &mut *config.shell());
    }
}

fn real_main(options: Options, config: &mut Config) -> cargo::CliResult {
    config.configure(
        options.flag_verbose,
        options.flag_quiet,
        &options.flag_color,
        options.flag_frozen,
        options.flag_locked,
        &options.flag_unstable,
    )?;

    let manifest = config.cwd().join("Cargo.toml");
    let ws = Workspace::new(&manifest, config)?;
    let members: Vec<&Package> = ws.members().collect();
    let (package_ids, resolve) = ops::resolve_ws(&ws)?;

    let mut packages = Vec::new();
    for package_id in resolve.iter() {
        let package = package_ids.get(package_id)?;
        if members.contains(&package) {
            // Skip listing our own packages in our workspace
            continue;
        }
        let name = package.name().to_owned();
        let version = format!("{}", package.version());
        let licenses = format!("{}", package_licenses(package));
        let license_files = package_license_files(package);
        packages.push((name, version, licenses, license_files));
    }

    packages.sort();

    let mut tw = tabwriter::TabWriter::new(io::stdout());

    writeln!(tw, "Name\t| Version\t| Licenses").expect("write");

    for (name, version, licenses, _) in packages.clone() {
        writeln!(tw, "{}\t| {}\t| {}", &name, &version, &licenses).expect("write");
    }

    tw.flush().expect("tw.flush"); // TabWriter flush() makes the actual write to stdout.

    println!();

    for (name, version, _, license_files) in packages {
        if license_files.is_empty() {
            continue;
        }

        println!("-----BEGIN {} {} LICENSES-----", name, version);

        let mut buf = String::new();
        for file in license_files {
            let mut fs = std::fs::File::open(file).expect("File::open");
            fs.read_to_string(&mut buf).expect("read_to_string");
            println!("{}", buf);
            buf.clear();
        }

        println!("-----END {} {} LICENSES-----", name, version);
        println!();
    }

    Ok(())
}

fn package_licenses(package: &Package) -> Licenses {
    let metadata = package.manifest().metadata();

    if let Some(ref license_str) = metadata.license {
        let mut licenses: Vec<String> = license_str
            .split('/')
            .map(|s| s.trim().to_owned())
            .collect();
        licenses.sort();
        licenses.dedup();
        return Licenses::Licenses(licenses);
    }

    if let Some(ref license_file) = metadata.license_file {
        return Licenses::File(license_file.to_owned());
    }

    Licenses::Missing
}

fn package_license_files(package: &Package) -> Vec<path::PathBuf> {
    let mut result = Vec::new();

    if let Some(path) = package.manifest_path().parent() {
        for entry in path.read_dir().expect("read_dir call failed") {
            if let Ok(entry) = entry {
                if let Ok(name) = entry.file_name().into_string() {
                    if name.starts_with("LICENSE") {
                        result.push(entry.path())
                    }
                }
            }
        }
    }

    result
}
<|endoftext|>


<commit_before>mod git_history;
mod git_client;
mod logger;
extern crate toml;
extern crate regex;

fn print_log(log_entries: Vec<git_history::LogEntry>) {
    for entry in log_entries {
        logger::stdout(entry.revision);
        logger::stdout(entry.title);
    }
}

fn main() {
    println!("semantic.rs 🚀");

    logger::stdout("Fetched git history");
    match git_client::log() {
        Ok(log) => print_log(log),
        Err(err) => {
            logger::stderr("fatal: Failed to retrieve git history");
            logger::stderr(err);
        }
    }
}
<commit_msg>Chore(main): Add toml_file module<commit_after>mod git_history;
mod git_client;
mod logger;
mod toml_file;
extern crate toml;
extern crate regex;

fn print_log(log_entries: Vec<git_history::LogEntry>) {
    for entry in log_entries {
        logger::stdout(entry.revision);
        logger::stdout(entry.title);
    }
}

fn main() {
    println!("semantic.rs 🚀");

    logger::stdout("Fetched git history");
    match git_client::log() {
        Ok(log) => print_log(log),
        Err(err) => {
            logger::stderr("fatal: Failed to retrieve git history");
            logger::stderr(err);
        }
    }
}
<|endoftext|>


<commit_before>//! # Entry point to the application.
//!
//! Gather and parse command line arguments and configuration files, pass
//! them into the application and do some post-exit clean up.

mod app;
#[macro_use]
mod utils;

use utils::*;

fn main() {
    if let Err(e) = app::run() {
        errln!("Error: {:?}", e);
        ::std::process::exit(1);
    }
}

<commit_msg>initial design for CLI<commit_after>//! # Entry point to the application.
//!
//! Gather and parse command line arguments and configuration files, pass
//! them into the application and do some post-exit clean up.

#[macro_use]
extern crate clap;
extern crate typed_arena;

mod app;
mod output;
#[macro_use]
mod utils;

use utils::*;

fn main() {
    parse_args();
    if let Err(e) = app::run() {
        errln!("Error: {:?}", e);
        ::std::process::exit(1);
    }
}

fn parse_args() {
    let args = clap_app! {
        @app (app_from_crate!())
        (@arg EXPRESSION: -e --expression +takes_value "Evaluates EXPRESSION")
        (@arg FILE: "Reads EXPRESSION from FILE and evaluates it.")
        (@arg format: -f --format +takes_value default_value("ansi") possible_values(&["ansi", "tmux"]) "Sets output format")
        (@arg debug: -d ... "Sets the level of debugging information")
        (@arg verbose: -v ... "Sets the level of verbosity")
    }.get_matches();
}

<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate docopt;
extern crate plist;
extern crate rustc_serialize;
extern crate tera;
extern crate urlparse;

mod applescript;
mod cli;
mod safari;
mod urls;

const NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

fn main() {
  let args = cli::parse_args(NAME);
  if args.flag_version {
    println!("{} v{}", NAME, VERSION);
  } else if args.cmd_url {
    safari::assert_safari_is_running();
    print!("{}", safari::get_url(args.flag_window, args.flag_tab));
  } else if args.cmd_urls_all {
    safari::assert_safari_is_running();
    let urls = safari::get_all_urls();
    for url in urls {
      println!("{}", url);
    }
  } else if args.cmd_close_tabs {
    safari::assert_safari_is_running();
    let patterns = args.arg_urls_to_close.split(",").collect();
    safari::close_tabs(patterns);
  } else if args.cmd_reading_list {
    let urls = safari::get_reading_list_urls();
    for url in urls {
      println!("{}", url);
    }
  }
}
<commit_msg>Space out main.rs for better readability<commit_after>#![deny(warnings)]

extern crate docopt;
extern crate plist;
extern crate rustc_serialize;
extern crate tera;
extern crate urlparse;

mod applescript;
mod cli;
mod safari;
mod urls;

const NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

fn main() {
  let args = cli::parse_args(NAME);

  if args.flag_version {
    println!("{} v{}", NAME, VERSION);
  }

  if args.cmd_url {
    safari::assert_safari_is_running();
    print!("{}", safari::get_url(args.flag_window, args.flag_tab));
  }

  if args.cmd_urls_all {
    safari::assert_safari_is_running();
    for url in safari::get_all_urls() {
      println!("{}", url);
    }
  }

  if args.cmd_close_tabs {
    safari::assert_safari_is_running();
    let patterns = args.arg_urls_to_close.split(",").collect();
    safari::close_tabs(patterns);
  }

  if args.cmd_reading_list {
    for url in safari::get_reading_list_urls() {
      println!("{}", url);
    }
  }
}
<|endoftext|>


<commit_before>extern crate ansi_term;

use std::io::{BufRead, BufReader};
use std::fs::File;
use std::{env, process};

enum LineType {
    Title(String),
    Empty(String),
    Description(String),
    ExampleText(String),
    ExampleCode(String),
}

struct Tokenizer<R: BufRead> {
    reader: R,
    current_line: String,
}

impl<R> Tokenizer<R> where R: BufRead {

    fn new(reader: R) -> Tokenizer<R> {
        Tokenizer {
            reader: reader,
            current_line: String::new(),
        }
    }

    fn next(&mut self) -> Option<&str> {
        self.current_line.clear();
        let bytes_read = self.reader.read_line(&mut self.current_line).unwrap();
        match bytes_read {
            0 => None,
            _ => Some(&self.current_line),
        }
    }

}

/// Open file, return a `BufRead` instance
fn get_file_reader(filepath: &str) -> Result<BufReader<File>, String> {
    let file = try!(
        File::open(filepath)
            .map_err(|msg| format!("Could not open file: {}", msg))
    );
    Ok(BufReader::new(file))
}

fn main() {

    // Parse arguments
    let args: Vec<_> = env::args().collect();
    if args.len() != 2 {
        println!("Usage: {} <command>", args[0]);
        process::exit(1);
    }

    // Open file
    let reader = get_file_reader(&args[1]).unwrap_or_else(|msg| {
        println!("{}", msg);
        process::exit(1);
    });

    // Create tokenizer
    let mut tokenizer = Tokenizer::new(reader);

    // Tokenize and print output
    while let Some(token) = tokenizer.next() {
        print!("{}", token);
    }

}
<commit_msg>Implement classification of token types<commit_after>extern crate ansi_term;

use std::io::{BufRead, BufReader};
use std::fs::File;
use std::{env, process};
use std::convert::From;

#[derive(Debug, Eq, PartialEq)]
enum LineType {
    Empty,
    Title(String),
    Description(String),
    ExampleText(String),
    ExampleCode(String),
    Other(String),
}

impl<'a> From<&'a str> for LineType {
    /// Convert a string slice to a LineType. Newlines and whitespace are trimmed.
    fn from(line: &'a str) -> LineType {
        let trimmed = line.trim();
        let mut chars = trimmed.chars();
        match chars.next() {
            None => LineType::Empty,
            Some('#') => LineType::Title(trimmed.trim_left_matches(|chr: char| chr == '#' || chr.is_whitespace()).into()),
            Some('>') => LineType::Description(trimmed.trim_left_matches(|chr: char| chr == '>' || chr.is_whitespace()).into()),
            Some('-') => LineType::ExampleText(trimmed.trim_left_matches(|chr: char| chr == '-' || chr.is_whitespace()).into()),
            Some('`') if chars.last() == Some('`') => LineType::ExampleCode(trimmed.trim_matches(|chr: char| chr == '`' || chr.is_whitespace()).into()),
            _ => LineType::Other(trimmed.into()),
        }
    }
}

#[derive(Debug)]
struct Tokenizer<R: BufRead> {
    reader: R,
    current_line: String,
}

impl<R> Tokenizer<R> where R: BufRead {

    fn new(reader: R) -> Tokenizer<R> {
        Tokenizer {
            reader: reader,
            current_line: String::new(),
        }
    }

    fn next(&mut self) -> Option<LineType> {
        self.current_line.clear();
        let bytes_read = self.reader.read_line(&mut self.current_line).unwrap();
        match bytes_read {
            0 => None,
            _ => Some(LineType::from(&self.current_line[..])),
        }
    }

}

/// Open file, return a `BufRead` instance
fn get_file_reader(filepath: &str) -> Result<BufReader<File>, String> {
    let file = try!(
        File::open(filepath)
            .map_err(|msg| format!("Could not open file: {}", msg))
    );
    Ok(BufReader::new(file))
}

fn main() {

    // Parse arguments
    let args: Vec<_> = env::args().collect();
    if args.len() != 2 {
        println!("Usage: {} <command>", args[0]);
        process::exit(1);
    }

    // Open file
    let reader = get_file_reader(&args[1]).unwrap_or_else(|msg| {
        println!("{}", msg);
        process::exit(1);
    });

    // Create tokenizer
    let mut tokenizer = Tokenizer::new(reader);

    // Tokenize and print output
    while let Some(token) = tokenizer.next() {
        println!("{:?}", token);
    }

}

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

    #[test]
    fn test_linetype_from_str() {
        assert_eq!(LineType::from(""), LineType::Empty);
        assert_eq!(LineType::from(" \n \r"), LineType::Empty);
        assert_eq!(LineType::from("# Hello there"), LineType::Title("Hello there".into()));
        assert_eq!(LineType::from("> tis a description \n"), LineType::Description("tis a description".into()));
        assert_eq!(LineType::from("- some command"), LineType::ExampleText("some command".into()));
        assert_eq!(LineType::from("`$ cargo run`"), LineType::ExampleCode("$ cargo run".into()));
        assert_eq!(LineType::from("`$ cargo run"), LineType::Other("`$ cargo run".into()));
        assert_eq!(LineType::from("asdf"), LineType::Other("asdf".into()));
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::io::{self, Write, StderrLock};
use std::process::{Command, exit};
use std::thread::{self, JoinHandle};
use std::sync::{Arc, Mutex};

/* TODO: Functionality can be increased to accept the following syntaxes from GNU Parallel:
 - Stdin support is currently missing.
 - Use a tokenizer for building commands instead of string replacements.
 - {N}, {N.}, etc.
 - parallel command {1} {2} {3} ::: 1 2 3 ::: 4 5 6 ::: 7 8 9
 - parallel command ::: * instead of parallel command {} ::: *
 - parallel ::: "command 1" "command 2"
 - paralllel command ::: a b c :::+ 1 2 3 ::: d e f :::+ 4 5 6
*/

/// A JobThread allows for the manipulation of content within.
struct JobThread {
    /// Allows us to know when a thread has completed all of it's tasks.
    handle: JoinHandle<()>,
}

/// Contains the parameters that each thread will acquire and manipulate.
struct Inputs {
    /// The counter that will be checked and incremented by each thread.
    counter: usize,
    /// The values that each thread will copy values from.
    values: Vec<String>
}

struct Flags {
    /// The number of jobs to create for processing inputs.
    ncores: usize,
}

fn main() {
    let stderr = io::stderr();
    let mut flags = Flags {
        ncores: 8
    };
    let mut command = String::new();
    let mut inputs = Inputs { counter: 0, values: Vec::new() };

    // Let's collect all parameters that we need from the program's arguments.
    // If an error is returned, this will handle that error as efficiently as possible.
    if let Err(why) = parse_arguments(&mut flags, &mut command, &mut inputs.values) {
        let mut stderr = stderr.lock();
        let _ = stderr.write(b"parallel: parsing error: ");
        match why {
            ParseErr::JobsNaN(value) => {
                let _ = stderr.write(b"jobs parameter, '");
                let _ = stderr.write(value.as_bytes());
                let _ = stderr.write(b"', is not a number.\n");
            },
            _ => {
                let message: &[u8] = match why {
                    ParseErr::InputVarsNotDefined => b"input variables were not defined.\n",
                    ParseErr::JobsNoValue         => b"no jobs parameter was defined.\n",
                    _ => unreachable!()
                };
                let _ = stderr.write(message);
            }
        };
        exit(1);
    }

    // It will be useful to know the number of inputs, to know when to quit.
    let num_inputs = inputs.values.len();

    // We will share the same list of inputs and counter with each thread.
    let shared_input = Arc::new(Mutex::new((inputs)));

    // First we will create as many threads as `flags.ncores` specifies.
    // The `threads` vector will contain the thread handles needed to
    // know when to quit the program.
    let mut threads: Vec<JobThread> = Vec::with_capacity(flags.ncores);
    for slot in 1..flags.ncores+1 {
        // The command that each input variable will be sent to.
        let command = command.clone();
        // Allow the thread to gain access to the list of inputs.
        let input = shared_input.clone();
        // Allow the thread to know when it's time to stop.
        let num_inputs = num_inputs.clone();

        // The actual thread where the work will happen on incoming data.
        let handle: JoinHandle<()> = thread::spawn(move || {
            let slot_number = slot;
            let stderr = io::stderr();
            loop {
                // Obtain the Nth item and it's job ID from the list of inputs.
                let (input_var, job_id) = {
                    // Lock the access to the counter and values so that other threads won't clash.
                    let mut input = input.lock().unwrap();
                    if input.counter == num_inputs {
                        break
                    } else {
                        // The value must then be copied so that we can unlock it.
                        let input_var = input.values[input.counter].to_owned();
                        let job_id = input.counter + 1;
                        input.counter += 1;
                        (input_var, job_id)
                    }
                };

                // Now the input can be processed with the command.
                if let Err(cmd_err) = cmd_builder(&input_var, &command, slot_number, job_id) {
                    let mut stderr = stderr.lock();
                    cmd_err.handle(&mut stderr);
                }
            }
        });

        // After the thread has been created, add the important pieces needed by the
        // main thread to the `threads` vector.
        threads.push(JobThread {
            handle: handle, // Gives the main thread access to using the thread's `join()` method.
        });
    }

    for thread in threads.into_iter() { thread.handle.join().unwrap(); }
}

enum CommandErr {
    NoCommandSpecified,
    Failed(String, Vec<String>)
}

impl CommandErr {
    fn handle(self, stderr: &mut StderrLock) {
        let _ = stderr.write(b"parallel: command error: ");
        match self {
            CommandErr::NoCommandSpecified => {
                let _ = stderr.write(b"no command specified.\n");
            },
            CommandErr::Failed(command, arguments) => {
                let _ = stderr.write(command.as_bytes());
                for arg in &arguments {
                    let _ = stderr.write(b" ");
                    let _ = stderr.write(arg.as_bytes());
                }
                let _ = stderr.write(b"\n");
            }
        }
    }
}

/// Builds the command and executes it
fn cmd_builder(input: &str, template: &str, slot_id: usize, job_id: usize) -> Result<(), CommandErr> {
    // TODO: Use a tokenizer for building the command from the template.
    let mut iterator = template.split_whitespace();
    let command = match iterator.next() {
        Some(command) => command,
        None          => return Err(CommandErr::NoCommandSpecified)
    };
    let mut arguments = Vec::new();
    for arg in iterator {
        if arg.contains("{}") {
            arguments.push(arg.replace("{}", input));
        } else if arg.contains("{.}") {
            arguments.push(arg.replace("{.}", remove_extension(input)));
        } else if arg.contains("{/}") {
            arguments.push(arg.replace("{/}", basename(input)));
        } else if arg.contains("{//}") {
            arguments.push(arg.replace("{//}", dirname(input)));
        } else if arg.contains("{/.}") {
            arguments.push(arg.replace("{/.}", basename(remove_extension(input))));
        } else if arg.contains("{#}") {
            arguments.push(arg.replace("{#}", &job_id.to_string()));
        } else if arg.contains("{%}") {
            arguments.push(arg.replace("{%}", &slot_id.to_string()));
        } else {
            arguments.push(arg.to_owned());
        }
    }

    if let Err(_) = Command::new(&command).args(&arguments).status() {
        return Err(CommandErr::Failed(String::from(command), arguments));
    }
    Ok(())
}

/// Removes the extension of a given input
fn remove_extension<'a>(input: &'a str) -> &'a str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '.' { index = id; }
    }
    if index == 0 { input } else { &input[0..index] }
}

fn basename<'a>(input: &'a str) -> &'a str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[index+1..] }
}

fn dirname<'a>(input: &'a str) -> &'a str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[0..index] }
}

enum ParseErr {
    JobsNaN(String),
    JobsNoValue,
    InputVarsNotDefined,
}

// Parses input arguments and stores their values into their associated variabless.
fn parse_arguments(flags: &mut Flags, command: &mut String, input_variables: &mut Vec<String>)
    -> Result<(), ParseErr>
{
    let mut parsing_arguments = true;
    let mut command_is_set    = false;
    let mut raw_args = env::args().skip(1).peekable();
    while let Some(argument) = raw_args.next() {
        if parsing_arguments {
            match argument.as_str() {
                // Defines the number of jobs to run in parallel.
                "-j"  => {
                    match raw_args.peek() {
                        Some(val) => match val.parse::<usize>() {
                            Ok(val) => flags.ncores = val,
                            Err(_)  => return Err(ParseErr::JobsNaN(val.clone()))
                        },
                        None => return Err(ParseErr::JobsNoValue)
                    }
                    let _ = raw_args.next();
                },
                // Arguments after `:::` are input values.
                ":::" => parsing_arguments = false,
                _ => {
                    if command_is_set {
                        command.push(' ');
                        command.push_str(&argument);
                    } else {
                        command.push_str(&argument);
                        command_is_set = true;
                    }

                }
            }
        } else {
            input_variables.push(argument);
        }
    }

    if input_variables.is_empty() { return Err(ParseErr::InputVarsNotDefined) }
    Ok(())
}
<commit_msg>Replace the `Mutex` with an atomic increment<commit_after>use std::env;
use std::io::{self, Write, StderrLock};
use std::process::{Command, exit};
use std::thread::{self, JoinHandle};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

/* TODO: Functionality can be increased to accept the following syntaxes from GNU Parallel:
 - Stdin support is currently missing.
 - Use a tokenizer for building commands instead of string replacements.
 - {N}, {N.}, etc.
 - parallel command {1} {2} {3} ::: 1 2 3 ::: 4 5 6 ::: 7 8 9
 - parallel command ::: * instead of parallel command {} ::: *
 - parallel ::: "command 1" "command 2"
 - paralllel command ::: a b c :::+ 1 2 3 ::: d e f :::+ 4 5 6
*/

/// A JobThread allows for the manipulation of content within.
struct JobThread {
    /// Allows us to know when a thread has completed all of it's tasks.
    handle: JoinHandle<()>,
}

/// Contains the parameters that each thread will acquire and manipulate.
struct Inputs {
    /// The values that each thread will copy values from.
    values: Vec<String>
}

struct Flags {
    /// The number of jobs to create for processing inputs.
    ncores: usize,
}

fn main() {
    let stderr = io::stderr();
    let mut flags = Flags {
        ncores: 8
    };
    let mut command = String::new();
    let mut inputs = Inputs { values: Vec::new() };

    // Let's collect all parameters that we need from the program's arguments.
    // If an error is returned, this will handle that error as efficiently as possible.
    if let Err(why) = parse_arguments(&mut flags, &mut command, &mut inputs.values) {
        let mut stderr = stderr.lock();
        let _ = stderr.write(b"parallel: parsing error: ");
        match why {
            ParseErr::JobsNaN(value) => {
                let _ = stderr.write(b"jobs parameter, '");
                let _ = stderr.write(value.as_bytes());
                let _ = stderr.write(b"', is not a number.\n");
            },
            _ => {
                let message: &[u8] = match why {
                    ParseErr::InputVarsNotDefined => b"input variables were not defined.\n",
                    ParseErr::JobsNoValue         => b"no jobs parameter was defined.\n",
                    _ => unreachable!()
                };
                let _ = stderr.write(message);
            }
        };
        exit(1);
    }

    // It will be useful to know the number of inputs, to know when to quit.
    let num_inputs = inputs.values.len();

    // Stores the next input to be processed
    let shared_counter = Arc::new(AtomicUsize::new(0));

    // We will share the same list of inputs with each thread.
    let shared_input = Arc::new(inputs);

    // First we will create as many threads as `flags.ncores` specifies.
    // The `threads` vector will contain the thread handles needed to
    // know when to quit the program.
    let mut threads: Vec<JobThread> = Vec::with_capacity(flags.ncores);
    for slot in 1..flags.ncores+1 {
        // The command that each input variable will be sent to.
        let command = command.clone();
        // Allow the thread to gain access to the list of inputs.
        let input = shared_input.clone();
        // Allow the thread to access the current command counter
        let counter = shared_counter.clone();
        // Allow the thread to know when it's time to stop.
        let num_inputs = num_inputs.clone();

        // The actual thread where the work will happen on incoming data.
        let handle: JoinHandle<()> = thread::spawn(move || {
            let slot_number = slot;
            let stderr = io::stderr();
            loop {
                // Obtain the Nth item and it's job ID from the list of inputs.
                let (input_var, job_id) = {
                    // Atomically increment the counter
                    let old_counter = counter.fetch_add(1, Ordering::SeqCst);
                    if old_counter >= num_inputs {
                        break
                    } else {
                        let input_var = &input.values[old_counter];
                        let job_id = old_counter + 1;
                        (input_var, job_id)
                    }
                };

                // Now the input can be processed with the command.
                if let Err(cmd_err) = cmd_builder(&input_var, &command, slot_number, job_id) {
                    let mut stderr = stderr.lock();
                    cmd_err.handle(&mut stderr);
                }
            }
        });

        // After the thread has been created, add the important pieces needed by the
        // main thread to the `threads` vector.
        threads.push(JobThread {
            handle: handle, // Gives the main thread access to using the thread's `join()` method.
        });
    }

    for thread in threads.into_iter() { thread.handle.join().unwrap(); }
}

enum CommandErr {
    NoCommandSpecified,
    Failed(String, Vec<String>)
}

impl CommandErr {
    fn handle(self, stderr: &mut StderrLock) {
        let _ = stderr.write(b"parallel: command error: ");
        match self {
            CommandErr::NoCommandSpecified => {
                let _ = stderr.write(b"no command specified.\n");
            },
            CommandErr::Failed(command, arguments) => {
                let _ = stderr.write(command.as_bytes());
                for arg in &arguments {
                    let _ = stderr.write(b" ");
                    let _ = stderr.write(arg.as_bytes());
                }
                let _ = stderr.write(b"\n");
            }
        }
    }
}

/// Builds the command and executes it
fn cmd_builder(input: &str, template: &str, slot_id: usize, job_id: usize) -> Result<(), CommandErr> {
    // TODO: Use a tokenizer for building the command from the template.
    let mut iterator = template.split_whitespace();
    let command = match iterator.next() {
        Some(command) => command,
        None          => return Err(CommandErr::NoCommandSpecified)
    };
    let mut arguments = Vec::new();
    for arg in iterator {
        if arg.contains("{}") {
            arguments.push(arg.replace("{}", input));
        } else if arg.contains("{.}") {
            arguments.push(arg.replace("{.}", remove_extension(input)));
        } else if arg.contains("{/}") {
            arguments.push(arg.replace("{/}", basename(input)));
        } else if arg.contains("{//}") {
            arguments.push(arg.replace("{//}", dirname(input)));
        } else if arg.contains("{/.}") {
            arguments.push(arg.replace("{/.}", basename(remove_extension(input))));
        } else if arg.contains("{#}") {
            arguments.push(arg.replace("{#}", &job_id.to_string()));
        } else if arg.contains("{%}") {
            arguments.push(arg.replace("{%}", &slot_id.to_string()));
        } else {
            arguments.push(arg.to_owned());
        }
    }

    if let Err(_) = Command::new(&command).args(&arguments).status() {
        return Err(CommandErr::Failed(String::from(command), arguments));
    }
    Ok(())
}

/// Removes the extension of a given input
fn remove_extension<'a>(input: &'a str) -> &'a str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '.' { index = id; }
    }
    if index == 0 { input } else { &input[0..index] }
}

fn basename<'a>(input: &'a str) -> &'a str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[index+1..] }
}

fn dirname<'a>(input: &'a str) -> &'a str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[0..index] }
}

enum ParseErr {
    JobsNaN(String),
    JobsNoValue,
    InputVarsNotDefined,
}

// Parses input arguments and stores their values into their associated variabless.
fn parse_arguments(flags: &mut Flags, command: &mut String, input_variables: &mut Vec<String>)
    -> Result<(), ParseErr>
{
    let mut parsing_arguments = true;
    let mut command_is_set    = false;
    let mut raw_args = env::args().skip(1).peekable();
    while let Some(argument) = raw_args.next() {
        if parsing_arguments {
            match argument.as_str() {
                // Defines the number of jobs to run in parallel.
                "-j"  => {
                    match raw_args.peek() {
                        Some(val) => match val.parse::<usize>() {
                            Ok(val) => flags.ncores = val,
                            Err(_)  => return Err(ParseErr::JobsNaN(val.clone()))
                        },
                        None => return Err(ParseErr::JobsNoValue)
                    }
                    let _ = raw_args.next();
                },
                // Arguments after `:::` are input values.
                ":::" => parsing_arguments = false,
                _ => {
                    if command_is_set {
                        command.push(' ');
                        command.push_str(&argument);
                    } else {
                        command.push_str(&argument);
                        command_is_set = true;
                    }

                }
            }
        } else {
            input_variables.push(argument);
        }
    }

    if input_variables.is_empty() { return Err(ParseErr::InputVarsNotDefined) }
    Ok(())
}
<|endoftext|>