<commit_before>use std::fs;
use std::env;
use std::fs::File;
use std::io::Read;
use std::hash::Hasher;
use std::collections::HashMap;
use std::os::unix::fs::MetadataExt;
use std::collections::hash_map::DefaultHasher;

#[derive(Debug)]
struct FileEntry {
    inode: u64,
    dev:  u64,
    path : String,
}

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<FileEntry>>) {
    let entries = match fs::read_dir(dir) {
        Ok(x) => x,
        _ => {
            println!("{}: invalid directory", dir);
            return
        },
    };

    for entry in entries {
        if let Ok(entry) = entry {
            let path = entry.path();
            if let Some(path_str) = path.to_str() {
                if let Ok(metadata) = path.symlink_metadata() {
                    let ft = metadata.file_type();
                    if ft.is_symlink() {
                        continue;
                    } else if ft.is_file() {
                        match metadata.len() {
                            0 => continue,
                            i => h.entry(i).or_insert_with(Vec::new)
                                  .push(
                                   FileEntry{ inode : metadata.ino(),
                                       dev   : metadata.dev(),
                                       path  : String::from(path_str),
                                   }),
                        };
                    } else if ft.is_dir() {
                        collect_files(&(String::from(path_str)), h);
                    }
                }
            }
        }
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<FileEntry>>,
                   vec: &Vec<FileEntry> ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(&file_entry.path) {
            let mut buf: [u8; 1024] = [0; 1024];
            if let Ok(nbytes) = file.read(&mut buf) {
                let mut hasher = DefaultHasher::new();
                hasher.write(&buf[..nbytes as usize]);
                let k = hasher.finish();
                duplicates.entry(k).or_insert_with(Vec::new)
                    .push(
                        FileEntry{ inode : file_entry.inode,
                            dev  : file_entry.dev,
                            path : file_entry.path.to_string(),
                        });
            }
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    // Print out usage if no directories are given.
    if args.len() == 1 {
        println!("Usage: {} <dir1> [dir2 [dir3 ...]]", args[0]);
        ::std::process::exit(0);
    }

    let mut hmap: HashMap<u64, Vec<FileEntry> > = HashMap::new();

    for dir in &args[1..] {
        collect_files(dir, &mut hmap);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap.iter() {
        let mut duplicates: HashMap<u64, Vec<FileEntry> > = HashMap::new();
        find_duplicates(&mut duplicates, &val);

        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                println!("{} files in cluster {} (size: {}, digest: {})",
                         vec.len(), cluster, key, hash);
                for file_entry in vec {
                    println!("{}", file_entry.path);
                }
                cluster += 1;
            }
        }
    }
}
<commit_msg>Whitespace changes<commit_after>use std::fs;
use std::env;
use std::fs::File;
use std::io::Read;
use std::hash::Hasher;
use std::collections::HashMap;
use std::os::unix::fs::MetadataExt;
use std::collections::hash_map::DefaultHasher;

#[derive(Debug)]
struct FileEntry {
    inode: u64,
    dev:  u64,
    path : String,
}

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<FileEntry>>) {
    let entries = match fs::read_dir(dir) {
        Ok(x) => x,
        _ => {
            println!("{}: invalid directory", dir);
            return
        },
    };

    for entry in entries {
        if let Ok(entry) = entry {
            let path = entry.path();
            if let Some(path_str) = path.to_str() {
                if let Ok(metadata) = path.symlink_metadata() {
                    let ft = metadata.file_type();
                    if ft.is_symlink() {
                        continue;
                    } else if ft.is_file() {
                        match metadata.len() {
                            0 => continue,
                            i => h.entry(i).or_insert_with(Vec::new)
                                  .push(
                                   FileEntry{
                                       inode : metadata.ino(),
                                       dev   : metadata.dev(),
                                       path  : String::from(path_str),
                                   }),
                        };
                    } else if ft.is_dir() {
                        collect_files(&(String::from(path_str)), h);
                    }
                }
            }
        }
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<FileEntry>>,
                   vec: &Vec<FileEntry> ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(&file_entry.path) {
            let mut buf: [u8; 1024] = [0; 1024];
            if let Ok(nbytes) = file.read(&mut buf) {
                let mut hasher = DefaultHasher::new();
                hasher.write(&buf[..nbytes as usize]);
                let k = hasher.finish();
                duplicates.entry(k).or_insert_with(Vec::new)
                    .push(
                        FileEntry{
                            inode : file_entry.inode,
                            dev   : file_entry.dev,
                            path  : file_entry.path.to_string(),
                        });
            }
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();

    // Print out usage if no directories are given.
    if args.len() == 1 {
        println!("Usage: {} <dir1> [dir2 [dir3 ...]]", args[0]);
        ::std::process::exit(0);
    }

    let mut hmap: HashMap<u64, Vec<FileEntry> > = HashMap::new();

    for dir in &args[1..] {
        collect_files(dir, &mut hmap);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap.iter() {
        let mut duplicates: HashMap<u64, Vec<FileEntry> > = HashMap::new();
        find_duplicates(&mut duplicates, &val);

        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                println!("{} files in cluster {} (size: {}, digest: {})",
                         vec.len(), cluster, key, hash);
                for file_entry in vec {
                    println!("{}", file_entry.path);
                }
                cluster += 1;
            }
        }
    }
}
<|endoftext|>


<commit_before>
extern crate clap;
use clap::{Arg, App};

use std::io::{self, Read};
use std::fs::File;
use std::path::Path;
use std::ffi::OsStr;

use std::collections::HashSet;
use std::collections::HashMap;

extern crate walkdir;
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

// Return a list of #include statements found in the file
fn scan_for_includes(file: &Path) -> Result<Vec<String>, io::Error> {
    let mut f = try!(File::open(file));
    let mut text = String::new();
    try!(f.read_to_string(&mut text));

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"#include [<|"](.*)[>|"]"##).unwrap();
    }

    for cap in RE.captures_iter(&text) {
        match cap.at(1) {
            Some(include_str) => includes.push(include_str.to_owned()),
            None => {}
        }
    }

    Ok(includes)
}

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version("0.1.0")
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("output_format")
//            .help("The output format to produce: PDF, DOT, PNG")
//            .value_name("OUTPUT")
//            .short("o"))
        .arg(Arg::with_name("source")
            .help("The source directory to scan")
            .value_name("PATH")
            .required(true)
            .multiple(false)
            .index(1))
        .get_matches();

    let root_dir_string = match args.value_of("source") {
        Some(path) => path,
        None => panic!("Unable to parse directory argument."),
    };

    println!("Scanning path: {}", root_dir_string);
    let root_dir = Path::new(root_dir_string);

    if !root_dir.exists() {
        println!("Unable to access directory: {}", root_dir.display());
    }

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsStr::new("h"));
    extensions.insert(OsStr::new("cpp"));
    extensions.insert(OsStr::new("hpp"));

    let mut files: HashSet<String> = HashSet::new();

    let walker = WalkDir::new(root_dir).into_iter();
    for entry in walker.filter_entry(|e| !is_hidden(e)) {
        let entry = entry.unwrap(); // What error values could there be?
        let path = entry.path();

        // If the file extension matches our set, queue full path for processing.
        match path.extension() {
            Some(ext) => {
                if extensions.contains(ext) {
                    files.insert(path.to_str().unwrap().to_owned());
                }
            }
            None => {}
        }
    }

    let mut include_table = HashMap::new();

    for path in files {
        let includes_result = scan_for_includes(Path::new(&path));
        // println!("{:?}", includes_result);
        match includes_result {
            Ok(includes) => {
                include_table.insert(path.clone(), includes);
            }
            Err(err) => {
                println!("Unable to process file {}: {}", path, err);
            }
        };
    }

    for (file, includes) in include_table {
        println!("{}", file);
        for inc in includes {
            println!("  {}", inc);
        }
    }
}
<commit_msg>Find absolute path of includes<commit_after>
extern crate clap;
use clap::{Arg, App};

use std::io::{self, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;

use std::collections::HashSet;
use std::collections::HashMap;

extern crate walkdir;
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

#[macro_use]
extern crate lazy_static;
extern crate regex;

use regex::Regex;

#[derive(Debug)]
enum IncludeStatus {
    Relative,     // e.g. <vector>
    Absolute,     // e.g. /usr/include/c++/4.8/vector
    FailedLookup, // Failed to locate an absolute include path.
}

#[derive(Debug)]
struct Include {
    path: PathBuf,
    is_system_include: bool,
    status: IncludeStatus,
}

impl Include {
    fn new(path: &Path, is_sys: bool, status: IncludeStatus) -> Include {
        Include{path: PathBuf::from(path),
            is_system_include: is_sys,
            status: status}
    }

    fn new_relative(name: &str, is_sys: bool) -> Include {
        Include{path: PathBuf::from(name),
            is_system_include: is_sys,
            status: IncludeStatus::Relative}
    }

    fn as_absolute(&self, absolute_path: &Path) -> Include {
        Include{path: PathBuf::from(absolute_path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::Absolute}
    }

    fn as_failed_lookup(&self) -> Include {
        Include{path: PathBuf::from(&self.path),
            is_system_include: self.is_system_include,
            status: IncludeStatus::FailedLookup}
    }
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

// Convert a relative include path (e.g. <vector>) into an absolute path for hashing.
fn find_absolute_include_path(include: &Include, local_search_path: &Path, search_paths: &[PathBuf]) -> Include {

    let local_full_path = local_search_path.join(&include.path);
    if local_full_path.exists() {
        return include.as_absolute(&local_full_path);
    }

    for search_prefix in search_paths {
        let full_path = search_prefix.join(&include.path);
        if full_path.exists() {
            return include.as_absolute(&full_path);
        }
    }
    include.as_failed_lookup()
}

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<Include>, io::Error> {
    let mut f = try!(File::open(file));
    let mut text = String::new();
    try!(f.read_to_string(&mut text));

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"#include ([<|"])(.*)[>|"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    for cap in RE.captures_iter(&text) {
        let inc_symbol = cap.at(1).unwrap_or("<");
        let is_system_include = inc_symbol == "<";
        match cap.at(2) {
            Some(include_name) => includes.push(Include::new_relative(include_name, is_system_include)),
            None => {}
        }
    }

    Ok(includes)
}

fn main() {
    // TODO: accept extra include paths.
    let args = App::new("IncludeGraph-rs")
        .version("0.1.0")
        .about("Generates an include graph from a tree of C++ files")
//        .arg(Arg::with_name("output_format")
//            .help("The output format to produce: PDF, DOT, PNG")
//            .value_name("OUTPUT")
//            .short("o"))
        .arg(Arg::with_name("source")
            .help("The source directory to scan")
            .value_name("PATH")
            .required(true)
            .multiple(false)
            .index(1))
        .get_matches();

    let root_dir_string = match args.value_of("source") {
        Some(path) => path,
        None => panic!("Unable to parse directory argument."),
    };

    println!("Scanning path: {}", root_dir_string);
    let root_dir = Path::new(root_dir_string);

    if !root_dir.exists() {
        println!("Unable to access directory: {}", root_dir.display());
    }

    // Create a list of default system include paths.
    let mut search_paths = Vec::new();
    search_paths.push(PathBuf::from(r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include"));

    // Restrict the file extensions to search.
    let mut extensions = HashSet::new();
    extensions.insert(OsStr::new("h"));
    extensions.insert(OsStr::new("cpp"));
    extensions.insert(OsStr::new("hpp"));

    let mut files: HashSet<String> = HashSet::new();

    // Collect all the files to scan
    let walker = WalkDir::new(root_dir).into_iter();
    for entry in walker.filter_entry(|e| !is_hidden(e)) {
        let entry = entry.unwrap(); // What error values could there be?
        let path = entry.path().canonicalize().unwrap();

        // If the file extension matches our set, queue full path for processing.
        match path.extension() {
            Some(ext) => {
                if extensions.contains(ext) {
                    files.insert(path.to_str().unwrap().to_owned());
                }
            }
            None => {}
        }
    }

    // Maps source_path => Vec<include_path>
    let mut include_table = HashMap::new();

    for path_str in files {
        let path = Path::new(&path_str);
        let includes_result = scan_file_for_includes(path);
        match includes_result {
            Ok(includes) => {
                let local_dir = path.parent().unwrap(); // strip the file name

                // Convert relative includes to absolute includes
                let absolute_includes: Vec<_> = includes.iter()
                    .map(|inc| find_absolute_include_path(inc, local_dir, &search_paths))
                    .collect();

                include_table.insert(PathBuf::from(path), absolute_includes);
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", path, err);
            }
        };
    }

    // Print all the includes
    for (file_path, includes) in include_table {
        println!("{:?}", file_path);
        for inc in includes {
            println!("  {:?}", inc);
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeSet;
use std::io;
use std::process;

const NSQRT: usize = 3;
const N: usize = NSQRT * NSQRT;
const NSQ: usize = N*N;

fn error(msg: String) -> ! {
    println!("error: {}", msg);
    process::exit(1);
}

/// Return the 0-based row index of `cell`.
fn row(cell: usize) -> usize {
    cell / N
}

/// Return the 0-based row index of `cell`.
fn col(cell: usize) -> usize {
    cell % N
}

/// Return the 0-based index of the upper-left cell of `cell`'s group.
fn group(cell: usize) -> usize {
    let r = row(cell);
    let c = col(cell);
    (N * (r - r % NSQRT)) + (c - c % NSQRT)
}

/// Return the neighbors of `cell`:
/// - The cells on the same row;
/// - The cells on the same column;
/// - The same in the same group.
/// Note: `cell` is not a neighbor of itself.
fn neighbors(cell: usize) -> BTreeSet<usize> {
    let mut all_neighbors: BTreeSet<usize> = BTreeSet::new();

    // Neighbors on row
    for col in 0..N {
        all_neighbors.insert((N * row(cell)) + col);
    }

    // Neighbors on column
    for row in 0..N {
        all_neighbors.insert((N * row) + col(cell));
    }

    // Neighbors in group
    let leader = group(cell);
    for r in row(leader) .. row(leader) + NSQRT {
        for c in col(leader) .. col(leader) + NSQRT {
            all_neighbors.insert(N * r + c);
        }
    }

    all_neighbors.remove(&cell);
    return all_neighbors;
}

/// A sudoku board is represented by a vector of bytes.
#[derive(Debug)]
struct SudokuBoard(Vec<BTreeSet<usize>>);

impl SudokuBoard {
    /// Create a new sudoku board from a string.
    /// A non-zero digit stands for itself,
    /// a dot stands for a blank cell,
    /// anything else is an error.
    fn from_str(digits: &str) -> Self {
        if digits.len() != NSQ {
            error(format!("invalid puzzle length; expected {}, got {}",
                          NSQ, digits.len()));
        }
        let mut v = Vec::with_capacity(NSQ);
        for d in digits.chars() {
            match d {
                '.' => {
                    let candidates: BTreeSet<usize> = (1 .. N+1).collect();
                    v.push(candidates);
                }
                '1' ... '9' => {
                    let mut singleton = BTreeSet::new();
                    singleton.insert(d.to_digit(10).unwrap() as usize);
                    v.push(singleton);
                }
                _ => { error(format!("invalid digit ({:?}) in string", d)); }
            }
        }
        SudokuBoard(v)
    }

    fn cell_solved(&self, cell: usize) -> bool {
        self.0[cell].len() == 1
    }

    fn cell_solvable(&self, cell: usize) -> bool {
        self.0[cell].len() != 0
    }

    fn solved(&self) -> bool {
        (0 .. NSQ).all(|cell| self.cell_solved(cell))
    }

    fn solvable(&self) -> bool {
        (0 .. NSQ).all(|cell| self.cell_solvable(cell))
    }

    fn non_candidates(&self, cell: usize) -> BTreeSet<usize> {
        let mut set: BTreeSet<usize> = BTreeSet::new();
        for n in neighbors(cell) {
            if self.cell_solved(n) {
                for x in self.0[n].iter() {
                    set.insert(*x);
                }
            }
        }
        set
    }

    fn propagate(&self) -> Self {
        let mut output = SudokuBoard(self.0.clone());
        loop {
            let mut candidates_changed = false;
            for i in 0 .. NSQ {
                for nc in output.non_candidates(i) {
                    let q = output.0[i].remove(&nc);
                    candidates_changed = q || candidates_changed;
                }
            }
            if !candidates_changed {
                break;
            }
        }
        return output;
    }

    fn most_promising(&self) -> Option<usize> {
        let mut min_len = N;
        let mut min_index = NSQ;

        for i in 0 .. NSQ {
            if self.cell_solved(i) {
                continue;
            }
            let len = self.0[i].len();
            if len < min_len {
                min_index = i;
                min_len = len;
            }
        }

        if min_index == NSQ {
            None
        } else {
            Some(min_index)
        }
    }

    fn solve(&self) -> Option<Self> {
        let mut newboard = self.propagate();

        if newboard.solved() {
            return Some(newboard);
        }

        if !newboard.solvable() {
            return None;
        }

        if let Some(cell) = newboard.most_promising() {
            let cell_candidates = newboard.0[cell].clone();
            for c in cell_candidates.iter() {
                newboard.0[cell].clear();
                newboard.0[cell].insert(*c);
                match newboard.solve() {
                    Some(solved_board) => { return Some(solved_board); }
                    None => { }
                }
            }
        }

        return None;
    }

    fn to_str(&self) -> String {
        let mut output = String::with_capacity(NSQ);
        for i in 0 .. NSQ {
            if self.cell_solved(i) {
                for c in self.0[i].iter() {
                    output.push_str(&format!("{}", c));
                }
            } else {
                output.push('.');
            }
        }
        output
    }
}

fn main() {
    let stdin = io::stdin();
    let mut buf = String::with_capacity(NSQ);

    loop {
        buf.clear();
        match stdin.read_line(&mut buf) {
            Err(e) => { error(format!("I/O error, {:?}", e)); }
            Ok(0) => { return; }
            Ok(_) => { /* pass through */ }
        }
        let sb = SudokuBoard::from_str(&buf.trim());
        match sb.solve() {
            Some(solution) => { println!("{}", solution.to_str()); }
            None => { println!("No solution"); }
        }
    }
}

#[test]
fn test_row_col() {
    assert_eq!(row(11), 1);
    assert_eq!(col(11), 2);
}

#[test]
fn test_group() {
    assert_eq!(group(0), 0);
    assert_eq!(group(1), 0);
    assert_eq!(group(2), 0);
    assert_eq!(group(9), 0);
    assert_eq!(group(10), 0);
    assert_eq!(group(11), 0);
    assert_eq!(group(18), 0);
    assert_eq!(group(19), 0);
    assert_eq!(group(20), 0);
    assert_eq!(group(60), 60);
    assert_eq!(group(61), 60);
    assert_eq!(group(62), 60);
    assert_eq!(group(69), 60);
    assert_eq!(group(70), 60);
    assert_eq!(group(71), 60);
    assert_eq!(group(78), 60);
    assert_eq!(group(79), 60);
    assert_eq!(group(80), 60);
}
<commit_msg>Add doc-comments to methods<commit_after>use std::collections::BTreeSet;
use std::io;
use std::process;

const NSQRT: usize = 3;
const N: usize = NSQRT * NSQRT;
const NSQ: usize = N*N;

fn error(msg: String) -> ! {
    println!("error: {}", msg);
    process::exit(1);
}

/// Return the 0-based row index of `cell`.
fn row(cell: usize) -> usize {
    cell / N
}

/// Return the 0-based row index of `cell`.
fn col(cell: usize) -> usize {
    cell % N
}

/// Return the 0-based index of the upper-left cell of `cell`'s group.
fn group(cell: usize) -> usize {
    let r = row(cell);
    let c = col(cell);
    (N * (r - r % NSQRT)) + (c - c % NSQRT)
}

/// Return the neighbors of `cell`:
/// - The cells on the same row;
/// - The cells on the same column;
/// - The same in the same group.
/// Note: `cell` is not a neighbor of itself.
fn neighbors(cell: usize) -> BTreeSet<usize> {
    let mut all_neighbors: BTreeSet<usize> = BTreeSet::new();

    // Neighbors in row and column
    for i in 0..N {
        all_neighbors.insert((N * row(cell)) + i);
        all_neighbors.insert((N * i) + col(cell));
    }

    // Neighbors in group
    let leader = group(cell);
    for r in row(leader) .. row(leader) + NSQRT {
        for c in col(leader) .. col(leader) + NSQRT {
            all_neighbors.insert(N * r + c);
        }
    }

    all_neighbors.remove(&cell);
    return all_neighbors;
}

/// A sudoku board is represented by a vector of bytes.
#[derive(Debug)]
struct SudokuBoard(Vec<BTreeSet<usize>>);

impl SudokuBoard {
    /// Create a new sudoku board from a string.
    /// A non-zero digit stands for itself,
    /// a dot stands for a blank cell,
    /// anything else is an error.
    fn from_str(digits: &str) -> Self {
        if digits.len() != NSQ {
            error(format!("invalid puzzle length; expected {}, got {}",
                          NSQ, digits.len()));
        }
        let mut v = Vec::with_capacity(NSQ);
        for d in digits.chars() {
            match d {
                '.' => {
                    let candidates: BTreeSet<usize> = (1 .. N+1).collect();
                    v.push(candidates);
                }
                '1' ... '9' => {
                    let mut singleton = BTreeSet::new();
                    singleton.insert(d.to_digit(10).unwrap() as usize);
                    v.push(singleton);
                }
                _ => { error(format!("invalid digit ({:?}) in string", d)); }
            }
        }
        SudokuBoard(v)
    }

    /// A cell is solved if its set of candidates is a singleton.
    fn cell_solved(&self, cell: usize) -> bool {
        self.0[cell].len() == 1
    }

    /// A cell is solvable if it has at least one candidate.
    fn cell_solvable(&self, cell: usize) -> bool {
        self.0[cell].len() != 0
    }

    /// The board is solved if all cells are solved.
    fn solved(&self) -> bool {
        (0 .. NSQ).all(|cell| self.cell_solved(cell))
    }

    /// The board is solvable is all cells are solvable.
    fn solvable(&self) -> bool {
        (0 .. NSQ).all(|cell| self.cell_solvable(cell))
    }

    /// The non-candidates of cells are the solved values in
    /// the cell's neighbors.
    fn non_candidates(&self, cell: usize) -> BTreeSet<usize> {
        let mut set: BTreeSet<usize> = BTreeSet::new();
        for n in neighbors(cell) {
            if self.cell_solved(n) {
                for x in self.0[n].iter() {
                    set.insert(*x);
                }
            }
        }
        set
    }

    /// Remove non-candidates from the cells of the board
    /// until a fixed point is reached, i.e., no more non-
    /// candidates can be removed anymore.
    fn propagate(&self) -> Self {
        let mut output = SudokuBoard(self.0.clone());
        loop {
            let mut candidates_changed = false;
            for i in 0 .. NSQ {
                for nc in output.non_candidates(i) {
                    let q = output.0[i].remove(&nc);
                    candidates_changed = q || candidates_changed;
                }
            }
            if !candidates_changed {
                break;
            }
        }
        return output;
    }

    /// Find the index of the unsolved cell with the
    /// fewest number of candidates.  Helps to speed
    /// up the solving process by making the search tree
    /// narrower.
    fn most_promising(&self) -> Option<usize> {
        let mut min_len = N;
        let mut min_index = NSQ;

        for i in 0 .. NSQ {
            if self.cell_solved(i) {
                continue;
            }
            let len = self.0[i].len();
            if len < min_len {
                min_index = i;
                min_len = len;
            }
        }

        if min_index == NSQ {
            None
        } else {
            Some(min_index)
        }
    }

    /// Solve the Sudoku board:
    /// 1. Propagate the set constraints
    /// 2a. If the board is solved, terminate.
    /// 2b. If the board is unsolvable, backtrack.
    /// 3. Pick the most promising cell and brute-force it.
    fn solve(&self) -> Option<Self> {
        let mut newboard = self.propagate();

        if newboard.solved() {
            return Some(newboard);
        }

        if !newboard.solvable() {
            return None;
        }

        if let Some(cell) = newboard.most_promising() {
            let cell_candidates = newboard.0[cell].clone();
            for c in cell_candidates.iter() {
                newboard.0[cell].clear();
                newboard.0[cell].insert(*c);
                match newboard.solve() {
                    Some(solved_board) => { return Some(solved_board); }
                    None => { }
                }
            }
        }

        return None;
    }

    /// Convert the board to a linear textual representation.
    fn to_str(&self) -> String {
        let mut output = String::with_capacity(NSQ);
        for i in 0 .. NSQ {
            if self.cell_solved(i) {
                for c in self.0[i].iter() {
                    output.push_str(&format!("{}", c));
                }
            } else {
                output.push('.');
            }
        }
        output
    }
}

fn main() {
    let stdin = io::stdin();
    let mut buf = String::with_capacity(NSQ);

    loop {
        buf.clear();
        match stdin.read_line(&mut buf) {
            Err(e) => { error(format!("I/O error, {:?}", e)); }
            Ok(0) => { return; }
            Ok(_) => { /* pass through */ }
        }
        let sb = SudokuBoard::from_str(&buf.trim());
        match sb.solve() {
            Some(solution) => { println!("{}", solution.to_str()); }
            None => { println!("No solution"); }
        }
    }
}

#[test]
fn test_row_col() {
    assert_eq!(row(11), 1);
    assert_eq!(col(11), 2);
}

#[test]
fn test_group() {
    assert_eq!(group(0), 0);
    assert_eq!(group(1), 0);
    assert_eq!(group(2), 0);
    assert_eq!(group(9), 0);
    assert_eq!(group(10), 0);
    assert_eq!(group(11), 0);
    assert_eq!(group(18), 0);
    assert_eq!(group(19), 0);
    assert_eq!(group(20), 0);
    assert_eq!(group(60), 60);
    assert_eq!(group(61), 60);
    assert_eq!(group(62), 60);
    assert_eq!(group(69), 60);
    assert_eq!(group(70), 60);
    assert_eq!(group(71), 60);
    assert_eq!(group(78), 60);
    assert_eq!(group(79), 60);
    assert_eq!(group(80), 60);
}
<|endoftext|>


<commit_before>// Copyright 2016 LambdaStack All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(unused_imports)]

// NOTE: This attribute only needs to be set once.
#![doc(html_logo_url = "https://lambdastackio.github.io/static/images/lambdastack-200x200.png",
       html_favicon_url = "https://lambdastackio.github.io/static/images/favicon.ico",
       html_root_url = "https://lambdastackio.github.io/s3lsio/s3lsio/index.html")]

#[macro_use]
extern crate lsio;
extern crate aws_sdk_rust;
extern crate rustc_serialize;
extern crate term;
extern crate url;
extern crate uuid;
#[macro_use]
extern crate log;
extern crate env_logger;
#[macro_use]
extern crate clap;
extern crate pbr;
extern crate unicase;

use std::io::{self, Write};
use std::env;
use std::path::PathBuf;

use clap::{Shell};
use url::Url;

use aws_sdk_rust::aws::errors::s3::S3Error;
use aws_sdk_rust::aws::s3::endpoint::*;
use aws_sdk_rust::aws::s3::s3client::S3Client;
use aws_sdk_rust::aws::common::region::Region;
use aws_sdk_rust::aws::common::credentials::{AwsCredentialsProvider, DefaultCredentialsProvider};
use aws_sdk_rust::aws::common::request::DispatchSignedRequest;

use common::progress::ProgressBar;

/// Allows you to set the output type for stderr and stdout.
///
#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    JSON,
    PrettyJSON,
    Plain,
    Serialize,
    None,
}

// Error and Output can't have derive(debug) because term does not have some of it's structs
// using fmt::debug etc.

/// Allows you to control Error output.
///
pub struct Error {
    /// Defaults to OutputFormat::serialize since it's easier to debug.
    ///
    /// Available formats are json, plain, serialize or none (don't output anything)
    pub format: OutputFormat,
    /// Can be any term color. Defaults to term::color::RED.
    pub color: term::color::Color,
}

/// Allows you to control non-Error output.
///
pub struct Output {
    /// Defaults to OutputFormat::plain.
    ///
    /// Available formats are json, plain, serialize or none (don't output anything).
    /// If plain is used then you can serialize structures with format! and then pass the output.
    pub format: OutputFormat,
    /// Can be any term color. Defaults to term::color::GREEN.
    pub color: term::color::Color,
}

/// Client structure holds a reference to the S3Client which also implements two traits:
/// AwsCredentialsProvider and DispatchSignedRequest
/// Since S3Client struct is takes those two traits as parameters then ALL functions called
/// that require passing in S3Client or Client must specify the trait signature as follows:
/// Example: fn whatever_function<P: AwsCredentialsProvider, D: DispatchSignedRequest>(client: &mut Client<P,D>)
/// Note: Could also specify 'where' P:... D:... instead.
///
pub struct Client<'a, P: 'a, D: 'a> //, T>
    where P: AwsCredentialsProvider,
          D: DispatchSignedRequest,
          //T: Write,
{
    pub s3client: &'a mut S3Client<P, D>,
    pub error: Error,
    pub output: Output,
    pub is_quiet: bool,
    pub is_default_config: bool,
    //pub pbr: ProgressBar<T>,
}

mod bucket;
mod object;
mod util;
mod common;
mod cli;

fn main() {
    // Gets overridden by cli option
    let mut is_quiet: bool = false;

    env_logger::init().unwrap();

    let version = format!("v{}", crate_version!());
    let mut home: PathBuf;
    // Get $HOME directory and set the default config. Let the CLI override the default.
    match env::home_dir() {
        Some(path) => {
            home = path;
            home.push(".s3lsio/config");
        },
        None => home = PathBuf::new(),
    }

    // NOTE: If the CLI info passed in does not meet the requirements then build_cli will panic!
    let matches = cli::build_cli("s3lsio", home.to_str().unwrap_or(""), &version).get_matches();

    if matches.is_present("generate-bash-completions") {
        cli::build_cli("s3lsio", home.to_str().unwrap_or(""), &version)
            .gen_completions_to("s3lsio", Shell::Bash, &mut io::stdout());
        ::std::process::exit(0);
    }

    // If the -q or --quiet flag was passed then shut off all output
    if matches.is_present("quiet") {
        is_quiet = true;
    }

    // NOTE: Get parameters or config for region, signature etc
    // Safe to unwrap since a default value is passed in. If a panic occurs then the environment
    // does not support a home directory.
    let config = matches.value_of("config").unwrap();
    let region = match matches.value_of("region").unwrap().to_string().to_lowercase().as_ref() {
        "useast1" => Region::UsEast1,
        "uswest1" => Region::UsWest1,
        "uswest2" => Region::UsWest2,
        "cnnorth1" => Region::CnNorth1,
        "eucentral1" => Region::EuCentral1,
        "euwest1" => Region::EuWest1,
        "saeast1" => Region::SaEast1,
        "apnortheast1" => Region::ApNortheast1,
        "apnortheast2" => Region::ApNortheast2,
        "apsouth1" => Region::ApSouth1,
        "apsoutheast1" => Region::ApSoutheast1,
        "apsoutheast2" => Region::ApSoutheast2,
        _ => Region::UsEast1,
    };

    // Option so None will be return if nothing is passed in.
    let ep = matches.value_of("endpoint");
    let proxy = matches.value_of("proxy");
    let output = match matches.value_of("output").unwrap().to_string().to_lowercase().as_ref() {
        "json" => OutputFormat::JSON,
        "pretty-json" => OutputFormat::PrettyJSON,
        "plain" => OutputFormat::Plain,
        "serialize" => OutputFormat::Serialize,
        _ => OutputFormat::PrettyJSON,
    };
    let output_color = match matches.value_of("output-color").unwrap().to_string().to_lowercase().as_ref() {
        "green" => term::color::GREEN,
        "red" => term::color::RED,
        "blue" => term::color::BLUE,
        "yellow" => term::color::YELLOW,
        "white" => term::color::WHITE,
        _ => term::color::GREEN,
    };

    let provider = DefaultCredentialsProvider::new(None).unwrap();

    let endpoint = Endpoint::new(region,
                                 if matches.value_of("signature").unwrap() == "V2" {
                                     Signature::V2
                                 } else {
                                     Signature::V4
                                 },
                                 if ep.is_some() {
                                     Some(Url::parse(ep.unwrap()).unwrap())
                                 } else {
                                     None
                                 },
                                 if proxy.is_some() {
                                     Some(Url::parse(proxy.unwrap()).unwrap())
                                 } else {
                                     None
                                 },
                                 Some(format!("s3lsio - {}", version)));

    let mut s3client = S3Client::new(provider, endpoint);

    let mut client = Client {
        s3client: &mut s3client,
        error: Error {
            format: OutputFormat::Serialize,
            color: term::color::RED,
        },
        output: Output {
            format: output,
            color: output_color,
        },
        is_quiet: is_quiet,
        is_default_config: config == home.to_str().unwrap(),
    };

    // Check which subcomamnd the user ran...
    let res = match matches.subcommand() {
        ("bucket", Some(sub_matches)) => bucket::commands(sub_matches, &mut client),
        ("object", Some(sub_matches)) => object::commands(sub_matches, &mut client),
        (e, _) => {
            println_color_quiet!(client.is_quiet, term::color::RED, "{}", e);
            Err(S3Error::new("A valid instruction is required"))
        },
    };

    if let Err(e) = res {
        println_color_quiet!(client.is_quiet, term::color::RED, "An error occured: {}", e);
        println_color_quiet!(client.is_quiet, term::color::RED, "{}", matches.usage());
        ::std::process::exit(1);
    }
}
<commit_msg>Removed unicase<commit_after>// Copyright 2016 LambdaStack All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![allow(unused_imports)]

// NOTE: This attribute only needs to be set once.
#![doc(html_logo_url = "https://lambdastackio.github.io/static/images/lambdastack-200x200.png",
       html_favicon_url = "https://lambdastackio.github.io/static/images/favicon.ico",
       html_root_url = "https://lambdastackio.github.io/s3lsio/s3lsio/index.html")]

#[macro_use]
extern crate lsio;
extern crate aws_sdk_rust;
extern crate rustc_serialize;
extern crate term;
extern crate url;
extern crate uuid;
#[macro_use]
extern crate log;
extern crate env_logger;
#[macro_use]
extern crate clap;
extern crate pbr;

use std::io::{self, Write};
use std::env;
use std::path::PathBuf;

use clap::{Shell};
use url::Url;

use aws_sdk_rust::aws::errors::s3::S3Error;
use aws_sdk_rust::aws::s3::endpoint::*;
use aws_sdk_rust::aws::s3::s3client::S3Client;
use aws_sdk_rust::aws::common::region::Region;
use aws_sdk_rust::aws::common::credentials::{AwsCredentialsProvider, DefaultCredentialsProvider};
use aws_sdk_rust::aws::common::request::DispatchSignedRequest;

use common::progress::ProgressBar;

/// Allows you to set the output type for stderr and stdout.
///
#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    JSON,
    PrettyJSON,
    Plain,
    Serialize,
    None,
}

// Error and Output can't have derive(debug) because term does not have some of it's structs
// using fmt::debug etc.

/// Allows you to control Error output.
///
pub struct Error {
    /// Defaults to OutputFormat::serialize since it's easier to debug.
    ///
    /// Available formats are json, plain, serialize or none (don't output anything)
    pub format: OutputFormat,
    /// Can be any term color. Defaults to term::color::RED.
    pub color: term::color::Color,
}

/// Allows you to control non-Error output.
///
pub struct Output {
    /// Defaults to OutputFormat::plain.
    ///
    /// Available formats are json, plain, serialize or none (don't output anything).
    /// If plain is used then you can serialize structures with format! and then pass the output.
    pub format: OutputFormat,
    /// Can be any term color. Defaults to term::color::GREEN.
    pub color: term::color::Color,
}

/// Client structure holds a reference to the S3Client which also implements two traits:
/// AwsCredentialsProvider and DispatchSignedRequest
/// Since S3Client struct is takes those two traits as parameters then ALL functions called
/// that require passing in S3Client or Client must specify the trait signature as follows:
/// Example: fn whatever_function<P: AwsCredentialsProvider, D: DispatchSignedRequest>(client: &mut Client<P,D>)
/// Note: Could also specify 'where' P:... D:... instead.
///
pub struct Client<'a, P: 'a, D: 'a> //, T>
    where P: AwsCredentialsProvider,
          D: DispatchSignedRequest,
          //T: Write,
{
    pub s3client: &'a mut S3Client<P, D>,
    pub error: Error,
    pub output: Output,
    pub is_quiet: bool,
    pub is_default_config: bool,
    //pub pbr: ProgressBar<T>,
}

mod bucket;
mod object;
mod util;
mod common;
mod cli;

fn main() {
    // Gets overridden by cli option
    let mut is_quiet: bool = false;

    env_logger::init().unwrap();

    let version = format!("v{}", crate_version!());
    let mut home: PathBuf;
    // Get $HOME directory and set the default config. Let the CLI override the default.
    match env::home_dir() {
        Some(path) => {
            home = path;
            home.push(".s3lsio/config");
        },
        None => home = PathBuf::new(),
    }

    // NOTE: If the CLI info passed in does not meet the requirements then build_cli will panic!
    let matches = cli::build_cli("s3lsio", home.to_str().unwrap_or(""), &version).get_matches();

    if matches.is_present("generate-bash-completions") {
        cli::build_cli("s3lsio", home.to_str().unwrap_or(""), &version)
            .gen_completions_to("s3lsio", Shell::Bash, &mut io::stdout());
        ::std::process::exit(0);
    }

    // If the -q or --quiet flag was passed then shut off all output
    if matches.is_present("quiet") {
        is_quiet = true;
    }

    // NOTE: Get parameters or config for region, signature etc
    // Safe to unwrap since a default value is passed in. If a panic occurs then the environment
    // does not support a home directory.
    let config = matches.value_of("config").unwrap();
    let region = match matches.value_of("region").unwrap().to_string().to_lowercase().as_ref() {
        "useast1" => Region::UsEast1,
        "uswest1" => Region::UsWest1,
        "uswest2" => Region::UsWest2,
        "cnnorth1" => Region::CnNorth1,
        "eucentral1" => Region::EuCentral1,
        "euwest1" => Region::EuWest1,
        "saeast1" => Region::SaEast1,
        "apnortheast1" => Region::ApNortheast1,
        "apnortheast2" => Region::ApNortheast2,
        "apsouth1" => Region::ApSouth1,
        "apsoutheast1" => Region::ApSoutheast1,
        "apsoutheast2" => Region::ApSoutheast2,
        _ => Region::UsEast1,
    };

    // Option so None will be return if nothing is passed in.
    let ep = matches.value_of("endpoint");
    let proxy = matches.value_of("proxy");
    let output = match matches.value_of("output").unwrap().to_string().to_lowercase().as_ref() {
        "json" => OutputFormat::JSON,
        "pretty-json" => OutputFormat::PrettyJSON,
        "plain" => OutputFormat::Plain,
        "serialize" => OutputFormat::Serialize,
        _ => OutputFormat::PrettyJSON,
    };
    let output_color = match matches.value_of("output-color").unwrap().to_string().to_lowercase().as_ref() {
        "green" => term::color::GREEN,
        "red" => term::color::RED,
        "blue" => term::color::BLUE,
        "yellow" => term::color::YELLOW,
        "white" => term::color::WHITE,
        _ => term::color::GREEN,
    };

    let provider = DefaultCredentialsProvider::new(None).unwrap();

    let endpoint = Endpoint::new(region,
                                 if matches.value_of("signature").unwrap() == "V2" {
                                     Signature::V2
                                 } else {
                                     Signature::V4
                                 },
                                 if ep.is_some() {
                                     Some(Url::parse(ep.unwrap()).unwrap())
                                 } else {
                                     None
                                 },
                                 if proxy.is_some() {
                                     Some(Url::parse(proxy.unwrap()).unwrap())
                                 } else {
                                     None
                                 },
                                 Some(format!("s3lsio - {}", version)));

    let mut s3client = S3Client::new(provider, endpoint);

    let mut client = Client {
        s3client: &mut s3client,
        error: Error {
            format: OutputFormat::Serialize,
            color: term::color::RED,
        },
        output: Output {
            format: output,
            color: output_color,
        },
        is_quiet: is_quiet,
        is_default_config: config == home.to_str().unwrap(),
    };

    // Check which subcomamnd the user ran...
    let res = match matches.subcommand() {
        ("bucket", Some(sub_matches)) => bucket::commands(sub_matches, &mut client),
        ("object", Some(sub_matches)) => object::commands(sub_matches, &mut client),
        (e, _) => {
            println_color_quiet!(client.is_quiet, term::color::RED, "{}", e);
            Err(S3Error::new("A valid instruction is required"))
        },
    };

    if let Err(e) = res {
        println_color_quiet!(client.is_quiet, term::color::RED, "An error occured: {}", e);
        println_color_quiet!(client.is_quiet, term::color::RED, "{}", matches.usage());
        ::std::process::exit(1);
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io::{self, BufWriter, ErrorKind, Read};
use std::path::Path;
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::parser::*;
use ppbert::pp::*;
use ppbert::prelude::*;

const PROG_NAME: &str = "ppbert";
const VERSION: &str = env!("CARGO_PKG_VERSION");

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt(
        "m",
        "per-line",
        "print at most NUM basic terms per line",
        "NUM",
    );
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("1", "bert1", "force ppbert to use regular BERT parser");
    opts.optflag("2", "bert2", "force ppbert to use BERT2 parser");
    opts.optflag("d", "disk-log", "force ppbert to use DiskLog parser");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag(
        "t",
        "transform-proplists",
        "convert proplists to JSON objects",
    );
    opts.optflag("b", "as-bert", "print as BERT");
    opts.optflag(
        "",
        "append-period",
        "append a period to terms (useful for loading terms with file:consult/1)",
    );

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let as_bert = matches.opt_present("as-bert");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");
    let append_period = matches.opt_present("append-period");

    let parser_choice: Option<ParserNext> = if matches.opt_present("bert1") {
        Some(BertParser::bert1_next)
    } else if matches.opt_present("bert2") {
        Some(BertParser::bert2_next)
    } else if matches.opt_present("disk-log") {
        Some(BertParser::disk_log_next)
    } else {
        None
    };

    let pp: Box<dyn PrettyPrinter> = if json {
        Box::new(JsonPrettyPrinter::new(transform_proplists))
    } else if as_bert {
        Box::new(BertWriter::new())
    } else {
        let terminator = if append_period { "." } else { "" };
        Box::new(ErlangPrettyPrinter::new(
            indent_width,
            max_per_line,
            terminator,
        ))
    };

    let mut return_code = 0;
    for file in &matches.free {
        if let Err(ref e) = handle_file(file, parse_only, verbose, parser_choice, &pp) {
            if broken_pipe(e) {
                break;
            }
            return_code = 1;
            eprintln!("{}: {}: {}", PROG_NAME, file, e);
        }
    }
    exit(return_code);
}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) => ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false,
    }
}

fn read_bytes(filename: &str) -> Result<Vec<u8>> {
    if filename == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        return Ok(buf);
    } else {
        let buf = fs::read(filename)?;
        return Ok(buf);
    }
}

fn parser_from_ext(filename: &str) -> ParserNext {
    let ext: Option<&str> = Path::new(filename).extension().and_then(|x| x.to_str());
    match ext {
        Some("bert") | Some("bert1") => BertParser::bert1_next,
        Some("bert2") => BertParser::bert2_next,
        Some("log") => BertParser::disk_log_next,
        _ => {
            eprintln!(
                "{}: cannot find an appropriate parser for {}; using BERT",
                PROG_NAME, filename
            );
            BertParser::bert1_next
        }
    }
}

fn handle_file(
    filename: &str,
    parse_only: bool,
    verbose: bool,
    parser_choice: Option<ParserNext>,
    pp: &Box<dyn PrettyPrinter>,
) -> Result<()> {
    // Read file or stdin into buffer
    let now = Instant::now();
    let bytes = read_bytes(filename)?;
    let read_dur = now.elapsed();
    let mut parser = BertParser::new(bytes);

    let parser_next: ParserNext = match parser_choice {
        Some(f) => f,
        None => parser_from_ext(filename),
    };

    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let term = match parser_next(&mut parser) {
            Some(term) => term?,
            None => break,
        };
        parse_dur += now.elapsed();
        if !parse_only {
            let now = Instant::now();
            let stdout = BufWriter::new(io::stdout());
            pp.write(&term, Box::new(stdout))?;
            pp_dur += now.elapsed();
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {:?}", PROG_NAME, filename, read_dur);
        eprintln!("{}: {} parse time: {:?}", PROG_NAME, filename, parse_dur);
        if !parse_only {
            eprintln!("{}: {} print time: {:?}", PROG_NAME, filename, pp_dur);
        }
    }

    return Ok(());
}
<commit_msg>Use env!() to get binary name<commit_after>use std::env;
use std::fs;
use std::io::{self, BufWriter, ErrorKind, Read};
use std::path::Path;
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::parser::*;
use ppbert::pp::*;
use ppbert::prelude::*;

const PROG_NAME: &str = env!("CARGO_BIN_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt(
        "m",
        "per-line",
        "print at most NUM basic terms per line",
        "NUM",
    );
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("1", "bert1", "force ppbert to use regular BERT parser");
    opts.optflag("2", "bert2", "force ppbert to use BERT2 parser");
    opts.optflag("d", "disk-log", "force ppbert to use DiskLog parser");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag(
        "t",
        "transform-proplists",
        "convert proplists to JSON objects",
    );
    opts.optflag("b", "as-bert", "print as BERT");
    opts.optflag(
        "",
        "append-period",
        "append a period to terms (useful for loading terms with file:consult/1)",
    );

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let as_bert = matches.opt_present("as-bert");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");
    let append_period = matches.opt_present("append-period");

    let parser_choice: Option<ParserNext> = if matches.opt_present("bert1") {
        Some(BertParser::bert1_next)
    } else if matches.opt_present("bert2") {
        Some(BertParser::bert2_next)
    } else if matches.opt_present("disk-log") {
        Some(BertParser::disk_log_next)
    } else {
        None
    };

    let pp: Box<dyn PrettyPrinter> = if json {
        Box::new(JsonPrettyPrinter::new(transform_proplists))
    } else if as_bert {
        Box::new(BertWriter::new())
    } else {
        let terminator = if append_period { "." } else { "" };
        Box::new(ErlangPrettyPrinter::new(
            indent_width,
            max_per_line,
            terminator,
        ))
    };

    let mut return_code = 0;
    for file in &matches.free {
        if let Err(ref e) = handle_file(file, parse_only, verbose, parser_choice, &pp) {
            if broken_pipe(e) {
                break;
            }
            return_code = 1;
            eprintln!("{}: {}: {}", PROG_NAME, file, e);
        }
    }
    exit(return_code);
}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) => ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false,
    }
}

fn read_bytes(filename: &str) -> Result<Vec<u8>> {
    if filename == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        return Ok(buf);
    } else {
        let buf = fs::read(filename)?;
        return Ok(buf);
    }
}

fn parser_from_ext(filename: &str) -> ParserNext {
    let ext: Option<&str> = Path::new(filename).extension().and_then(|x| x.to_str());
    match ext {
        Some("bert") | Some("bert1") => BertParser::bert1_next,
        Some("bert2") => BertParser::bert2_next,
        Some("log") => BertParser::disk_log_next,
        _ => {
            eprintln!(
                "{}: cannot find an appropriate parser for {}; using BERT",
                PROG_NAME, filename
            );
            BertParser::bert1_next
        }
    }
}

fn handle_file(
    filename: &str,
    parse_only: bool,
    verbose: bool,
    parser_choice: Option<ParserNext>,
    pp: &Box<dyn PrettyPrinter>,
) -> Result<()> {
    // Read file or stdin into buffer
    let now = Instant::now();
    let bytes = read_bytes(filename)?;
    let read_dur = now.elapsed();
    let mut parser = BertParser::new(bytes);

    let parser_next: ParserNext = match parser_choice {
        Some(f) => f,
        None => parser_from_ext(filename),
    };

    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let term = match parser_next(&mut parser) {
            Some(term) => term?,
            None => break,
        };
        parse_dur += now.elapsed();
        if !parse_only {
            let now = Instant::now();
            let stdout = BufWriter::new(io::stdout());
            pp.write(&term, Box::new(stdout))?;
            pp_dur += now.elapsed();
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {:?}", PROG_NAME, filename, read_dur);
        eprintln!("{}: {} parse time: {:?}", PROG_NAME, filename, parse_dur);
        if !parse_only {
            eprintln!("{}: {} print time: {:?}", PROG_NAME, filename, pp_dur);
        }
    }

    return Ok(());
}
<|endoftext|>


<commit_before>extern crate atty;
extern crate clap;
extern crate rand;

use clap::{App, Arg};
use rand::Rng;
use std::fs::File;
use std::io;
use std::io::BufRead;
use std::io::BufReader;
use std::thread::sleep;
use std::time::Duration;

mod cat;

fn main() {
    let mut filename: String = "".to_string();
    let mut c = parse_cli_args(&mut filename);
    let stdin = io::stdin(); // For lifetime reasons

    if filename == "" {
        for line in stdin.lock().lines() {
            cat::print_with_lolcat(line.unwrap(), &mut c);
            if c.dialup_mode {
                let stall = Duration::from_millis(rand::thread_rng().gen_range(30, 200));
                sleep(stall);
            }
        }
    } else if lolcat_file(&filename, &mut c).is_err() {
        println!("Error opening file {}.", filename)
    }
}

fn lolcat_file(filename: &str, c: &mut cat::Control) -> Result<(), io::Error> {
    let f = File::open(filename)?;
    let file = BufReader::new(&f);
    for line in file.lines() {
        cat::print_with_lolcat(line.unwrap(), c);

        if c.dialup_mode {
            let stall = Duration::from_millis(rand::thread_rng().gen_range(30, 700));
            sleep(stall);
        }
    }
    Ok(())
}

fn parse_cli_args(filename: &mut String) -> cat::Control {
    let matches = App::new("lolcat")
        .version("1.0.2")
        .author("Umang Raghuvanshi <u@umangis.me>")
        .about("The good ol' lolcat, now with fearless concurrency.")
        .arg(
            Arg::with_name("seed")
                .short("s")
                .long("seed")
                .help("A seed for your lolcat. Setting this to 0 randomizes the seed.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("spread")
                .short("S")
                .long("spread")
                .help("How much should we spread dem colors? Defaults to 3.0")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("frequency")
                .short("f")
                .long("frequency")
                .help("Frequency - used in our math. Defaults to 0.1")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("background")
                .short("B")
                .long("bg")
                .help("Background mode - If selected the background will be rainbow. Default false")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("dialup")
                .short("D")
                .long("dialup")
                .help("Dialup mode - Simulate dialup connection")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("filename")
                .short("i")
                .long("input file name")
                .help("Lolcat this file. Reads from STDIN if missing")
                .takes_value(true)
                .index(1),
        )
        .arg(
            Arg::with_name("help")
                .short("h")
                .long("help")
                .help("Prints help information")
                .takes_value(false)
        )
        .arg(
            Arg::with_name("version")
                .short("V")
                .long("version")
                .help("Prints version information")
                .takes_value(false)
        )
        .get_matches();

    if matches.is_present("help") {
        print_rainbow_help(false);
        std::process::exit(0);
    }
    if matches.is_present("version") {
        print_rainbow_help(true);
        std::process::exit(0);
    }

    let seed = matches.value_of("seed").unwrap_or("0.0");
    let spread = matches.value_of("spread").unwrap_or("3.0");
    let frequency = matches.value_of("frequency").unwrap_or("0.1");
    let background = matches.is_present("background");
    let dialup = matches.is_present("dialup");

    *filename = matches.value_of("filename").unwrap_or("").to_string();

    let mut seed: f64 = seed.parse().unwrap();
    let spread: f64 = spread.parse().unwrap();
    let frequency: f64 = frequency.parse().unwrap();

    if seed == 0.0 {
        seed = rand::random::<f64>() * 10e9;
    }

    cat::Control {
        seed,
        spread,
        frequency,
        background_mode: background,
        dialup_mode: dialup,
    }
}

fn print_rainbow_help(only_version: bool) {
    let app = App::new("lolcat")
        .version("1.0.2")
        .author("Umang Raghuvanshi <u@umangis.me>")
        .about("The good ol' lolcat, now with fearless concurrency.")
        .arg(
            Arg::with_name("seed")
                .short("s")
                .long("seed")
                .help("A seed for your lolcat. Setting this to 0 randomizes the seed.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("spread")
                .short("S")
                .long("spread")
                .help("How much should we spread dem colors? Defaults to 3.0")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("frequency")
                .short("f")
                .long("frequency")
                .help("Frequency - used in our math. Defaults to 0.1")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("background")
                .short("B")
                .long("bg")
                .help("Background mode - If selected the background will be rainbow. Default false")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("dialup")
                .short("D")
                .long("dialup")
                .help("Dialup mode - Simulate dialup connection")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("filename")
                .short("i")
                .long("input file name")
                .help("Lolcat this file. Reads from STDIN if missing")
                .takes_value(true)
                .index(1),
        )
        .arg(
            Arg::with_name("help")
                .short("h")
                .long("help")
                .help("Prints help information")
                .takes_value(false)
        )
        .arg(
            Arg::with_name("version")
                .short("V")
                .long("version")
                .help("Prints version information")
                .takes_value(false)
        );

    let mut help = Vec::new();
    if only_version {
        app.write_version(&mut help).unwrap();
    } else {
        app.write_help(&mut help).unwrap();
    }
    let help = String::from_utf8(help).unwrap();

    let mut default_settings = cat::Control {
        seed: rand::random::<f64>() * 10e9,
        spread: 3.0,
        frequency: 0.1,
        background_mode: false,
        dialup_mode: false,
    };

    for line in help.lines() {
        cat::print_with_lolcat(
            line.to_string(),
            &mut default_settings
        );
    }
}
<commit_msg>Remove code duplication<commit_after>extern crate atty;
extern crate clap;
extern crate rand;

use clap::{App, Arg};
use rand::Rng;
use std::fs::File;
use std::io;
use std::io::BufRead;
use std::io::BufReader;
use std::thread::sleep;
use std::time::Duration;

mod cat;

fn main() {
    let mut filename: String = "".to_string();
    let mut c = parse_cli_args(&mut filename);
    let stdin = io::stdin(); // For lifetime reasons

    if filename == "" {
        for line in stdin.lock().lines() {
            cat::print_with_lolcat(line.unwrap(), &mut c);
            if c.dialup_mode {
                let stall = Duration::from_millis(rand::thread_rng().gen_range(30, 200));
                sleep(stall);
            }
        }
    } else if lolcat_file(&filename, &mut c).is_err() {
        println!("Error opening file {}.", filename)
    }
}

fn lolcat_file(filename: &str, c: &mut cat::Control) -> Result<(), io::Error> {
    let f = File::open(filename)?;
    let file = BufReader::new(&f);
    for line in file.lines() {
        cat::print_with_lolcat(line.unwrap(), c);

        if c.dialup_mode {
            let stall = Duration::from_millis(rand::thread_rng().gen_range(30, 700));
            sleep(stall);
        }
    }
    Ok(())
}

fn parse_cli_args(filename: &mut String) -> cat::Control {
    let matches = lolcat_clap_app()
        .get_matches();

    if matches.is_present("help") {
        print_rainbow_help(false);
        std::process::exit(0);
    }
    if matches.is_present("version") {
        print_rainbow_help(true);
        std::process::exit(0);
    }

    let seed = matches.value_of("seed").unwrap_or("0.0");
    let spread = matches.value_of("spread").unwrap_or("3.0");
    let frequency = matches.value_of("frequency").unwrap_or("0.1");
    let background = matches.is_present("background");
    let dialup = matches.is_present("dialup");

    *filename = matches.value_of("filename").unwrap_or("").to_string();

    let mut seed: f64 = seed.parse().unwrap();
    let spread: f64 = spread.parse().unwrap();
    let frequency: f64 = frequency.parse().unwrap();

    if seed == 0.0 {
        seed = rand::random::<f64>() * 10e9;
    }

    cat::Control {
        seed,
        spread,
        frequency,
        background_mode: background,
        dialup_mode: dialup,
    }
}

fn print_rainbow_help(only_version: bool) {
    let app = lolcat_clap_app();

    let mut help = Vec::new();
    if only_version {
        app.write_version(&mut help).unwrap();
    } else {
        app.write_help(&mut help).unwrap();
    }
    let help = String::from_utf8(help).unwrap();

    let mut default_settings = cat::Control {
        seed: rand::random::<f64>() * 10e9,
        spread: 3.0,
        frequency: 0.1,
        background_mode: false,
        dialup_mode: false,
    };

    for line in help.lines() {
        cat::print_with_lolcat(
            line.to_string(),
            &mut default_settings
        );
    }
}

fn lolcat_clap_app() -> App<'static, 'static> {
    App::new("lolcat")
        .version("1.0.2")
        .author("Umang Raghuvanshi <u@umangis.me>")
        .about("The good ol' lolcat, now with fearless concurrency.")
        .arg(
            Arg::with_name("seed")
                .short("s")
                .long("seed")
                .help("A seed for your lolcat. Setting this to 0 randomizes the seed.")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("spread")
                .short("S")
                .long("spread")
                .help("How much should we spread dem colors? Defaults to 3.0")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("frequency")
                .short("f")
                .long("frequency")
                .help("Frequency - used in our math. Defaults to 0.1")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("background")
                .short("B")
                .long("bg")
                .help("Background mode - If selected the background will be rainbow. Default false")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("dialup")
                .short("D")
                .long("dialup")
                .help("Dialup mode - Simulate dialup connection")
                .takes_value(false),
        )
        .arg(
            Arg::with_name("filename")
                .short("i")
                .long("input file name")
                .help("Lolcat this file. Reads from STDIN if missing")
                .takes_value(true)
                .index(1),
        )
        .arg(
            Arg::with_name("help")
                .short("h")
                .long("help")
                .help("Prints help information")
                .takes_value(false)
        )
        .arg(
            Arg::with_name("version")
                .short("V")
                .long("version")
                .help("Prints version information")
                .takes_value(false)
        )
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2016 University of Glasgow
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

extern crate byteorder;
extern crate mio;

mod packets;
mod session;

use session::*;
use mio::*;
use mio::udp::*;
use std::str::FromStr;

const TOKEN : mio::Token = mio::Token(0);

fn main() {
//    let session = Session::<Inactive>::new();
//
//    let active = session.join();
//    let leaving = active.leave();

    let address = "0.0.0.0:2223".parse().unwrap();
    let network = UdpSocket::bind(&address).unwrap();
    network.join_multicast_v4(&std::net::Ipv4Addr::from_str("224.2.2.2").unwrap(),
                              &std::net::Ipv4Addr::from_str("0.0.0.0").unwrap());

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);

    poll.register(&network, TOKEN, Ready::readable(), PollOpt::edge()).unwrap();

    loop {
        poll.poll(&mut events, None).unwrap();

        for event in &events {
            println!("got event");
        }
    }
}

<commit_msg>Check token<commit_after>// Copyright (c) 2014-2016 University of Glasgow
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

extern crate byteorder;
extern crate mio;

mod packets;
mod session;

use session::*;
use mio::*;
use mio::udp::*;
use std::str::FromStr;

const TOKEN : mio::Token = mio::Token(0);

fn main() {
//    let session = Session::<Inactive>::new();
//
//    let active = session.join();
//    let leaving = active.leave();

    let address = "0.0.0.0:2223".parse().unwrap();
    let network = UdpSocket::bind(&address).unwrap();
    network.join_multicast_v4(&std::net::Ipv4Addr::from_str("224.2.2.2").unwrap(),
                              &std::net::Ipv4Addr::from_str("0.0.0.0").unwrap());

    let poll = Poll::new().unwrap();
    let mut events = Events::with_capacity(1024);

    poll.register(&network, TOKEN, Ready::readable(), PollOpt::edge()).unwrap();

    loop {
        poll.poll(&mut events, None).unwrap();

        for event in &events {
            match event.token() {
                TOKEN => {
                    println!("got event");
                }
                _ => panic!("event with unexpected token")
            }
        }
    }
}

<|endoftext|>


<commit_before>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();
  let initial_search = args.initial_search.clone();
  if args.use_first {
    let matches = matching::compute_matches(&choices, initial_search.as_slice());
    println!("{}", matches[0]);
    return;
  } else {
    event_loop(choices, initial_search.as_slice());
  }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
  let mut search = Search::new(&choices, initial_search.to_string());
  let mut screen = Screen::open_screen();

  loop {
    search.recompute_matches();
    draw_screen(&mut screen, &search);

    let chars = screen.get_buffered_keys();
    for char in chars.iter() {
      match *char {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('u') => search.clear_query(),
        Control('c') => return,
        Control('n') => search.down(screen.visible_choices),
        Control('p') => search.up(),
        Enter => {
          screen.move_cursor_to_bottom();
          println!("{}", search.get_selection());
          return;
        }
        _ => panic!("Unexpected input"),
      }
    }
  }
}

struct Search<'a> {
  choices: &'a Vec<String>,
  query: String,
  matches: Vec<&'a String>,
  stale: bool,
  index: usize,
}

impl<'a> Search<'a> {
  fn new(choices: &'a Vec<String>, initial_search: String) -> Search<'a> {
    Search {
      choices: choices,
      query: initial_search,
      matches: Vec::new(),
      stale: true,
      index: 0,
    }
  }

  fn up(&mut self) {
    self.index = if self.index == 0 { 0 } else { self.index - 1 };
  }

  fn down(&mut self, visible_choices: u16) {
    let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
    self.index = min(self.index + 1, limit);
  }

  fn backspace(&mut self) {
    self.query.pop();
    self.stale = true;
    self.index = 0;
  }

  fn append(&mut self, c: char) {
    self.query.push(c);
    self.stale = true;
  }

  fn clear_query(&mut self) {
    self.query.clear();
    self.stale = true;
  }

  fn recompute_matches(&mut self) {
    if self.stale {
      self.matches = matching::compute_matches(self.choices, self.query.as_slice());
      self.stale = false;
    }
  }

  fn get_selection(&mut self) -> &String {
    self.recompute_matches();
    self.matches[self.index]
  }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
  screen.hide_cursor();
  screen.blank_screen();
  screen.write(format!("> {} ({} choices)\n", search.query, search.matches.len()).as_slice());

  print_matches(screen, &search.matches, search.index);

  let start = screen.start_line;
  screen.move_cursor(start, 2 + search.query.len() as u16);
  screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
  let mut i = 1;
  for choice in matches.iter() {
    if i == index + 1 {
      screen.write_inverted(choice.as_slice());
    } else {
      screen.write(choice.as_slice());
    }
    if i >= screen.visible_choices as usize {
      return;
    } else {
      screen.write("\n");
    }
    i += 1;
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  while let Ok(mut s) = stdin.read_line() {
    trim(&mut s);
    lines.push(s);
  }

  lines
}

fn trim(s: &mut String) {
  while let Some(x) = s.pop() {
    if x != '\n' {
      s.push(x);
      return;
    }
  }
}
<commit_msg>Add SearchState enum<commit_after>#![allow(unstable, dead_code)]

extern crate libc;

mod args;
mod ansi;
mod matching;
mod screen;

use std::os;
use std::io;
use std::cmp::min;
use screen::Screen;
use screen::Key::*;
use self::SearchState::*;

fn main() {
  let args = match args::parse_args() {
    Some(args) => args,
    None => {
      os::set_exit_status(1);
      return;
    },
  };

  if args.help { return; }

  let choices = read_choices();
  let initial_search = args.initial_search.clone();
  if args.use_first {
    let matches = matching::compute_matches(&choices, initial_search.as_slice());
    println!("{}", matches[0]);
    return;
  } else {
    event_loop(choices, initial_search.as_slice());
  }
}

fn event_loop(choices: Vec<String>, initial_search: &str) {
  let mut search = Search::new(&choices, initial_search.to_string());
  let mut screen = Screen::open_screen();

  loop {
    search.recompute_matches();

    match search.state {
      InProgress => draw_screen(&mut screen, &search),
      _ => break,
    }

    let chars = screen.get_buffered_keys();
    for char in chars.iter() {
      match *char {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('u') => search.clear_query(),
        Control('c') => search.cancel(),
        Control('g') => search.cancel(),
        Control('n') => search.down(screen.visible_choices),
        Control('p') => search.up(),
        Enter => search.done(),
        _ => {}
      }
    }
  }

  screen.move_cursor_to_bottom();
  println!("{}", search.get_selection());
}

struct Search<'a> {
  choices: &'a Vec<String>,
  query: String,
  matches: Vec<&'a String>,
  stale: bool,
  index: usize,
  state: SearchState,
}

#[derive(PartialEq, Eq)]
enum SearchState {
  InProgress,
  Done,
  Canceled,
}

impl<'a> Search<'a> {
  fn new(choices: &'a Vec<String>, initial_search: String) -> Search<'a> {
    Search {
      choices: choices,
      query: initial_search,
      matches: Vec::new(),
      stale: true,
      index: 0,
      state: InProgress,
    }
  }

  fn up(&mut self) {
    self.index = if self.index == 0 { 0 } else { self.index - 1 };
  }

  fn down(&mut self, visible_choices: u16) {
    let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
    self.index = min(self.index + 1, limit);
  }

  fn backspace(&mut self) {
    self.query.pop();
    self.stale = true;
    self.index = 0;
  }

  fn append(&mut self, c: char) {
    self.query.push(c);
    self.stale = true;
  }

  fn clear_query(&mut self) {
    self.query.clear();
    self.stale = true;
  }

  fn recompute_matches(&mut self) {
    if self.stale {
      self.matches = matching::compute_matches(self.choices, self.query.as_slice());
      self.stale = false;
    }
  }

  fn get_selection(&mut self) -> &str {
    if self.state == Canceled {
      ""
    } else {
      self.recompute_matches();
      self.matches[self.index].as_slice()
    }
  }

  fn cancel(&mut self) {
    self.state = Canceled
  }

  fn done(&mut self) {
    self.state = Done
  }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
  screen.hide_cursor();
  screen.blank_screen();
  screen.write(format!("> {} ({} choices)\n", search.query, search.matches.len()).as_slice());

  print_matches(screen, &search.matches, search.index);

  let start = screen.start_line;
  screen.move_cursor(start, 2 + search.query.len() as u16);
  screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &Vec<&String>, index: usize) {
  let mut i = 1;
  for choice in matches.iter() {
    if i == index + 1 {
      screen.write_inverted(choice.as_slice());
    } else {
      screen.write(choice.as_slice());
    }
    if i >= screen.visible_choices as usize {
      return;
    } else {
      screen.write("\n");
    }
    i += 1;
  }
}

fn read_choices() -> Vec<String> {
  let mut stdin = io::stdio::stdin();
  let mut lines = Vec::new();

  while let Ok(mut s) = stdin.read_line() {
    trim(&mut s);
    lines.push(s);
  }

  lines
}

fn trim(s: &mut String) {
  while let Some(x) = s.pop() {
    if x != '\n' {
      s.push(x);
      return;
    }
  }
}
<|endoftext|>


<commit_before>#![feature(conservative_impl_trait)]

extern crate curl;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;
extern crate tokio_curl;
extern crate threadpool;
#[macro_use]
extern crate clap;
extern crate chrono;
extern crate histogram;

mod args;

use args::{Config, parse_args};

use curl::easy::Easy;
use futures::{Future, stream};
use futures::future::{ok, loop_fn, Loop};
use futures::{Stream};
use tokio_core::reactor::{Core};
use tokio_curl::{PerformError, Session};
use threadpool::ThreadPool;
use chrono::*;
use std::sync::mpsc::channel;
use histogram::*;

struct Worker {
    /// Number of succesful requests this worker has made
    num_requests: usize,
    histogram: Histogram
}

impl Worker {
    pub fn new() -> Self {
        Worker { num_requests: 0, histogram: Histogram::new()}
    }

    fn send_request(mut self, easy_request: Easy, session: &Session) -> impl Future<Item=(Self, Easy), Error=PerformError> {
        session.perform(easy_request)
            .and_then(move |mut easy| {
                self.num_requests += 1;
                let latency = Duration::from_std(easy.total_time().unwrap()).unwrap();
                self.histogram.increment(latency.num_nanoseconds().unwrap() as u64).unwrap();
                ok((self, easy))
            })
    }
}

struct RunInfo {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    pub duration: Duration,
    histogram: Histogram,
}

impl RunInfo {
    pub fn requests_per_second(&self) -> f32 {
        (self.requests_completed as f32) / (self.duration.num_seconds() as f32)
    }
}

/// Delegates and manages all the work.
struct Boss {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    /// Number of active connections
    pub connections: usize,

    thread_pool: ThreadPool,
    num_threads: usize,
}

impl Boss {
    pub fn new(num_threads: usize) -> Self {
        Boss {
            requests_completed: 0,
            connections: 0,
            thread_pool: ThreadPool::new(num_threads),
            num_threads: num_threads,
        }
    }

    pub fn start_workforce(&self, desired_connections: usize, url: String, duration: Duration) -> RunInfo {
        let (tx, rx) = channel();
        for _ in 0..self.num_threads {
            let tx = tx.clone();
            let url = url.clone();
            self.thread_pool.execute(move || {

                let mut lp = Core::new().unwrap();
                let start_time = Local::now();
                let wanted_end_time = start_time + duration;
                let session = Session::new(lp.handle());

                let iterator = (0..desired_connections).map(|_| {
                    let mut easy_request = Easy::new();
                    easy_request.get(true).unwrap();
                    easy_request.url(&url).unwrap();
                    loop_fn((Worker::new(), easy_request), |(worker, easy)| {
                        worker.send_request(easy, &session)
                            .and_then(|state| {
                                let now_time = Local::now();
                                if now_time < wanted_end_time {
                                    Ok(Loop::Continue(state))
                                } else {
                                    Ok(Loop::Break(state))
                                }
                            })
                    })
                });
                let future = stream::futures_unordered(iterator)
                    .fold((0, Histogram::new()), |(request_acc, _), (res, _)|  {
                        let requests = request_acc + res.num_requests;
                        ok((requests, res.histogram))
                    });
                let res = lp.run(future).unwrap();
                tx.send(res).unwrap();
            });
        }
        // collect information from all threads
        let (total_num_requests, histogram): (usize, Histogram) = rx.iter().take(self.num_threads).fold((0, Histogram::new()), |(request_acc, mut histogram_acc), (request, histogram)|  {
                        let requests = request_acc + request;
                        histogram_acc.merge(&histogram);
                        (requests, histogram_acc)
                    });
        RunInfo {requests_completed: total_num_requests, duration: duration, histogram: histogram}
    }
}

fn start(config: Config) -> RunInfo {
    let boss = Boss::new(config.num_threads);
    boss.start_workforce(config.num_connections, config.url, config.duration)
}

fn nanoseconds_to_milliseconds(nanoseconds: u64) -> f64 {
    nanoseconds as f64 / 1_000_000_f64
}

/// Presents the results to the user
fn present(run_info: RunInfo) {
    let requests_completed = run_info.requests_completed;
    println!("{} requests in {}s", requests_completed, run_info.duration.num_seconds());
    println!("Requests per second: {}", run_info.requests_per_second());
    println!("Latency distribution: \n50%: {} ms\n75%: {} ms\n90%: {} ms\n95%: {} ms\n99%: {} ms",
        run_info.histogram.percentile(50.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(75.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(90.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(95.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(99.0).map(nanoseconds_to_milliseconds).unwrap(),
    );
}

fn main() {
    env_logger::init().unwrap();
    let config = parse_args();
    let run_info = start(config);
    present(run_info)
}
<commit_msg>track failed requests<commit_after>#![feature(conservative_impl_trait)]

extern crate curl;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;
extern crate tokio_curl;
extern crate threadpool;
#[macro_use]
extern crate clap;
extern crate chrono;
extern crate histogram;

mod args;

use args::{Config, parse_args};

use curl::easy::Easy;
use futures::{Future, stream};
use futures::future::{ok, loop_fn, Loop};
use futures::{Stream};
use tokio_core::reactor::{Core};
use tokio_curl::{PerformError, Session};
use threadpool::ThreadPool;
use chrono::*;
use std::sync::mpsc::channel;
use histogram::*;

struct Worker {
    /// Number of succesful requests this worker has made
    num_requests: usize,
    histogram: Histogram,
    /// Number of failed requests this worker has made
    num_failed_requests: usize,
}

impl Worker {
    pub fn new() -> Self {
        Worker { num_requests: 0, histogram: Histogram::new(), num_failed_requests: 0}
    }

    fn send_request(mut self, easy_request: Easy, session: &Session) -> impl Future<Item=(Self, Easy), Error=PerformError> {
        session.perform(easy_request)
            .then(move |res| {
                match res {
                    Ok(mut easy) => {
                        (&mut self).num_requests += 1;
                        let latency = Duration::from_std(easy.total_time().unwrap()).unwrap();
                        self.histogram.increment(latency.num_nanoseconds().unwrap() as u64).unwrap();
                        Ok((self, easy))
                    },
                    Err(error) => {
                        (&mut self).num_failed_requests += 1;
                        Err(error)
                    }
                }
            })
    }
}

struct RunInfo {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    pub duration: Duration,
    histogram: Histogram,
    /// Number of failed requests
    num_failed_requests: usize,
}

impl RunInfo {
    pub fn requests_per_second(&self) -> f32 {
        (self.requests_completed as f32) / (self.duration.num_seconds() as f32)
    }
}

/// Delegates and manages all the work.
struct Boss {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    /// Number of active connections
    pub connections: usize,

    thread_pool: ThreadPool,
    num_threads: usize,
}

impl Boss {
    pub fn new(num_threads: usize) -> Self {
        Boss {
            requests_completed: 0,
            connections: 0,
            thread_pool: ThreadPool::new(num_threads),
            num_threads: num_threads,
        }
    }

    pub fn start_workforce(&self, desired_connections: usize, url: String, duration: Duration) -> RunInfo {
        let (tx, rx) = channel();
        for _ in 0..self.num_threads {
            let tx = tx.clone();
            let url = url.clone();
            self.thread_pool.execute(move || {

                let mut lp = Core::new().unwrap();
                let start_time = Local::now();
                let wanted_end_time = start_time + duration;
                let session = Session::new(lp.handle());

                let iterator = (0..desired_connections).map(|_| {
                    let mut easy_request = Easy::new();
                    easy_request.get(true).unwrap();
                    easy_request.url(&url).unwrap();
                    loop_fn((Worker::new(), easy_request), |(worker, easy)| {
                        worker.send_request(easy, &session)
                            .and_then(|state| {
                                let now_time = Local::now();
                                if now_time < wanted_end_time {
                                    Ok(Loop::Continue(state))
                                } else {
                                    Ok(Loop::Break(state))
                                }
                            })
                    })
                });
                let future = stream::futures_unordered(iterator)
                    .fold((0, 0, Histogram::new()), |(request_acc, request_failed_acc, _), (res, _)|  {
                        let requests = request_acc + res.num_requests;
                        let requests_failed = request_failed_acc + res.num_failed_requests;
                        ok((requests, requests_failed, res.histogram))
                    });
                let res = lp.run(future).unwrap();
                tx.send(res).unwrap();
            });
        }
        // collect information from all threads
        let (total_num_requests, requests_failed, histogram): (usize, usize, Histogram) = rx.iter().take(self.num_threads).fold((0, 0, Histogram::new()), |(request_acc, request_failed_acc, mut histogram_acc), (request, request_failed, histogram)|  {
                        let requests = request_acc + request;
                        let requests_failed = request_failed_acc + request_failed;
                        histogram_acc.merge(&histogram);
                        (requests, requests_failed, histogram_acc)
                    });
        RunInfo {requests_completed: total_num_requests, num_failed_requests: requests_failed, duration: duration, histogram: histogram}
    }
}

fn start(config: Config) -> RunInfo {
    let boss = Boss::new(config.num_threads);
    boss.start_workforce(config.num_connections, config.url, config.duration)
}

fn nanoseconds_to_milliseconds(nanoseconds: u64) -> f64 {
    nanoseconds as f64 / 1_000_000_f64
}

/// Presents the results to the user
fn present(run_info: RunInfo) {
    let requests_completed = run_info.requests_completed;
    println!("{} requests in {}s", requests_completed, run_info.duration.num_seconds());
    println!("{} failed requests", run_info.num_failed_requests);
    println!("Requests per second: {}", run_info.requests_per_second());
    println!("Latency distribution: \n50%: {} ms\n75%: {} ms\n90%: {} ms\n95%: {} ms\n99%: {} ms",
        run_info.histogram.percentile(50.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(75.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(90.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(95.0).map(nanoseconds_to_milliseconds).unwrap(),
        run_info.histogram.percentile(99.0).map(nanoseconds_to_milliseconds).unwrap(),
    );
}

fn main() {
    env_logger::init().unwrap();
    let config = parse_args();
    let run_info = start(config);
    present(run_info)
}
<|endoftext|>