<commit_before>#![deny(warnings)]
extern crate coreutils;

use std::io;
use std::io::{Write, BufRead, BufReader, Stderr};
use std::env;
use std::fs::File;
use std::path::Path;
use std::error::Error;
use std::process::exit;

use coreutils::extra::{OptionalExt, WriteExt};

static MAN_PAGE: &'static str = r#"
    NAME
        grep - print lines matching a pattern
    SYNOPSIS
        grep [-h | --help] [-n --line-number] PATTERN [FILE...]
    DESCRIPTION
        grep searches the named input FILEs for lines containing a match to the given PATTERN. If no files are specified, grep searches the standard input. grep prints the matching lines.

    OPTIONS
        -h
        --help
            Print this manual page.

        -n
        --line-number
            Prefix each line of output with the line number of the match.
"#;

#[derive(Copy, Clone)]
struct Flags {
    line_numbers: bool,
}

impl Flags {
    fn new() -> Flags {
        Flags { line_numbers: false }
    }
}

fn main() {
    let args = env::args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    let stdin = io::stdin();
    let stdin = stdin.lock();

    let mut flags = Flags::new();
    let mut pattern = String::new();
    let mut files = Vec::with_capacity(args.len());
    for arg in args {
        if arg.starts_with("-") {
            match arg.as_str() {
                "-h" | "--help" => {
                    stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
                },
                "-n" | "--line-number" => flags.line_numbers = true,
                _ => {
                    stderr.write(b"Unkown option: ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        } else if pattern.is_empty() {
            pattern = arg.clone();
        } else {
            match File::open(&Path::new(&arg)) {
                Ok(f) => files.push(f),
                Err(e) => {
                    stderr.write(b"Error opening ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b": ").try(&mut stderr);
                    stderr.write(e.description().as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        }
    }

    if pattern.is_empty() {
        stderr.write_all(b"You must provide a pattern\n").try(&mut stderr);
        exit(1);
    }

    if files.is_empty() {
        do_simple_search(stdin, &pattern, &mut stdout, &mut stderr, flags);
    } else {
        for f in files {
            do_simple_search(BufReader::new(f), &pattern, &mut stdout, &mut stderr, flags);
        }
    }
}

fn do_simple_search<T: BufRead, O: Write + WriteExt>(reader: T, pattern: &str, out: &mut O, stderr: &mut Stderr, flags: Flags) {
    let mut line_num = 0;
    for result in reader.lines() {
        line_num += 1;
        if let Ok(line) = result {
            if line.contains(pattern) {
                if flags.line_numbers {
                    out.write_all((line_num.to_string() + ": ").as_bytes()).try(stderr);
                }
                out.writeln(line.as_bytes()).try(stderr);
            }
        }
    }
}
<commit_msg>Wrap stdinlock in bufreader<commit_after>#![deny(warnings)]
extern crate coreutils;

use std::io;
use std::io::{Write, BufRead, BufReader, Stderr};
use std::env;
use std::fs::File;
use std::path::Path;
use std::error::Error;
use std::process::exit;

use coreutils::extra::{OptionalExt, WriteExt};

static MAN_PAGE: &'static str = r#"
    NAME
        grep - print lines matching a pattern
    SYNOPSIS
        grep [-h | --help] [-n --line-number] PATTERN [FILE...]
    DESCRIPTION
        grep searches the named input FILEs for lines containing a match to the given PATTERN. If no files are specified, grep searches the standard input. grep prints the matching lines.

    OPTIONS
        -h
        --help
            Print this manual page.

        -n
        --line-number
            Prefix each line of output with the line number of the match.
"#;

#[derive(Copy, Clone)]
struct Flags {
    line_numbers: bool,
}

impl Flags {
    fn new() -> Flags {
        Flags { line_numbers: false }
    }
}

fn main() {
    let args = env::args().skip(1);
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    let stdin = io::stdin();
    let stdin = stdin.lock();

    let mut flags = Flags::new();
    let mut pattern = String::new();
    let mut files = Vec::with_capacity(args.len());
    for arg in args {
        if arg.starts_with("-") {
            match arg.as_str() {
                "-h" | "--help" => {
                    stdout.writeln(MAN_PAGE.as_bytes()).try(&mut stderr);
                },
                "-n" | "--line-number" => flags.line_numbers = true,
                _ => {
                    stderr.write(b"Unkown option: ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        } else if pattern.is_empty() {
            pattern = arg.clone();
        } else {
            match File::open(&Path::new(&arg)) {
                Ok(f) => files.push(f),
                Err(e) => {
                    stderr.write(b"Error opening ").try(&mut stderr);
                    stderr.write(arg.as_bytes()).try(&mut stderr);
                    stderr.write(b": ").try(&mut stderr);
                    stderr.write(e.description().as_bytes()).try(&mut stderr);
                    stderr.write(b"\n").try(&mut stderr);
                    let _ = stderr.flush();
                    exit(1);
                }
            }
        }
    }

    if pattern.is_empty() {
        stderr.write_all(b"You must provide a pattern\n").try(&mut stderr);
        exit(1);
    }

    if files.is_empty() {
        do_simple_search(BufReader::new(stdin), &pattern, &mut stdout, &mut stderr, flags);
    } else {
        for f in files {
            do_simple_search(BufReader::new(f), &pattern, &mut stdout, &mut stderr, flags);
        }
    }
}

fn do_simple_search<T: BufRead, O: Write + WriteExt>(reader: T, pattern: &str, out: &mut O, stderr: &mut Stderr, flags: Flags) {
    let mut line_num = 0;
    for result in reader.lines() {
        line_num += 1;
        if let Ok(line) = result {
            if line.contains(pattern) {
                if flags.line_numbers {
                    out.write_all((line_num.to_string() + ": ").as_bytes()).try(stderr);
                }
                out.writeln(line.as_bytes()).try(stderr);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate extra;

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

static MAN_PAGE: &str = /* @MANSTART{grep} */ r#"
NAME
    grep - print lines matching a pattern

SYNOPSIS
    grep [-h | --help] [-n --line-number] PATTERN [FILE...]

DESCRIPTION
    grep searches the named input FILEs for lines containing a match to the given PATTERN. If no
    files are specified, grep searches the standard input. grep prints the matching lines.

OPTIONS
    -h
    --help
        Print this manual page.

    -v
    --invert-match
        Invert matching.

    -c
    --count
        Print count of matching lines, instead of those lines.

    -n
    --line-number
        Prefix each line of output with the line number of the match.
"#; /* @MANEND */

#[derive(Copy, Clone)]
struct Flags {
    line_numbers: bool,
    invert_match: bool,
    count: bool,
}

impl Flags {
    fn new() -> Flags {
        Flags { line_numbers: false, invert_match: false, count: false }
    }
}

fn main() {
    let args = env::args().skip(1);
    let stdin = io::stdin();
    let stdin = stdin.lock();

    let mut flags = Flags::new();
    let mut pattern = String::new();
    let mut files = Vec::with_capacity(args.len());
    for arg in args {
        if arg.starts_with('-') {
            match arg.as_str() {
                "-h" | "--help" => {
                    print!("{}", MAN_PAGE);
                },
                "-n" | "--line-number" => flags.line_numbers = true,
                "-v" | "--invert-match" => flags.invert_match = true,
                "-c" | "--count" => flags.count = true,
                _ => {
                    eprintln!("Error, unknown option: {}", arg);
                    exit(1);
                }
            }
        } else if pattern.is_empty() {
            pattern = arg.clone();
        } else {
            match File::open(&Path::new(&arg)) {
                Ok(f) => files.push(f),
                Err(err) => {
                    eprintln!("Error opening {}: {}", arg, err);
                    exit(1);
                }
            }
        }
    }

    if pattern.is_empty() {
        eprintln!("You must provide a pattern");
        exit(1);
    }

    if files.is_empty() {
        do_simple_search(BufReader::new(stdin), &pattern, flags);
    } else {
        for f in files {
            do_simple_search(BufReader::new(f), &pattern, flags);
        }
    }
}

fn do_simple_search<T: BufRead>(reader: T, pattern: &str, flags: Flags) {
    let mut count = 0;
    for (line_num, result) in reader.lines().enumerate() {
        if let Ok(line) = result {
            let is_match = if flags.invert_match {
                !line.contains(pattern)
            } else {
                line.contains(pattern)
            };
            if is_match && flags.count {
                count += 1;
            } else if is_match {
                if flags.line_numbers {
                    print!("{}: ", line_num + 1);
                }
                println!("{}", line);
            }
        }
    }

    if flags.count {
        println!("{}", count);
    }
}
<commit_msg>Clarify and simplify match logic and counting<commit_after>extern crate extra;

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

static MAN_PAGE: &str = /* @MANSTART{grep} */ r#"
NAME
    grep - print lines matching a pattern

SYNOPSIS
    grep [-h | --help] [-n --line-number] PATTERN [FILE...]

DESCRIPTION
    grep searches the named input FILEs for lines containing a match to the given PATTERN. If no
    files are specified, grep searches the standard input. grep prints the matching lines.

OPTIONS
    -h
    --help
        Print this manual page.

    -v
    --invert-match
        Invert matching.

    -c
    --count
        Print count of matching lines, instead of those lines.

    -n
    --line-number
        Prefix each line of output with the line number of the match.
"#; /* @MANEND */

#[derive(Copy, Clone)]
struct Flags {
    line_numbers: bool,
    invert_match: bool,
    count: bool,
}

impl Flags {
    fn new() -> Flags {
        Flags { line_numbers: false, invert_match: false, count: false }
    }
}

fn main() {
    let args = env::args().skip(1);
    let stdin = io::stdin();
    let stdin = stdin.lock();

    let mut flags = Flags::new();
    let mut pattern = String::new();
    let mut files = Vec::with_capacity(args.len());
    for arg in args {
        if arg.starts_with('-') {
            match arg.as_str() {
                "-h" | "--help" => {
                    print!("{}", MAN_PAGE);
                },
                "-n" | "--line-number" => flags.line_numbers = true,
                "-v" | "--invert-match" => flags.invert_match = true,
                "-c" | "--count" => flags.count = true,
                _ => {
                    eprintln!("Error, unknown option: {}", arg);
                    exit(1);
                }
            }
        } else if pattern.is_empty() {
            pattern = arg.clone();
        } else {
            match File::open(&Path::new(&arg)) {
                Ok(f) => files.push(f),
                Err(err) => {
                    eprintln!("Error opening {}: {}", arg, err);
                    exit(1);
                }
            }
        }
    }

    if pattern.is_empty() {
        eprintln!("You must provide a pattern");
        exit(1);
    }

    if files.is_empty() {
        do_simple_search(BufReader::new(stdin), &pattern, flags);
    } else {
        for f in files {
            do_simple_search(BufReader::new(f), &pattern, flags);
        }
    }
}

fn do_simple_search<T: BufRead>(reader: T, pattern: &str, flags: Flags) {
    let mut count = 0;
    for (line_num, result) in reader.lines().enumerate() {
        if let Ok(line) = result {
            let mut is_match = line.contains(pattern);
            if flags.invert_match {
                is_match = !is_match
            }
            if is_match {
                count += 1;
                if flags.line_numbers {
                    print!("{}: ", line_num + 1);
                }
                println!("{}", line);
            }
        }
    }

    if flags.count {
        println!("{}", count);
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![feature(custom_derive)]
#![plugin(rocket_codegen)]

extern crate yaml_rust;
extern crate rocket;
extern crate failure;
extern crate rocket_contrib;
extern crate serde;
extern crate r2d2;
extern crate r2d2_diesel;
extern crate ring;
extern crate diesel;
extern crate config;
#[macro_use]
extern crate clap;

extern crate _aardwolf as aardwolf;

use common::{db_conn_str, configure};

use ring::rand::SystemRandom;
use rocket::Rocket;
use rocket_contrib::Template;
use diesel::pg::PgConnection;
use r2d2_diesel::ConnectionManager;
use config::Config;
use clap::App;
use std::path::PathBuf;
use std::env;

mod common;

type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;

fn db_pool(rocket: &Rocket) -> Pool {
    let database_url = rocket.config().get_str("database_url").expect("Must set Database.url in config");
    let manager = ConnectionManager::<PgConnection>::new(database_url);
    r2d2::Pool::builder().build(manager).expect("Could not get DB connection pool")
}

fn app(config: config::Config) -> Rocket {
    let db_url = db_conn_str(&config);

    let c = rocket::Config::build(rocket::config::Environment::Development)
        .address(config.get_str("Web.Listen.address").unwrap())
        .port(config.get::<u16>("Web.Listen.port").unwrap())
        .extra("database_url", db_url.as_str())
        .unwrap();

    let r = rocket::custom(c, true)
        .mount("/api/v1", routes![
            aardwolf::routes::applications::register_application
        ])
        .mount("/", routes![
            aardwolf::routes::auth::sign_up_form,
            aardwolf::routes::auth::sign_in_form,
            aardwolf::routes::auth::sign_up,
            aardwolf::routes::auth::sign_in,
            aardwolf::routes::auth::confirm,
            aardwolf::routes::auth::sign_out,

            aardwolf::routes::app::home,
            aardwolf::routes::app::home_redirect,

            // Adding route to /
            aardwolf::routes::app::index,
        ])
        .attach(Template::fairing())
        .manage(SystemRandom::new());

    // we need an instance of the app to access the config values in Rocket.toml,
    // so we pass it to the db_pool function, get the pool, and _then_ return the instance
    let pool = db_pool(&r);
    r.manage(pool)
}

fn cli<'a, 'b>(yaml: &'a yaml_rust::yaml::Yaml) -> App<'a, 'b> {
    App::from_yaml(yaml)
        .name(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let cli = cli(&yaml);
    let config = configure(cli);

    app(config).launch();
}
<commit_msg>These are no longer used in `src/bin/main.rs`<commit_after>#![feature(plugin)]
#![feature(custom_derive)]
#![plugin(rocket_codegen)]

extern crate yaml_rust;
extern crate rocket;
extern crate failure;
extern crate rocket_contrib;
extern crate serde;
extern crate r2d2;
extern crate r2d2_diesel;
extern crate ring;
extern crate diesel;
extern crate config;
#[macro_use]
extern crate clap;

extern crate _aardwolf as aardwolf;

use common::{db_conn_str, configure};

use ring::rand::SystemRandom;
use rocket::Rocket;
use rocket_contrib::Template;
use diesel::pg::PgConnection;
use r2d2_diesel::ConnectionManager;
use clap::App;

mod common;

type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;

fn db_pool(rocket: &Rocket) -> Pool {
    let database_url = rocket.config().get_str("database_url").expect("Must set Database.url in config");
    let manager = ConnectionManager::<PgConnection>::new(database_url);
    r2d2::Pool::builder().build(manager).expect("Could not get DB connection pool")
}

fn app(config: config::Config) -> Rocket {
    let db_url = db_conn_str(&config);

    let c = rocket::Config::build(rocket::config::Environment::Development)
        .address(config.get_str("Web.Listen.address").unwrap())
        .port(config.get::<u16>("Web.Listen.port").unwrap())
        .extra("database_url", db_url.as_str())
        .unwrap();

    let r = rocket::custom(c, true)
        .mount("/api/v1", routes![
            aardwolf::routes::applications::register_application
        ])
        .mount("/", routes![
            aardwolf::routes::auth::sign_up_form,
            aardwolf::routes::auth::sign_in_form,
            aardwolf::routes::auth::sign_up,
            aardwolf::routes::auth::sign_in,
            aardwolf::routes::auth::confirm,
            aardwolf::routes::auth::sign_out,

            aardwolf::routes::app::home,
            aardwolf::routes::app::home_redirect,

            // Adding route to /
            aardwolf::routes::app::index,
        ])
        .attach(Template::fairing())
        .manage(SystemRandom::new());

    // we need an instance of the app to access the config values in Rocket.toml,
    // so we pass it to the db_pool function, get the pool, and _then_ return the instance
    let pool = db_pool(&r);
    r.manage(pool)
}

fn cli<'a, 'b>(yaml: &'a yaml_rust::yaml::Yaml) -> App<'a, 'b> {
    App::from_yaml(yaml)
        .name(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let cli = cli(&yaml);
    let config = configure(cli);

    app(config).launch();
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate arg_parser;
extern crate extra;
extern crate time;
extern crate redox_users;

use std::{env, fmt, fs};
use std::io::{stdout, stderr, Write};
use std::os::unix::fs::MetadataExt;
use std::process::exit;

use arg_parser::ArgParser;
use extra::option::OptionalExt;
use redox_users::{AllUsers, AllGroups};

use time::Timespec;

const MAN_PAGE: &'static str = /* @MANSTART{stat} */ r#"
NAME
    stat - display file status

SYNOPSIS
    stat [ -h | --help ] FILE...

DESCRIPTION
    Displays file status.

OPTIONS
    --help, -h
        print this message
"#; /* @MANEND */

const TIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S.%f %z";

struct Perms(u32);

impl fmt::Display for Perms {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(0{:o}/", self.0 & 0o777)?;
        let perm = |i, c| {
            if self.0 & ((1 << i) as u32) != 0 {
                c
            } else {
                "-"
            }
        };
        write!(f, "{}{}{}", perm(8, "r"), perm(7, "w"), perm(6, "x"))?;
        write!(f, "{}{}{}", perm(5, "r"), perm(4, "w"), perm(3, "x"))?;
        write!(f, "{}{}{}", perm(2, "r"), perm(1, "w"), perm(0, "x"))?;
        write!(f, ")")?;
        Ok(())
    }
}

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();
    let mut parser = ArgParser::new(1)
        .add_flag(&["h", "help"]);
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        return;
    }
    let (all_users, all_groups) = match (AllUsers::new(), AllGroups::new()) {
        (Ok(all_users), Ok(all_groups)) => (all_users, all_groups),
        (Err(_), Ok(_)) => {
            eprintln!("Unable to access password file");
            exit(1);
        },
        (Ok(_), Err(_)) => {
            eprintln!("Unable to access group file");
            exit(1);
        }
        _ => {
            eprintln!("Unable to access password and group file");
            exit(1);
        }
    };

    for path in &parser.args[0..] {
        let meta = fs::symlink_metadata(path).unwrap();
        let file_type = if meta.file_type().is_symlink() {
            "symbolic link"
        } else if meta.is_file() {
            "regular file"
        } else if meta.is_dir() {
            "directory"
        } else {
            ""
        };
        if meta.file_type().is_symlink() {
            println!("File: {} -> {}", path, fs::read_link(path).unwrap().display());
        } else {
            println!("File: {}", path);
        }
        let username = all_users.get_by_id(meta.uid() as usize)
            .map(|x| x.user.to_string())
            .unwrap_or("UNKNOWN".to_string());
        let groupname = all_groups.get_by_id(meta.gid() as usize)
            .map(|x| x.group.to_string())
            .unwrap_or("UNKNOWN".to_string());
        println!("Size: {}  Blocks: {}  IO Block: {} {}", meta.size(), meta.blocks(), meta.blksize(), file_type);
        println!("Device: {}  Inode: {}  Links: {}", meta.dev(), meta.ino(), meta.nlink());
        println!("Access: {}  Uid: ({}/{})  Gid: ({}/{})", Perms(meta.mode()),
                 meta.uid(), username, meta.gid(), groupname);
        println!("Access: {}", time::at(Timespec::new(meta.atime(), meta.atime_nsec() as i32)).strftime(TIME_FMT).unwrap());
        println!("Modify: {}", time::at(Timespec::new(meta.mtime(), meta.mtime_nsec() as i32)).strftime(TIME_FMT).unwrap());
        println!("Change: {}", time::at(Timespec::new(meta.ctime(), meta.ctime_nsec() as i32)).strftime(TIME_FMT).unwrap());
    }
}
<commit_msg>Fix for new redox_users<commit_after>#![deny(warnings)]

extern crate arg_parser;
extern crate extra;
extern crate time;
extern crate redox_users;

use std::{env, fmt, fs};
use std::io::{stdout, stderr, Write};
use std::os::unix::fs::MetadataExt;
use std::process::exit;

use arg_parser::ArgParser;
use extra::option::OptionalExt;
use redox_users::{AllUsers, AllGroups};

use time::Timespec;

const MAN_PAGE: &'static str = /* @MANSTART{stat} */ r#"
NAME
    stat - display file status

SYNOPSIS
    stat [ -h | --help ] FILE...

DESCRIPTION
    Displays file status.

OPTIONS
    --help, -h
        print this message
"#; /* @MANEND */

const TIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S.%f %z";

struct Perms(u32);

impl fmt::Display for Perms {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(0{:o}/", self.0 & 0o777)?;
        let perm = |i, c| {
            if self.0 & ((1 << i) as u32) != 0 {
                c
            } else {
                "-"
            }
        };
        write!(f, "{}{}{}", perm(8, "r"), perm(7, "w"), perm(6, "x"))?;
        write!(f, "{}{}{}", perm(5, "r"), perm(4, "w"), perm(3, "x"))?;
        write!(f, "{}{}{}", perm(2, "r"), perm(1, "w"), perm(0, "x"))?;
        write!(f, ")")?;
        Ok(())
    }
}

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();
    let mut parser = ArgParser::new(1)
        .add_flag(&["h", "help"]);
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write_all(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        return;
    }
    let (all_users, all_groups) = match (AllUsers::new(false), AllGroups::new()) {
        (Ok(all_users), Ok(all_groups)) => (all_users, all_groups),
        (Err(_), Ok(_)) => {
            eprintln!("Unable to access password file");
            exit(1);
        },
        (Ok(_), Err(_)) => {
            eprintln!("Unable to access group file");
            exit(1);
        }
        _ => {
            eprintln!("Unable to access password and group file");
            exit(1);
        }
    };

    for path in &parser.args[0..] {
        let meta = fs::symlink_metadata(path).unwrap();
        let file_type = if meta.file_type().is_symlink() {
            "symbolic link"
        } else if meta.is_file() {
            "regular file"
        } else if meta.is_dir() {
            "directory"
        } else {
            ""
        };
        if meta.file_type().is_symlink() {
            println!("File: {} -> {}", path, fs::read_link(path).unwrap().display());
        } else {
            println!("File: {}", path);
        }
        let username = all_users.get_by_id(meta.uid() as usize)
            .map(|x| x.user.to_string())
            .unwrap_or("UNKNOWN".to_string());
        let groupname = all_groups.get_by_id(meta.gid() as usize)
            .map(|x| x.group.to_string())
            .unwrap_or("UNKNOWN".to_string());
        println!("Size: {}  Blocks: {}  IO Block: {} {}", meta.size(), meta.blocks(), meta.blksize(), file_type);
        println!("Device: {}  Inode: {}  Links: {}", meta.dev(), meta.ino(), meta.nlink());
        println!("Access: {}  Uid: ({}/{})  Gid: ({}/{})", Perms(meta.mode()),
                 meta.uid(), username, meta.gid(), groupname);
        println!("Access: {}", time::at(Timespec::new(meta.atime(), meta.atime_nsec() as i32)).strftime(TIME_FMT).unwrap());
        println!("Modify: {}", time::at(Timespec::new(meta.mtime(), meta.mtime_nsec() as i32)).strftime(TIME_FMT).unwrap());
        println!("Change: {}", time::at(Timespec::new(meta.ctime(), meta.ctime_nsec() as i32)).strftime(TIME_FMT).unwrap());
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod macros;
pub mod error;
pub mod arguments;
pub mod configuration;
pub mod engines;

extern crate std;
extern crate config;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use config::types::Config;
use bins::error::*;
use bins::arguments::Arguments;
use bins::engines::Engine;
use bins::engines::gist::Gist;
use bins::engines::hastebin::Hastebin;
use bins::engines::pastie::Pastie;
use bins::engines::pastebin::Pastebin;

#[derive(Clone)]
pub struct PasteFile {
  pub name: String,
  pub data: String
}

impl PasteFile {
  fn new(name: String, data: String) -> Self {
    PasteFile { name: name, data: data }
  }
}

pub struct Bins {
  pub config: Config,
  pub arguments: Arguments
}

impl Bins {
  pub fn new(config: Config, arguments: Arguments) -> Self {
    Bins {
      config: config,
      arguments: arguments
    }
  }

  pub fn get_engine(&self) -> Result<Box<Engine>> {
    let service = match self.arguments.service {
      Some(ref s) => s,
      None => return Err("no service was specified and no default service was set.".into())
    };
    match service.to_lowercase().as_ref() {
      "gist" => Ok(Box::new(Gist::new())),
      "hastebin" => Ok(Box::new(Hastebin::new())),
      "pastie" => Ok(Box::new(Pastie::new())),
      "pastebin" => Ok(Box::new(Pastebin::new())),
      _ => Err(format!("unknown service \"{}\"", service).into())
    }
  }

  fn read_file<P: AsRef<Path>>(&self, p: P) -> Result<String> {
    let path = p.as_ref();
    let name = match path.to_str() {
      Some(s) => s,
      None => return Err(String::from("file name was not valid unicode").into())
    };
    if !path.exists() {
      return Err(format!("{} does not exist", name).into());
    }
    if !path.is_file() {
      return Err(format!("{} is not a file", name).into());
    }
    let mut file = match File::open(path) {
      Ok(f) => f,
      Err(e) => {
        return Err(format!("could not open {}: {}", name, e).into());
      }
    };
    let mut s = String::new();
    if let Err(e) = file.read_to_string(&mut s) {
      return Err(format!("could not read {}: {}", name, e).into());
    }
    Ok(s)
  }

  fn read_file_to_pastefile<P: AsRef<Path>>(&self, p: P) -> Result<PasteFile> {
    let path = p.as_ref();
    match self.read_file(path) {
      Ok(s) => {
        let n = match path.file_name() {
          Some(x) => x,
          None => return Err("not a valid file name".into())
        };
        Ok(PasteFile::new(n.to_string_lossy().into_owned(), s))
      },
      Err(s) => return Err(s)
    }
  }

  pub fn get_to_paste(&self) -> Result<Vec<PasteFile>> {
    let arguments = &self.arguments;
    let message = &arguments.message;
    let paste_files: Vec<PasteFile> = if !message.is_empty() {
      vec![PasteFile::new(String::from("message"), message.to_owned())]
    } else if !arguments.files.is_empty() {
      let files = arguments.files.clone();
      let results = files.iter()
        .map(|s| Path::new(s))
        .map(|p| self.read_file_to_pastefile(p))
        .map(|r| r.map_err(|e| e.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("\n")))
        .collect::<Vec<_>>();
      for res in results.iter().cloned() {
        if res.is_err() {
          return Err(res.err().unwrap().into());
        }
      }
      let mut pastes = results.iter().cloned().map(|r| r.unwrap()).filter(|p| !p.data.trim().is_empty()).collect::<Vec<_>>();
      if pastes.len() < 1 {
        return Err("no files (or all empty files) to paste".into());
      }
      self.handle_duplicate_file_names(&mut pastes);
      pastes
    } else {
      let mut buffer = String::new();
      if let Err(e) = std::io::stdin().read_to_string(&mut buffer) {
        return Err(format!("error reading stdin: {}", e).into());
      }
      vec![PasteFile::new(String::from("stdin"), buffer)]
    };
    Ok(paste_files)
  }

  fn handle_duplicate_file_names(&self, pastes: &mut Vec<PasteFile>) {
    let mut names_map: HashMap<String, i32> = HashMap::new();
    for mut paste in pastes {
      let name = paste.name.clone();
      if names_map.contains_key(&name) {
        let number = names_map.entry(name.clone()).or_insert(1);
        paste.name = format!("{}_{}", name, number);
        *number += 1;
      }
      names_map.entry(name.clone()).or_insert(1);
    }
  }
}
<commit_msg>fix(bins): add the number for file name conflicts before the file extension, if any<commit_after>#[macro_use]
pub mod macros;
pub mod error;
pub mod arguments;
pub mod configuration;
pub mod engines;

extern crate std;
extern crate config;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use config::types::Config;
use bins::error::*;
use bins::arguments::Arguments;
use bins::engines::Engine;
use bins::engines::gist::Gist;
use bins::engines::hastebin::Hastebin;
use bins::engines::pastie::Pastie;
use bins::engines::pastebin::Pastebin;

#[derive(Clone)]
pub struct PasteFile {
  pub name: String,
  pub data: String
}

impl PasteFile {
  fn new(name: String, data: String) -> Self {
    PasteFile { name: name, data: data }
  }
}

pub struct Bins {
  pub config: Config,
  pub arguments: Arguments
}

impl Bins {
  pub fn new(config: Config, arguments: Arguments) -> Self {
    Bins {
      config: config,
      arguments: arguments
    }
  }

  pub fn get_engine(&self) -> Result<Box<Engine>> {
    let service = match self.arguments.service {
      Some(ref s) => s,
      None => return Err("no service was specified and no default service was set.".into())
    };
    match service.to_lowercase().as_ref() {
      "gist" => Ok(Box::new(Gist::new())),
      "hastebin" => Ok(Box::new(Hastebin::new())),
      "pastie" => Ok(Box::new(Pastie::new())),
      "pastebin" => Ok(Box::new(Pastebin::new())),
      _ => Err(format!("unknown service \"{}\"", service).into())
    }
  }

  fn read_file<P: AsRef<Path>>(&self, p: P) -> Result<String> {
    let path = p.as_ref();
    let name = match path.to_str() {
      Some(s) => s,
      None => return Err(String::from("file name was not valid unicode").into())
    };
    if !path.exists() {
      return Err(format!("{} does not exist", name).into());
    }
    if !path.is_file() {
      return Err(format!("{} is not a file", name).into());
    }
    let mut file = match File::open(path) {
      Ok(f) => f,
      Err(e) => {
        return Err(format!("could not open {}: {}", name, e).into());
      }
    };
    let mut s = String::new();
    if let Err(e) = file.read_to_string(&mut s) {
      return Err(format!("could not read {}: {}", name, e).into());
    }
    Ok(s)
  }

  fn read_file_to_pastefile<P: AsRef<Path>>(&self, p: P) -> Result<PasteFile> {
    let path = p.as_ref();
    match self.read_file(path) {
      Ok(s) => {
        let n = match path.file_name() {
          Some(x) => x,
          None => return Err("not a valid file name".into())
        };
        Ok(PasteFile::new(n.to_string_lossy().into_owned(), s))
      },
      Err(s) => return Err(s)
    }
  }

  pub fn get_to_paste(&self) -> Result<Vec<PasteFile>> {
    let arguments = &self.arguments;
    let message = &arguments.message;
    let paste_files: Vec<PasteFile> = if !message.is_empty() {
      vec![PasteFile::new(String::from("message"), message.to_owned())]
    } else if !arguments.files.is_empty() {
      let files = arguments.files.clone();
      let results = files.iter()
        .map(|s| Path::new(s))
        .map(|p| self.read_file_to_pastefile(p))
        .map(|r| r.map_err(|e| e.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("\n")))
        .collect::<Vec<_>>();
      for res in results.iter().cloned() {
        if res.is_err() {
          return Err(res.err().unwrap().into());
        }
      }
      let mut pastes = results.iter().cloned().map(|r| r.unwrap()).filter(|p| !p.data.trim().is_empty()).collect::<Vec<_>>();
      if pastes.len() < 1 {
        return Err("no files (or all empty files) to paste".into());
      }
      self.handle_duplicate_file_names(&mut pastes);
      pastes
    } else {
      let mut buffer = String::new();
      if let Err(e) = std::io::stdin().read_to_string(&mut buffer) {
        return Err(format!("error reading stdin: {}", e).into());
      }
      vec![PasteFile::new(String::from("stdin"), buffer)]
    };
    Ok(paste_files)
  }

  fn handle_duplicate_file_names(&self, pastes: &mut Vec<PasteFile>) {
    let mut names_map: HashMap<String, i32> = HashMap::new();
    for mut paste in pastes {
      let name = paste.name.clone();
      if names_map.contains_key(&name) {
          let parts = name.rsplit(".");
        let (beginning, end) = if parts.clone().count() > 1 {
          let mut beginning_parts = parts.clone().skip(1).collect::<Vec<_>>();
          beginning_parts.reverse();
          let beginning = beginning_parts.join(".");
          let end = parts.take(1).next().map(|s| String::from(".") + s).unwrap_or(String::from(""));
          (beginning, end)
        } else {
          (name.clone(), String::from(""))
        };
        let number = names_map.entry(name.clone()).or_insert(1);
        paste.name = format!("{}_{}{}", beginning, number, end);
        *number += 1;
      }
      names_map.entry(name.clone()).or_insert(1);
    }
  }
}
<|endoftext|>


<commit_before>// Implements the `zakopane checksum` subcommand.

use std::convert::TryInto;
use std::io::Read;
use std::io::Write;

use crate::structs::ChecksumCliOptions;
use crate::structs::ZakopaneError;

const READ_SIZE: usize = 1 << 20;
const BIG_FILE_BYTES: usize = 1 << 27;

struct ChecksumWithPath {
    checksum: String,
    path: std::path::PathBuf,
}

impl ChecksumWithPath {
    pub fn new(checksum: String, path: std::path::PathBuf) -> Self {
        Self { checksum, path }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    cli_options: ChecksumCliOptions,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        cli_options: ChecksumCliOptions,
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(cli_options.max_tasks)),
            cli_options,
            spawn_counter,
            sender,
        }
    }
}

struct FileDetails {
    is_file: bool,
    is_big: bool,
}

fn get_file_details(path: &std::path::PathBuf) -> FileDetails {
    let metadata = match std::fs::symlink_metadata(path) {
        Ok(m) => m,
        Err(_) => {
            return FileDetails {
                is_file: false,
                is_big: false,
            }
        }
    };

    FileDetails {
        is_file: metadata.file_type().is_file(),
        is_big: metadata.len() > BIG_FILE_BYTES.try_into().unwrap(),
    }
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

fn checksum_task_impl(path: std::path::PathBuf) -> ChecksumResult {
    let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA256);
    let mut buffer: Vec<u8> = vec![0; READ_SIZE];
    let mut file = std::fs::File::open(&path).map_err(ZakopaneError::Io)?;
    loop {
        let read_bytes = file.read(&mut buffer).map_err(ZakopaneError::Io)?;
        if read_bytes == 0 {
            let checksum = hasher
                .finish()
                .into_iter()
                .map(|byte| format!("{:02x}", byte))
                .collect::<Vec<String>>()
                .join("");
            return Ok(ChecksumWithPath::new(checksum, path));
        }
        hasher
            .write_all(&buffer[..read_bytes])
            .map_err(ZakopaneError::Io)?;
    }
}

// Represents a spawned checksum task.
//
// `_permit` is moved into this checksum task simply to hold onto the
// semaphore-dispensed resource for the duration of this task.
fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    _permit: tokio::sync::OwnedSemaphorePermit,
) {
    let result = checksum_task_impl(path);
    checksum_task_send_result(result, sender);
}

// Represents the spawned collection task.
async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            break;
        }
    }
    if errors != 0 {
        eprintln!("WARNING: {} errors collected", errors);
    }
    sums
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector(
    options: ChecksumCliOptions,
) -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(options.max_tasks);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let join_handle = tokio::task::spawn(collector_task(receiver, spawn_counter.clone()));
    (
        ChecksumTaskDispatcherData::new(options, spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    let walk_iter = walkdir::WalkDir::new(&context.cli_options.path).into_iter();
    // The `filter_entry()` call is crafted s.t.
    // *    we skip and don't descend into hidden directories
    // *    unless the hidden directory is the target directory, because
    //      the target directory is always the first yielded value from
    //      the `WalkDir`.
    for entry in walk_iter.filter_entry(|e| {
        e.path() == &context.cli_options.path
            || !e
                .file_name()
                .to_str()
                .map(|path| path.starts_with("."))
                .unwrap_or(false)
    }) {
        if let Ok(direntry) = entry {
            let path = direntry.into_path();
            let file_details = get_file_details(&path);
            if !file_details.is_file {
                continue;
            }

            // If the file is "big" (arbitrarily defined), attempt to
            // seize all permits to force us momentarily to work with a
            // bit less I/O contention.
            let permit = if file_details.is_big {
                context
                    .semaphore
                    .clone()
                    .acquire_many_owned(context.cli_options.max_tasks.try_into().unwrap())
                    .await
                    .unwrap()
            } else {
                context.semaphore.clone().acquire_owned().await.unwrap()
            };

            context
                .spawn_counter
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let sender = context.sender.clone();
            tokio::task::spawn_blocking(move || checksum_task(path, sender, permit));
        }
    }
}

// Pretty-prints the sorted `checksums` in a format much like what the
// `sha256sum` binary outputs.
//
// Note that the standard zakopane snapshot header is not added here.
fn pretty_format_checksums(path: std::path::PathBuf, checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = checksums
        .into_iter()
        .map(|e| {
            format!(
                "{}  ./{}",
                e.checksum,
                e.path.strip_prefix(&path).unwrap().to_str().unwrap()
            )
        })
        .collect();
    buffer.push(String::new());
    buffer.join("\n")
}

async fn checksum_impl(options: ChecksumCliOptions) -> String {
    let path = options.path.clone();
    let (dispatcher_data, join_handle) = spawn_collector(options).await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(path, checksums)
}

pub fn checksum(options: ChecksumCliOptions) -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl(options))
}
<commit_msg>checksum: Refactor permit handling<commit_after>// Implements the `zakopane checksum` subcommand.

use std::convert::TryInto;
use std::io::Read;
use std::io::Write;

use crate::structs::ChecksumCliOptions;
use crate::structs::ZakopaneError;

const READ_SIZE: usize = 1 << 20;
const BIG_FILE_BYTES: usize = 1 << 27;

struct ChecksumWithPath {
    checksum: String,
    path: std::path::PathBuf,
}

impl ChecksumWithPath {
    pub fn new(checksum: String, path: std::path::PathBuf) -> Self {
        Self { checksum, path }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    cli_options: ChecksumCliOptions,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        cli_options: ChecksumCliOptions,
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(cli_options.max_tasks)),
            cli_options,
            spawn_counter,
            sender,
        }
    }
}

struct FileDetails {
    is_file: bool,
    is_big: bool,
}

fn get_file_details(path: &std::path::PathBuf) -> FileDetails {
    let metadata = match std::fs::symlink_metadata(path) {
        Ok(m) => m,
        Err(_) => {
            return FileDetails {
                is_file: false,
                is_big: false,
            }
        }
    };

    FileDetails {
        is_file: metadata.file_type().is_file(),
        is_big: metadata.len() > BIG_FILE_BYTES.try_into().unwrap(),
    }
}

async fn get_semaphore_permit(
    path: &std::path::PathBuf,
    context: &ChecksumTaskDispatcherData,
) -> Option<tokio::sync::OwnedSemaphorePermit> {
    let file_details = get_file_details(&path);
    if !file_details.is_file {
        return None;
    }

    // If the file is "big" (arbitrarily defined by user), attempt to
    // seize all permits to force us momentarily to work with a bit less
    // I/O contention.
    let permit = if file_details.is_big {
        context
            .semaphore
            .clone()
            .acquire_many_owned(context.cli_options.max_tasks.try_into().unwrap())
            .await
            .unwrap()
    } else {
        context.semaphore.clone().acquire_owned().await.unwrap()
    };
    Some(permit)
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

fn checksum_task_impl(path: std::path::PathBuf) -> ChecksumResult {
    let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA256);
    let mut buffer: Vec<u8> = vec![0; READ_SIZE];
    let mut file = std::fs::File::open(&path).map_err(ZakopaneError::Io)?;
    loop {
        let read_bytes = file.read(&mut buffer).map_err(ZakopaneError::Io)?;
        if read_bytes == 0 {
            let checksum = hasher
                .finish()
                .into_iter()
                .map(|byte| format!("{:02x}", byte))
                .collect::<Vec<String>>()
                .join("");
            return Ok(ChecksumWithPath::new(checksum, path));
        }
        hasher
            .write_all(&buffer[..read_bytes])
            .map_err(ZakopaneError::Io)?;
    }
}

// Represents a spawned checksum task.
//
// `_permit` is moved into this checksum task simply to hold onto the
// semaphore-dispensed resource for the duration of this task.
fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    _permit: tokio::sync::OwnedSemaphorePermit,
) {
    let result = checksum_task_impl(path);
    checksum_task_send_result(result, sender);
}

// Represents the spawned collection task.
async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            break;
        }
    }
    if errors != 0 {
        eprintln!("WARNING: {} errors collected", errors);
    }
    sums
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector(
    options: ChecksumCliOptions,
) -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(options.max_tasks);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let join_handle = tokio::task::spawn(collector_task(receiver, spawn_counter.clone()));
    (
        ChecksumTaskDispatcherData::new(options, spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    let walk_iter = walkdir::WalkDir::new(&context.cli_options.path).into_iter();
    // The `filter_entry()` call is crafted s.t.
    // *    we skip and don't descend into hidden directories
    // *    unless the hidden directory is the target directory, because
    //      the target directory is always the first yielded value from
    //      the `WalkDir`.
    for entry in walk_iter.filter_entry(|e| {
        e.path() == &context.cli_options.path
            || !e
                .file_name()
                .to_str()
                .map(|path| path.starts_with("."))
                .unwrap_or(false)
    }) {
        let path = match entry {
            Ok(d) => d,
            Err(_) => continue,
        }
        .into_path();
        let permit = match get_semaphore_permit(&path, &context).await {
            Some(p) => p,
            None => continue,
        };

        context
            .spawn_counter
            .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
        let sender = context.sender.clone();
        tokio::task::spawn_blocking(move || checksum_task(path, sender, permit));
    }
}

// Pretty-prints the sorted `checksums` in a format much like what the
// `sha256sum` binary outputs.
//
// Note that the standard zakopane snapshot header is not added here.
fn pretty_format_checksums(path: std::path::PathBuf, checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = checksums
        .into_iter()
        .map(|e| {
            format!(
                "{}  ./{}",
                e.checksum,
                e.path.strip_prefix(&path).unwrap().to_str().unwrap()
            )
        })
        .collect();
    buffer.push(String::new());
    buffer.join("\n")
}

async fn checksum_impl(options: ChecksumCliOptions) -> String {
    let path = options.path.clone();
    let (dispatcher_data, join_handle) = spawn_collector(options).await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(path, checksums)
}

pub fn checksum(options: ChecksumCliOptions) -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl(options))
}
<|endoftext|>


<commit_before>//! The `conductor exec` command.

use command_runner::{Command, CommandRunner};
#[cfg(test)]
use command_runner::TestCommandRunner;
use exec::{self, ToArgs};
use ext::service::ServiceExt;
use ovr::Override;
use project::Project;
use util::Error;

/// We implement `conductor exec` with a trait so we can put it in its own
/// module.
pub trait CommandExec {
    /// Exectute a command inside a running container.  Even though we
    /// package up most of our arguments into structs, we still have a
    /// ridiculous number of arguments.
    fn exec<CR>(&self, runner: &CR,
                target: &exec::Target,
                command: &exec::Command,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner;

    /// Execute an interactive shell inside a running container.
    fn shell<CR>(&self, runner: &CR,
                target: &exec::Target,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner;
}

impl CommandExec for Project {
    fn exec<CR>(&self, runner: &CR,
                target: &exec::Target,
                command: &exec::Command,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner
    {

        let status = try!(runner.build("docker-compose")
            .args(&try!(target.pod().compose_args(self, target.ovr())))
            .arg("exec")
            .args(&opts.to_args())
            .arg(target.service_name())
            .args(&command.to_args())
            .status());
        if !status.success() {
            return Err(err!("Error running docker-compose"));
        }

        Ok(())
    }

    fn shell<CR>(&self, runner: &CR,
                target: &exec::Target,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner
    {
        // Sanity-check our arguments.
        if opts.detached {
            return Err(err!("Can't run shell in detached mode"));
        }
        if !opts.allocate_tty {
            return Err(err!("Can't run shell without a TTY"));
        }

        let shell = try!(target.service().shell());
        self.exec(runner, target, &exec::Command::new(shell), opts)
    }
}

#[test]
fn invokes_docker_exec() {
    let proj = Project::from_example("hello").unwrap();
    let ovr = proj.ovr("development").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();
    let target = exec::Target::new(&proj, &ovr, "frontend", "web").unwrap();
    let command = exec::Command::new("true");
    let opts = exec::Options { allocate_tty: false, ..Default::default() };
    proj.exec(&runner, &target, &command, &opts).unwrap();

    assert_ran!(runner, {
        ["docker-compose",
         "-p", "frontend",
         "-f", proj.output_dir().join("pods/frontend.yml"),
         "-f", proj.output_dir().join("pods/overrides/development/frontend.yml"),
         "exec", "-T", "web", "true"]
    });

    proj.remove_test_output().unwrap();
}

#[test]
fn runs_shells() {
    let proj = Project::from_example("hello").unwrap();
    let ovr = proj.ovr("development").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();
    let target = exec::Target::new(&proj, &ovr, "frontend", "web").unwrap();
    proj.shell(&runner, &target, &Default::default()).unwrap();

    assert_ran!(runner, {
        ["docker-compose",
         "-p", "frontend",
         "-f", proj.output_dir().join("pods/frontend.yml"),
         "-f", proj.output_dir().join("pods/overrides/development/frontend.yml"),
         "exec", "web", "sh"]
    });

    proj.remove_test_output().unwrap();
}
<commit_msg>Fix build warning<commit_after>//! The `conductor exec` command.

use command_runner::{Command, CommandRunner};
#[cfg(test)]
use command_runner::TestCommandRunner;
use exec::{self, ToArgs};
use ext::service::ServiceExt;
use project::Project;
use util::Error;

/// We implement `conductor exec` with a trait so we can put it in its own
/// module.
pub trait CommandExec {
    /// Exectute a command inside a running container.  Even though we
    /// package up most of our arguments into structs, we still have a
    /// ridiculous number of arguments.
    fn exec<CR>(&self, runner: &CR,
                target: &exec::Target,
                command: &exec::Command,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner;

    /// Execute an interactive shell inside a running container.
    fn shell<CR>(&self, runner: &CR,
                target: &exec::Target,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner;
}

impl CommandExec for Project {
    fn exec<CR>(&self, runner: &CR,
                target: &exec::Target,
                command: &exec::Command,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner
    {

        let status = try!(runner.build("docker-compose")
            .args(&try!(target.pod().compose_args(self, target.ovr())))
            .arg("exec")
            .args(&opts.to_args())
            .arg(target.service_name())
            .args(&command.to_args())
            .status());
        if !status.success() {
            return Err(err!("Error running docker-compose"));
        }

        Ok(())
    }

    fn shell<CR>(&self, runner: &CR,
                target: &exec::Target,
                opts: &exec::Options) ->
        Result<(), Error>
        where CR: CommandRunner
    {
        // Sanity-check our arguments.
        if opts.detached {
            return Err(err!("Can't run shell in detached mode"));
        }
        if !opts.allocate_tty {
            return Err(err!("Can't run shell without a TTY"));
        }

        let shell = try!(target.service().shell());
        self.exec(runner, target, &exec::Command::new(shell), opts)
    }
}

#[test]
fn invokes_docker_exec() {
    let proj = Project::from_example("hello").unwrap();
    let ovr = proj.ovr("development").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();
    let target = exec::Target::new(&proj, &ovr, "frontend", "web").unwrap();
    let command = exec::Command::new("true");
    let opts = exec::Options { allocate_tty: false, ..Default::default() };
    proj.exec(&runner, &target, &command, &opts).unwrap();

    assert_ran!(runner, {
        ["docker-compose",
         "-p", "frontend",
         "-f", proj.output_dir().join("pods/frontend.yml"),
         "-f", proj.output_dir().join("pods/overrides/development/frontend.yml"),
         "exec", "-T", "web", "true"]
    });

    proj.remove_test_output().unwrap();
}

#[test]
fn runs_shells() {
    let proj = Project::from_example("hello").unwrap();
    let ovr = proj.ovr("development").unwrap();
    let runner = TestCommandRunner::new();
    proj.output().unwrap();
    let target = exec::Target::new(&proj, &ovr, "frontend", "web").unwrap();
    proj.shell(&runner, &target, &Default::default()).unwrap();

    assert_ran!(runner, {
        ["docker-compose",
         "-p", "frontend",
         "-f", proj.output_dir().join("pods/frontend.yml"),
         "-f", proj.output_dir().join("pods/overrides/development/frontend.yml"),
         "exec", "web", "sh"]
    });

    proj.remove_test_output().unwrap();
}
<|endoftext|>


<commit_before>//*****************************
// Java Codecity data model

extern crate classreader;

use self::classreader::*;
use std::collections::HashMap;
use std::cmp;

#[derive(Debug)]
pub struct Rect {
    // Dimenstions
    width:  u16,
    depth:  u16,
    // Placement
    pos_w:  u16,
    pos_d:  u16
}

impl Rect {
    fn new(w: u16, d: u16) -> Rect {
        Rect {
            width: w,
            depth: d,
            pos_w: 0,
            pos_d: 0
        }
    }

    fn area(self: &Rect) -> u16 {self.width * self.depth}
}

#[derive(Debug)]
pub struct Building {
    name:   String,
    rect:   Rect,
    height: u16,
}

impl Building {
    fn new(class: &Class) -> Building {
        Building {
            name: get_class_name(class).to_string(),
            rect: Rect::new(class.methods.len() as u16 + 1, class.fields.len() as u16 + 1),
            height: get_total_code_size(&class) as u16 / 10 + 1,
        }
    }

    fn pos(self: &Building) -> (u16, u16) {
        (self.rect.pos_w, self.rect.pos_d)
    }
}

enum Direction {
    Width,
    Depth
}

#[derive(Debug)]
pub struct Package {
    name:  String,
    width: u16,
    depth: u16,
    packages:  HashMap<String, Package>,
    buildings: Vec<Building>
}

struct PackState {
    free_w: u16,
    free_d: u16,
    cur_w: u16,
    cur_d: u16,
    dir: Direction
}

impl Package {
    /// Create a new Package.
    pub fn new(name: &str) -> Package {
        Package {
            name: name.to_string(),
            width: 1,
            depth: 1,
            packages: HashMap::new(),
            buildings: Vec::new()
        }
    }

    fn size(self: &Package) -> (u16, u16) {
        (self.width, self.depth)
    }

    /// Add a class or package to this package
    fn add(&mut self, names: &[&str], class: &Class) {
        if names.len() == 1 {
            self.buildings.push(Building::new(class));
        } else {
            match names.split_first() {
                Some((head, tail)) => {
                    self.packages.entry(head.to_string()).or_insert_with(|| Package::new(head)).add(&tail, class);
                },
                _ => {}
            }
        }
    }

    /// Pack own members for the nice rendering
    fn pack(&mut self) {
        for (_, ch) in &mut self.packages {
            ch.pack();
        }
        let mut state = PackState {
            free_w: 0,
            free_d: 0,
            cur_w: 1,
            cur_d: 1,
            dir: Direction::Width
        };

        // TODO Packages
        // Buildings
        self.buildings.sort_by_key(|b| b.rect.area());
        self.buildings.reverse();
        for b in &mut self.buildings {
            let mut r = Rect {.. b.rect};
            self.place_rectangle(&mut state, &mut r);
            b.rect.pos_w = r.pos_w;
            b.rect.pos_d = r.pos_d;
        }
    }

    fn place_rectangle(self: &mut Package, state: &mut PackState, b: &mut Rect) {
        // println!("Used: {}x{} free: {}x{} placing {}x{}", cur_w, cur_d, free_w, free_d, b.width, b.depth);
        if b.width > state.free_w || b.depth > state.free_d {
            // decide the direction for growth
            if self.width <= self.depth {
                // Grow by width
                state.dir = Direction::Width;
                b.pos_w = self.width;
                b.pos_d = 1;
                state.cur_w = self.width;
                state.cur_d = 2 + b.depth;
                self.width += b.width + 1;
                self.depth = cmp::max(self.depth, b.depth + 2);
                state.free_w = b.width;
                state.free_d = self.depth - b.depth - 2;
            } else {
                // Grow by depth
                state.dir = Direction::Depth;
                b.pos_d = self.depth;
                b.pos_w = 1;
                state.cur_d = self.depth;
                state.cur_w = 2 + b.width;
                self.depth += b.depth + 1;
                self.width = cmp::max(self.width, b.width + 2);
                state.free_d = b.depth;
                state.free_w = self.width - b.width - 2;
            }
        } else {
            // Enough room
            match state.dir {
                Direction::Width => {
                    b.pos_w = state.cur_w;
                    b.pos_d = state.cur_d;
                    state.free_d -= b.depth + 1;
                    state.cur_d += b.depth + 1;
                }
                Direction::Depth => {
                    b.pos_d = state.cur_d;
                    b.pos_w = state.cur_w;
                    state.free_w -= b.width + 1;
                    state.cur_w += b.width + 1;
                }
            }
        }
    }
}

pub fn build_packages(classes: &Vec<Class>) -> Package {
    let mut root_pkg = Package::new("_root_");
    for c in classes {
        let name = get_class_name(&c);
        let names: Vec<&str> = name.split('/').collect();
        root_pkg.add(&names, c);
    }
    return root_pkg;
}

pub fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => {
                    let end = get_opt_line_number(line_table.last());
                    let start = get_opt_line_number(line_table.first());
                    if start > end { // TODO: Investigate when it happens
                        0
                    } else {
                        end - start
                    }
                },
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

//*****************************
// Unit tests

fn mock(name: &str, w: u16, d: u16) -> Building {
  Building {
    name: name.to_string(),
    rect: Rect::new(w, d),
    height: 0
  }
}

/// A function to print out details about placement
fn debug_test(pkg: &Package) {
    println!("{:?}", pkg);
    for b in &pkg.buildings {
        println!("{:?}", b);
    }
}

#[test]
fn pack_4_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..4 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[3].pos() == (3, 3));
}

#[test]
fn pack_5_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..5 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (7, 5));
    assert!(pkg.buildings[4].pos() == (5, 1));
}

#[test]
fn pack_16_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..16 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (9, 9));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[15].pos() == (7, 7));
}

#[test]
fn pack_smart_square_width() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("w", 3, 1));
    for _ in 0..2 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[2].pos() == (3, 3));
}

#[test]
fn pack_buildings() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("b", 8, 3));
    pkg.buildings.push(mock("c", 4, 2));
    pkg.buildings.push(mock("a", 10, 7));
    pkg.buildings.push(mock("d", 3, 2));
    pkg.buildings.push(mock("e1", 1, 1));
    pkg.buildings.push(mock("e2", 1, 1));
    pkg.buildings.push(mock("e3", 1, 1));
    println!("Starting the test");
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (17, 13));
    assert!(pkg.buildings[0].pos() == (1, 1));
    assert!(pkg.buildings[1].pos() == (1, 9));
    assert!(pkg.buildings[2].pos() == (12, 1));
    assert!(pkg.buildings[3].pos() == (12, 4));
    assert!(pkg.buildings[4].pos() == (12, 7));
    assert!(pkg.buildings[5].pos() == (12, 9));
    assert!(pkg.buildings[6].pos() == (12, 11));

}<commit_msg>Refactorign to extract common code<commit_after>//*****************************
// Java Codecity data model

extern crate classreader;

use self::classreader::*;
use std::collections::HashMap;
use std::cmp;

#[derive(Debug)]
pub struct Rect {
    // Dimenstions
    width:  u16,
    depth:  u16,
    // Placement
    pos_w:  u16,
    pos_d:  u16
}

impl Rect {
    fn new(w: u16, d: u16) -> Rect {
        Rect {
            width: w,
            depth: d,
            pos_w: 0,
            pos_d: 0
        }
    }

    fn area(self: &Rect) -> u16 {self.width * self.depth}

    fn place(self: &mut Rect, state: &mut PackState) {
        // println!("Used: {}x{} free: {}x{} placing {}x{}", cur_w, cur_d, free_w, free_d, b.width, b.depth);
        if self.width > state.free_w || self.depth > state.free_d {
            // decide the direction for growth
            if state.width <= state.depth {
                // Grow by width
                state.dir = Direction::Width;
                self.pos_w = state.width;
                self.pos_d = 1;
                state.cur_w = state.width;
                state.cur_d = 2 + self.depth;
                state.width += self.width + 1;
                state.depth = cmp::max(state.depth, self.depth + 2);
                state.free_w = self.width;
                state.free_d = state.depth - self.depth - 2;
            } else {
                // Grow by depth
                state.dir = Direction::Depth;
                self.pos_d = state.depth;
                self.pos_w = 1;
                state.cur_d = state.depth;
                state.cur_w = 2 + self.width;
                state.depth += self.depth + 1;
                state.width = cmp::max(state.width, self.width + 2);
                state.free_d = self.depth;
                state.free_w = state.width - self.width - 2;
            }
        } else {
            // Enough room
            match state.dir {
                Direction::Width => {
                    self.pos_w = state.cur_w;
                    self.pos_d = state.cur_d;
                    state.free_d -= self.depth + 1;
                    state.cur_d += self.depth + 1;
                }
                Direction::Depth => {
                    self.pos_d = state.cur_d;
                    self.pos_w = state.cur_w;
                    state.free_w -= self.width + 1;
                    state.cur_w += self.width + 1;
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct Building {
    name:   String,
    rect:   Rect,
    height: u16,
}

impl Building {
    fn new(class: &Class) -> Building {
        Building {
            name: get_class_name(class).to_string(),
            rect: Rect::new(class.methods.len() as u16 + 1, class.fields.len() as u16 + 1),
            height: get_total_code_size(&class) as u16 / 10 + 1,
        }
    }

    fn pos(self: &Building) -> (u16, u16) {
        (self.rect.pos_w, self.rect.pos_d)
    }
}

enum Direction {
    Width,
    Depth
}

#[derive(Debug)]
pub struct Package {
    name:  String,
    width: u16,
    depth: u16,
    packages:  HashMap<String, Package>,
    buildings: Vec<Building>
}

struct PackState {
    // Total aread
    width: u16,
    depth: u16,
    // Free in the corrent row
    free_w: u16,
    free_d: u16,
    // Position of the next placement
    cur_w: u16,
    cur_d: u16,
    // Direction we just grew the area
    dir: Direction
}

impl Package {
    /// Create a new Package.
    pub fn new(name: &str) -> Package {
        Package {
            name: name.to_string(),
            width: 1,
            depth: 1,
            packages: HashMap::new(),
            buildings: Vec::new()
        }
    }

    fn size(self: &Package) -> (u16, u16) {
        (self.width, self.depth)
    }

    /// Add a class or package to this package
    fn add(&mut self, names: &[&str], class: &Class) {
        if names.len() == 1 {
            self.buildings.push(Building::new(class));
        } else {
            match names.split_first() {
                Some((head, tail)) => {
                    self.packages.entry(head.to_string()).or_insert_with(|| Package::new(head)).add(&tail, class);
                },
                _ => {}
            }
        }
    }

    /// Pack own members for the nice rendering
    fn pack(&mut self) {
        for (_, ch) in &mut self.packages {
            ch.pack();
        }
        let mut state = PackState {
            width: self.width,
            depth: self.depth,
            free_w: 0,
            free_d: 0,
            cur_w: 1,
            cur_d: 1,
            dir: Direction::Width
        };

        // TODO Packages
        // Buildings
        self.buildings.sort_by_key(|b| b.rect.area());
        self.buildings.reverse();
        for b in &mut self.buildings {
            b.rect.place(&mut state);
        }
        self.width = state.width;
        self.depth = state.depth;
    }

}

pub fn build_packages(classes: &Vec<Class>) -> Package {
    let mut root_pkg = Package::new("_root_");
    for c in classes {
        let name = get_class_name(&c);
        let names: Vec<&str> = name.split('/').collect();
        root_pkg.add(&names, c);
    }
    return root_pkg;
}

pub fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => {
                    let end = get_opt_line_number(line_table.last());
                    let start = get_opt_line_number(line_table.first());
                    if start > end { // TODO: Investigate when it happens
                        0
                    } else {
                        end - start
                    }
                },
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

//*****************************
// Unit tests

fn mock(name: &str, w: u16, d: u16) -> Building {
  Building {
    name: name.to_string(),
    rect: Rect::new(w, d),
    height: 0
  }
}

/// A function to print out details about placement
fn debug_test(pkg: &Package) {
    println!("{:?}", pkg);
    for b in &pkg.buildings {
        println!("{:?}", b);
    }
}

#[test]
fn pack_4_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..4 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[3].pos() == (3, 3));
}

#[test]
fn pack_5_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..5 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (7, 5));
    assert!(pkg.buildings[4].pos() == (5, 1));
}

#[test]
fn pack_16_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..16 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (9, 9));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[15].pos() == (7, 7));
}

#[test]
fn pack_smart_square_width() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("w", 3, 1));
    for _ in 0..2 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[2].pos() == (3, 3));
}

#[test]
fn pack_buildings() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("b", 8, 3));
    pkg.buildings.push(mock("c", 4, 2));
    pkg.buildings.push(mock("a", 10, 7));
    pkg.buildings.push(mock("d", 3, 2));
    pkg.buildings.push(mock("e1", 1, 1));
    pkg.buildings.push(mock("e2", 1, 1));
    pkg.buildings.push(mock("e3", 1, 1));
    println!("Starting the test");
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (17, 13));
    assert!(pkg.buildings[0].pos() == (1, 1));
    assert!(pkg.buildings[1].pos() == (1, 9));
    assert!(pkg.buildings[2].pos() == (12, 1));
    assert!(pkg.buildings[3].pos() == (12, 4));
    assert!(pkg.buildings[4].pos() == (12, 7));
    assert!(pkg.buildings[5].pos() == (12, 9));
    assert!(pkg.buildings[6].pos() == (12, 11));

}<|endoftext|>