<commit_before>#[macro_use]
extern crate clap;

mod lib;

use clap::{App, Arg};
use lib::Petnames;
use rand::seq::IteratorRandom;
use std::collections::HashSet;
use std::str::FromStr;

fn main() {
    let matches = App::new("rust-petname")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generate human readable random names.")
        .after_help(concat!(
            "Based on Dustin Kirkland's petname project ",
            "<https://github.com/dustinkirkland/petname>."
        ))
        .arg(
            Arg::with_name("words")
                .short("w")
                .long("words")
                .value_name("WORDS")
                .default_value("2")
                .help("Number of words in name")
                .takes_value(true)
                .validator(can_be_parsed::<u8>),
        )
        .arg(
            Arg::with_name("separator")
                .short("s")
                .long("separator")
                .value_name("SEP")
                .default_value("-")
                .help("Separator between words")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("complexity")
                .short("c")
                .long("complexity")
                .value_name("COM")
                .possible_values(&["0", "1", "2"])
                .hide_possible_values(true)
                .default_value("0")
                .help("Use small words (0), medium words (1), or large words (2)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("count")
                .long("count")
                .value_name("COUNT")
                .default_value("1")
                .help("Generate multiple names; pass 0 to produce infinite names!")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("letters")
                .short("l")
                .long("letters")
                .value_name("LETTERS")
                .default_value("0")
                .help("Maxiumum number of letters in each word; 0 for unlimited")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("alliterate")
                .short("a")
                .long("alliterate")
                .help("Generate names where each word begins with the same letter")
                .takes_value(false),
        )
        .get_matches();

    // Unwrapping is safe because these options have defaults.
    let opt_separator = matches.value_of("separator").unwrap();
    let opt_words = matches.value_of("words").unwrap();
    let opt_complexity = matches.value_of("complexity").unwrap();
    let opt_count = matches.value_of("count").unwrap();
    let opt_letters = matches.value_of("letters").unwrap();
    let opt_alliterate = matches.is_present("alliterate");

    // Parse numbers. Validated so unwrapping is okay.
    let opt_words: u8 = opt_words.parse().unwrap();
    let opt_count: usize = opt_count.parse().unwrap();
    let opt_letters: usize = opt_letters.parse().unwrap();

    // Select the appropriate word list.
    let mut petnames = match opt_complexity {
        "0" => Petnames::small(),
        "1" => Petnames::medium(),
        "2" => Petnames::large(),
        _ => Petnames::small(),
    };

    // If requested, limit the number of letters.
    if opt_letters != 0 {
        petnames.retain(|s| s.len() <= opt_letters);
    }

    // We're going to need a source of randomness.
    let mut rng = rand::thread_rng();

    // If requested, choose a random letter then discard all words that do not
    // begin with that letter.
    if opt_alliterate {
        // We choose the first letter from the intersection of the first letters
        // of each word list in `petnames`.
        let firsts =
            common_first_letters(&petnames.adjectives, &[&petnames.adverbs, &petnames.names]);
        // Choose the first letter at random; fails if there are no letters.
        match firsts.iter().choose(&mut rng) {
            Some(c) => petnames.retain(|s| s.chars().next() == Some(*c)),
            None => panic!("no letters in common"), // TODO: do this without a panic.
        };
    }

    // Stream if count is 0.
    if opt_count == 0 {
        loop {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    } else {
        for _ in 1..=opt_count {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    }
}

fn can_be_parsed<INTO>(value: String) -> Result<(), String>
where
    INTO: FromStr,
    <INTO as FromStr>::Err: std::fmt::Display,
{
    match value.parse::<INTO>() {
        Err(e) => Err(format!("{}", e)),
        Ok(_) => Ok(()),
    }
}

fn common_first_letters(init: &[&str], more: &[&[&str]]) -> HashSet<char> {
    let mut firsts = first_letters(init);
    let firsts_other: Vec<HashSet<char>> = more.iter().map(|list| first_letters(list)).collect();
    firsts.retain(|c| firsts_other.iter().all(|fs| fs.contains(c)));
    firsts
}

fn first_letters(names: &[&str]) -> HashSet<char> {
    names.iter().filter_map(|s| s.chars().next()).collect()
}
<commit_msg>Add -u / --ubuntu as alias for -a / --alliterate.<commit_after>#[macro_use]
extern crate clap;

mod lib;

use clap::{App, Arg};
use lib::Petnames;
use rand::seq::IteratorRandom;
use std::collections::HashSet;
use std::str::FromStr;

fn main() {
    let matches = App::new("rust-petname")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generate human readable random names.")
        .after_help(concat!(
            "Based on Dustin Kirkland's petname project ",
            "<https://github.com/dustinkirkland/petname>."
        ))
        .arg(
            Arg::with_name("words")
                .short("w")
                .long("words")
                .value_name("WORDS")
                .default_value("2")
                .help("Number of words in name")
                .takes_value(true)
                .validator(can_be_parsed::<u8>),
        )
        .arg(
            Arg::with_name("separator")
                .short("s")
                .long("separator")
                .value_name("SEP")
                .default_value("-")
                .help("Separator between words")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("complexity")
                .short("c")
                .long("complexity")
                .value_name("COM")
                .possible_values(&["0", "1", "2"])
                .hide_possible_values(true)
                .default_value("0")
                .help("Use small words (0), medium words (1), or large words (2)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("count")
                .long("count")
                .value_name("COUNT")
                .default_value("1")
                .help("Generate multiple names; pass 0 to produce infinite names!")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("letters")
                .short("l")
                .long("letters")
                .value_name("LETTERS")
                .default_value("0")
                .help("Maxiumum number of letters in each word; 0 for unlimited")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("alliterate")
                .short("a")
                .long("alliterate")
                .help("Generate names where each word begins with the same letter")
                .takes_value(false),
        )
        .arg(
            // For compatibility with upstream.
            Arg::with_name("ubuntu")
                .short("u")
                .long("ubuntu")
                .help("Alias; see --alliterate")
                .takes_value(false),
        )
        .get_matches();

    // Unwrapping is safe because these options have defaults.
    let opt_separator = matches.value_of("separator").unwrap();
    let opt_words = matches.value_of("words").unwrap();
    let opt_complexity = matches.value_of("complexity").unwrap();
    let opt_count = matches.value_of("count").unwrap();
    let opt_letters = matches.value_of("letters").unwrap();
    let opt_alliterate = matches.is_present("alliterate") || matches.is_present("ubuntu");

    // Parse numbers. Validated so unwrapping is okay.
    let opt_words: u8 = opt_words.parse().unwrap();
    let opt_count: usize = opt_count.parse().unwrap();
    let opt_letters: usize = opt_letters.parse().unwrap();

    // Select the appropriate word list.
    let mut petnames = match opt_complexity {
        "0" => Petnames::small(),
        "1" => Petnames::medium(),
        "2" => Petnames::large(),
        _ => Petnames::small(),
    };

    // If requested, limit the number of letters.
    if opt_letters != 0 {
        petnames.retain(|s| s.len() <= opt_letters);
    }

    // We're going to need a source of randomness.
    let mut rng = rand::thread_rng();

    // If requested, choose a random letter then discard all words that do not
    // begin with that letter.
    if opt_alliterate {
        // We choose the first letter from the intersection of the first letters
        // of each word list in `petnames`.
        let firsts =
            common_first_letters(&petnames.adjectives, &[&petnames.adverbs, &petnames.names]);
        // Choose the first letter at random; fails if there are no letters.
        match firsts.iter().choose(&mut rng) {
            Some(c) => petnames.retain(|s| s.chars().next() == Some(*c)),
            None => panic!("no letters in common"), // TODO: do this without a panic.
        };
    }

    // Stream if count is 0.
    if opt_count == 0 {
        loop {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    } else {
        for _ in 1..=opt_count {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    }
}

fn can_be_parsed<INTO>(value: String) -> Result<(), String>
where
    INTO: FromStr,
    <INTO as FromStr>::Err: std::fmt::Display,
{
    match value.parse::<INTO>() {
        Err(e) => Err(format!("{}", e)),
        Ok(_) => Ok(()),
    }
}

fn common_first_letters(init: &[&str], more: &[&[&str]]) -> HashSet<char> {
    let mut firsts = first_letters(init);
    let firsts_other: Vec<HashSet<char>> = more.iter().map(|list| first_letters(list)).collect();
    firsts.retain(|c| firsts_other.iter().all(|fs| fs.contains(c)));
    firsts
}

fn first_letters(names: &[&str]) -> HashSet<char> {
    names.iter().filter_map(|s| s.chars().next()).collect()
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;
use docopt::Docopt;

extern crate rand;
use rand::{ Rng, OsRng };

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

use std::{process, env};
use std::io::{self, Read, Write};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::error::Error;

mod keepasshttp;

const VERSION: &'static str = "0.1.0";
const USAGE: &'static str = "
cmdipass

Usage:
  cmdipass get <search-string>
  cmdipass get-one <search-string> <index> [--password-only | --username-only]
  cmdipass --version
  cmdipass (-h | --help)
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_get: bool,
    flag_version: bool,
    flag_help: bool,
    arg_search_string: String,
    arg_index: Option<u8>,
    flag_password_only: bool,
    flag_username_only: bool
}

fn config_path() -> PathBuf {
    env::var("CMDIPASS_CONFIG").map(|e| PathBuf::from(e)).unwrap_or_else(|_| {
        let mut pathbuf = env::home_dir().unwrap_or(PathBuf::from(""));
        pathbuf.push(".cmdipass");
        pathbuf
    })
}

fn config_exists() -> bool {
    config_path().as_path().exists()
}

fn load_config() -> io::Result<keepasshttp::Config> {
    let mut res = File::open(config_path())?;
    let mut buf = String::new();
    res.read_to_string(&mut buf)?;

    let config: keepasshttp::Config = serde_json::from_str(buf.as_str())
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("Invalid JSON: {}", e.description())))?;
    Ok(config)
}

fn write_config_file(config: &keepasshttp::Config) {
    let mut file = File::create(config_path()).unwrap();
    file.write_all(serde_json::to_string(&config).unwrap().as_bytes()).unwrap();
}

fn handle_entries(result: &Result<Vec<keepasshttp::Entry>, String>, args: &Args) {
    let entries = result.as_ref().unwrap_or_else(|e| {
        writeln!(io::stderr(), "Error - Server said: '{}'", e);
        process::exit(1);
    });

    match args.arg_index {
        Some(index) => {
            let entry = &entries.get(index as usize).unwrap_or_else(|| {
                writeln!(io::stderr(), "Results did not contain an entry at index {}", index);
                process::exit(1);
            });

            if args.flag_username_only {
                println!("{}", entry.login);
            } else if args.flag_password_only {
                println!("{}", entry.password);
            } else {
                println!("{}", entry);
            }
        },
        None => {
            for(i, entry) in entries.iter().enumerate() {
                println!("{}: {}", i, entry);
            }
        }
    }
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    if args.flag_help {
        println!("{}", USAGE);
        process::exit(0);
    }

    if args.flag_version {
        println!("cmdipass-{}", VERSION);
        process::exit(0);
    }

    match config_exists() {
        true => {
            let config = load_config().unwrap();
            let success = keepasshttp::test_associate(&config);
            if !success {
                writeln!(io::stderr(), "Config rejected by keepasshttp. Make sure that the correct database is open, or delete your config file ({}) and re-associate", config_path().to_string_lossy()).unwrap();
                process::exit(1);
            }
            let entries = keepasshttp::get_logins(&config, &args.arg_search_string);
            handle_entries(&entries, &args);
        },
        false => {
            writeln!(io::stderr(), "Config file not found at '{}', generating new key and registering with server", config_path().to_string_lossy()).unwrap();
            write_config_file(&keepasshttp::associate().unwrap());
            writeln!(io::stderr(), "Config file written. Re-run this command").unwrap();
        }
    }
}
<commit_msg>No longer need to rerun the command after successfully registering with KeePassHttp<commit_after>extern crate rustc_serialize;
extern crate docopt;
use docopt::Docopt;

extern crate rand;
use rand::{ Rng, OsRng };

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

use std::{process, env};
use std::io::{self, Read, Write};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::error::Error;

mod keepasshttp;

const VERSION: &'static str = "0.1.0";
const USAGE: &'static str = "
cmdipass

Usage:
  cmdipass get <search-string>
  cmdipass get-one <search-string> <index> [--password-only | --username-only]
  cmdipass --version
  cmdipass (-h | --help)
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_get: bool,
    flag_version: bool,
    flag_help: bool,
    arg_search_string: String,
    arg_index: Option<u8>,
    flag_password_only: bool,
    flag_username_only: bool
}

fn config_path() -> PathBuf {
    env::var("CMDIPASS_CONFIG").map(|e| PathBuf::from(e)).unwrap_or_else(|_| {
        let mut pathbuf = env::home_dir().unwrap_or(PathBuf::from(""));
        pathbuf.push(".cmdipass");
        pathbuf
    })
}

fn config_exists() -> bool {
    config_path().as_path().exists()
}

fn load_config() -> io::Result<keepasshttp::Config> {
    let mut res = File::open(config_path())?;
    let mut buf = String::new();
    res.read_to_string(&mut buf)?;

    let config: keepasshttp::Config = serde_json::from_str(buf.as_str())
        .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, format!("Invalid JSON: {}", e.description())))?;
    Ok(config)
}

fn write_config_file(config: &keepasshttp::Config) {
    let mut file = File::create(config_path()).unwrap();
    file.write_all(serde_json::to_string(&config).unwrap().as_bytes()).unwrap();
}

fn handle_entries(result: &Result<Vec<keepasshttp::Entry>, String>, args: &Args) {
    let entries = result.as_ref().unwrap_or_else(|e| {
        writeln!(io::stderr(), "Error - Server said: '{}'", e);
        process::exit(1);
    });

    match args.arg_index {
        Some(index) => {
            let entry = &entries.get(index as usize).unwrap_or_else(|| {
                writeln!(io::stderr(), "Results did not contain an entry at index {}", index);
                process::exit(1);
            });

            if args.flag_username_only {
                println!("{}", entry.login);
            } else if args.flag_password_only {
                println!("{}", entry.password);
            } else {
                println!("{}", entry);
            }
        },
        None => {
            for(i, entry) in entries.iter().enumerate() {
                println!("{}: {}", i, entry);
            }
        }
    }
}

fn get(args: &Args) {
    let config = load_config().unwrap();
    let success = keepasshttp::test_associate(&config);
    if !success {
        writeln!(io::stderr(), "Config rejected by keepasshttp. Make sure that the correct database is open, or delete your config file ({}) and re-associate", config_path().to_string_lossy()).unwrap();
        process::exit(1);
    }
    let entries = keepasshttp::get_logins(&config, &args.arg_search_string);
    handle_entries(&entries, args);
}

fn main() {
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());

    if args.flag_help {
        println!("{}", USAGE);
        process::exit(0);
    }

    if args.flag_version {
        println!("cmdipass-{}", VERSION);
        process::exit(0);
    }

    match config_exists() {
        true => {
            get(&args);
        },
        false => {
            writeln!(io::stderr(), "Config file not found at '{}', generating new key and registering with server", config_path().to_string_lossy()).unwrap();
            write_config_file(&keepasshttp::associate().unwrap());
            writeln!(io::stderr(), "Config file written.").unwrap();
            get(&args);
        }
    }
}
<|endoftext|>


<commit_before>extern crate ansi_term;
extern crate clap;
extern crate config;
extern crate git2;

pub mod utils;
pub mod modules;

use utils::*;
use modules::*;

fn main() {
    println!("Hello, world!");
}
<commit_msg>feat: arg-parsing with clap and fetch module names<commit_after>extern crate ansi_term;
extern crate clap;
extern crate config;
extern crate git2;

use std::str::FromStr;

use clap::{App, Arg, Shell};
use config::{Config, File, FileFormat};

pub mod utils;
pub mod modules;

use utils::*;
use modules::*;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const APP_NAME: &'static str = "contrail";

fn main() {
    let matches = App::new(APP_NAME)
        .version(VERSION)
        .about("Fast and configurable shell prompter")
        .arg(Arg::with_name("exit_code")
                 .short("e")
                 .long("exit_code")
                 .value_name("CODE")
                 .help("Exit code of the last-executed command")
                 .takes_value(true))
        .arg(Arg::with_name("config")
                 .short("c")
                 .long("config")
                 .value_name("FILE")
                 .help("Location of configuration file")
                 .takes_value(true))
        .arg(Arg::with_name("shell")
                 .long("shell")
                 .value_name("SHELL")
                 .takes_value(true)
                 .possible_values(&["bash", "zsh", "fish", "powershell"]))
        .get_matches();

    let mut c = Config::new();

    if let Some(f) = matches.value_of("config") {
        c.merge(File::new(f, FileFormat::Toml).required(false))
            .expect("Failed to merge in config file!");
    }

    let exit_code = matches
        .value_of("exit_code")
        .unwrap_or("255")
        .parse::<u8>()
        .expect("Exit code passed was not a u8!");

    let shell = if let Some(s) = matches.value_of("shell") {
        // This shouldn't panic, clap will enforce that correct shell
        // types were passed at runtime
        Shell::from_str(s).expect("Invalid shell type passed!")
    } else {
        Shell::Bash
    };

    let module_names: Vec<String> = if let Some(arr) = utils::ref_get_array("global.modules", &c) {
        // into_str() always succeeds, so it's safe to call unwrap()
        arr.into_iter().map(|m| m.into_str().unwrap()).collect()
    } else {
        vec![String::from("cwd"),
             String::from("git"),
             String::from("prompt")]
    };

    for name in &module_names {
        // Format the module
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use std::collections::{HashMap, HashSet};

use rand::random;

// Documents
type DocId = u64;

// Index
struct InvertedIndex {
    // Token --> [ DocId ]
    index: HashMap<String, Vec<DocId>>,

    // DocId --> Body
    docs: HashMap<DocId, String>,
}

impl InvertedIndex {
    fn new() -> InvertedIndex {
        return InvertedIndex {
            index: HashMap::new(),
            docs: HashMap::new(),
        };
    }

    fn tokenize(&self, payload: &str) -> Vec<String> {
        let lower = payload.to_lowercase();
        let tokens: Vec<_> = lower.split_whitespace().collect();
        let mut ret = Vec::new();
        for token in tokens {
            ret.push(token.to_string());
        }
        ret
    }

    fn index(&mut self, payload: String) -> DocId {
        // Generate document id
        let id: DocId = random();

        // Tokenize document
        let tokens = self.tokenize(&payload);

        // Store whole document
        self.docs.insert(id, payload);

        // Index document by tokens
        for token in tokens {
            self.index
                .entry(token)
                .or_insert(vec![])
                .push(id);
        }
        id
    }

    fn search(&self, query: String) -> HashSet<DocId> {
        let mut docs = HashSet::new();
        let tokens = self.tokenize(&query);
        for token in &tokens {
            match self.index.get(token) {
                Some(ids) => {
                    for id in ids {
                        docs.insert(*id);
                    }
                }
                _ => {}
            }
        }
        docs
    }

    fn lookup(&self, doc_id: DocId) -> Option<&String> {
        self.docs.get(&doc_id)
        // match self.docs.get(&doc_id) {
        //     Some(doc) => println!("{}: {}", doc_id, doc),
        //     None => println!("None"),
        // }
    }
}

fn main() {
    // Create and populate documents
    let mut idx = InvertedIndex::new();
    idx.index("The quick brown fox jumps over the lazy dog".to_string());
    idx.index("life, universe, and everything".to_string());
    idx.index("The world is flat".to_string());

    // Search for a term
    let query = "what the";
    let doc_ids = idx.search(query.to_string());

    // Display found documents
    for doc_id in &doc_ids {
        let doc = idx.lookup(*doc_id);
        println!("Query({}) -> DocId({}) -> Document({:?})",
                 query,
                 doc_id,
                 doc);
    }
}
<commit_msg>Ability to remove docs<commit_after>use std::collections::{HashMap, HashSet};

// Documents
type DocId = u64;

// Index
#[derive(Debug)]
struct InvertedIndex {
    // Token --> [ DocId ]
    index: HashMap<String, Vec<DocId>>,

    // DocId --> Body
    docs: HashMap<DocId, String>,

    num_docs: u64,
}

impl InvertedIndex {
    fn new() -> InvertedIndex {
        return InvertedIndex {
            index: HashMap::new(),
            docs: HashMap::new(),
            num_docs: 0,
        };
    }

    fn tokenize(&self, payload: &str) -> Vec<String> {
        let lower = payload.to_lowercase();
        lower.split_whitespace().map(|token| token.to_string()).collect()
    }

    fn index(&mut self, payload: String) -> DocId {
        // Generate document id
        let id: DocId = self.num_docs;
        self.num_docs += 1;

        // Tokenize document
        let tokens = self.tokenize(&payload);

        // Store whole document
        self.docs.insert(id, payload);

        // Index document by tokens
        for token in tokens {
            self.index
                .entry(token)
                .or_insert(Vec::new())
                .push(id);
        }
        id
    }

    fn delete(&mut self, doc_id: DocId) -> () {
        // Remove DocId from each token's posting list
        for (token, mut posting_list) in &mut self.index {
            posting_list.retain(|val| *val != doc_id)
        }

        // Remove document
        self.docs.remove(&doc_id);

        // Decrement num_docs
        self.num_docs -= 1;
    }

    fn search(&self, query: String) -> HashSet<DocId> {
        let mut docs = HashSet::new();
        let tokens = self.tokenize(&query);
        for token in &tokens {
            match self.index.get(token) {
                Some(ids) => {
                    for id in ids {
                        docs.insert(*id);
                    }
                }
                _ => {}
            }
        }
        docs
    }

    fn lookup(&self, doc_id: DocId) -> Option<&String> {
        self.docs.get(&doc_id)
    }
}

fn main() {
    // Create and populate documents
    let mut idx = InvertedIndex::new();
    idx.index("The quick brown fox jumps over the lazy dog".to_string());
    idx.index("life, universe, and everything".to_string());
    idx.index("The world is flat".to_string());

    // Search for a term
    println!("{:?}", idx);
    println!();
    let query = "what the";
    let doc_ids = idx.search(query.to_string());

    // Display found documents
    for doc_id in &doc_ids {
        let doc = idx.lookup(*doc_id);
        println!("Query({}) -> DocId({}) -> Document({:?})",
                 query,
                 doc_id,
                 doc);
    }
    idx.delete(0);
    println!("{:?}", idx);
    println!();

    // Display found documents
    let doc_ids = idx.search(query.to_string());
    for doc_id in &doc_ids {
        let doc = idx.lookup(*doc_id);
        println!("Query({}) -> DocId({}) -> Document({:?})",
                 query,
                 doc_id,
                 doc);
    }
}
<|endoftext|>


<commit_before>extern crate ai;

use ai::agent::Agent;
use ai::environment::Environment;
use ai::environment::CoinFlip;
use ai::random::RandomImpl;
use ai::types::{Action, Observation, Reward};

// Without this, cargo test warns that "main" is unused.
#[cfg_attr(test, allow(dead_code))]
fn main() {
  // Use one RNG to bootstrap the others so that we only have one magic seed constant.
  let mut rand1 = Box::new(RandomImpl::new(5761567));
  let rand2 = Box::new(rand1.new_child());

  // Setup the agent and the environment.
  let mut environment = CoinFlip::new(rand1);
  let mut agent = Agent::new(environment.num_actions(), rand2);

  // Let the agent loose on the environment.
  let n_cycles = 10;
  println!("Starting simulation with {} cycles", n_cycles);
  for cycle in 0..n_cycles {
    let observation = environment.observation();
    let reward = Reward(environment.reward());

    agent.update(observation, reward);
    let action = agent.act();
    assert!(action >= 0 && action < environment.num_actions());

    println!("Cycle: {}, [observation={}, {:?}], took action {}",
        cycle, observation, reward, action);

    environment.update(action);
  }

  // Report results.
  println!("The average reward after {} rounds is {:?}", agent.age(), agent.average_reward());
}
<commit_msg>Remove the last build and run warnings.<commit_after>extern crate ai;

use ai::agent::Agent;
use ai::environment::Environment;
use ai::environment::CoinFlip;
use ai::random::RandomImpl;
use ai::types::Reward;

// Without this, cargo test warns that "main" is unused.
#[cfg_attr(test, allow(dead_code))]
fn main() {
  // Use one RNG to bootstrap the others so that we only have one magic seed constant.
  let mut rand1 = Box::new(RandomImpl::new(5761567));
  let rand2 = Box::new(rand1.new_child());

  // Setup the agent and the environment.
  let mut environment = CoinFlip::new(rand1);
  let mut agent = Agent::new(environment.num_actions(), rand2);

  // Let the agent loose on the environment.
  let n_cycles = 10;
  println!("Starting simulation with {} cycles", n_cycles);
  for cycle in 0..n_cycles {
    let observation = environment.observation();
    let reward = Reward(environment.reward());

    agent.update(observation, reward);
    let action = agent.act();
    assert!(action >= 0 && action < environment.num_actions());

    println!("Cycle: {}, [observation={}, {:?}], took action {}",
        cycle, observation, reward, action);

    environment.update(action);
  }

  // Report results.
  println!("The average reward after {} rounds is {:?}", agent.age(), agent.average_reward());
}
<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
extern crate url;
extern crate rss;
extern crate hyper;
extern crate curl; // https://docs.rs/curl/0.4.6/curl/easy/
extern crate rusqlite;

mod schema;
mod common;

use std::process;
use clap::{App, ArgMatches};
use schema::*;
use url::Url;
use rusqlite::Connection;
use curl::easy::Easy;
use std::path::{Path};
use std::fs::{File, create_dir};
use std::env::home_dir;

fn main() {
    let lock_socket = common::create_app_lock(12345); // https://rosettacode.org/wiki/Category:Rust

    // Clap
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml).get_matches();

    // Init Sqlite database
    let mut database_url = String::new();

    if matches.is_present("database") { // get path from command line
        database_url = matches.value_of("database").unwrap().to_string();
    } else { // or put database file into ~/.config/rusty/rusty.sqlite3
        let db_path = home_dir().expect("/tmp/").into_os_string().into_string().unwrap() + "/.config/rusty";
        if !Path::new(&db_path).exists() { // If path doesn't exist we create it
            create_dir(&db_path).unwrap();
        }
        database_url = db_path + "/rusty.sqlite3";
    }
    let connection = match Connection::open(&database_url) {
        Ok(connection) => connection,
        Err(e) => {
            println!("Error database connection: {} {}", e, database_url);
            process::exit(1)
        }
    };
    init(&connection); // Initialize database

    match matches.subcommand() {
        ("subscribe", Some(sub_matches)) => subscribe(sub_matches, &connection),
        ("unsubscribe", Some(sub_matches)) => unsubscribe(sub_matches, &connection),
        ("list", Some(_)) => list(&connection),
        ("update", Some(sub_matches)) => {
            let feed_id = sub_matches.value_of("id").unwrap_or("0").parse::<i64>().unwrap();
            update(sub_matches, &connection, feed_id);
        },
        ("pending", Some(_)) => pending(&connection),
        ("download", Some(_)) => download(&connection),
        ("download-dir", Some(sub_matches)) => downloaddir(sub_matches, &connection),
        ("", None) => println!("No subcommand was used"),
        _ => println!("No!"),
    }

    common::remove_app_lock(lock_socket);
}

fn init(connection: &Connection) {
    // execute only the first query if there are several queries. So we need to used split()
    let split = include_str!("sql/init.sql").split(";");
    for s in split {
        if s.trim() == "" {
            continue;
        }
        connection.execute(s, &[]).unwrap();
    }
}

fn subscribe(args: &ArgMatches, connection: &Connection) {
    match Url::parse(args.value_of("url").unwrap()) {
        Ok(url) => {
            let subscription = Subscription { id: 0, url: url.as_str().to_string(), label: String::new()};
            connection.execute("insert into subscription (url, label) values (?1, '')", &[&subscription.url]).unwrap();
            println!("Subscribing to: {}", subscription.url);
            update(args, connection, connection.last_insert_rowid());
            println!("Subscribed");
        }
        Err(e) => println!("Could not parse url '{}' {}", args.value_of("url").unwrap(), e),
    }
}

fn unsubscribe(args: &ArgMatches, connection: &Connection) {
    use std::io;
    use std::io::prelude::*;
    let mut stdin = io::stdin();
    let id = args.value_of("id").unwrap();
    let mut buffer = [0;1];

    let mut stmt = connection.prepare("select id, url, label from subscription where id = ?1").unwrap();
    let mut rows = stmt.query_map(&[&id], Subscription::map).unwrap();
    match rows.next() {
        Some(row) => {
            let subscription = row.unwrap();
            println!("Unsubscribing from: {}", subscription.url);
            println!("Sure? [y/N]");
            stdin.read_exact(&mut buffer).unwrap();
            if buffer[0] == 121u8 { // 121 is ascii code for 'y'
                connection.execute("delete from subscription where id = ?1", &[&id]).unwrap();
                println!("Unsubscribed from: {}", subscription.url);
            }
        },
        None => println!("Subscription doesn't exist"),
    }
}

fn list(connection: &Connection) {
    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();

    println!("Subscriptions list:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    No subscription");
    }

    for row in stmt.query_map(&[], Subscription::map).unwrap() {
        let subscription = row.unwrap();
        println!("    {}: {} ({})", subscription.id, subscription.label, subscription.url);
    }
}

fn update(args: &ArgMatches, connection: &Connection, feed_id: i64) {
    use hyper::Client; // https://hyper.rs/hyper/v0.10.9/hyper/index.html
    use std::io::Read; // needed for read_to_string trait
    use std::str::FromStr; // needed for FromStr trait on Channel
    use rss::Channel;

    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();
    let client = Client::new(); // create http client

    let mut as_downloaded = 0;
    if args.is_present("as-downloaded") {
        as_downloaded = 1;
    }

    for row in stmt.query_map(&[], Subscription::map).unwrap() {
        let subscription = row.unwrap();
        println!("Updating {}:", subscription.label);

        if feed_id != subscription.id && feed_id > 0 { // if an id is set, update/populate only this id
            continue;
        }

        let mut res = client.get(subscription.clone()).send().unwrap(); // get query result thanks to IntoUrl trait implement for Subscription
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap(); // extract body from query result

        let channel = Channel::from_str(&body).unwrap(); // parse rss into channel
        connection.execute("update subscription set label = ?1 where id = ?2", &[&channel.title(), &subscription.id]).unwrap(); // update podcast feed name
        for item in channel.items() {
            let url = Url::parse(item.enclosure().unwrap().url()).unwrap();
            let  path_segments = url.path_segments().unwrap();

            // create filename
            let mut filename = String::new();
            for segment in path_segments {
                if segment == "enclosure.mp3" || segment == "listen.mp3" {
                    filename = filename + ".mp3";
                    break;
                }
                filename = segment.to_string();
            }

            let podcast = Podcast { id: 0, subscription_id: subscription.id, url: url.as_str().to_string(), filename: filename};
            let previous_insert_rowid = connection.last_insert_rowid();
            connection.execute("insert or ignore into podcast (subscription_id, url, filename, downloaded) values (?1, ?2, ?3, ?4)", &[&podcast.subscription_id, &podcast.url, &podcast.filename, &as_downloaded]).unwrap();
            if previous_insert_rowid != connection.last_insert_rowid() {
                println!("    New podcast added: {:?}", podcast.filename);
            }

        }
        println!("Updated");
    }
}

fn pending(connection: &Connection) {
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Pending list:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    Nothing to download");
    }
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        println!("    {} ({})", podcast.filename, podcast.url);
    }
}

fn downloaddir(args: &ArgMatches, connection: &Connection) {
    let path = args.value_of("path").unwrap();
    connection.execute("insert or replace into config (key, value) values ('downloaddir', ?1)", &[&path]).unwrap();
    println!("Download dir set to: {}", path);
}

fn download(connection: &Connection) {
    use std::io::Write;
    use std::ops::Mul;
    let mut curl = Easy::new();

    curl.progress(true).unwrap();
    curl.follow_location(true).unwrap();
    curl.progress_function( |a, b, _, _| {
        print!("    Downloading: {}%      \r", (b.mul(100_f64)/a).floor());
        true
    }).unwrap();
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Download pending podcast:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    Nothing to download");
    }
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        let temp = "/tmp/".to_string() + podcast.filename.as_str();
        let path = Path::new(&temp);

        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {}",
                               why),
            Ok(file) => file,
        };
        curl.url(&podcast.url).unwrap();
        curl.write_function( move |data| {
            Ok(file.write(data).unwrap())
        }).unwrap();
        println!("    {}", podcast.filename);
        curl.perform().unwrap();
        connection.execute("update podcast set downloaded = 1, downloaded_at = current_timestamp where id = ?1", &[&podcast.id]).unwrap();
    }
}
<commit_msg>Check if feed has already been subscrided too<commit_after>#[macro_use] extern crate clap;
extern crate url;
extern crate rss;
extern crate hyper;
extern crate curl; // https://docs.rs/curl/0.4.6/curl/easy/
extern crate rusqlite;

mod schema;
mod common;

use std::process;
use clap::{App, ArgMatches};
use schema::*;
use url::Url;
use rusqlite::Connection;
use curl::easy::Easy;
use std::path::{Path};
use std::fs::{File, create_dir};
use std::env::home_dir;

fn main() {
    let lock_socket = common::create_app_lock(12345); // https://rosettacode.org/wiki/Category:Rust

    // Clap
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml).get_matches();

    // Init Sqlite database
    let mut database_url = String::new();

    if matches.is_present("database") { // get path from command line
        database_url = matches.value_of("database").unwrap().to_string();
    } else { // or put database file into ~/.config/rusty/rusty.sqlite3
        let db_path = home_dir().expect("/tmp/").into_os_string().into_string().unwrap() + "/.config/rusty";
        if !Path::new(&db_path).exists() { // If path doesn't exist we create it
            create_dir(&db_path).unwrap();
        }
        database_url = db_path + "/rusty.sqlite3";
    }
    let connection = match Connection::open(&database_url) {
        Ok(connection) => connection,
        Err(e) => {
            println!("Error database connection: {} {}", e, database_url);
            process::exit(1)
        }
    };
    init(&connection); // Initialize database

    match matches.subcommand() {
        ("subscribe", Some(sub_matches)) => subscribe(sub_matches, &connection),
        ("unsubscribe", Some(sub_matches)) => unsubscribe(sub_matches, &connection),
        ("list", Some(_)) => list(&connection),
        ("update", Some(sub_matches)) => {
            let feed_id = sub_matches.value_of("id").unwrap_or("0").parse::<i64>().unwrap();
            update(sub_matches, &connection, feed_id);
        },
        ("pending", Some(_)) => pending(&connection),
        ("download", Some(_)) => download(&connection),
        ("download-dir", Some(sub_matches)) => downloaddir(sub_matches, &connection),
        ("", None) => println!("No subcommand was used"),
        _ => println!("No!"),
    }

    common::remove_app_lock(lock_socket);
}

fn init(connection: &Connection) {
    // execute only the first query if there are several queries. So we need to used split()
    let split = include_str!("sql/init.sql").split(";");
    for s in split {
        if s.trim() == "" {
            continue;
        }
        connection.execute(s, &[]).unwrap();
    }
}

fn subscribe(args: &ArgMatches, connection: &Connection) {
    match Url::parse(args.value_of("url").unwrap()) {
        Ok(url) => {
            let subscription = Subscription { id: 0, url: url.as_str().to_string(), label: String::new()};
            println!("Subscribing to: {}", subscription.url);

            let mut stmt = connection.prepare("select * from subscription where url = ?1").unwrap();
            if !stmt.exists(&[&subscription.url]).unwrap() {
                connection.execute("insert into subscription (url, label) values (?1, '')", &[&subscription.url]).unwrap();
            } else {
                println!("    Feed already subscribed to");
                return
            }
            update(args, connection, connection.last_insert_rowid());
            println!("Subscribed");
        }
        Err(e) => println!("Could not parse url '{}' {}", args.value_of("url").unwrap(), e),
    }
}

fn unsubscribe(args: &ArgMatches, connection: &Connection) {
    use std::io;
    use std::io::prelude::*;
    let mut stdin = io::stdin();
    let id = args.value_of("id").unwrap();
    let mut buffer = [0;1];

    let mut stmt = connection.prepare("select id, url, label from subscription where id = ?1").unwrap();
    let mut rows = stmt.query_map(&[&id], Subscription::map).unwrap();
    match rows.next() {
        Some(row) => {
            let subscription = row.unwrap();
            println!("Unsubscribing from: {}", subscription.url);
            println!("Sure? [y/N]");
            stdin.read_exact(&mut buffer).unwrap();
            if buffer[0] == 121u8 { // 121 is ascii code for 'y'
                connection.execute("delete from subscription where id = ?1", &[&id]).unwrap();
                println!("Unsubscribed from: {}", subscription.url);
            }
        },
        None => println!("Subscription doesn't exist"),
    }
}

fn list(connection: &Connection) {
    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();

    println!("Subscriptions list:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    No subscription");
    }

    for row in stmt.query_map(&[], Subscription::map).unwrap() {
        let subscription = row.unwrap();
        println!("    {}: {} ({})", subscription.id, subscription.label, subscription.url);
    }
}

fn update(args: &ArgMatches, connection: &Connection, feed_id: i64) {
    use hyper::Client; // https://hyper.rs/hyper/v0.10.9/hyper/index.html
    use std::io::Read; // needed for read_to_string trait
    use std::str::FromStr; // needed for FromStr trait on Channel
    use rss::Channel;

    let mut stmt = connection.prepare("select id, url, label from subscription").unwrap();
    let client = Client::new(); // create http client

    let mut as_downloaded = 0;
    if args.is_present("as-downloaded") {
        as_downloaded = 1;
    }

    for row in stmt.query_map(&[], Subscription::map).unwrap() {
        let subscription = row.unwrap();
        println!("Updating {}:", subscription.label);

        if feed_id != subscription.id && feed_id > 0 { // if an id is set, update/populate only this id
            continue;
        }

        let mut res = client.get(subscription.clone()).send().unwrap(); // get query result thanks to IntoUrl trait implement for Subscription
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap(); // extract body from query result

        let channel = Channel::from_str(&body).unwrap(); // parse rss into channel
        connection.execute("update subscription set label = ?1 where id = ?2", &[&channel.title(), &subscription.id]).unwrap(); // update podcast feed name
        for item in channel.items() {
            let url = Url::parse(item.enclosure().unwrap().url()).unwrap();
            let  path_segments = url.path_segments().unwrap();

            // create filename
            let mut filename = String::new();
            for segment in path_segments {
                if segment == "enclosure.mp3" || segment == "listen.mp3" {
                    filename = filename + ".mp3";
                    break;
                }
                filename = segment.to_string();
            }

            let podcast = Podcast { id: 0, subscription_id: subscription.id, url: url.as_str().to_string(), filename: filename};
            let previous_insert_rowid = connection.last_insert_rowid();
            connection.execute("insert or ignore into podcast (subscription_id, url, filename, downloaded) values (?1, ?2, ?3, ?4)", &[&podcast.subscription_id, &podcast.url, &podcast.filename, &as_downloaded]).unwrap();
            if previous_insert_rowid != connection.last_insert_rowid() {
                println!("    New podcast added: {:?}", podcast.filename);
            }

        }
        println!("Updated");
    }
}

fn pending(connection: &Connection) {
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Pending list:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    Nothing to download");
    }
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        println!("    {} ({})", podcast.filename, podcast.url);
    }
}

fn downloaddir(args: &ArgMatches, connection: &Connection) {
    let path = args.value_of("path").unwrap();
    connection.execute("insert or replace into config (key, value) values ('downloaddir', ?1)", &[&path]).unwrap();
    println!("Download dir set to: {}", path);
}

fn download(connection: &Connection) {
    use std::io::Write;
    use std::ops::Mul;
    let mut curl = Easy::new();

    curl.progress(true).unwrap();
    curl.follow_location(true).unwrap();
    curl.progress_function( |a, b, _, _| {
        print!("    Downloading: {}%      \r", (b.mul(100_f64)/a).floor());
        true
    }).unwrap();
    let mut stmt = connection.prepare("select id, subscription_id, url, filename from podcast where downloaded = 0").unwrap();

    println!("Download pending podcast:");
    if !stmt.exists(&[]).unwrap() {
        println!("{}", "    Nothing to download");
    }
    for row in stmt.query_map(&[], Podcast::map).unwrap(){
        let podcast = row.unwrap();
        let temp = "/tmp/".to_string() + podcast.filename.as_str();
        let path = Path::new(&temp);

        let mut file = match File::create(&path) {
            Err(why) => panic!("couldn't create {}",
                               why),
            Ok(file) => file,
        };
        curl.url(&podcast.url).unwrap();
        curl.write_function( move |data| {
            Ok(file.write(data).unwrap())
        }).unwrap();
        println!("    {}", podcast.filename);
        curl.perform().unwrap();
        connection.execute("update podcast set downloaded = 1, downloaded_at = current_timestamp where id = ?1", &[&podcast.id]).unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;
extern crate chrono;

use std::default::Default;

use rustbox::{Color, RustBox, Key};
use rustc_serialize::json;
use rustc_serialize::json::Json;

use chrono::*;

use hkg::utility::cache;
use hkg::model::ListTopicItem;
use hkg::model::ShowItem;

#[derive(PartialEq, Eq, Copy, Clone)]
enum Status {
    List,
    Show,
}

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let title = String::from("高登");
    let s = cache::readfile(String::from("topics.json"));
    let collection: Vec<ListTopicItem> = json::decode(&s).unwrap();

    let t = cache::readfile(String::from("view.json"));
    let item: ShowItem = json::decode(&t).unwrap();

    let mut status = String::from("> ");

    let mut state = Status::List;
    let mut prev_state = state;

    let mut list = hkg::screen::list::List::new(&rustbox);
    let mut show = hkg::screen::show::Show::new(&rustbox);

    loop {

        // show UI
        if prev_state != state {
            hkg::screen::common::clear(&rustbox); // clear screen when switching state
            prev_state = state;
        }

        match state {
            Status::List => {
                list.print(&title, &collection);
            }
            Status::Show => {
                show.print(&title, &item);
            }
        }

        print_status(&rustbox, &status);

        rustbox.present();

        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => {
                        break;
                    }

                    Key::Up => {
                        let w = rustbox.width();
                        status = format_status(status, w, "U");
                        let tmp = list.get_selected_topic();
                        if tmp > 1 {
                            list.select_topic( tmp - 1 );
                        }
                    }
                    Key::Down => {
                        let w = rustbox.width();
                        status = format_status(status, w, "D");
                        let tmp = list.get_selected_topic();
                        if tmp < list.body_height() {
                            list.select_topic( tmp + 1 );
                    Key::Enter => {
                        let w = rustbox.width();
                        status = format_status(status, w, "E");
                        match state {
                            Status::List => {
                                state = Status::Show;
                            }
                            Status::Show => {}
                        }
                    }
                    Key::Backspace => {
                        let w = rustbox.width();
                        status = format_status(status, w, "B");
                        match state {
                            Status::List => {}
                            Status::Show => {
                                state = Status::List;
                            }
                        }
                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_status(rustbox : &rustbox::RustBox, status: &str)
{
    // for status bar only
    let w = rustbox.width();
    let h = rustbox.height();

    let status_width = if w > status.len() {
        w - status.len()
    } else {
        0
    };
    let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

    rustbox.print(0,
                  h - 1,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  &format!("{status}{status_spacing}",
                           status = status,
                           status_spacing = status_spacing));

}

fn format_status(status: String, w: usize, s: &str) -> String
{
    if status.len() >= w {
        String::from(format!("{}{}", &"> ", s))
    } else {
        String::from(format!("{}{}", &status, s))
    }
}

// fn date_operation_example(rustbox: &rustbox::RustBox) {
//     let now = Local::now();
//
//     let dt1 = match Local.datetime_from_str("30/4/2016 9:22", "%d/%m/%Y %H:%M") {
//         Ok(v) => v,
//         Err(e) => Local::now(),
//     };
//
//     let dt2 = now.checked_sub(Duration::seconds(46)).unwrap();
//     let dt3 = now.checked_sub(Duration::minutes(6)).unwrap();
//     let dt4 = now.checked_sub(Duration::days(17)).unwrap();
//     let dt5 = now.checked_sub(Duration::weeks(9)).unwrap();
//
//     rustbox.print(0,
//                   0,
//                   rustbox::RB_BOLD,
//                   Color::White,
//                   Color::Black,
//                   &format!("{} {} {} {}",
//                    duration_format(&(now - dt2)),
//                    duration_format(&(now - dt3)),
//                    duration_format(&(now - dt4)),
//                    duration_format(&(now - dt5))
//               ));
//
// }

// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<commit_msg>[FIX] screen broken after window resize<commit_after>extern crate hkg;
extern crate rustbox;
extern crate rustc_serialize;
extern crate chrono;

use std::default::Default;

use rustbox::{Color, RustBox, Key};
use rustc_serialize::json;
use rustc_serialize::json::Json;

use chrono::*;

use hkg::utility::cache;
use hkg::model::ListTopicItem;
use hkg::model::ShowItem;

#[derive(PartialEq, Eq, Copy, Clone)]
enum Status {
    List,
    Show,
}

fn main() {

    // GUI init
    let rustbox = match RustBox::init(Default::default()) {
        Result::Ok(v) => v,
        Result::Err(e) => panic!("{}", e),
    };

    let title = String::from("高登");
    let s = cache::readfile(String::from("topics.json"));
    let collection: Vec<ListTopicItem> = json::decode(&s).unwrap();

    let t = cache::readfile(String::from("view.json"));
    let item: ShowItem = json::decode(&t).unwrap();

    let mut status = String::from("> ");

    let mut state = Status::List;
    let mut prev_state = state;
    let mut prev_width = rustbox.width();

    let mut list = hkg::screen::list::List::new(&rustbox);
    let mut show = hkg::screen::show::Show::new(&rustbox);

    loop {

        // show UI
        if prev_state != state {
            hkg::screen::common::clear(&rustbox); // clear screen when switching state
            prev_state = state;
        }

        match state {
            Status::List => {
                list.print(&title, &collection);
            }
            Status::Show => {
                show.print(&title, &item);
            }
        }

        print_status(&rustbox, &status);

        rustbox.present();

        match rustbox.poll_event(false) {
            Ok(rustbox::Event::KeyEvent(key)) => {

                if prev_width != rustbox.width() {
                    hkg::screen::common::clear(&rustbox);
                    prev_width = rustbox.width();
                }

                match key {
                    Key::Char('q') => {
                        break;
                    }

                    Key::Up => {
                        let w = rustbox.width();
                        status = format_status(status, w, "U");
                        let tmp = list.get_selected_topic();
                        if tmp > 1 {
                            list.select_topic( tmp - 1 );
                        }
                    }
                    Key::Down => {
                        let w = rustbox.width();
                        status = format_status(status, w, "D");
                        let tmp = list.get_selected_topic();
                        if tmp < list.body_height() {
                            list.select_topic( tmp + 1 );
                    Key::Enter => {
                        let w = rustbox.width();
                        status = format_status(status, w, "E");
                        match state {
                            Status::List => {
                                state = Status::Show;
                            }
                            Status::Show => {}
                        }
                    }
                    Key::Backspace => {
                        let w = rustbox.width();
                        status = format_status(status, w, "B");
                        match state {
                            Status::List => {}
                            Status::Show => {
                                state = Status::List;
                            }
                        }
                    }

                    _ => {}
                }
            }
            Err(e) => panic!("{}", e),
            _ => {}
        }
    }
}

fn print_status(rustbox : &rustbox::RustBox, status: &str)
{
    // for status bar only
    let w = rustbox.width();
    let h = rustbox.height();

    let status_width = if w > status.len() {
        w - status.len()
    } else {
        0
    };
    let status_spacing = (0..status_width).map(|_| " ").collect::<Vec<_>>().join("");

    rustbox.print(0,
                  h - 1,
                  rustbox::RB_BOLD,
                  Color::White,
                  Color::Black,
                  &format!("{status}{status_spacing}",
                           status = status,
                           status_spacing = status_spacing));

}

fn format_status(status: String, w: usize, s: &str) -> String
{
    if status.len() >= w {
        String::from(format!("{}{}", &"> ", s))
    } else {
        String::from(format!("{}{}", &status, s))
    }
}

// fn date_operation_example(rustbox: &rustbox::RustBox) {
//     let now = Local::now();
//
//     let dt1 = match Local.datetime_from_str("30/4/2016 9:22", "%d/%m/%Y %H:%M") {
//         Ok(v) => v,
//         Err(e) => Local::now(),
//     };
//
//     let dt2 = now.checked_sub(Duration::seconds(46)).unwrap();
//     let dt3 = now.checked_sub(Duration::minutes(6)).unwrap();
//     let dt4 = now.checked_sub(Duration::days(17)).unwrap();
//     let dt5 = now.checked_sub(Duration::weeks(9)).unwrap();
//
//     rustbox.print(0,
//                   0,
//                   rustbox::RB_BOLD,
//                   Color::White,
//                   Color::Black,
//                   &format!("{} {} {} {}",
//                    duration_format(&(now - dt2)),
//                    duration_format(&(now - dt3)),
//                    duration_format(&(now - dt4)),
//                    duration_format(&(now - dt5))
//               ));
//
// }

// fn debug_load_and_print_topics() {
//     let s = cache::readfile(String::from("topics.json"));
//     let collection: Vec<TopicItem> = json::decode(&s).unwrap();
//
//     println!("topics {:?}", collection.len());
//     debug_print_topics(collection);
// }
//
// fn debug_print_topics(collection: Vec<TopicItem>) {
//     for (i, item) in collection.iter().enumerate() {
//
//         println!("item[{}]= {title} {author_name} {last_replied_date} {last_replied_time} \
//                   {reply_count} {rating}",
//                  i,
//                  title = item.titles[0].text,
//                  author_name = item.author.name,
//                  last_replied_date = item.last_replied_date,
//                  last_replied_time = item.last_replied_time,
//                  reply_count = item.reply_count,
//                  rating = item.rating);
//     }
// }
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use regex::Regex;
use std::io::{self, BufRead, BufWriter, Write};

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes {
        lines.push(format!("{}{}{}", REMOVE, remove_line, NORMAL));
    }

    for add_line in adds {
        lines.push(format!("{}{}{}", ADD, add_line, NORMAL))
    }

    return lines;
}

/// Joins multiple lines into a single string.
///
/// The first character of each line is skipped, because it is assumed to be
/// either a `+` or a `-` in a diff.
///
/// Between each joined line a `\n` linefeed character is inserted.
#[must_use]
fn join_skip_first(lines: &[String]) -> String {
    let mut joined = String::new();
    for line in lines {
        if !joined.is_empty() {
            joined.push_str("\n")
        }
        joined.push_str(&line[1..]);
    }

    return joined;
}

#[must_use]
fn format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Join inputs by linefeeds into strings
    let adds = join_skip_first(adds);
    let removes = join_skip_first(removes);

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn highlight_diff(input: &mut dyn BufRead, output: &mut dyn io::Write) {
    let mut adds: Vec<String> = Vec::new();
    let mut removes: Vec<String> = Vec::new();
    let mut output = BufWriter::new(output);
    for line in input.lines() {
        let line = line.unwrap();

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                output.write(line.as_bytes()).unwrap();
                output.write(b"\n").unwrap();
            }
            adds.clear();
            removes.clear();

            output.write(fixed_highlight.as_bytes()).unwrap();
            output.write(line.as_bytes()).unwrap();
            output.write(NORMAL.as_bytes()).unwrap();
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push(line);
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push(line);
            continue;
        }

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            output.write(line.as_bytes()).unwrap();
            output.write(b"\n").unwrap();
        }
        adds.clear();
        removes.clear();

        // Print current line
        output.write(line.as_bytes()).unwrap();
        output.write(b"\n").unwrap();
    }
    for line in format_adds_and_removes(&adds, &removes) {
        output.write(line.as_bytes()).unwrap();
        output.write(b"\n").unwrap();
    }
}

fn main() {
    highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_join_skip_first() {
        assert_eq!(
            join_skip_first(&["Xa".to_string(), "Xb".to_string()]),
            "a\nb"
        );
    }

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format_adds_and_removes(&empty, &empty), empty);

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string()], &empty),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string(), "+b".to_string()], &empty),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string()]),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string(), "-b".to_string()]),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&["+[quotes]".to_string()], &["-<quotes>".to_string()]),
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    REMOVE, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }
}
<commit_msg>Test highlighting removed newline at EOF<commit_after>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use regex::Regex;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::str;

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan
const NO_EOF_NEWLINE: &str = "\x1b[2m"; // Faint

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes {
        lines.push(format!("{}{}{}", REMOVE, remove_line, NORMAL));
    }

    for add_line in adds {
        lines.push(format!("{}{}{}", ADD, add_line, NORMAL))
    }

    return lines;
}

/// Joins multiple lines into a single string.
///
/// The first character of each line is skipped, because it is assumed to be
/// either a `+` or a `-` in a diff.
///
/// Between each joined line a `\n` linefeed character is inserted.
#[must_use]
fn join_skip_first(lines: &[String]) -> String {
    let mut joined = String::new();
    for line in lines {
        if !joined.is_empty() {
            joined.push_str("\n")
        }
        joined.push_str(&line[1..]);
    }

    return joined;
}

#[must_use]
fn format_adds_and_removes(adds: &[String], removes: &[String]) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Join inputs by linefeeds into strings
    let adds = join_skip_first(adds);
    let removes = join_skip_first(removes);

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut adds: Vec<String> = Vec::new();
    let mut removes: Vec<String> = Vec::new();
    let input = BufReader::new(input);
    let mut output = BufWriter::new(output);
    for line in input.lines() {
        let line = line.unwrap();

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                output.write(line.as_bytes()).unwrap();
                output.write(b"\n").unwrap();
            }
            adds.clear();
            removes.clear();

            output.write(fixed_highlight.as_bytes()).unwrap();
            output.write(line.as_bytes()).unwrap();
            output.write(NORMAL.as_bytes()).unwrap();
            output.write(b"\n").unwrap();
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push(line);
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push(line);
            continue;
        }

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            output.write(line.as_bytes()).unwrap();
            output.write(b"\n").unwrap();
        }
        adds.clear();
        removes.clear();

        // Print current line
        output.write(line.as_bytes()).unwrap();
        output.write(b"\n").unwrap();
    }
    for line in format_adds_and_removes(&adds, &removes) {
        output.write(line.as_bytes()).unwrap();
        output.write(b"\n").unwrap();
    }
}

fn main() {
    highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_join_skip_first() {
        assert_eq!(
            join_skip_first(&["Xa".to_string(), "Xb".to_string()]),
            "a\nb"
        );
    }

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(simple_format_adds_and_removes(&empty, &empty), empty);

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string()], &empty),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&["+a".to_string(), "+b".to_string()], &empty),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string()]),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&empty, &["-a".to_string(), "-b".to_string()]),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&["+[quotes]".to_string()], &["-<quotes>".to_string()]),
            [
                format!(
                    "{}-{}<{}quotes{}>{}",
                    REMOVE, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    fn remove(text: &str) -> String {
        return format!("{}{}{}", REMOVE, text, NORMAL);
    }

    fn add(text: &str) -> String {
        return format!("{}{}{}", ADD, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            remove(&format!("-hej{}⏎", INVERSE_VIDEO)),
            add("+hej"),
            format!("{}\\ No newline at end of file{}", NO_EOF_NEWLINE, NORMAL)
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }
}
<|endoftext|>


<commit_before>use self::binary::{builtins, InteractiveShell};
use atty::Stream;
use ion_shell::{BuiltinMap, IonError, PipelineError, Shell, Value};
use liner::KeyBindings;
use std::{
    io::{stdin, BufReader},
    process,
};

#[cfg(not(feature = "advanced_arg_parsing"))]
use crate::binary::MAN_ION;
#[cfg(not(feature = "advanced_arg_parsing"))]
use std::env;
#[cfg(feature = "advanced_arg_parsing")]
use std::str::FromStr;
#[cfg(feature = "advanced_arg_parsing")]
use structopt::StructOpt;

mod binary;

struct KeyBindingsWrapper(KeyBindings);

#[cfg(feature = "advanced_arg_parsing")]
impl FromStr for KeyBindingsWrapper {
    type Err = String;

    fn from_str(input: &str) -> Result<Self, Self::Err> {
        match input {
            "vi" => Ok(KeyBindingsWrapper(KeyBindings::Vi)),
            "emacs" => Ok(KeyBindingsWrapper(KeyBindings::Emacs)),
            _ => Err("unknown key bindings".to_string()),
        }
    }
}

/// Ion is a commandline shell created to be a faster and easier to use alternative to the
/// currently available shells. It is not POSIX compliant.
#[cfg_attr(feature = "advanced_arg_parsing", derive(StructOpt))]
#[cfg_attr(
    feature = "advanced_arg_parsing",
    structopt(
        name = "Ion - The Ion Shell",
        author = "",
        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
    )
)]
struct CommandLineArgs {
    /// Shortcut layout. Valid options: "vi", "emacs"
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-o"))]
    key_bindings: Option<KeyBindingsWrapper>,
    /// Print commands before execution
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-x"))]
    print_commands: bool,
    /// Force interactive mode
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-i", long = "--interactive"))]
    interactive: bool,
    /// Do not execute any commands, perform only syntax checking
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-n", long = "--no-execute"))]
    no_execute: bool,
    /// Evaluate given commands instead of reading from the commandline
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-c"))]
    command: Option<String>,
    /// Print the version, platform and revision of Ion then exit
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-v", long = "--version"))]
    version: bool,
    /// Script arguments (@args). If the -c option is not specified,
    /// the first parameter is taken as a filename to execute
    #[cfg_attr(feature = "advanced_arg_parsing", structopt())]
    args: Vec<String>,
}

fn version() -> String { include!(concat!(env!("OUT_DIR"), "/version_string")).to_string() }

#[cfg(feature = "advanced_arg_parsing")]
fn parse_args() -> CommandLineArgs { CommandLineArgs::from_args() }

#[cfg(not(feature = "advanced_arg_parsing"))]
fn parse_args() -> CommandLineArgs {
    let mut args = env::args().skip(1);
    let mut command = None;
    let mut key_bindings = None;
    let mut no_execute = false;
    let mut print_commands = false;
    let mut interactive = false;
    let mut version = false;
    let mut additional_arguments = Vec::new();
    while let Some(arg) = args.next() {
        match arg.as_str() {
            "-o" => {
                key_bindings = match args.next().as_ref().map(|s| s.as_str()) {
                    Some("vi") => Some(KeyBindingsWrapper(KeyBindings::Vi)),
                    Some("emacs") => Some(KeyBindingsWrapper(KeyBindings::Emacs)),
                    Some(_) => {
                        eprintln!("ion: invalid option for option -o");
                        process::exit(1);
                    }
                    None => {
                        eprintln!("ion: no option given for option -o");
                        process::exit(1);
                    }
                }
            }
            "-x" => print_commands = true,
            "-n" | "--no-execute" => no_execute = true,
            "-c" => command = args.next(),
            "-v" | "--version" => version = true,
            "-h" | "--help" => {
                println!("{}", MAN_ION);
                process::exit(0);
            }
            "-i" | "--interactive" => interactive = true,
            _ => {
                additional_arguments.push(arg);
            }
        }
    }
    CommandLineArgs {
        key_bindings,
        print_commands,
        interactive,
        no_execute,
        command,
        version,
        args: additional_arguments,
    }
}

fn main() {
    let command_line_args = parse_args();

    if command_line_args.version {
        println!("{}", version());
        return;
    }

    let mut builtins = BuiltinMap::default();
    builtins
        .with_unsafe()
        .add("exec", &builtins::builtin_exec, "Replace the shell with the given command.")
        .add("exit", &builtins::builtin_exit, "Exits the current session")
        .add("suspend", &builtins::builtin_suspend, "Suspends the shell with a SIGTSTOP signal");

    let stdin_is_a_tty = atty::is(Stream::Stdin);
    let mut shell = Shell::with_builtins(builtins);

    shell.opts_mut().print_comms = command_line_args.print_commands;
    shell.opts_mut().no_exec = command_line_args.no_execute;
    shell.opts_mut().is_background_shell = !stdin_is_a_tty;

    let script_path = command_line_args.args.get(0).cloned();
    shell.variables_mut().set(
        "args",
        Value::Array(
            command_line_args.args.into_iter().map(|arg| Value::Str(arg.into())).collect(),
        ),
    );

    let err = if let Some(command) = command_line_args.command {
        shell.execute_command(command.as_bytes())
    } else if let Some(path) = script_path {
        shell.execute_file(path)
    } else if stdin_is_a_tty || command_line_args.interactive {
        let mut interactive = InteractiveShell::new(shell);
        if let Some(key_bindings) = command_line_args.key_bindings {
            interactive.set_keybindings(key_bindings.0);
        }
        interactive.add_callbacks();
        interactive.execute_interactive();
    } else {
        shell.execute_command(BufReader::new(stdin()))
    };
    if let Err(why) = err {
        eprintln!("ion: {}", why);
        process::exit(
            if let IonError::PipelineExecutionError(PipelineError::Interrupted(_, signal)) = why {
                signal
            } else {
                1
            },
        );
    }
    if let Err(why) = shell.wait_for_background() {
        eprintln!("ion: {}", why);
        process::exit(if let PipelineError::Interrupted(_, signal) = why { signal } else { 1 });
    }
    process::exit(shell.previous_status().as_os_code());
}
<commit_msg>:bug: The set_unique_pid was removed, but it was necessary<commit_after>use self::binary::{builtins, InteractiveShell};
use atty::Stream;
use ion_shell::{BuiltinMap, IonError, PipelineError, Shell, Value};
use liner::KeyBindings;
use std::{
    io::{self, stdin, BufReader},
    process,
};

#[cfg(not(feature = "advanced_arg_parsing"))]
use crate::binary::MAN_ION;
#[cfg(not(feature = "advanced_arg_parsing"))]
use std::env;
#[cfg(feature = "advanced_arg_parsing")]
use std::str::FromStr;
#[cfg(feature = "advanced_arg_parsing")]
use structopt::StructOpt;

mod binary;

struct KeyBindingsWrapper(KeyBindings);

#[cfg(feature = "advanced_arg_parsing")]
impl FromStr for KeyBindingsWrapper {
    type Err = String;

    fn from_str(input: &str) -> Result<Self, Self::Err> {
        match input {
            "vi" => Ok(KeyBindingsWrapper(KeyBindings::Vi)),
            "emacs" => Ok(KeyBindingsWrapper(KeyBindings::Emacs)),
            _ => Err("unknown key bindings".to_string()),
        }
    }
}

/// Ion is a commandline shell created to be a faster and easier to use alternative to the
/// currently available shells. It is not POSIX compliant.
#[cfg_attr(feature = "advanced_arg_parsing", derive(StructOpt))]
#[cfg_attr(
    feature = "advanced_arg_parsing",
    structopt(
        name = "Ion - The Ion Shell",
        author = "",
        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
    )
)]
struct CommandLineArgs {
    /// Shortcut layout. Valid options: "vi", "emacs"
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-o"))]
    key_bindings: Option<KeyBindingsWrapper>,
    /// Print commands before execution
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-x"))]
    print_commands: bool,
    /// Force interactive mode
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-i", long = "--interactive"))]
    interactive: bool,
    /// Do not execute any commands, perform only syntax checking
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-n", long = "--no-execute"))]
    no_execute: bool,
    /// Evaluate given commands instead of reading from the commandline
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-c"))]
    command: Option<String>,
    /// Print the version, platform and revision of Ion then exit
    #[cfg_attr(feature = "advanced_arg_parsing", structopt(short = "-v", long = "--version"))]
    version: bool,
    /// Script arguments (@args). If the -c option is not specified,
    /// the first parameter is taken as a filename to execute
    #[cfg_attr(feature = "advanced_arg_parsing", structopt())]
    args: Vec<String>,
}

fn version() -> String { include!(concat!(env!("OUT_DIR"), "/version_string")).to_string() }

#[cfg(feature = "advanced_arg_parsing")]
fn parse_args() -> CommandLineArgs { CommandLineArgs::from_args() }

#[cfg(not(feature = "advanced_arg_parsing"))]
fn parse_args() -> CommandLineArgs {
    let mut args = env::args().skip(1);
    let mut command = None;
    let mut key_bindings = None;
    let mut no_execute = false;
    let mut print_commands = false;
    let mut interactive = false;
    let mut version = false;
    let mut additional_arguments = Vec::new();
    while let Some(arg) = args.next() {
        match arg.as_str() {
            "-o" => {
                key_bindings = match args.next().as_ref().map(|s| s.as_str()) {
                    Some("vi") => Some(KeyBindingsWrapper(KeyBindings::Vi)),
                    Some("emacs") => Some(KeyBindingsWrapper(KeyBindings::Emacs)),
                    Some(_) => {
                        eprintln!("ion: invalid option for option -o");
                        process::exit(1);
                    }
                    None => {
                        eprintln!("ion: no option given for option -o");
                        process::exit(1);
                    }
                }
            }
            "-x" => print_commands = true,
            "-n" | "--no-execute" => no_execute = true,
            "-c" => command = args.next(),
            "-v" | "--version" => version = true,
            "-h" | "--help" => {
                println!("{}", MAN_ION);
                process::exit(0);
            }
            "-i" | "--interactive" => interactive = true,
            _ => {
                additional_arguments.push(arg);
            }
        }
    }
    CommandLineArgs {
        key_bindings,
        print_commands,
        interactive,
        no_execute,
        command,
        version,
        args: additional_arguments,
    }
}

fn set_unique_pid() -> io::Result<()> {
    unsafe {
        let pgid = libc::getpid();
        libc::setpgid(0, pgid);
        libc::signal(libc::SIGTTOU, libc::SIG_IGN);
        libc::tcsetpgrp(libc::STDIN_FILENO, pgid);
    }
    Ok(())
}

fn main() {
    let command_line_args = parse_args();

    if command_line_args.version {
        println!("{}", version());
        return;
    }

    let mut builtins = BuiltinMap::default();
    builtins
        .with_unsafe()
        .add("exec", &builtins::builtin_exec, "Replace the shell with the given command.")
        .add("exit", &builtins::builtin_exit, "Exits the current session")
        .add("suspend", &builtins::builtin_suspend, "Suspends the shell with a SIGTSTOP signal");

    let stdin_is_a_tty = atty::is(Stream::Stdin);
    let mut shell = Shell::with_builtins(builtins);

    if stdin_is_a_tty {
        if let Err(err) = set_unique_pid() {
            println!("ion: could not bring shell to foreground: {}", err);
        }
    }

    shell.opts_mut().print_comms = command_line_args.print_commands;
    shell.opts_mut().no_exec = command_line_args.no_execute;
    shell.opts_mut().is_background_shell = !stdin_is_a_tty;

    let script_path = command_line_args.args.get(0).cloned();
    shell.variables_mut().set(
        "args",
        Value::Array(
            command_line_args.args.into_iter().map(|arg| Value::Str(arg.into())).collect(),
        ),
    );

    let err = if let Some(command) = command_line_args.command {
        shell.execute_command(command.as_bytes())
    } else if let Some(path) = script_path {
        shell.execute_file(path)
    } else if stdin_is_a_tty || command_line_args.interactive {
        let mut interactive = InteractiveShell::new(shell);
        if let Some(key_bindings) = command_line_args.key_bindings {
            interactive.set_keybindings(key_bindings.0);
        }
        interactive.add_callbacks();
        interactive.execute_interactive();
    } else {
        shell.execute_command(BufReader::new(stdin()))
    };
    if let Err(why) = err {
        eprintln!("ion: {}", why);
        process::exit(
            if let IonError::PipelineExecutionError(PipelineError::Interrupted(_, signal)) = why {
                signal
            } else {
                1
            },
        );
    }
    if let Err(why) = shell.wait_for_background() {
        eprintln!("ion: {}", why);
        process::exit(if let PipelineError::Interrupted(_, signal) = why { signal } else { 1 });
    }
    process::exit(shell.previous_status().as_os_code());
}
<|endoftext|>


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

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

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

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

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

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

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

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

    move_up(&origin.y);

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

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

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

    for _ in 0..19 { // _ instead of x so that rust does not complain about x not being used
        move_up(&origin.y); // moves up 19 times
    }

    assert_eq!(23, origin.y.get());
}
<commit_msg>consistent comment formatting<commit_after>use std::cell::Cell;

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

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

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

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

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

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

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

    move_up(&origin.y);

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

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

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

    for _ in 0..19 { // _ instead of x so that rust does not complain about x not being used
        move_up(&origin.y); // moves up 19 times
    }

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