<commit_before>#![feature(if_let)]

#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

extern crate toml;
extern crate glob;

use std::io::fs::PathExtensions;
use std::io::fs;
use std::io;
use std::os;

use app_result::{AppResult, app_err};

use dependencies::{
   TagsRoot,
   SourceKind,
   read_dependencies
};

use tags::{
   Tags,
   update_git_tags,
   update_crates_io_tags,
   create_tags,
   merge_tags
};

use dirs::tags_dir;

mod app_result;
mod dependencies;
mod dirs;
mod tags;

fn main()
{
   update_tags().unwrap_or_else(|err| {
      let stderr = &mut io::stderr();
      let _ = writeln!(stderr, "rusty-tags: {}", err);
      os::set_exit_status(1);
   });
}

fn update_tags() -> AppResult<()>
{
   let cwd = try!(os::getcwd());
   let cargo_dir = try!(find_cargo_toml_dir(&cwd));
   let tags_roots = try!(read_dependencies(&cargo_dir));

   for tags_root in tags_roots.iter() {
      let mut tag_files: Vec<Path> = Vec::new();
      let mut tag_dir: Option<Path> = None;

      match *tags_root {
         TagsRoot::Src { ref src_dir, ref dependencies } => {
            let mut src_tags = src_dir.clone();
            src_tags.push("rusty.tags");
            try!(create_tags(src_dir, &src_tags));
            tag_files.push(src_tags);

            for dep in dependencies.iter() {
               tag_files.push(try!(update_tags_of(dep)).tags_file);
            }

            tag_dir = Some(src_dir.clone());
         },

         TagsRoot::Lib { ref src_kind, ref dependencies } => {
            let lib_tags = try!(update_tags_of(src_kind));
            if lib_tags.cached {
               continue;
            }

            tag_files.push(lib_tags.tags_file);
            for dep in dependencies.iter() {
               tag_files.push(try!(update_tags_of(dep)).tags_file);
            }

            tag_dir = Some(lib_tags.src_dir.clone());
         }
      }

      if tag_files.is_empty() || tag_dir.is_none() {
         continue;
      }

      let mut rust_tags = try!(tags_dir());
      rust_tags.push("rust");
      if rust_tags.is_file() {
         tag_files.push(rust_tags);
      }

      let mut tags_file = tag_dir.unwrap();
      tags_file.push("rusty.tags");

      try!(merge_tags(&tag_files, &tags_file));
   }

   Ok(())
}

fn update_tags_of(src_kind: &SourceKind) -> AppResult<Tags>
{
   match *src_kind {
      SourceKind::Git { ref lib_name, ref commit_hash } => {
         update_git_tags(lib_name, commit_hash)
      },

      SourceKind::CratesIo { ref lib_name, ref version } => {
         update_crates_io_tags(lib_name, version)
      }
   }
}

/// Searches for a directory containing a `Cargo.toml` file starting at
/// `start_dir` and continuing the search upwards the directory tree
/// until a directory is found.
fn find_cargo_toml_dir(start_dir: &Path) -> AppResult<Path>
{
   let mut dir = start_dir.clone();
   loop {
      if let Ok(files) = fs::readdir(&dir) {
         for file in files.iter() {
            if file.is_file() {
               if let Some("Cargo.toml") = file.filename_str() {
                  return Ok(dir);
               }
            }
         }
      }

      if ! dir.pop() {
         return Err(app_err(format!("Couldn't find 'Cargo.toml' starting at directory '{}'!", start_dir.display())));
      }
   }
}
<commit_msg>if_let isn't a feature anymore<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

extern crate toml;
extern crate glob;

use std::io::fs::PathExtensions;
use std::io::fs;
use std::io;
use std::os;

use app_result::{AppResult, app_err};

use dependencies::{
   TagsRoot,
   SourceKind,
   read_dependencies
};

use tags::{
   Tags,
   update_git_tags,
   update_crates_io_tags,
   create_tags,
   merge_tags
};

use dirs::tags_dir;

mod app_result;
mod dependencies;
mod dirs;
mod tags;

fn main()
{
   update_tags().unwrap_or_else(|err| {
      let stderr = &mut io::stderr();
      let _ = writeln!(stderr, "rusty-tags: {}", err);
      os::set_exit_status(1);
   });
}

fn update_tags() -> AppResult<()>
{
   let cwd = try!(os::getcwd());
   let cargo_dir = try!(find_cargo_toml_dir(&cwd));
   let tags_roots = try!(read_dependencies(&cargo_dir));

   for tags_root in tags_roots.iter() {
      let mut tag_files: Vec<Path> = Vec::new();
      let mut tag_dir: Option<Path> = None;

      match *tags_root {
         TagsRoot::Src { ref src_dir, ref dependencies } => {
            let mut src_tags = src_dir.clone();
            src_tags.push("rusty.tags");
            try!(create_tags(src_dir, &src_tags));
            tag_files.push(src_tags);

            for dep in dependencies.iter() {
               tag_files.push(try!(update_tags_of(dep)).tags_file);
            }

            tag_dir = Some(src_dir.clone());
         },

         TagsRoot::Lib { ref src_kind, ref dependencies } => {
            let lib_tags = try!(update_tags_of(src_kind));
            if lib_tags.cached {
               continue;
            }

            tag_files.push(lib_tags.tags_file);
            for dep in dependencies.iter() {
               tag_files.push(try!(update_tags_of(dep)).tags_file);
            }

            tag_dir = Some(lib_tags.src_dir.clone());
         }
      }

      if tag_files.is_empty() || tag_dir.is_none() {
         continue;
      }

      let mut rust_tags = try!(tags_dir());
      rust_tags.push("rust");
      if rust_tags.is_file() {
         tag_files.push(rust_tags);
      }

      let mut tags_file = tag_dir.unwrap();
      tags_file.push("rusty.tags");

      try!(merge_tags(&tag_files, &tags_file));
   }

   Ok(())
}

fn update_tags_of(src_kind: &SourceKind) -> AppResult<Tags>
{
   match *src_kind {
      SourceKind::Git { ref lib_name, ref commit_hash } => {
         update_git_tags(lib_name, commit_hash)
      },

      SourceKind::CratesIo { ref lib_name, ref version } => {
         update_crates_io_tags(lib_name, version)
      }
   }
}

/// Searches for a directory containing a `Cargo.toml` file starting at
/// `start_dir` and continuing the search upwards the directory tree
/// until a directory is found.
fn find_cargo_toml_dir(start_dir: &Path) -> AppResult<Path>
{
   let mut dir = start_dir.clone();
   loop {
      if let Ok(files) = fs::readdir(&dir) {
         for file in files.iter() {
            if file.is_file() {
               if let Some("Cargo.toml") = file.filename_str() {
                  return Ok(dir);
               }
            }
         }
      }

      if ! dir.pop() {
         return Err(app_err(format!("Couldn't find 'Cargo.toml' starting at directory '{}'!", start_dir.display())));
      }
   }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

use std::collections::{HashSet, HashMap};
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Seek, SeekFrom};
use std::path::Path;

use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata};

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }
}

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

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

impl<'a> std::fmt::Display for CsvDesc<'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<CsvDesc<'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(CsvDesc {file_path: &csv_file_path, delimiter: csv_delimiter, quote: csv_quote})
}

fn get_csv_cols(csv_desc: &CsvDesc) -> 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: &CsvDesc) -> 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: &CsvDesc) -> 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: &CsvDesc, 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: &CsvDesc, 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 " " ""

*/

    /*** 0 ***/
    log::set_logger(|max_log_level| {
        max_log_level.set(LogLevelFilter::Info);
        Box::new(SimpleLogger)
    }).unwrap();

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

    let csv_desc_1: CsvDesc = 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: CsvDesc = 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);
    }
    info!("{:?}", 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);
    }
    info!("{:?}", 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);
        };
    }
    info!("{:?}", 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);
        };
    }
    info!("{:?}", 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();

        info!("comparing {}:", row_key);
        info!("line #1: {:?}", row_1);
        info!("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();

            info!("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>Remove the old build_index and get_csv_row. They are really slow.<commit_after>#[macro_use]
extern crate log;

use std::collections::{HashSet, HashMap};
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Seek, SeekFrom};
use std::path::Path;

use log::{LogRecord, LogLevel, LogLevelFilter, LogMetadata};

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }
}

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

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

impl<'a> std::fmt::Display for CsvDesc<'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<CsvDesc<'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(CsvDesc {file_path: &csv_file_path, delimiter: csv_delimiter, quote: csv_quote})
}

fn get_csv_cols(csv_desc: &CsvDesc) -> 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: &CsvDesc) -> 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: &CsvDesc, 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 " " ""

*/

    /*** 0 ***/
    log::set_logger(|max_log_level| {
        max_log_level.set(LogLevelFilter::Info);
        Box::new(SimpleLogger)
    }).unwrap();

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

    let csv_desc_1: CsvDesc = 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: CsvDesc = 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);
    }
    info!("{:?}", 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);
    }
    info!("{:?}", 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);
        };
    }
    info!("{:?}", 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);
        };
    }
    info!("{:?}", 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();

        info!("comparing {}:", row_key);
        info!("line #1: {:?}", row_1);
        info!("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();

            info!("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>const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");

static FORMAT: &'static str = "{request-time}: {method} {uri} {status} {response-time}";

#[macro_use]
extern crate clap;
use clap::{Arg, App, SubCommand};

extern crate toml;
extern crate rustc_serialize;
extern crate regex;

extern crate iron;
use iron::prelude::*;
use iron::Handler;
use iron::method::Method;
use std::str::FromStr;

extern crate router;
use router::Router;

extern crate staticfile;
use staticfile::Static;

extern crate mount;
use mount::Mount;

#[macro_use]
extern crate log;
extern crate simplelog;
use simplelog::{SimpleLogger, LogLevelFilter};

extern crate logger;
use logger::Logger;
use logger::format::Format;

mod config;
use config::{Config, ServerConfig, RequestConfig, ResponseConfig};
use std::collections::HashMap;
use std::ascii::AsciiExt;
mod error;
use error::HaxoniteError;
mod handlers;
use handlers::*;
mod utils;

use std::path::Path;

fn main() {
    let _ = SimpleLogger::init(LogLevelFilter::Info, simplelog::Config::default());

    match run() {
        Ok(_) => {}
        Err(err) => {
            error!("{}!", err);
            std::process::exit(-1);
        }
    }
}

fn run() -> Result<(), HaxoniteError> {
    let matches = define_command_line_options().get_matches();
    if let Some(matches) = matches.subcommand_matches("new") {
        let generate_full_project = matches.is_present("full");
        if let Some(project_name) = matches.value_of("project_name") {
            try!(utils::create_new_project(project_name, generate_full_project));
        }
        return Ok(());
    }

    let config_file = matches.value_of("config_file").unwrap_or("config.toml");
    let config_content = try!(config::read_config(config_file));
    let config: Config = toml::decode_str(config_content.as_ref()).unwrap(); // TODO: remove unwrap to handle malformed config
    debug!("Using config: {:?}!", config);

    let server_config = config.server.unwrap_or_else(ServerConfig::default);
    let host = match value_t!(matches, "host", String) { // TODO: rewrite these matches
        Ok(host) => host,
        Err(_) => server_config.host.unwrap_or_else(config::default_host),
    };
    let port_number = match value_t!(matches, "port_number", u16) {
        Ok(port_number) => port_number,
        Err(_) => server_config.port.unwrap_or_else(config::default_port),
    };

    // Initialize Iron's router and pass it to config processing function to define routes
    let mut router = Router::new();
    let mut mount = Mount::new();
    match config.requests {
        Some(requests) => {
            try!(process_config_requests(&requests, &mut mount, &mut router));
        }
        None => return Err(HaxoniteError::NoRequestDefined),
    }
    mount.mount("/", router);

    // Insert logger middlewares before and after router
    let mut chain = Chain::new(mount);
    let format = Format::new(FORMAT);
    let (logger_before, logger_after) = Logger::new(Some(format.unwrap()));
    chain.link_before(logger_before);
    chain.link_after(logger_after);

    // Initialize Iron to process requests
    let _iron = try!(Iron::new(chain).http((host.as_ref(), port_number)));
    info!("Haxonite running on port: {}!", port_number);

    Ok(())
}

fn process_config_requests(requests: &HashMap<String, RequestConfig>,
                           mut mount: &mut Mount,
                           mut router: &mut Router)
                           -> Result<(), HaxoniteError> {
    info!("Processing config!");
    for (request_name, request_config) in requests {
        info!("Processing config for {}: {:?}!",
              request_name,
              request_config);
        try!(define_route(request_name.clone(),
                          request_config.clone(),
                          &mut mount,
                          &mut router));
    }
    Ok(())
}

fn define_route(request_name: String, request_config: RequestConfig, mount: &mut Mount, router: &mut Router) -> Result<(), HaxoniteError> {
    let method = request_config.method.unwrap_or_else(config::default_method);
    let path = match request_config.path {
        Some(path) => path,
        None => return Err(HaxoniteError::NoPathDefined(request_name.clone())),
    };

    let type_ = request_config.type_.unwrap_or_else(config::default_type);
    let response_configs = match request_config.responses {
        Some(response_configs) => response_configs,
        None => return Err(HaxoniteError::NoResponseDefined(request_name.clone())),
    };
    let handler: Box<Handler> = match type_.as_ref() {
        "single" => {
            let response_config = try!(response_configs.first()
                .ok_or(HaxoniteError::NoResponseDefined(request_name.clone())));
            Box::new(try!(get_single_response_handler(request_name.clone(), response_config.clone())))
        }
        "random" => {
            let mut handler = RandomResponse::new();
            for response_config in &response_configs {
                let weight = response_config.weight.unwrap_or_else(config::default_weight);
                handler.add_handler(weight,
                                    try!(get_single_response_handler(request_name.clone(), response_config.clone())));
            }
            Box::new(handler)
        }
        "roundrobin" => {
            let mut handler = IteratingResponse::new("roundrobin");
            for response_config in &response_configs {
                handler.add_handler(try!(get_single_response_handler(request_name.clone(), response_config.clone())));
            }
            Box::new(handler)
        }
        "chain" => {
            let mut handler = IteratingResponse::new("chain");
            for response_config in &response_configs {
                handler.add_handler(try!(get_single_response_handler(request_name.clone(), response_config.clone())));
            }
            Box::new(handler)
        }
        "static" => {
            let response_config = try!(response_configs.first()
                .ok_or(HaxoniteError::NoResponseDefined(request_name.clone())));
            let response = match response_config.response {
                Some(ref response) => response,
                None => return Err(HaxoniteError::NoResponseDefined(request_name.clone())),
            };
            let handler = Static::new(Path::new(&response));
            Box::new(handler)
        }
        _ => return Err(HaxoniteError::InvalidType(request_name.clone())),
    };

    match Method::from_str(method.to_ascii_uppercase().as_ref()) {
        Ok(Method::Extension(_)) |
        Err(_) => return Err(HaxoniteError::InvalidHTTPMethod(request_name.clone())),
        Ok(a) => {
            match type_.as_ref() {
                "static" => {
                    info!("Mounting static for: {} using {} type of handler.\n",
                          path,
                          type_);
                    mount.mount(path.as_ref(), handler);
                }
                _ => {
                    info!("Defining route for: {} using {} type of handler.\n",
                          path,
                          type_);
                    router.route(a, path.clone(), handler, request_name);
                }
            }
        }
    };
    Ok(())
}

fn get_single_response_handler(request_name: String, response_config: ResponseConfig) -> Result<SingleResponse, HaxoniteError> {
    let headers = process_headers(response_config.headers);
    let status = match response_config.status {
        Some(status) if status >= 100 && status <= 599 => status, // switch to Range's contains() when it will be stable
        Some(status) => return Err(HaxoniteError::InvalidHTTPStatus(request_name.clone(), status)),
        None => config::default_status(),
    };
    let response = match response_config.response {
        Some(response) => response,
        None => return Err(HaxoniteError::NoResponseDefined(request_name.clone())),
    };
    if !Path::new(&response).exists() {
        return Err(HaxoniteError::ResponseDoesNotExist(request_name.clone()));
    }
    Ok(SingleResponse::new(status, headers, response, response_config.delay))
}

fn process_headers(headers: Option<Vec<String>>) -> Vec<Vec<String>> {
    // TODO: This method should be optimized to not compile regex on every run,
    // not match on header when the default is used (it's valid)
    use regex::Regex;
    let re = Regex::new(r"([\w-]+):\s+([\w/]+)").unwrap();

    let headers = headers.unwrap_or_else(config::default_headers);
    headers.iter()
        .filter(|header| {
            if re.is_match(header) {
                true
            } else {
                warn!("Header {} is invalid (does not match \": \"). Skipping.",
                      header);
                false
            }
        })
        .map(|header| {
            header.split(": ")
                .map(// TODO: This is quite stupid and could be done with &str,
                     // but I decided to use String to not fight borrow checker for now...
                     |hdr| hdr.to_string())
                .collect()
        })
        .collect()
}

fn define_command_line_options<'a, 'b>() -> App<'a, 'b> {
    App::new("Haxonite")
        .version(VERSION)
        .author(AUTHORS)
        .about("Easy API mocking")
        .arg(Arg::with_name("host")
            .short("h")
            .long("host")
            .value_name("HOST")
            .help("Run Haxonite on the host. Default: localhost.")
            .takes_value(true))
        .arg(Arg::with_name("port_number")
            .short("p")
            .long("port")
            .value_name("PORT")
            .help("Run Haxonite on the specified port. Default: 4000.")
            .takes_value(true))
        .arg(Arg::with_name("config_file")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Sets a custom config file. Default: config.toml.")
            .takes_value(true))
        .subcommand(SubCommand::with_name("new")
            .about("Create new Haxonite project")
            .version(VERSION)
            .author(AUTHORS)
            .arg(Arg::with_name("full")
                .help("Use to create a project with full config.toml file")
                .short("f")
                .long("full"))
            .arg(Arg::with_name("project_name")
                .help("Name of the project (will be used as a directory name)")
                .index(1)
                .required(true)))
}
<commit_msg>#6: Add serve function for futher use<commit_after>const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");

static FORMAT: &'static str = "{request-time}: {method} {uri} {status} {response-time}";

#[macro_use]
extern crate clap;
use clap::{Arg, ArgMatches, App, SubCommand};

extern crate toml;
extern crate rustc_serialize;
extern crate regex;

extern crate iron;
use iron::prelude::*;
use iron::Handler;
use iron::method::Method;
use std::str::FromStr;

extern crate router;
use router::Router;

extern crate staticfile;
use staticfile::Static;

extern crate mount;
use mount::Mount;

#[macro_use]
extern crate log;
extern crate simplelog;
use simplelog::{SimpleLogger, LogLevelFilter};

extern crate logger;
use logger::Logger;
use logger::format::Format;

mod config;
use config::{Config, ServerConfig, RequestConfig, ResponseConfig};
use std::collections::HashMap;
use std::ascii::AsciiExt;
mod error;
use error::HaxoniteError;
mod handlers;
use handlers::*;
mod utils;

use std::path::Path;

fn main() {
    let _ = SimpleLogger::init(LogLevelFilter::Info, simplelog::Config::default());

    match run() {
        Ok(_) => {}
        Err(err) => {
            error!("{}!", err);
            std::process::exit(-1);
        }
    }
}

fn run() -> Result<(), HaxoniteError> {
    let matches = define_command_line_options().get_matches();
    if let Some(matches) = matches.subcommand_matches("new") {
        let generate_full_project = matches.is_present("full");
        if let Some(project_name) = matches.value_of("project_name") {
            try!(utils::create_new_project(project_name, generate_full_project));
        }
        return Ok(());
    }

    try!(serve(matches));

    Ok(())
}

fn serve(matches: ArgMatches) -> Result<(), HaxoniteError> {
    let config_file = matches.value_of("config_file").unwrap_or("config.toml");
    let config_content = try!(config::read_config(config_file));
    let config: Config = toml::decode_str(config_content.as_ref()).unwrap(); // TODO: remove unwrap to handle malformed config
    debug!("Using config: {:?}!", config);

    let server_config = config.server.unwrap_or_else(ServerConfig::default);
    let host = match value_t!(matches, "host", String) { // TODO: rewrite these matches
        Ok(host) => host,
        Err(_) => server_config.host.unwrap_or_else(config::default_host),
    };
    let port_number = match value_t!(matches, "port_number", u16) {
        Ok(port_number) => port_number,
        Err(_) => server_config.port.unwrap_or_else(config::default_port),
    };

    // Initialize Iron's router and pass it to config processing function to define routes
    let mut router = Router::new();
    let mut mount = Mount::new();
    match config.requests {
        Some(requests) => {
            try!(process_config_requests(&requests, &mut mount, &mut router));
        }
        None => return Err(HaxoniteError::NoRequestDefined),
    }
    mount.mount("/", router);

    // Insert logger middlewares before and after router
    let mut chain = Chain::new(mount);
    let format = Format::new(FORMAT);
    let (logger_before, logger_after) = Logger::new(Some(format.unwrap()));
    chain.link_before(logger_before);
    chain.link_after(logger_after);

    // Initialize Iron to process requests
    let _iron = try!(Iron::new(chain).http((host.as_ref(), port_number)));
    info!("Haxonite running on port: {}!", port_number);
    Ok(())
}

fn process_config_requests(requests: &HashMap<String, RequestConfig>,
                           mut mount: &mut Mount,
                           mut router: &mut Router)
                           -> Result<(), HaxoniteError> {
    info!("Processing config!");
    for (request_name, request_config) in requests {
        info!("Processing config for {}: {:?}!",
              request_name,
              request_config);
        try!(define_route(request_name.clone(),
                          request_config.clone(),
                          &mut mount,
                          &mut router));
    }
    Ok(())
}

fn define_route(request_name: String, request_config: RequestConfig, mount: &mut Mount, router: &mut Router) -> Result<(), HaxoniteError> {
    let method = request_config.method.unwrap_or_else(config::default_method);
    let path = match request_config.path {
        Some(path) => path,
        None => return Err(HaxoniteError::NoPathDefined(request_name.clone())),
    };

    let type_ = request_config.type_.unwrap_or_else(config::default_type);
    let response_configs = match request_config.responses {
        Some(response_configs) => response_configs,
        None => return Err(HaxoniteError::NoResponseDefined(request_name.clone())),
    };
    let handler: Box<Handler> = match type_.as_ref() {
        "single" => {
            let response_config = try!(response_configs.first()
                .ok_or(HaxoniteError::NoResponseDefined(request_name.clone())));
            Box::new(try!(get_single_response_handler(request_name.clone(), response_config.clone())))
        }
        "random" => {
            let mut handler = RandomResponse::new();
            for response_config in &response_configs {
                let weight = response_config.weight.unwrap_or_else(config::default_weight);
                handler.add_handler(weight,
                                    try!(get_single_response_handler(request_name.clone(), response_config.clone())));
            }
            Box::new(handler)
        }
        "roundrobin" => {
            let mut handler = IteratingResponse::new("roundrobin");
            for response_config in &response_configs {
                handler.add_handler(try!(get_single_response_handler(request_name.clone(), response_config.clone())));
            }
            Box::new(handler)
        }
        "chain" => {
            let mut handler = IteratingResponse::new("chain");
            for response_config in &response_configs {
                handler.add_handler(try!(get_single_response_handler(request_name.clone(), response_config.clone())));
            }
            Box::new(handler)
        }
        "static" => {
            let response_config = try!(response_configs.first()
                .ok_or(HaxoniteError::NoResponseDefined(request_name.clone())));
            let response = match response_config.response {
                Some(ref response) => response,
                None => return Err(HaxoniteError::NoResponseDefined(request_name.clone())),
            };
            let handler = Static::new(Path::new(&response));
            Box::new(handler)
        }
        _ => return Err(HaxoniteError::InvalidType(request_name.clone())),
    };

    match Method::from_str(method.to_ascii_uppercase().as_ref()) {
        Ok(Method::Extension(_)) |
        Err(_) => return Err(HaxoniteError::InvalidHTTPMethod(request_name.clone())),
        Ok(a) => {
            match type_.as_ref() {
                "static" => {
                    info!("Mounting static for: {} using {} type of handler.\n",
                          path,
                          type_);
                    mount.mount(path.as_ref(), handler);
                }
                _ => {
                    info!("Defining route for: {} using {} type of handler.\n",
                          path,
                          type_);
                    router.route(a, path.clone(), handler, request_name);
                }
            }
        }
    };
    Ok(())
}

fn get_single_response_handler(request_name: String, response_config: ResponseConfig) -> Result<SingleResponse, HaxoniteError> {
    let headers = process_headers(response_config.headers);
    let status = match response_config.status {
        Some(status) if status >= 100 && status <= 599 => status, // switch to Range's contains() when it will be stable
        Some(status) => return Err(HaxoniteError::InvalidHTTPStatus(request_name.clone(), status)),
        None => config::default_status(),
    };
    let response = match response_config.response {
        Some(response) => response,
        None => return Err(HaxoniteError::NoResponseDefined(request_name.clone())),
    };
    if !Path::new(&response).exists() {
        return Err(HaxoniteError::ResponseDoesNotExist(request_name.clone()));
    }
    Ok(SingleResponse::new(status, headers, response, response_config.delay))
}

fn process_headers(headers: Option<Vec<String>>) -> Vec<Vec<String>> {
    // TODO: This method should be optimized to not compile regex on every run,
    // not match on header when the default is used (it's valid)
    use regex::Regex;
    let re = Regex::new(r"([\w-]+):\s+([\w/]+)").unwrap();

    let headers = headers.unwrap_or_else(config::default_headers);
    headers.iter()
        .filter(|header| {
            if re.is_match(header) {
                true
            } else {
                warn!("Header {} is invalid (does not match \": \"). Skipping.",
                      header);
                false
            }
        })
        .map(|header| {
            header.split(": ")
                .map(// TODO: This is quite stupid and could be done with &str,
                     // but I decided to use String to not fight borrow checker for now...
                     |hdr| hdr.to_string())
                .collect()
        })
        .collect()
}

fn define_command_line_options<'a, 'b>() -> App<'a, 'b> {
    App::new("Haxonite")
        .version(VERSION)
        .author(AUTHORS)
        .about("Easy API mocking")
        .arg(Arg::with_name("host")
            .short("h")
            .long("host")
            .value_name("HOST")
            .help("Run Haxonite on the host. Default: localhost.")
            .takes_value(true))
        .arg(Arg::with_name("port_number")
            .short("p")
            .long("port")
            .value_name("PORT")
            .help("Run Haxonite on the specified port. Default: 4000.")
            .takes_value(true))
        .arg(Arg::with_name("config_file")
            .short("c")
            .long("config")
            .value_name("FILE")
            .help("Sets a custom config file. Default: config.toml.")
            .takes_value(true))
        .subcommand(SubCommand::with_name("new")
            .about("Create new Haxonite project")
            .version(VERSION)
            .author(AUTHORS)
            .arg(Arg::with_name("full")
                .help("Use to create a project with full config.toml file")
                .short("f")
                .long("full"))
            .arg(Arg::with_name("project_name")
                .help("Name of the project (will be used as a directory name)")
                .index(1)
                .required(true)))
}
<|endoftext|>


<commit_before>mod operation;
mod tokenize;
mod log_entry;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate chrono;
extern crate clap;

use log_entry::log_entry::LogEntry;
use operation::operation::OperationSource;
use clap::{Arg, App};
use std::io::prelude::*;
use std::path::Path;
use std::fs::File;
use std::io::BufReader;
use std::collections::HashMap;
use chrono::{DateTime, UTC};

#[derive(Debug, Serialize, Deserialize)]
struct OutputEntry {
    unique_id : String,
    timestamp : DateTime<UTC>,
    episode_number: u16,
    complete_download: bool
}

impl OutputEntry {
    fn from(e : &LogEntry) -> OutputEntry {
        OutputEntry {
            unique_id: match e.request_id {
                Some(ref x) => x.clone(),
                None => "".to_string()
            },
            timestamp: e.timestamp.clone(),
            episode_number: 0,
            complete_download: e.was_complete_download()
        }
    }
}

fn main() {
    let matches = App::new("Podlog")
        .version("0.1")
        .arg(Arg::with_name("FILENAME")
            .required(true)
            .index(1))
        .get_matches();

    let filename = matches.value_of("FILENAME").unwrap();
    let mut results : HashMap<String, Vec<OutputEntry> > = HashMap::new();
    foo(filename, &mut results);
    let storage = serde_json::to_string_pretty(&results).unwrap();
    println!("{}", storage);
}

fn foo(filename : &str, results : &mut HashMap<String, Vec<OutputEntry> >) -> () {
    let path = Path::new(filename);
    let file = BufReader::new(File::open(&path).unwrap());
    let lines = file.lines().filter_map(|result| result.ok()); // Filter out bad rows
    let entries = lines.map(|x| LogEntry::from_str(&x))
                       .filter(|x| x.operation.source == OperationSource::WEBSITE)
                       .filter(|ref x| x.is_audio_file());

    for entry in entries {
        let path = entry.path.clone().unwrap();
        if !results.contains_key(&path) {
            results.insert(path, Vec::new());
        } else {
            let mut x = results.get_mut(&path);
            if x.is_some() {
                x.as_mut().unwrap().push(OutputEntry::from(&entry));
            }
        }
    }
}

// Output Format:
// UniqueID, DateTime, Episode, Was Complete Download
<commit_msg>Refactoring done to allow directory processing<commit_after>mod operation;
mod tokenize;
mod log_entry;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate chrono;
extern crate clap;

use log_entry::log_entry::LogEntry;
use operation::operation::OperationSource;
use clap::{Arg, App};
use std::io::prelude::*;
use std::path::Path;
use std::fs::File;
use std::io::BufReader;
use std::collections::HashMap;
use chrono::{DateTime, UTC};

#[derive(Debug, Serialize, Deserialize)]
struct OutputEntry {
    unique_id : String,
    timestamp : DateTime<UTC>,
    episode_number: u16,
    complete_download: bool
}

impl OutputEntry {
    fn from(e : &LogEntry) -> OutputEntry {
        OutputEntry {
            unique_id: match e.request_id {
                Some(ref x) => x.clone(),
                None => "".to_string()
            },
            timestamp: e.timestamp.clone(),
            episode_number: 0,
            complete_download: e.was_complete_download()
        }
    }
}

fn main() {
    let matches = App::new("Podlog")
        .version("0.1")
        .arg(Arg::with_name("DIRECTORY")
            .required(true)
            .index(1))
        .get_matches();

    let directory = Path::new(matches.value_of("DIRECTORY").unwrap());
    if !directory.is_dir() {
        panic!("{} isn't a directory", directory.to_str().unwrap());
    }

    let mut results : HashMap<String, Vec<OutputEntry> > = HashMap::new();

    let directory_entries = directory.read_dir().ok().unwrap();
    for file in directory_entries {
        let entry = file.ok().unwrap();
        process_log_file(&entry.path(), &mut results);
    }

    let storage = serde_json::to_string_pretty(&results).unwrap();
    println!("{}", storage);
}

fn process_log_file(path : &Path, results : &mut HashMap<String, Vec<OutputEntry> >) -> () {
    let file = BufReader::new(File::open(&path).unwrap());
    let lines = file.lines().filter_map(|result| result.ok()); // Filter out bad rows
    let entries = lines.map(|x| LogEntry::from_str(&x))
                       .filter(|x| x.operation.source == OperationSource::WEBSITE)
                       .filter(|ref x| x.is_audio_file());

    for entry in entries {
        let path = entry.path.clone().unwrap();
        if !results.contains_key(&path) {
            results.insert(path, Vec::new());
        } else {
            let mut x = results.get_mut(&path);
            if x.is_some() {
                x.as_mut().unwrap().push(OutputEntry::from(&entry));
            }
        }
    }
}

// Output Format:
// UniqueID, DateTime, Episode, Was Complete Download
<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;

use std::io;
use std::str;
use std::collections::BTreeMap;
use tokio_core::io::{Codec, EasyBuf};
use tokio_core::io::{Io, Framed};
use tokio_proto::pipeline::ServerProto;
use tokio_proto::TcpServer;
use tokio_service::Service;
use futures::{future, Future, BoxFuture};

#[derive(Default)]
pub struct CliCodec {
    /// Offset within the incoming EasyBuf at which newline search last ended
    search_offset: usize,
}

impl Codec for CliCodec {
    /// Input type is a tuple with the command name and an optional argument string
    type In = (String, Option<String>);

    /// Response type is just a string
    type Out = String;

    /// Find a newline, decode to a tuple of command name and argument string
    fn decode(&mut self, buf: &mut EasyBuf) -> io::Result<Option<Self::In>> {
        // Find newline
        let m = buf
            .as_slice()
            .iter()
            .skip(self.search_offset)
            .position(|&b| b == b'\n');

        match m {
            // Process the line if a newline was found
            Some(idx) => {
                println!("Found newline at position {}, started at {}", idx, self.search_offset); // XXX

                let line = buf.drain_to(self.search_offset + idx);
                buf.drain_to(1);

                self.search_offset = 0;

                match str::from_utf8(line.as_slice()) {
                    // Return the string on successful UTF-8 decode
                    // (TODO: split on the first space, ignore blank lines)
                    Ok(s) => Ok(Some(
                            ("Received".to_string(), Some(s.trim().to_string()))
                            )),

                    // Return an error if invalid UTF-8 is received
                    Err(e) => Err(io::Error::new(io::ErrorKind::Other, format!("invalid UTF-8: {:?}", e))),
                }
            },

            // Save our position if a newline wasn't found
            _ => {
                println!("Haven't received newline yet!  We started at offset {}, now at {}", self.search_offset, buf.len()); // XXX

                self.search_offset = buf.len();

                Ok(None)
            }
        }
    }

    /// Append the String to the outgoing buffer
    fn encode(&mut self, msg: String, buf: &mut Vec<u8>) -> io::Result<()> {
        buf.extend(msg.as_bytes());
        buf.push(b'\n');
        Ok(())
    }
}

pub struct CliProto;

impl<T: Io + 'static> ServerProto<T> for CliProto {
    type Request = (String, Option<String>);
    type Response = String;

    type Transport = Framed<T, CliCodec>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        Ok(io.framed(CliCodec::default()))
    }
}

trait CliCommand {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn call(&self, args: Option<String>) -> String;
}

#[derive(Default)]
pub struct CliServer<'a> {
    commands: BTreeMap<&'a str, &'a CliCommand>,
}

impl<'a> CliServer<'a> {
    fn add_command(&mut self, cmd: &'a CliCommand) {
        self.commands.insert(cmd.name(), cmd);
    }
}

impl<'a> Service for CliServer<'a> {
    type Request = (String, Option<String>);
    type Response = String;

    type Error = io::Error;

    type Future = BoxFuture<Self::Response, Self::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {
        match self.commands.get(&req.0[..]) {
            Some(cmd) => {
                println!("Calling command {}", req.0);
                // TODO: have commands return futures?
                future::ok(cmd.call(req.1)).boxed()
            },
            None => {
                println!("No match found for command {}", req.0);
                future::ok(format!("Received: {:?}", req)).boxed()
            }
        }
    }
}

fn main() {
    let addr = "0.0.0.0:14311".parse().unwrap();
    let server = TcpServer::new(CliProto, addr);

    println!("Serving on {}", addr);
    server.serve(|| Ok(CliServer::default()));
}
<commit_msg>Implemented command/argument splitting within each request line.<commit_after>extern crate futures;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;

use std::io;
use std::str;
use std::collections::BTreeMap;
use tokio_core::io::{Codec, EasyBuf};
use tokio_core::io::{Io, Framed};
use tokio_proto::pipeline::ServerProto;
use tokio_proto::TcpServer;
use tokio_service::Service;
use futures::{future, Future, BoxFuture};

#[derive(Default)]
pub struct CliCodec {
    /// Offset within the incoming EasyBuf at which newline search last ended
    search_offset: usize,
}

impl Codec for CliCodec {
    /// Input type is a tuple with the command name and an optional argument string
    type In = (String, Option<String>);

    /// Response type is just a string
    type Out = String;

    /// Find a newline, decode to a tuple of command name and argument string
    fn decode(&mut self, buf: &mut EasyBuf) -> io::Result<Option<Self::In>> {
        // Find newline
        let m = buf
            .as_slice()
            .iter()
            .skip(self.search_offset)
            .position(|&b| b == b'\n');

        match m {
            // Process the line if a newline was found
            Some(idx) => {
                println!("Found newline at position {}, started at {}", idx, self.search_offset); // XXX

                let line = buf.drain_to(self.search_offset + idx);
                buf.drain_to(1);

                self.search_offset = 0;

                match str::from_utf8(line.as_slice()) {
                    // Return the string on successful UTF-8 decode
                    // (TODO: ignore blank lines)
                    Ok(s) => {
                        // TODO: Find a better way to split the string
                        let s = s.trim();

                        let request = match s.find(' ') {
                            Some(i) => {
                                let (cmd, args) = s.split_at(i);
                                (cmd.trim().to_string(), Some(args.trim().to_string()))
                            },
                            None => (s.to_string(), None)
                        };

                        Ok(Some(request))
                    },

                    // Return an error if invalid UTF-8 is received
                    Err(e) => Err(io::Error::new(io::ErrorKind::Other, format!("invalid UTF-8: {:?}", e))),
                }
            },

            // Save our position if a newline wasn't found
            _ => {
                println!("Haven't received newline yet!  We started at offset {}, now at {}", self.search_offset, buf.len()); // XXX

                self.search_offset = buf.len();

                Ok(None)
            }
        }
    }

    /// Append the String to the outgoing buffer
    fn encode(&mut self, msg: String, buf: &mut Vec<u8>) -> io::Result<()> {
        buf.extend(msg.as_bytes());
        buf.push(b'\n');
        Ok(())
    }
}

pub struct CliProto;

impl<T: Io + 'static> ServerProto<T> for CliProto {
    type Request = (String, Option<String>);
    type Response = String;

    type Transport = Framed<T, CliCodec>;
    type BindTransport = Result<Self::Transport, io::Error>;

    fn bind_transport(&self, io: T) -> Self::BindTransport {
        Ok(io.framed(CliCodec::default()))
    }
}

trait CliCommand {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn call(&self, args: Option<String>) -> String;
}

#[derive(Default)]
pub struct CliServer<'a> {
    commands: BTreeMap<&'a str, &'a CliCommand>,
}

impl<'a> CliServer<'a> {
    fn add_command(&mut self, cmd: &'a CliCommand) {
        self.commands.insert(cmd.name(), cmd);
    }
}

impl<'a> Service for CliServer<'a> {
    type Request = (String, Option<String>);
    type Response = String;

    type Error = io::Error;

    type Future = BoxFuture<Self::Response, Self::Error>;

    fn call(&self, req: Self::Request) -> Self::Future {
        match self.commands.get(&req.0[..]) {
            Some(cmd) => {
                println!("Calling command {}", req.0);
                // TODO: have commands return futures?
                future::ok(cmd.call(req.1)).boxed()
            },
            None => {
                println!("No match found for command {}", req.0);
                future::ok(format!("Received: {:?}", req)).boxed()
            }
        }
    }
}

fn main() {
    let addr = "0.0.0.0:14311".parse().unwrap();
    let server = TcpServer::new(CliProto, addr);

    println!("Serving on {}", addr);
    server.serve(|| Ok(CliServer::default()));
}
<|endoftext|>


<commit_before>#![feature(float_from_str_radix, convert, collections)]

use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::prelude::*;
use std::rc::Rc;

type Dictionary = HashMap<String, Rc<Entry>>;
type Statement = Vec<Op>;
type NativeFn = Box<Fn(&mut Vec<Op>, &mut Interpreter)>;

#[derive(Clone)]
enum Op {
	Number(f64),
	Word(Rc<Entry>),
}

enum Code {
	Native(NativeFn),
	Forth(Statement),
}

#[derive(Clone)]
struct Entry {
	name: String,
	code: Rc<Code>,
}

impl PartialEq for Entry {
	fn eq(&self, other: &Self) -> bool { self.name == other.name }
}

impl Eq for Entry {}

impl Hash for Entry {
	fn hash<H>(&self, state: &mut H) where H: Hasher {
		self.name.hash(state)
	}
}

impl Entry {
	fn new(name: String, code: Code) -> Entry {
		Entry {
			name: name,
			code: Rc::new(code),
		}
	}
}

impl Code {
	fn run(&self, stack: &mut Statement, interp: &mut Interpreter) {
		match self {
			&Code::Native(ref f) => {
				f(stack, interp);
			},
			&Code::Forth(ref statement) => {
				for ref op in statement {
					match *op {
						&Op::Number(ref n) => { break; }
						&Op::Word(ref w) => {
							(*w).code.run(stack, interp);
						},
					}
				}
			},
		}
	}
}

trait StatementExt {
	fn pop_number(&mut self, &mut Interpreter) -> f64;
	fn run(&mut self, &mut Interpreter) -> f64;
}

impl StatementExt for Statement {
	fn pop_number(&mut self, interp: &mut Interpreter) -> f64 {
		loop {
			match self.pop() {
				Some(Op::Number(n)) => { return n; },
				Some(Op::Word(s)) => s.code.run(self, interp),
				None => { panic!("Popped empty statement"); },
			}
		}
	}
	fn run(&mut self, interp: &mut Interpreter) -> f64 {
		while self.len() > 0 {
			match self.pop() {
				Some(Op::Number(n)) => { return n },
				Some(Op::Word(w)) => {
					let code = interp.dictionary[w.name.as_str()].code.clone();
					code.run(self, interp);
				},
				None => { panic!("Empty Statement") },
			};
		}
		unreachable!();
	}
}

trait DictionaryExt {
	fn insert_entry(&mut self, Entry);
}

impl DictionaryExt for Dictionary {
	fn insert_entry(&mut self, entry: Entry) {
		self.insert(entry.name.clone(), Rc::new(entry));
	}
}

struct Interpreter {
    dictionary: Dictionary,
}

macro_rules! binary_entry {
	($name:expr, $o:expr) => {
		Entry::new(
				String::from_str($name),
				Code::Native(Box::new(|stack: &mut Statement, interp: &mut Interpreter| {
					let b = stack.pop_number(interp);
					let a = stack.pop_number(interp);
					stack.push(Op::Number($o(a, b)));
				})),
			)
	}
}

macro_rules! unary_entry {
	($name:expr, $o:expr) => {
		Entry::new(
				String::from_str($name),
				Code::Native(Box::new(|stack: &mut Statement, interp: &mut Interpreter| {
					let b = stack.pop_number(interp);
					stack.push(Op::Number($o(a)));
				})),
			)
	}
}

macro_rules! nonary_entry {
	($name:expr, $o:expr) => {
		Entry::new(
				String::from_str($name),
				Code::Native(Box::new(|stack: &mut Statement, interp: &mut Interpreter| {
					Op::Number($o);
				})),
			)
	}
}

impl Interpreter {
	fn new() -> Interpreter {
		let mut dict = Dictionary::new();

		dict.insert_entry(binary_entry!("/", ::std::ops::Div::div));
		dict.insert_entry(binary_entry!("*", ::std::ops::Mul::mul));
		dict.insert_entry(binary_entry!("+", ::std::ops::Add::add));
		dict.insert_entry(binary_entry!("-", ::std::ops::Sub::sub));
		dict.insert_entry(binary_entry!("%", ::std::ops::Rem::rem));

		dict.insert_entry(nonary_entry!("QUIT", ::std::process::exit(0)));
		dict.insert_entry(nonary_entry!("bye", ::std::process::exit(0)));

		Interpreter {
			dictionary: dict,
		}
	}

	fn lex(&mut self, statement: String) -> Statement {
		let words = statement.split(' ');
		let mut ret = Statement::new();
		for word in words {
			if let Some(n) = f64::from_str_radix(word, 10).ok() {
				ret.push(Op::Number(n));
			} else {
				ret.push(Op::Word(self.dictionary[word].clone()));
			}
		};
		ret
	}

	fn exec(&mut self, statement: String) -> f64 {
		let mut statement = self.lex(statement);
		return statement.run(self);
	}
}

fn main() {
	println!("forth-rs version 0.1.0");
	println!("Type 'bye' to exit");
    let mut interp = Interpreter::new();
    let mut stdin = io::stdin();
    loop {
    	print!("> ");
    	io::stdout().flush().ok().expect("Could not flush stdout");
    	let mut line = String::new();
    	stdin.read_line(&mut line).ok().expect("Unable to read from stdin");
    	let result = interp.exec(String::from_str(line.trim_right()));
    	println!("{}", result);
    }
}
<commit_msg>Add DUP. Silence warnings.<commit_after>#![feature(float_from_str_radix, convert, collections)]
#![allow(unused_variables)]

use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::prelude::*;
use std::rc::Rc;

type Dictionary = HashMap<String, Rc<Entry>>;
type Statement = Vec<Op>;
type NativeFn = Box<Fn(&mut Vec<Op>, &mut Interpreter)>;

#[derive(Clone)]
enum Op {
	Number(f64),
	Word(Rc<Entry>),
}

enum Code {
	Native(NativeFn),
	Forth(Statement),
}

#[derive(Clone)]
struct Entry {
	name: String,
	code: Rc<Code>,
}

impl PartialEq for Entry {
	fn eq(&self, other: &Self) -> bool { self.name == other.name }
}

impl Eq for Entry {}

impl Hash for Entry {
	fn hash<H>(&self, state: &mut H) where H: Hasher {
		self.name.hash(state)
	}
}

impl Entry {
	fn new(name: String, code: Code) -> Entry {
		Entry {
			name: name,
			code: Rc::new(code),
		}
	}
}

impl Code {
	fn run(&self, stack: &mut Statement, interp: &mut Interpreter) {
		match self {
			&Code::Native(ref f) => {
				f(stack, interp);
			},
			&Code::Forth(ref statement) => {
				for ref op in statement {
					match *op {
						&Op::Number(ref n) => { break; }
						&Op::Word(ref w) => {
							(*w).code.run(stack, interp);
						},
					}
				}
			},
		}
	}
}

trait StatementExt {
	fn pop_number(&mut self, &mut Interpreter) -> f64;
	fn run(&mut self, &mut Interpreter) -> f64;
}

impl StatementExt for Statement {
	fn pop_number(&mut self, interp: &mut Interpreter) -> f64 {
		loop {
			match self.pop() {
				Some(Op::Number(n)) => { return n; },
				Some(Op::Word(s)) => s.code.run(self, interp),
				None => { panic!("Popped empty statement"); },
			}
		}
	}
	fn run(&mut self, interp: &mut Interpreter) -> f64 {
		while self.len() > 0 {
			match self.pop() {
				Some(Op::Number(n)) => { return n },
				Some(Op::Word(w)) => {
					let code = interp.dictionary[w.name.as_str()].code.clone();
					code.run(self, interp);
				},
				None => { panic!("Empty Statement") },
			};
		}
		unreachable!();
	}
}

trait DictionaryExt {
	fn insert_entry(&mut self, Entry);
}

impl DictionaryExt for Dictionary {
	fn insert_entry(&mut self, entry: Entry) {
		self.insert(entry.name.clone(), Rc::new(entry));
	}
}

struct Interpreter {
    dictionary: Dictionary,
}

macro_rules! binary_entry {
	($name:expr, $o:expr) => {
		Entry::new(
				String::from_str($name),
				Code::Native(Box::new(|stack: &mut Statement, interp: &mut Interpreter| {
					let b = stack.pop_number(interp);
					let a = stack.pop_number(interp);
					stack.push(Op::Number($o(a, b)));
				})),
			)
	}
}

macro_rules! unary_entry {
	($name:expr, $o:expr) => {
		Entry::new(
				String::from_str($name),
				Code::Native(Box::new(|stack: &mut Statement, interp: &mut Interpreter| {
					let b = stack.pop_number(interp);
					stack.push(Op::Number($o(a)));
				})),
			)
	}
}

macro_rules! nonary_entry {
	($name:expr, $o:expr) => {
		Entry::new(
				String::from_str($name),
				Code::Native(Box::new(|stack: &mut Statement, interp: &mut Interpreter| {
					Op::Number($o);
				})),
			)
	}
}

impl Interpreter {
	fn new() -> Interpreter {
		let mut dict = Dictionary::new();

		dict.insert_entry(binary_entry!("/", ::std::ops::Div::div));
		dict.insert_entry(binary_entry!("*", ::std::ops::Mul::mul));
		dict.insert_entry(binary_entry!("+", ::std::ops::Add::add));
		dict.insert_entry(binary_entry!("-", ::std::ops::Sub::sub));
		dict.insert_entry(binary_entry!("%", ::std::ops::Rem::rem));

		dict.insert_entry(nonary_entry!("QUIT", ::std::process::exit(0)));
		dict.insert_entry(nonary_entry!("bye", ::std::process::exit(0)));

		dict.insert_entry(Entry::new(
				String::from_str("DUP"),
				Code::Native(Box::new(|stack: &mut Statement, interp: &mut Interpreter| {
					let x = stack.pop().unwrap();
					stack.push(x.clone());
					stack.push(x);
				})),
			));

		Interpreter {
			dictionary: dict,
		}
	}

	fn lex(&mut self, statement: String) -> Statement {
		let words = statement.split(' ');
		let mut ret = Statement::new();
		for word in words {
			if let Some(n) = f64::from_str_radix(word, 10).ok() {
				ret.push(Op::Number(n));
			} else {
				ret.push(Op::Word(self.dictionary[word].clone()));
			}
		};
		ret
	}

	fn exec(&mut self, statement: String) -> f64 {
		let mut statement = self.lex(statement);
		return statement.run(self);
	}
}

fn main() {
	println!("forth-rs version 0.1.0");
	println!("Type 'bye' to exit");
    let mut interp = Interpreter::new();
    let mut stdin = io::stdin();
    loop {
    	print!("> ");
    	io::stdout().flush().ok().expect("Could not flush stdout");
    	let mut line = String::new();
    	stdin.read_line(&mut line).ok().expect("Unable to read from stdin");
    	let result = interp.exec(String::from_str(line.trim_right()));
    	println!("{}", result);
    }
}
<|endoftext|>


<commit_before>extern crate lxd;

use lxd::list_containers;

fn format_output(headers: &Vec<String>, items: &Vec<Vec<String>>) -> String{
    let mut widths = Vec::new();
    for header in headers {
        widths.push(header.len());
    }
    for item in items {
        for (n, column) in item.iter().enumerate() {
            if column.len() > widths[n] {
                widths[n] = column.len();
            }
        }
    }
    let mut dividing_line = "+".to_string();
    let mut header_line = "|".to_string();
    for (n, header) in headers.iter().enumerate() {
        dividing_line.push_str(&vec!["-"; widths[n] + 2].concat());
        dividing_line.push_str("+");
        header_line.push_str(&format!("{:^1$}", &header, widths[n] + 2));
        header_line.push_str("|");
    }
    let mut output_string = String::new();
    output_string.push_str(&dividing_line);
    output_string.push_str("\n");
    output_string.push_str(&header_line);
    output_string.push_str("\n");
    output_string.push_str(&dividing_line);
    output_string.push_str("\n");
    for item in items {
        output_string.push_str("|");
        for (n, column_content) in item.iter().enumerate() {
            output_string.push_str(" ");
            output_string.push_str(&format!("{:1$}", &column_content, widths[n] + 1));
            output_string.push_str("|");
        }
        output_string.push_str("\n");
    }
    output_string.push_str(&dividing_line);
    output_string
}

fn main() {
    let containers = list_containers();
    let headers = vec!["NAME".to_string(), "STATE".to_string(), "IPV4".to_string(), "IPV6".to_string(), "EPHEMERAL".to_string(), "SNAPSHOTS".to_string()];
    let container_items = containers.iter().map(
        |c| {
            let mut ipv4_address = String::new();
            let mut ipv6_address = String::new();
            for ip in &c.status.ips {
                if ip.protocol == "IPV4" && ip.address != "127.0.0.1" {
                    ipv4_address = ip.address.clone();
                }
                if ip.protocol == "IPV6" && ip.address != "::1" {
                    ipv6_address = ip.address.clone();
                }
            }
            let ephemeral = if c.ephemeral { "YES" } else { "NO" };
            vec![c.name.clone(), c.status.status.clone().to_uppercase(), ipv4_address.to_string(), ipv6_address.to_string(), ephemeral.to_string(), c.snapshot_urls.len().to_string()]
        }).collect();
    println!("{}", format_output(&headers, &container_items));
}
<commit_msg>Use string literal titles properly.<commit_after>extern crate lxd;

use lxd::list_containers;

fn format_output(headers: &Vec<&str>, items: &Vec<Vec<String>>) -> String{
    let mut widths = Vec::new();
    for header in headers {
        widths.push(header.len());
    }
    for item in items {
        for (n, column) in item.iter().enumerate() {
            if column.len() > widths[n] {
                widths[n] = column.len();
            }
        }
    }
    let mut dividing_line = "+".to_string();
    let mut header_line = "|".to_string();
    for (n, header) in headers.iter().enumerate() {
        dividing_line.push_str(&vec!["-"; widths[n] + 2].concat());
        dividing_line.push_str("+");
        header_line.push_str(&format!("{:^1$}", &header, widths[n] + 2));
        header_line.push_str("|");
    }
    let mut output_string = String::new();
    output_string.push_str(&dividing_line);
    output_string.push_str("\n");
    output_string.push_str(&header_line);
    output_string.push_str("\n");
    output_string.push_str(&dividing_line);
    output_string.push_str("\n");
    for item in items {
        output_string.push_str("|");
        for (n, column_content) in item.iter().enumerate() {
            output_string.push_str(" ");
            output_string.push_str(&format!("{:1$}", &column_content, widths[n] + 1));
            output_string.push_str("|");
        }
        output_string.push_str("\n");
    }
    output_string.push_str(&dividing_line);
    output_string
}

fn main() {
    let containers = list_containers();
    let headers = vec!["NAME", "STATE", "IPV4", "IPV6", "EPHEMERAL", "SNAPSHOTS"];
    let container_items = containers.iter().map(
        |c| {
            let mut ipv4_address = String::new();
            let mut ipv6_address = String::new();
            for ip in &c.status.ips {
                if ip.protocol == "IPV4" && ip.address != "127.0.0.1" {
                    ipv4_address = ip.address.clone();
                }
                if ip.protocol == "IPV6" && ip.address != "::1" {
                    ipv6_address = ip.address.clone();
                }
            }
            let ephemeral = if c.ephemeral { "YES" } else { "NO" };
            vec![c.name.clone(), c.status.status.clone().to_uppercase(), ipv4_address.to_string(), ipv6_address.to_string(), ephemeral.to_string(), c.snapshot_urls.len().to_string()]
        }).collect();
    println!("{}", format_output(&headers, &container_items));
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Write;

#[derive(Copy, Clone, Debug)]
struct Tile
{
    population : f64,
    fertility : f64,
    gold : f64,
}

#[derive(Debug)]
struct Producer
{
    name : String,
    population : f64,
    gold: f64,

    happiness : f64,

    products : [f64; 2], // 0 food, 1 goods
    product_usage: [f64; 2],

    production : Production,
}

#[derive(Debug)]
enum Production {
    None,
    Relative {product : usize, ratio : f64},
    Dimmishing {product : usize, multiple : f64, dimminish : f64}
}

impl Production {
    fn produce(&self, population: f64) -> (usize, f64) {
        match self {
            &Production::None => (0, 0.0),
            &Production::Relative { product, ratio } => (product, population * ratio),
            &Production::Dimmishing { product, multiple, dimminish } => (product, ((population+1.0).powf(1.0-dimminish) - 1.0)/(1.0-dimminish) * multiple),
        }
    }
}

// calculates diminishing return coeficient so that 0 population produces zero and at_one population produces 1
fn calc_diminish(product: usize, zero:f64, at_one:f64) -> Production {
    let mult = zero;
    let dimm = zero.log10()/(at_one + 1.0).log10();

    Production::Dimmishing {product:product, multiple:mult, dimminish : dimm }
}

fn main() {

    let mut producers : Vec<Producer> = vec!();

    producers.push(Producer {name:"owners".to_string(),     population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: Production::None, product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"food high".to_string(),  population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(0, 2.0, 30.0), product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"food low".to_string(),   population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(0, 1.5, 60.0), product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"goods high".to_string(), population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(1, 3.0, 10.0), product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"goods low".to_string(),  population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(1, 1.1, 20.0), product_usage: [1.0, 1.0], happiness: 0.0});

    let producer_count = producers.len();

    let mut prices : [f64; 2] = [1_f64, 1_f64];

    let mut prices_output : File = File::create("prices.csv").unwrap();
    writeln!(&mut prices_output, "Step;Price 1;Price 2").unwrap();

    let mut population_output : File = File::create("population.csv").unwrap();
    {
        let producers_names = producers.iter().map(|prod| prod.name.to_string()).collect::<Vec<_>>().join(";");
        writeln!(&mut population_output, "Step;{0}", producers_names).unwrap();
    }

    for step_id in 0..500
    {
        let mut total_product = [0_f64, 0_f64];
        let mut total_gold = 0_f64;

        // produce goods
        for x in 0..producer_count {
            let mut producer = &mut producers[x];

            let (item, amount) = producer.production.produce(producer.population);
            producer.products[item] += amount;
        }

        for _ in 0..4 {
            // calculate totals for trading
            for x in 0..producer_count {
                let producer = &producers[x];

                total_product[0] += producer.products[0];
                total_product[1] += producer.products[1];
                total_gold += producer.gold;
            }

            // trade between producers
            for product_id in 0..2 {

                let average_gold = total_gold / producer_count as f64;
                let needed_resources = total_product[product_id] / producer_count as f64;

                let (_, sellers_buyers) : (_, Vec<&mut Producer>) = producers.iter_mut().partition(|prod| prod.products[product_id] > needed_resources && prod.gold > average_gold);
                let (mut buyers, mut sellers) : (Vec<&mut Producer>, Vec<&mut Producer>) = sellers_buyers.into_iter().partition(|prod| prod.products[product_id] < needed_resources);

                if sellers.len() <= 0 || buyers.len() <= 0 {
                    continue;
                }

                let mut wanted_resources = 0_f64;
                let mut offered_gold = 0_f64;

                for buyer in &buyers {
                    wanted_resources += needed_resources - buyer.products[product_id];
                    offered_gold += buyer.gold;
                }

                let mut wanted_gold = 0_f64;
                let mut offered_resource = 0_f64;
                for seller in &sellers {
                    wanted_gold += average_gold - seller.gold;
                    offered_resource += seller.products[product_id] - needed_resources;
                }

                if offered_resource <= 0.1 { // if there is nothing to sell, no trading
                    continue;
                }

                let price = offered_gold / offered_resource;
                prices[product_id] = price;

                let mut total_bough = 0_f64;

                for buyer in &mut buyers {
                    let can_buy = buyer.gold / price;
                    let want = needed_resources - buyer.products[product_id];
                    let buys = can_buy.min(want);
                    buyer.products[product_id] += buys;
                    buyer.gold -= buys * price;
                    total_bough += buys;
                }

                let sold_ratio = total_bough / offered_resource;

                if sold_ratio > 1.0 {
                    println!("ERROR; sold more than is available");
                }

                for seller in &mut sellers {
                    let offered = seller.products[product_id] - needed_resources;
                    let seller_sold = offered * sold_ratio;

                    seller.products[product_id] -= seller_sold;
                    seller.gold += seller_sold * price;
                }
            }
        }

        // consume goods
        for x in 0..producer_count {
            let mut producer = &mut producers[x];

            let needed_food = producer.population * producer.product_usage[0];
            let eaten_food = producer.products[0].min(needed_food);
            producer.products[0] -= eaten_food;

            let needed_goods = producer.population * producer.product_usage[1];
            let used_goods = producer.products[1].min(needed_goods);
            producer.products[1] -= used_goods;

            let food_ratio = eaten_food / needed_food;
            let goods_ratio = used_goods / needed_goods;

            producer.happiness = food_ratio + food_ratio * goods_ratio * 0.5;

            if x == 0 {
                producer.happiness *= 1.01;
            }
        }

        // move people between producers
        {
            let mut producers_vec: Vec<&mut Producer> = producers.iter_mut().collect::<Vec<_>>();

            producers_vec.sort_by(|a, b| a.happiness.partial_cmp(&b.happiness).unwrap());

            for from in 0 .. producers_vec.len() {
                for to in from+1 .. producers_vec.len(){

                    let move_people = producers_vec[to].happiness - producers_vec[from].happiness;
                    let move_people = move_people * 0.1;
                    let target_population = 0.1_f64.max(producers_vec[from].population - move_people);
                    let move_people = producers_vec[from].population - target_population;

                    producers_vec[from].population -= move_people;
                    producers_vec[to].population += move_people;
                }
            }
        }

        // taxes (duh!)
        {
            let tax_rate = 0.2;

            let mut total_tax = 0.0;
            for x in 1..producer_count {
                let tax = producers[x].gold * tax_rate;
                producers[x].gold -= tax;
                total_tax += tax;
            }

            producers[0].gold += total_tax;
        }

        // sanity checks
        {
            for prod in &producers{
                for x in 0..2 {
                    if prod.products[x] < -0.0000001 {
                        println!("ERROR; product {0} is negative", x)
                    }
                }
                if prod.gold < -0.0000001 {
                    println!("ERROR; gold is negative:{0:.10}", prod.gold)
                }
            }
        }

        {
            let producers_population = producers.iter().map(|prod| prod.population.to_string()).collect::<Vec<_>>().join(";");
            writeln!(&mut population_output, "{0};{1}", step_id, producers_population).unwrap();
            writeln!(&mut prices_output, "{0};{1:.3};{2:.3}", step_id, prices[0], prices[1]).unwrap();
        }
    }
}
<commit_msg>Added offset to sold ration error check, because of floating point imprecission<commit_after>use std::fs::File;
use std::io::Write;

#[derive(Copy, Clone, Debug)]
struct Tile
{
    population : f64,
    fertility : f64,
    gold : f64,
}

#[derive(Debug)]
struct Producer
{
    name : String,
    population : f64,
    gold: f64,

    happiness : f64,

    products : [f64; 2], // 0 food, 1 goods
    product_usage: [f64; 2],

    production : Production,
}

#[derive(Debug)]
enum Production {
    None,
    Relative {product : usize, ratio : f64},
    Dimmishing {product : usize, multiple : f64, dimminish : f64}
}

impl Production {
    fn produce(&self, population: f64) -> (usize, f64) {
        match self {
            &Production::None => (0, 0.0),
            &Production::Relative { product, ratio } => (product, population * ratio),
            &Production::Dimmishing { product, multiple, dimminish } => (product, ((population+1.0).powf(1.0-dimminish) - 1.0)/(1.0-dimminish) * multiple),
        }
    }
}

// calculates diminishing return coeficient so that 0 population produces zero and at_one population produces 1
fn calc_diminish(product: usize, zero:f64, at_one:f64) -> Production {
    let mult = zero;
    let dimm = zero.log10()/(at_one + 1.0).log10();

    Production::Dimmishing {product:product, multiple:mult, dimminish : dimm }
}

fn main() {

    let mut producers : Vec<Producer> = vec!();

    producers.push(Producer {name:"owners".to_string(),     population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: Production::None, product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"food high".to_string(),  population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(0, 2.0, 30.0), product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"food low".to_string(),   population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(0, 1.5, 60.0), product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"goods high".to_string(), population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(1, 3.0, 10.0), product_usage: [1.0, 1.0], happiness: 0.0});
    producers.push(Producer {name:"goods low".to_string(),  population : 20_f64, gold: 1000_f64, products: [0_f64, 0_f64], production: calc_diminish(1, 1.1, 20.0), product_usage: [1.0, 1.0], happiness: 0.0});

    let producer_count = producers.len();

    let mut prices : [f64; 2] = [1_f64, 1_f64];

    let mut prices_output : File = File::create("prices.csv").unwrap();
    writeln!(&mut prices_output, "Step;Price 1;Price 2").unwrap();

    let mut population_output : File = File::create("population.csv").unwrap();
    {
        let producers_names = producers.iter().map(|prod| prod.name.to_string()).collect::<Vec<_>>().join(";");
        writeln!(&mut population_output, "Step;{0}", producers_names).unwrap();
    }

    for step_id in 0..500
    {
        let mut total_product = [0_f64, 0_f64];
        let mut total_gold = 0_f64;

        // produce goods
        for x in 0..producer_count {
            let mut producer = &mut producers[x];

            let (item, amount) = producer.production.produce(producer.population);
            producer.products[item] += amount;
        }

        for _ in 0..4 {
            // calculate totals for trading
            for x in 0..producer_count {
                let producer = &producers[x];

                total_product[0] += producer.products[0];
                total_product[1] += producer.products[1];
                total_gold += producer.gold;
            }

            // trade between producers
            for product_id in 0..2 {

                let average_gold = total_gold / producer_count as f64;
                let needed_resources = total_product[product_id] / producer_count as f64;

                let (_, sellers_buyers) : (_, Vec<&mut Producer>) = producers.iter_mut().partition(|prod| prod.products[product_id] > needed_resources && prod.gold > average_gold);
                let (mut buyers, mut sellers) : (Vec<&mut Producer>, Vec<&mut Producer>) = sellers_buyers.into_iter().partition(|prod| prod.products[product_id] < needed_resources);

                if sellers.len() <= 0 || buyers.len() <= 0 {
                    continue;
                }

                let mut wanted_resources = 0_f64;
                let mut offered_gold = 0_f64;

                for buyer in &buyers {
                    wanted_resources += needed_resources - buyer.products[product_id];
                    offered_gold += buyer.gold;
                }

                let mut wanted_gold = 0_f64;
                let mut offered_resource = 0_f64;
                for seller in &sellers {
                    wanted_gold += average_gold - seller.gold;
                    offered_resource += seller.products[product_id] - needed_resources;
                }

                if offered_resource <= 0.1 { // if there is nothing to sell, no trading
                    continue;
                }

                let price = offered_gold / offered_resource;
                prices[product_id] = price;

                let mut total_bough = 0_f64;

                for buyer in &mut buyers {
                    let can_buy = buyer.gold / price;
                    let want = needed_resources - buyer.products[product_id];
                    let buys = can_buy.min(want);
                    buyer.products[product_id] += buys;
                    buyer.gold -= buys * price;
                    total_bough += buys;
                }

                let sold_ratio = total_bough / offered_resource;

                if sold_ratio > 1.000001 {
                    println!("ERROR; sold more than is available;{0};{1}", offered_resource, total_bough);
                }

                for seller in &mut sellers {
                    let offered = seller.products[product_id] - needed_resources;
                    let seller_sold = offered * sold_ratio;

                    seller.products[product_id] -= seller_sold;
                    seller.gold += seller_sold * price;
                }
            }
        }

        // consume goods
        for x in 0..producer_count {
            let mut producer = &mut producers[x];

            let needed_food = producer.population * producer.product_usage[0];
            let eaten_food = producer.products[0].min(needed_food);
            producer.products[0] -= eaten_food;

            let needed_goods = producer.population * producer.product_usage[1];
            let used_goods = producer.products[1].min(needed_goods);
            producer.products[1] -= used_goods;

            let food_ratio = eaten_food / needed_food;
            let goods_ratio = used_goods / needed_goods;

            producer.happiness = food_ratio + food_ratio * goods_ratio * 0.5;

            if x == 0 {
                producer.happiness *= 1.01;
            }
        }

        // move people between producers
        {
            let mut producers_vec: Vec<&mut Producer> = producers.iter_mut().collect::<Vec<_>>();

            producers_vec.sort_by(|a, b| a.happiness.partial_cmp(&b.happiness).unwrap());

            for from in 0 .. producers_vec.len() {
                for to in from+1 .. producers_vec.len(){

                    let move_people = producers_vec[to].happiness - producers_vec[from].happiness;
                    let move_people = move_people * 0.1;
                    let target_population = 0.1_f64.max(producers_vec[from].population - move_people);
                    let move_people = producers_vec[from].population - target_population;

                    producers_vec[from].population -= move_people;
                    producers_vec[to].population += move_people;
                }
            }
        }

        // taxes (duh!)
        {
            let tax_rate = 0.2;

            let mut total_tax = 0.0;
            for x in 1..producer_count {
                let tax = producers[x].gold * tax_rate;
                producers[x].gold -= tax;
                total_tax += tax;
            }

            producers[0].gold += total_tax;
        }

        // sanity checks
        {
            for prod in &producers{
                for x in 0..2 {
                    if prod.products[x] < -0.0000001 {
                        println!("ERROR; product {0} is negative", x)
                    }
                }
                if prod.gold < -0.0000001 {
                    println!("ERROR; gold is negative:{0:.10}", prod.gold)
                }
            }
        }

        {
            let producers_population = producers.iter().map(|prod| prod.population.to_string()).collect::<Vec<_>>().join(";");
            writeln!(&mut population_output, "{0};{1}", step_id, producers_population).unwrap();
            writeln!(&mut prices_output, "{0};{1:.3};{2:.3}", step_id, prices[0], prices[1]).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;

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 " " ""

*/

}
<commit_msg>Parse arguments<commit_after>use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;

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

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

impl<'a> std::fmt::Display for CSV_descriptor<'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) -> CSV_descriptor<'a> {

    let csv_file_path = Path::new(path_arg);

    let csv_delimiter = match delimiter_arg.chars().next() {
                            Some(result) => result,
                            None         => panic!("error parsing delimiter: {}", delimiter_arg),
                        };

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

    CSV_descriptor {
                        file_path:  &csv_file_path,
                        delimiter:  csv_delimiter,
                        quote:      csv_quote
                   }
}

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 " " ""

*/

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

    for arg in &args {
        println!("{}", arg);
    }

    let csv_desc: CSV_descriptor = parse_args(&args[1], &args[2], &args[3]);
    println!("{}", csv_desc);

    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,
    };

}
<|endoftext|>