<commit_before>//! A Rust snippet runner.
//!
//! Please see [readme](https://github.com/stevedonovan/runner/blob/master/readme.md)
extern crate easy_shortcuts as es;
extern crate lapp;
use es::traits::*;
use std::process;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::collections::HashMap;
use std::io::Write;

mod crate_utils;
mod platform;

use std::env::consts::{EXE_SUFFIX,DLL_SUFFIX,DLL_PREFIX};

use platform::{open,edit};

fn rustup_lib() -> String {
    es::shell("rustc --print sysroot") + "/lib"
}

// this will be initially written to ~/.cargo/.runner/prelude and
// can then be edited.
const PRELUDE: &'static str = "
#![allow(unused_imports)]
#![allow(dead_code)]
use std::fs;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::env;
use std::path::{PathBuf,Path};
#[allow(unused_macros)]
macro_rules! debug {
    ($x:expr) => {
        println!(\"{} = {:?}\",stringify!($x),$x);
    }
}

";

fn runner_directory() -> PathBuf {
    let mut home = crate_utils::cargo_home();
    home.push(".runner");
    home
}

fn cargo(args: &[&str]) -> bool {
    let res = process::Command::new("cargo")
        .args(args)
        .status()
        .or_die("can't run cargo");
    res.success()
}

const STATIC_CACHE: &str = "static-cache";
const DYNAMIC_CACHE: &str = "dy-cache";

fn static_cache_dir() -> PathBuf {
    runner_directory().join(STATIC_CACHE)
}

fn static_cache_dir_check(args: &lapp::Args) -> PathBuf {
    let static_cache = static_cache_dir();
    if ! static_cache.exists() {
        args.quit("please build static cache with --create first");
    }
    static_cache
}

fn build_static_cache() -> bool {
    cargo(&["build"]) &&
    cargo(&["build","--release"]) &&
    cargo(&["doc"])
}

fn create_static_cache(crates: &[String], create: bool) {
    use std::io::prelude::*;
    let mut home = runner_directory();
    env::set_current_dir(&home).or_die("cannot change to home directory");
    if create {
        if ! cargo(&["new","--bin",STATIC_CACHE]) {
            es::quit("cannot create static cache");
        }
    }
    home.push(STATIC_CACHE);
    env::set_current_dir(&home).or_die("could not change to static cache directory");
    let tmpfile = env::temp_dir().join("Cargo.toml");
    fs::copy("Cargo.toml",&tmpfile).or_die("cannot back up Cargo.toml");
    {
        let mut deps = fs::OpenOptions::new().append(true)
            .open("Cargo.toml").or_die("could not append to Cargo.toml");
        for c in crates {
            write!(deps,"{}=\"*\"\n",c).or_die("could not modify Cargo.toml");
        }
    }
    if ! build_static_cache() {
        println!("Error occurred - restoring Cargo.toml");
        fs::copy(&tmpfile,"Cargo.toml").or_die("cannot restore Cargo.toml");
    }
}

// this is always called first and has the important role to ensure that
// runner's directory structure is created properly.
fn get_prelude() -> String {
    let home = runner_directory();
    let pristine = ! home.is_dir();
    if pristine {
        fs::create_dir(&home).or_die("cannot create runner directory");
    }
    let prelude = home.join("prelude");
    let bin = home.join("bin");
    if pristine {
        es::write_all(&prelude,PRELUDE);
        fs::create_dir(&home.join(DYNAMIC_CACHE)).or_die("cannot create dynamic cache");
    }
    if pristine || ! bin.is_dir() {
        fs::create_dir(&bin).or_die("cannot create output directory");
    }
    es::read_to_string(&prelude)
}

fn get_cache(build_static: bool, optimize: bool) -> PathBuf {
    let mut home = runner_directory();
    let scache;
    let cache = if build_static {
        let dir = if optimize {"release"} else {"debug"};
        scache = format!("{}/target/{}/deps",STATIC_CACHE,dir);
        &scache
    } else {DYNAMIC_CACHE};
    home.push(cache);
    home
}

fn add_aliases(aliases: Vec<String>) {
    if aliases.len() == 0 { return; }
    let alias_file = runner_directory().join("alias");
    let mut f = if alias_file.is_file() {
        fs::OpenOptions::new().append(true).open(&alias_file)
    } else {
        fs::File::create(&alias_file)
    }.or_die("cannot open runner alias file");

    for crate_alias in aliases {
        write!(f,"{}\n",crate_alias).or_die("cannot write to runner alias file");
    }
}

fn get_aliases() -> HashMap<String,String> {
    let alias_file = runner_directory().join("alias");
    if ! alias_file.is_file() { return HashMap::new(); }
	es::lines(es::open(&alias_file))
      .filter_map(|s| s.split_at_delim('=').trim()) // split into (String,String)
	  .to_map()
}

fn massage_snippet(code: String, prelude: String, extern_crates: Vec<String>, wild_crates: Vec<String>) -> String {
    fn indent_line(line: &str) -> String {
        format!("    {}\n",line)
    }
    let mut prefix = prelude;
    let mut body = String::new();
    {
        if extern_crates.len() > 0 {
            let aliases = get_aliases();
            for c in extern_crates {
                prefix += &if let Some(aliased) = aliases.get(&c) {
                    format!("extern crate {} as {};",aliased,c)
                } else {
                    format!("extern crate {};",c)
                };
            }
            for c in wild_crates {
                prefix += &format!("use {}::*;",c);
            }
        }
        let mut lines = code.lines();
        let mut first = true;
        for line in lines.by_ref() {
            let line = line.trim_left();
            if first { // files may start with #! shebang...
                if line.starts_with("#!") {
                    continue;
                }
                first = false;
            }
            // crate import, use should go at the top.
            // Particularly need to force crate-level attributes to the top
            // - currently only 'macro_use'.
            if line.starts_with("//") || line.starts_with("#[macro_use") ||
                line.starts_with("extern ") || line.starts_with("use ") {
                prefix += line;
                prefix.push('\n');
            } else
            if line.len() > 0 {
                body += &indent_line(line);
                break;
            }
        }
        // and indent the rest!
        body.extend(lines.map(indent_line));
    }

    format!("{}
use std::error::Error;
fn run() -> Result<(),Box<Error>> {{
{}    Ok(())
}}
fn main() {{
    run().unwrap();
}}
        ",prefix,body)

}

const USAGE: &str = "
Compile and run small Rust snippets
  -s, --static build statically (default is dynamic)
  -O, --optimize optimized static build
  -e, --expression evaluate an expression
  -i, --iterator evaluate an iterator
  -n, --lines evaluate expression over stdin; 'line' is defined
  -x, --extern... (string) add an extern crate to the snippet
  -X, --wild... (string) like -x but implies wildcard use

  Cache Management:
  --create (string...) initialize the static cache with crates
  --add  (string...) add new crates to the cache (after --create)
  --edit  edit the static cache Cargo.toml
  --build rebuild the static cache
  --doc  display documentation
  --edit-prelude edit the default prelude for snippets
  --alias (string...) crate aliases in form alias=crate_name (used with -x)

  Dynamic compilation:
  -P, --crate-path show path of crate source in Cargo cache
  -C, --compile  compile crate dynamically (limited)
  --cfg... (string) pass configuration variables to rustc
  --libc  link dynamically against libc (special case)

  <program> (string) Rust program, snippet or expression
  <args> (string...) arguments to pass to program
";

fn main() {
    let args = lapp::parse_args(USAGE);
    let prelude = get_prelude();

    let aliases = args.get_strings("alias");
    if aliases.len() > 0 {
        add_aliases(aliases);
        return;
    }

    if args.get_bool("edit-prelude") {
        let rdir = runner_directory().join("prelude");
        edit(&rdir);
        return;
    }

    // Static Cache Management
    let crates = args.get_strings("create");
    if crates.len() > 0 {
        create_static_cache(&crates,true);
        return;
    }
    let crates = args.get_strings("add");
    if crates.len() > 0 {
        static_cache_dir_check(&args);
        create_static_cache(&crates,false);
        return;
    }

    if args.get_bool("edit") || args.get_bool("build") || args.get_bool("doc") {
        let static_cache = static_cache_dir_check(&args);
        if args.get_bool("build") {
            env::set_current_dir(&static_cache).or_die("static cache wasn't a directory?");
            build_static_cache();
        } else
        if args.get_bool("doc") {
            let docs = static_cache.join("target/doc/static_cache/index.html");
            open(&docs);
        } else {
            let toml = static_cache.join("Cargo.toml");
            edit(&toml);
        }
        return;
    }

    let first_arg = args.get_string("program");
    let file = PathBuf::from(&first_arg);

    // Dynamically linking crates (experimental!)
    if args.get_bool("crate-path") || args.get_bool("compile") {
        let (crate_path,crate_name) = if file.exists() {
            let filename = crate_utils::path_file_name(&file);
            if file.is_dir() { // assumed to be Cargo directory
                if ! file.join("Cargo.toml").exists() {
                    args.quit(&format!("not a Cargo project directory: {}",file.display()));
                }
                (file.join("src/lib.rs"), filename)
            } else { // should be just a Rust source file
                if file.extension().or_die("expecting extension") != "rs" {
                    args.quit("expecting Rust source file");
                }
                let name = crate_utils::path_file_name(&file.with_extension(""));
                (file, name)
            }
        } else {
            (crate_utils::cache_path(&first_arg).join("src/lib.rs"), first_arg)
        };
        if args.get_bool("crate-path") {
            println!("{}",crate_utils::cache_path(&crate_name).display());
        } else {
            let valid_crate_name = crate_name.replace('-',"_");
            let cache = get_cache(false, false);
            let mut builder = process::Command::new("rustc");
                builder.args(&["-C","prefer-dynamic"]).args(&["-C","debuginfo=0"])
                .arg("-L").arg(&cache)
                .args(&["--crate-type","dylib"])
                .arg("--out-dir").arg(&cache)
                .arg("--crate-name").arg(&valid_crate_name)
                .arg(crate_path);
           for c in args.get_strings("cfg") {
                builder.arg("--cfg").arg(&c);
           }
           // implicit linking works fine, until it doesn't
           let mut extern_crates = args.get_strings("extern");
           if args.get_bool("libc") { // libc is such a special case
                extern_crates.push("libc".into());
           }
           for c in  extern_crates {
                let ext = format!("{}={}/{}{}{}",c,cache.display(),DLL_PREFIX,c,DLL_SUFFIX);
                builder.arg("--extern").arg(&ext);
           }
           builder.status().or_die("can't run rustc");
        }
        return;
    }

    let build_static = args.get_bool("static");
    let optimize = args.get_bool("optimize");

    // we'll pass rest of arguments to program
    let program_args = args.get_strings("args");

    let cache = get_cache(build_static, optimize);

    let mut snippet = false;
    let mut code = if args.get_bool("expression") {
        // Evaluating an expression: just print it out.
        format!("println!(\"{{:?}}\",{});", first_arg)
    } else
    if args.get_bool("iterator") {
        // The expression is anything that implements IntoIterator
        format!("let iter = {};\n for val in iter {{ println!(\"{{:?}}\",val);}}", first_arg)
    } else
    if args.get_bool("lines") {
        // The variable 'line' is available to an expression, evaluated for each line in stdin
        format!("
            let stdin = io::stdin();
            for line in stdin.lock().lines() {{
                let line = line?;
                let val = {};
                println!(\"{{:?}}\",val);
            }}
            ", first_arg)
    } else { // otherwise, just a file
        snippet = true;
        es::read_to_string(&file)
    };

    // ALL executables go into the Runner bin directory...
    let mut bin = runner_directory().join("bin");

    // proper Rust programs are accepted (this is a bit rough)
    let proper = code.find("fn main").is_some();
    let (rust_file, program) = if ! proper {
        // otherwise we must create a proper program from the snippet
        // and write this as a file in the Runner bin directory...
        let mut extern_crates = args.get_strings("extern");
        let wild_crates = args.get_strings("wild");
        if wild_crates.len() > 0 {
            extern_crates.extend(wild_crates.iter().cloned());
        }
        code = massage_snippet(code,prelude, extern_crates, wild_crates);
        if snippet {
            bin.push(&file);
            bin.set_extension("rs");
        } else {
            bin.push("tmp.rs");
        }
        es::write_all(&bin,&code);
        let program = bin.with_extension(EXE_SUFFIX);
        (bin, program)
    } else {
        bin.push(&file);
        let program = bin.with_extension(EXE_SUFFIX);
        (file, program)
    };

    // We now have a proper Rust file to compile
    let mut builder = process::Command::new("rustc");
    if ! build_static { // stripped-down dynamic link
        builder.args(&["-C","prefer-dynamic"]).args(&["-C","debuginfo=0"]);
    } else { // static debug build
        builder.arg(if optimize {"-O"} else {"-g"});
    }
    // implicitly linking against crates in the dynamic or static cache
    builder.arg("-L").arg(&cache);
    let status = builder.arg("-o").arg(&program)
        .arg(rust_file)
        .status().or_die("can't run rustc");
    if ! status.success() {
        return;
    }

    // Finally run the compiled program
    let mut builder = process::Command::new(&program);
    if ! build_static {
        builder.env("LD_LIBRARY_PATH",format!("{}:{}",rustup_lib(),cache.display()));
    }
    builder.args(&program_args)
        .status()
        .or_die(&format!("can't run program {:?}",program));

}

<commit_msg>prelude updated; --prepend flag for inserting code into body for -e, -i and -n<commit_after>//! A Rust snippet runner.
//!
//! Please see [readme](https://github.com/stevedonovan/runner/blob/master/readme.md)
extern crate easy_shortcuts as es;
extern crate lapp;
use es::traits::*;
use std::process;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::collections::HashMap;
use std::io::Write;

mod crate_utils;
mod platform;

use std::env::consts::{EXE_SUFFIX,DLL_SUFFIX,DLL_PREFIX};

use platform::{open,edit};

const USAGE: &str = "
Compile and run small Rust snippets
  -s, --static build statically (default is dynamic)
  -O, --optimize optimized static build
  -e, --expression evaluate an expression
  -i, --iterator evaluate an iterator
  -n, --lines evaluate expression over stdin; 'line' is defined
  -x, --extern... (string) add an extern crate to the snippet
  -X, --wild... (string) like -x but implies wildcard use
  -p, --prepend (default '') prepend contents of this file to body

  Cache Management:
  --create (string...) initialize the static cache with crates
  --add  (string...) add new crates to the cache (after --create)
  --edit  edit the static cache Cargo.toml
  --build rebuild the static cache
  --doc  display documentation
  --edit-prelude edit the default prelude for snippets
  --alias (string...) crate aliases in form alias=crate_name (used with -x)

  Dynamic compilation:
  -P, --crate-path show path of crate source in Cargo cache
  -C, --compile  compile crate dynamically (limited)
  --cfg... (string) pass configuration variables to rustc
  --libc  link dynamically against libc (special case)

  <program> (string) Rust program, snippet or expression
  <args> (string...) arguments to pass to program
";

fn rustup_lib() -> String {
    es::shell("rustc --print sysroot") + "/lib"
}

// this will be initially written to ~/.cargo/.runner/prelude and
// can then be edited.
const PRELUDE: &'static str = "
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(unused_macros)]
use std::fs;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::env;
use std::path::{PathBuf,Path};

macro_rules! debug {
    ($x:expr) => {
        println!(\"{} = {:?}\",stringify!($x),$x);
    }
}

";

fn runner_directory() -> PathBuf {
    crate_utils::cargo_home().join(".runner")
}

fn cargo(args: &[&str]) -> bool {
    let res = process::Command::new("cargo")
        .args(args)
        .status()
        .or_die("can't run cargo");
    res.success()
}

const STATIC_CACHE: &str = "static-cache";
const DYNAMIC_CACHE: &str = "dy-cache";

fn static_cache_dir() -> PathBuf {
    runner_directory().join(STATIC_CACHE)
}

fn static_cache_dir_check(args: &lapp::Args) -> PathBuf {
    let static_cache = static_cache_dir();
    if ! static_cache.exists() {
        args.quit("please build static cache with --create first");
    }
    static_cache
}

fn build_static_cache() -> bool {
    cargo(&["build"]) &&
    cargo(&["build","--release"]) &&
    cargo(&["doc"])
}

fn create_static_cache(crates: &[String], create: bool) {
    use std::io::prelude::*;
    let mut home = runner_directory();
    env::set_current_dir(&home).or_die("cannot change to home directory");
    if create {
        if ! cargo(&["new","--bin",STATIC_CACHE]) {
            es::quit("cannot create static cache");
        }
    }
    home.push(STATIC_CACHE);
    env::set_current_dir(&home).or_die("could not change to static cache directory");
    let tmpfile = env::temp_dir().join("Cargo.toml");
    fs::copy("Cargo.toml",&tmpfile).or_die("cannot back up Cargo.toml");
    {
        let mut deps = fs::OpenOptions::new().append(true)
            .open("Cargo.toml").or_die("could not append to Cargo.toml");
        for c in crates {
            write!(deps,"{}=\"*\"\n",c).or_die("could not modify Cargo.toml");
        }
    }
    if ! build_static_cache() {
        println!("Error occurred - restoring Cargo.toml");
        fs::copy(&tmpfile,"Cargo.toml").or_die("cannot restore Cargo.toml");
    }
}

// this is always called first and has the important role to ensure that
// runner's directory structure is created properly.
fn get_prelude() -> String {
    let home = runner_directory();
    let pristine = ! home.is_dir();
    if pristine {
        fs::create_dir(&home).or_die("cannot create runner directory");
    }
    let prelude = home.join("prelude");
    let bin = home.join("bin");
    if pristine {
        es::write_all(&prelude,PRELUDE);
        fs::create_dir(&home.join(DYNAMIC_CACHE)).or_die("cannot create dynamic cache");
    }
    if pristine || ! bin.is_dir() {
        fs::create_dir(&bin).or_die("cannot create output directory");
    }
    es::read_to_string(&prelude)
}

fn get_cache(build_static: bool, optimize: bool) -> PathBuf {
    let mut home = runner_directory();
    let scache;
    let cache = if build_static {
        let dir = if optimize {"release"} else {"debug"};
        scache = format!("{}/target/{}/deps",STATIC_CACHE,dir);
        &scache
    } else {DYNAMIC_CACHE};
    home.push(cache);
    home
}

fn add_aliases(aliases: Vec<String>) {
    if aliases.len() == 0 { return; }
    let alias_file = runner_directory().join("alias");
    let mut f = if alias_file.is_file() {
        fs::OpenOptions::new().append(true).open(&alias_file)
    } else {
        fs::File::create(&alias_file)
    }.or_die("cannot open runner alias file");

    for crate_alias in aliases {
        write!(f,"{}\n",crate_alias).or_die("cannot write to runner alias file");
    }
}

fn get_aliases() -> HashMap<String,String> {
    let alias_file = runner_directory().join("alias");
    if ! alias_file.is_file() { return HashMap::new(); }
	es::lines(es::open(&alias_file))
      .filter_map(|s| s.split_at_delim('=').trim()) // split into (String,String)
	  .to_map()
}

fn massage_snippet(code: String, prelude: String, extern_crates: Vec<String>, wild_crates: Vec<String>, body_prelude: String) -> String {
    fn indent_line(line: &str) -> String {
        format!("    {}\n",line)
    }
    let mut prefix = prelude;
    let mut body = String::new();
    body += &body_prelude;
    {
        if extern_crates.len() > 0 {
            let aliases = get_aliases();
            for c in extern_crates {
                prefix += &if let Some(aliased) = aliases.get(&c) {
                    format!("extern crate {} as {};",aliased,c)
                } else {
                    format!("extern crate {};",c)
                };
            }
            for c in wild_crates {
                prefix += &format!("use {}::*;",c);
            }
        }
        let mut lines = code.lines();
        let mut first = true;
        for line in lines.by_ref() {
            let line = line.trim_left();
            if first { // files may start with #! shebang...
                if line.starts_with("#!") {
                    continue;
                }
                first = false;
            }
            // crate import, use should go at the top.
            // Particularly need to force crate-level attributes to the top
            // - currently only 'macro_use'.
            if line.starts_with("//") || line.starts_with("#[macro_use") ||
                line.starts_with("extern ") || line.starts_with("use ") {
                prefix += line;
                prefix.push('\n');
            } else
            if line.len() > 0 {
                body += &indent_line(line);
                break;
            }
        }
        // and indent the rest!
        body.extend(lines.map(indent_line));
    }

    format!("{}
use std::error::Error;
fn run() -> Result<(),Box<Error>> {{
{}    Ok(())
}}
fn main() {{
    run().unwrap();
}}
",prefix,body)

}

fn main() {
    let args = lapp::parse_args(USAGE);
    let prelude = get_prelude();

    let aliases = args.get_strings("alias");
    if aliases.len() > 0 {
        add_aliases(aliases);
        return;
    }

    if args.get_bool("edit-prelude") {
        let rdir = runner_directory().join("prelude");
        edit(&rdir);
        return;
    }

    // Static Cache Management
    let crates = args.get_strings("create");
    if crates.len() > 0 {
        create_static_cache(&crates,true);
        return;
    }
    let crates = args.get_strings("add");
    if crates.len() > 0 {
        static_cache_dir_check(&args);
        create_static_cache(&crates,false);
        return;
    }

    if args.get_bool("edit") || args.get_bool("build") || args.get_bool("doc") {
        let static_cache = static_cache_dir_check(&args);
        if args.get_bool("build") {
            env::set_current_dir(&static_cache).or_die("static cache wasn't a directory?");
            build_static_cache();
        } else
        if args.get_bool("doc") {
            let docs = static_cache.join("target/doc/static_cache/index.html");
            open(&docs);
        } else {
            let toml = static_cache.join("Cargo.toml");
            edit(&toml);
        }
        return;
    }

    let first_arg = args.get_string("program");
    let file = PathBuf::from(&first_arg);

    // Dynamically linking crates (experimental!)
    if args.get_bool("crate-path") || args.get_bool("compile") {
        let (crate_path,crate_name) = if file.exists() {
            let filename = crate_utils::path_file_name(&file);
            if file.is_dir() { // assumed to be Cargo directory
                if ! file.join("Cargo.toml").exists() {
                    args.quit(&format!("not a Cargo project directory: {}",file.display()));
                }
                (file.join("src/lib.rs"), filename)
            } else { // should be just a Rust source file
                if file.extension().or_die("expecting extension") != "rs" {
                    args.quit("expecting Rust source file");
                }
                let name = crate_utils::path_file_name(&file.with_extension(""));
                (file, name)
            }
        } else {
            (crate_utils::cache_path(&first_arg).join("src/lib.rs"), first_arg)
        };
        if args.get_bool("crate-path") {
            println!("{}",crate_utils::cache_path(&crate_name).display());
        } else {
            println!("building crate '{}' at {}",crate_name, crate_path.display());
            let valid_crate_name = crate_name.replace('-',"_");
            let cache = get_cache(false, false);
            let mut builder = process::Command::new("rustc");
                builder.args(&["-C","prefer-dynamic"]).args(&["-C","debuginfo=0"])
                .arg("-L").arg(&cache)
                .args(&["--crate-type","dylib"])
                .arg("--out-dir").arg(&cache)
                .arg("--crate-name").arg(&valid_crate_name)
                .arg(crate_path);
           for c in args.get_strings("cfg") {
                builder.arg("--cfg").arg(&c);
           }
           // implicit linking works fine, until it doesn't
           let mut extern_crates = args.get_strings("extern");
           if args.get_bool("libc") { // libc is such a special case
                extern_crates.push("libc".into());
           }
           for c in  extern_crates {
                let ext = format!("{}={}/{}{}{}",c,cache.display(),DLL_PREFIX,c,DLL_SUFFIX);
                builder.arg("--extern").arg(&ext);
           }
           builder.status().or_die("can't run rustc");
        }
        return;
    }

    let build_static = args.get_bool("static");
    let optimize = args.get_bool("optimize");

    // we'll pass rest of arguments to program
    let program_args = args.get_strings("args");

    let cache = get_cache(build_static, optimize);

    let mut snippet = false;
    let mut code = if args.get_bool("expression") {
        // Evaluating an expression: just print it out.
        format!("println!(\"{{:?}}\",{});", first_arg)
    } else
    if args.get_bool("iterator") {
        // The expression is anything that implements IntoIterator
        format!("let iter = {};\n for val in iter {{ println!(\"{{:?}}\",val);}}", first_arg)
    } else
    if args.get_bool("lines") {
        // The variable 'line' is available to an expression, evaluated for each line in stdin
        format!("
            let stdin = io::stdin();
            for line in stdin.lock().lines() {{
                let line = line?;
                let val = {};
                println!(\"{{:?}}\",val);
            }}
            ", first_arg)
    } else { // otherwise, just a file
        snippet = true;
        es::read_to_string(&file)
    };

    // ALL executables go into the Runner bin directory...
    let mut bin = runner_directory().join("bin");

    // proper Rust programs are accepted (this is a bit rough)
    let proper = code.find("fn main").is_some();
    let (rust_file, program) = if ! proper {
        // otherwise we must create a proper program from the snippet
        // and write this as a file in the Runner bin directory...
        let mut extern_crates = args.get_strings("extern");
        let wild_crates = args.get_strings("wild");
        if wild_crates.len() > 0 {
            extern_crates.extend(wild_crates.iter().cloned());
        }
        let mut extra = args.get_string("prepend");
        if extra != "" {
            extra = es::read_to_string(&extra);
        }
        code = massage_snippet(code, prelude, extern_crates, wild_crates, extra);
        if snippet {
            bin.push(&file);
            bin.set_extension("rs");
        } else {
            bin.push("tmp.rs");
        }
        es::write_all(&bin,&code);
        let program = bin.with_extension(EXE_SUFFIX);
        (bin, program)
    } else {
        bin.push(&file);
        let program = bin.with_extension(EXE_SUFFIX);
        (file, program)
    };

    // We now have a proper Rust file to compile
    let mut builder = process::Command::new("rustc");
    if ! build_static { // stripped-down dynamic link
        builder.args(&["-C","prefer-dynamic"]).args(&["-C","debuginfo=0"]);
    } else { // static debug build
        builder.arg(if optimize {"-O"} else {"-g"});
    }
    // implicitly linking against crates in the dynamic or static cache
    builder.arg("-L").arg(&cache);
    let status = builder.arg("-o").arg(&program)
        .arg(rust_file)
        .status().or_die("can't run rustc");
    if ! status.success() {
        return;
    }

    // Finally run the compiled program
    let mut builder = process::Command::new(&program);
    if ! build_static {
        builder.env("LD_LIBRARY_PATH",format!("{}:{}",rustup_lib(),cache.display()));
    }
    builder.args(&program_args)
        .status()
        .or_die(&format!("can't run program {:?}",program));

}

<|endoftext|>


<commit_before>use std::iter;
use std::env;

fn main() {
    match get_file_names() {
        Ok(file_names) =>
            for file_name in file_names {
                println!("{}", file_name);
            },

        Err(message) =>
            println!("ERROR: {}", message)
    }
}

fn get_file_names() -> Result<iter::Skip<env::Args>, String> {
    let args = env::args();
    match args.len() {
        1 => Err("No file(s) specified".to_string()),
        _ => Ok(args.skip(1))
    }
}
<commit_msg>probably best to return error code on failure<commit_after>use std::iter;
use std::env;

fn main() {
    match get_file_names() {
        Ok(file_names) =>
            for file_name in file_names {
                println!("{}", file_name);
            },

        Err(message) => {
            println!("ERROR: {}", message);
            std::process::exit(1);
        }
    }
}

fn get_file_names() -> Result<iter::Skip<env::Args>, String> {
    let args = env::args();
    match args.len() {
        1 => Err("No file(s) specified".to_string()),
        _ => Ok(args.skip(1))
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // TODO: make sure "topological" here means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&subtree_id);

                // TODO: rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    let head = repo.head().expect("Couldn't obtain current repo's HEAD");
    let current_ref = head.name().expect("Couldn't obtain the name HEAD points to");
    let mut reference = repo.find_reference(current_ref).expect("Couldn't find the reference HEAD points to");
    let current_id = reference.target().expect("Couldn't resolve current reference to ID");
    let updated_id = old_id_to_new[&current_id];
    reference.set_target(updated_id, &format!("git-submerge reset {} to rewritten history", current_ref)).expect("Couldn't update current reference to point to revritten history");
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<commit_msg>Deal with `sub/sub/file` problem<commit_after>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index, Commit};
use clap::{Arg, App};
use std::path::Path;
use std::collections::{HashMap, HashSet};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    // TODO: make sure "topological" here means "parents are always visited before their children".
    // We need that in order to be sure that our old-to-new-ids map always contains everything we
    // need it to contain.
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    // TODO: what mode, owner, mtime etc. does the newly created dir get?
                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry).expect("Couldn't add an entry to the index");
                }
                let tree_id = new_index.write_tree_to(&repo).expect("Couldn't write the index into a tree");
                old_id_to_new.insert(tree.id(), tree_id);
                let tree = repo.find_tree(tree_id).expect("Couldn't retrieve the tree we just created");
                // 4.3. Create new commit with the new tree
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let new_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(new_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };
                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &tree,
                    &parents_refs[..])
                    .expect("Failed to commit");
                // 4.4. Update the map with the new commit's ID
                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 7. Remove submodule's remote
    repo.remote_delete(submodule_dir).expect("Couldn't remove submodule's remote");
    // 8. Run through master's history, doing two things:
    revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    let head = repo.head().expect("Couldn't obtain repo's HEAD");
    let head_id = head.target().expect("Couldn't resolve repo's HEAD to a commit ID");
    // TODO: push all branches and tags, not just HEAD
    revwalk.push(head_id).expect("Couldn't add repo's HEAD to RevWalk list");

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 8.1 updating the tree to contain the relevant tree from submodule
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));

                let submodule_subdir = match tree.get_path(submodule_path) {
                    Ok(tree) => tree,
                    Err(e) => {
                        if e.code() == git2::ErrorCode::NotFound
                            && e.class() == git2::ErrorClass::Tree
                        {
                            // It's okay. The tree lacks the subtree corresponding to the
                            // submodule. In other words, the commit doesn't include the submodule.
                            // That's totally fine. Let's map it into itself and move on.
                            old_id_to_new.insert(oid, oid);
                            continue;
                        } else {
                            // Unexpected error; let's report it and abort the program
                            // TODO: clean things up before aborting
                            panic!("Error getting submodule's subdir from the tree: {:?}", e);
                        };
                    },
                };

                // **INVARIANT**: if we got this far, current commit contains a submodule and
                // should be rewritten

                let submodule_commit_id = submodule_subdir.id();
                let new_submodule_commit_id = old_id_to_new[&submodule_commit_id];
                let submodule_commit = repo.find_commit(new_submodule_commit_id).expect("Couldn't obtain submodule's commit");
                let subtree_id = submodule_commit.tree()
                    .and_then(|t| t.get_path(submodule_path))
                    .and_then(|te| Ok(te.id()))
                    .expect("Couldn't obtain submodule's subtree ID");

                let mut treebuilder = repo.treebuilder(Some(&tree)).expect("Couldn't create TreeBuilder");
                treebuilder.remove(submodule_path).expect("Couldn't remove submodule path from TreeBuilder");
                treebuilder.insert(submodule_path, subtree_id, 0o040000).expect("Couldn't add submodule as a subdir to TreeBuilder");
                let new_tree_id = treebuilder.write().expect("Couldn't write TreeBuilder into a Tree");
                let new_tree = repo.find_tree(new_tree_id).expect("Couldn't read back the Tree we just wrote");

                // 8.2 in commits that used to update the submodule, add a parent pointing to
                //   appropriate commit in new submodule history
                let mut parent_subtree_ids = HashSet::new();
                for parent in commit.parents() {
                    let parent_tree = parent.tree().expect("Couldn't obtain parent's tree");
                    let parent_subdir_tree_id = parent_tree.get_path(submodule_path).and_then(|x| Ok(x.id()));

                    match parent_subdir_tree_id {
                        Ok(id) => {
                            parent_subtree_ids.insert(id);
                            ()
                        },
                        Err(e) => {
                            if e.code() == git2::ErrorCode::NotFound
                                && e.class() == git2::ErrorClass::Tree
                            {
                                // It's okay; carry on.
                                continue;
                            } else {
                                panic!("Error getting submodule's subdir from the tree: {:?}", e);
                            };
                        },
                    }
                };
                // true if
                //
                // o--o--o--A--
                //             `,-C
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--o--A--B
                //
                // false if
                //
                // o--o--o--A--
                //             `,-A
                //  o--o--o--B-
                //
                //  or
                //
                // o--o--o--A--
                //             `,-B
                //  o--o--o--B-
                let submodule_updated: bool = !parent_subtree_ids.contains(&subtree_id);

                // TODO: rewrite the parents if the submodule was updated
                let parents = {
                    let mut p: Vec<Commit> = Vec::new();
                    for parent_id in commit.parent_ids() {
                        let actual_parent_id = old_id_to_new[&parent_id];
                        let parent = repo.find_commit(actual_parent_id).expect("Couldn't find parent commit by its id");
                        p.push(parent);
                    };

                    if submodule_updated {
                        p.push(submodule_commit);
                    }

                    p
                };

                let mut parents_refs: Vec<&Commit> = Vec::new();
                for i in 0 .. parents.len() {
                    parents_refs.push(&parents[i]);
                }
                let new_commit_id = repo.commit(
                    None,
                    &commit.author(),
                    &commit.committer(),
                    &commit.message().expect("Couldn't retrieve commit's message"),
                    &new_tree,
                    &parents_refs[..])
                    .expect("Failed to commit");

                old_id_to_new.insert(oid, new_commit_id);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };

    let head = repo.head().expect("Couldn't obtain current repo's HEAD");
    let current_ref = head.name().expect("Couldn't obtain the name HEAD points to");
    let mut reference = repo.find_reference(current_ref).expect("Couldn't find the reference HEAD points to");
    let current_id = reference.target().expect("Couldn't resolve current reference to ID");
    let updated_id = old_id_to_new[&current_id];
    reference.set_target(updated_id, &format!("git-submerge reset {} to rewritten history", current_ref)).expect("Couldn't update current reference to point to revritten history");
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<|endoftext|>


<commit_before>#![allow(unknown_lints)]
#![allow(while_let_on_iterator)]

// For a performance boost on Linux
// #![feature(alloc_system)]
// extern crate alloc_system;

#[macro_use]
extern crate bitflags;
extern crate fnv;
extern crate glob;
extern crate liner;
extern crate smallvec;
extern crate smallstring;

#[cfg(not(target_os = "redox"))] extern crate futures;
#[cfg(not(target_os = "redox"))] extern crate tokio_core;
#[cfg(not(target_os = "redox"))] extern crate tokio_signal;

#[cfg(all(unix, not(target_os = "redox")))]
extern crate users as users_unix;

#[macro_use] mod parser;
mod builtins;
mod shell;
mod ascii_helpers;
mod types;

use std::io::{stderr, Write, ErrorKind};

use builtins::Builtin;
use shell::Shell;

#[cfg(not(target_os = "redox"))] use tokio_core::reactor::Core;
#[cfg(not(target_os = "redox"))] use futures::{Future, Stream};

use std::sync::mpsc;
use std::thread;

fn inner_main(sigint_rx : mpsc::Receiver<bool>) {
   let builtins = Builtin::map();
   let mut shell = Shell::new(&builtins, sigint_rx);
   shell.evaluate_init_file();

   if "1" == shell.variables.get_var_or_empty("HISTORY_FILE_ENABLED") {
       shell.context.history.set_file_name(shell.variables.get_var("HISTORY_FILE"));
       match shell.context.history.load_history() {
           Ok(()) => {
               // pass
           }
           Err(ref err) if err.kind() == ErrorKind::NotFound => {
               let history_filename = shell.variables.get_var_or_empty("HISTORY_FILE");
               let _ = writeln!(stderr(), "ion: failed to find history file {}: {}", history_filename, err);
           },
           Err(err) => {
               let _ = writeln!(stderr(), "ion: failed to load history: {}", err);
           }
       }
   }
   shell.execute();
}

#[cfg(not(target_os = "redox"))]
fn main() {
    let (sigint_tx, sigint_rx) = mpsc::channel();

    thread::spawn(move || inner_main(sigint_rx));

    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let ctrl_c = tokio_signal::ctrl_c(&handle).flatten_stream();
    let signal_handler = ctrl_c.for_each(|()| {
        eprintln!("ion: received SIGINT (Ctrl+C)");
        let _ = sigint_tx.send(true);
        Ok(())
    });
    core.run(signal_handler).unwrap();
}

#[cfg(target_os = "redox")]
fn main() {
    let (_, sigint_rx) = mpsc::channel();
    inner_main(sigint_rx);
}
<commit_msg>Replace eprintln! with writeln!(stderr()<commit_after>#![allow(unknown_lints)]
#![allow(while_let_on_iterator)]

// For a performance boost on Linux
// #![feature(alloc_system)]
// extern crate alloc_system;

#[macro_use]
extern crate bitflags;
extern crate fnv;
extern crate glob;
extern crate liner;
extern crate smallvec;
extern crate smallstring;

#[cfg(not(target_os = "redox"))] extern crate futures;
#[cfg(not(target_os = "redox"))] extern crate tokio_core;
#[cfg(not(target_os = "redox"))] extern crate tokio_signal;

#[cfg(all(unix, not(target_os = "redox")))]
extern crate users as users_unix;

#[macro_use] mod parser;
mod builtins;
mod shell;
mod ascii_helpers;
mod types;

use std::io::{stderr, Write, ErrorKind};

use builtins::Builtin;
use shell::Shell;

#[cfg(not(target_os = "redox"))] use tokio_core::reactor::Core;
#[cfg(not(target_os = "redox"))] use futures::{Future, Stream};

use std::sync::mpsc;
use std::thread;

fn inner_main(sigint_rx : mpsc::Receiver<bool>) {
   let builtins = Builtin::map();
   let mut shell = Shell::new(&builtins, sigint_rx);
   shell.evaluate_init_file();

   if "1" == shell.variables.get_var_or_empty("HISTORY_FILE_ENABLED") {
       shell.context.history.set_file_name(shell.variables.get_var("HISTORY_FILE"));
       match shell.context.history.load_history() {
           Ok(()) => {
               // pass
           }
           Err(ref err) if err.kind() == ErrorKind::NotFound => {
               let history_filename = shell.variables.get_var_or_empty("HISTORY_FILE");
               let _ = writeln!(stderr(), "ion: failed to find history file {}: {}", history_filename, err);
           },
           Err(err) => {
               let _ = writeln!(stderr(), "ion: failed to load history: {}", err);
           }
       }
   }
   shell.execute();
}

#[cfg(not(target_os = "redox"))]
fn main() {
    let (sigint_tx, sigint_rx) = mpsc::channel();

    thread::spawn(move || inner_main(sigint_rx));

    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let ctrl_c = tokio_signal::ctrl_c(&handle).flatten_stream();
    let signal_handler = ctrl_c.for_each(|()| {
        let _ = writeln!(stderr(), "ion: received SIGINT (Ctrl+C)");
        let _ = sigint_tx.send(true);
        Ok(())
    });
    core.run(signal_handler).unwrap();
}

#[cfg(target_os = "redox")]
fn main() {
    let (_, sigint_rx) = mpsc::channel();
    inner_main(sigint_rx);
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate chrono;
extern crate curl;
extern crate env_logger;
extern crate flate2;
extern crate rustc_version;
extern crate tar;
extern crate tempdir;
extern crate toml;

#[macro_use]
extern crate log;

use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::hash::{Hash, Hasher, SipHasher};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

use toml::{Parser, Value};

// TODO proper error handling/reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|e| panic!("{} with {}", stringify!($e), e))
    }
}

mod sysroot;

fn main() {
    init_logger();

    let (mut cargo, target) = parse_args();

    if let Some(target) = target.or_else(|| read_config()) {
        let sysroot = sysroot::create(&target);

        if let Some(mut rustflags) = env::var("RUSTFLAGS").ok() {
            rustflags.push_str(&format!(" --sysroot {}", sysroot.display()));
            cargo.env("RUSTFLAGS", rustflags);
        } else {
            cargo.env("RUSTFLAGS", format!("--sysroot {}", sysroot.display()));
        }
    }

    if let Some(code) = try!(try!(cargo.spawn()).wait()).code() {
        process::exit(code);
    }
}

fn init_logger() {
    try!(env_logger::init());
}

/// Custom target with specification file
#[derive(Debug)]
pub struct Target {
    hash: u64,
    path: PathBuf,
    triple: String,
}

impl Target {
    fn from(s: &str) -> Option<Target> {
        let path = &PathBuf::from(format!("{}.json", s));
        if path.is_file() {
            return Some(Target::from_path(path));
        }

        let target_path = &env::var_os("RUST_TARGET_PATH").unwrap_or(OsString::new());

        for dir in env::split_paths(target_path) {
            let path = &dir.join(path);

            if path.is_file() {
                return Some(Target::from_path(path));
            }
        }

        None
    }

    fn from_path(path: &Path) -> Target {
        fn hash(path: &Path) -> u64 {
            let h = &mut SipHasher::new();
            let contents = &mut String::new();
            try!(try!(File::open(path)).read_to_string(contents));
            contents.hash(h);
            h.finish()
        }

        let triple = path.file_stem().unwrap().to_string_lossy().into_owned();
        info!("target: {}", triple);
        Target {
            hash: hash(path),
            path: try!(fs::canonicalize(path)),
            triple: triple,
        }
    }
}

fn parse_args() -> (Command, Option<Target>) {
    let mut cmd = Command::new("cargo");
    let mut target = None;

    let mut next_is_target = false;
    for arg_os in env::args_os().skip(1) {
        if target.is_none() {
            let arg = &*arg_os.to_string_lossy();

            if next_is_target {
                target = Target::from(arg);
            } else {
                if arg == "--target" {
                    next_is_target = true;
                } else if arg.starts_with("--target=") {
                    target = arg.split('=').skip(1).next().and_then(|s| Target::from(s))
                }
            }
        }

        cmd.arg(arg_os);
    }

    (cmd, target)
}

/// Retrieves the build.target field of .cargo/config
fn read_config() -> Option<Target> {
    let mut config = None;
    let mut current = &*try!(env::current_dir());

    // NOTE Same logic as cargo's src/cargo/util/config.rs
    loop {
        let possible = current.join(".cargo/config");

        if fs::metadata(&possible).is_ok() {
            config = Some(possible);
        }

        if let Some(p) = current.parent() {
            current = p;
        } else {
            break;
        }
    }

    config.or_else(|| {
              let home = &env::home_dir().unwrap();

              if !current.starts_with(home) {
                  let config = home.join(".cargo/config");

                  if fs::metadata(&config).is_ok() {
                      Some(config)
                  } else {
                      None
                  }
              } else {
                  None
              }
          })
          .and_then(|p| {
              let s = &mut String::new();
              try!(try!(File::open(p)).read_to_string(s));
              Value::Table(Parser::new(s).parse().unwrap())
                  .lookup("build.target")
                  .and_then(|v| Target::from(v.as_str().unwrap()))
          })
}
<commit_msg>scan arguments that contain whitespace by splitting them first<commit_after>#![deny(warnings)]

extern crate chrono;
extern crate curl;
extern crate env_logger;
extern crate flate2;
extern crate rustc_version;
extern crate tar;
extern crate tempdir;
extern crate toml;

#[macro_use]
extern crate log;

use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::hash::{Hash, Hasher, SipHasher};
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

use toml::{Parser, Value};

// TODO proper error handling/reporting
macro_rules! try {
    ($e:expr) => {
        $e.unwrap_or_else(|e| panic!("{} with {}", stringify!($e), e))
    }
}

mod sysroot;

fn main() {
    init_logger();

    let (mut cargo, target) = parse_args();

    if let Some(target) = target.or_else(|| read_config()) {
        let sysroot = sysroot::create(&target);

        if let Some(mut rustflags) = env::var("RUSTFLAGS").ok() {
            rustflags.push_str(&format!(" --sysroot {}", sysroot.display()));
            cargo.env("RUSTFLAGS", rustflags);
        } else {
            cargo.env("RUSTFLAGS", format!("--sysroot {}", sysroot.display()));
        }
    }

    if let Some(code) = try!(try!(cargo.spawn()).wait()).code() {
        process::exit(code);
    }
}

fn init_logger() {
    try!(env_logger::init());
}

/// Custom target with specification file
#[derive(Debug)]
pub struct Target {
    hash: u64,
    path: PathBuf,
    triple: String,
}

impl Target {
    fn from(s: &str) -> Option<Target> {
        let path = &PathBuf::from(format!("{}.json", s));
        if path.is_file() {
            return Some(Target::from_path(path));
        }

        let target_path = &env::var_os("RUST_TARGET_PATH").unwrap_or(OsString::new());

        for dir in env::split_paths(target_path) {
            let path = &dir.join(path);

            if path.is_file() {
                return Some(Target::from_path(path));
            }
        }

        None
    }

    fn from_path(path: &Path) -> Target {
        fn hash(path: &Path) -> u64 {
            let h = &mut SipHasher::new();
            let contents = &mut String::new();
            try!(try!(File::open(path)).read_to_string(contents));
            contents.hash(h);
            h.finish()
        }

        let triple = path.file_stem().unwrap().to_string_lossy().into_owned();
        info!("target: {}", triple);
        Target {
            hash: hash(path),
            path: try!(fs::canonicalize(path)),
            triple: triple,
        }
    }
}

fn parse_args() -> (Command, Option<Target>) {
    let mut cmd = Command::new("cargo");
    let mut target = None;

    let mut next_is_target = false;
    for arg_os in env::args_os().skip(1) {
        for arg in arg_os.to_string_lossy().split(' ') {
            if target.is_none() {
                if next_is_target {
                    target = Target::from(arg);
                } else {
                    if arg == "--target" {
                        next_is_target = true;
                    } else if arg.starts_with("--target=") {
                        target = arg.split('=').skip(1).next().and_then(|s| Target::from(s))
                    }
                }
            }
        }

        cmd.arg(arg_os);
    }

    (cmd, target)
}

/// Retrieves the build.target field of .cargo/config
fn read_config() -> Option<Target> {
    let mut config = None;
    let mut current = &*try!(env::current_dir());

    // NOTE Same logic as cargo's src/cargo/util/config.rs
    loop {
        let possible = current.join(".cargo/config");

        if fs::metadata(&possible).is_ok() {
            config = Some(possible);
        }

        if let Some(p) = current.parent() {
            current = p;
        } else {
            break;
        }
    }

    config.or_else(|| {
              let home = &env::home_dir().unwrap();

              if !current.starts_with(home) {
                  let config = home.join(".cargo/config");

                  if fs::metadata(&config).is_ok() {
                      Some(config)
                  } else {
                      None
                  }
              } else {
                  None
              }
          })
          .and_then(|p| {
              let s = &mut String::new();
              try!(try!(File::open(p)).read_to_string(s));
              Value::Table(Parser::new(s).parse().unwrap())
                  .lookup("build.target")
                  .and_then(|v| Target::from(v.as_str().unwrap()))
          })
}
<|endoftext|>


<commit_before>// Copyright 2016 rust-fuzz developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use anyhow::Result;
use structopt::StructOpt;

#[macro_use]
mod templates;
mod options;
mod project;
mod utils;

static FUZZ_TARGETS_DIR_OLD: &str = "fuzzers";
static FUZZ_TARGETS_DIR: &str = "fuzz_targets";

// It turns out that `clap`'s `long_about()` makes `cargo fuzz --help`
// unreadable, and its `before_help()` injects our long about text before the
// version, so change the default template slightly.
const LONG_ABOUT_TEMPLATE: &str = "\
{bin} {version}
{about}

USAGE:
    {usage}

{before-help}

{all-args}

{after-help}";

const RUN_BEFORE_HELP: &str = "\
The fuzz target name is the same as the name of the fuzz target script in
fuzz/fuzz_targets/, i.e. the name picked when running `cargo fuzz add`.

This will run the script inside the fuzz target with varying inputs until it
finds a crash, at which point it will save the crash input to the artifact
directory, print some output, and exit. Unless you configure it otherwise (see
libFuzzer options below), this will run indefinitely.

By default fuzz targets are built with optimizations equivalent to
`cargo build --release`, but with debug assertions and overflow checks enabled.
Address Sanitizer is also enabled by default.";

const RUN_AFTER_HELP: &str = "\

A full list of libFuzzer options can be found at
http://llvm.org/docs/LibFuzzer.html#options

You can also get this by running `cargo fuzz run fuzz_target -- -help=1`

Some useful options (to be used as `cargo fuzz run fuzz_target -- <options>`)
include:

  * `-max_len=<len>`: Will limit the length of the input string to `<len>`

  * `-runs=<number>`: Will limit the number of tries (runs) before it gives up

  * `-max_total_time=<time>`: Will limit the amount of time to fuzz before it
    gives up

  * `-timeout=<time>`: Will limit the amount of time for a single run before it
    considers that run a failure

  * `-only_ascii`: Only provide ASCII input

  * `-dict=<file>`: Use a keyword dictionary from specified file. See
    http://llvm.org/docs/LibFuzzer.html#dictionaries\
";

const BUILD_BEFORE_HELP: &str = "\
By default fuzz targets are built with optimizations equivalent to
`cargo build --release`, but with debug assertions and overflow checks enabled.
Address Sanitizer is also enabled by default.";

const BUILD_AFTER_HELP: &str = "\
Sanitizers perform checks necessary for detecting bugs in unsafe code
at the cost of some performance. For more information on sanitizers see
https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html\
";

/// A trait for running our various commands.
trait RunCommand {
    /// Run this command!
    fn run_command(&mut self) -> Result<()>;
}

#[derive(Clone, Debug, StructOpt)]
#[structopt(
    setting(structopt::clap::AppSettings::SubcommandRequiredElseHelp),
    setting(structopt::clap::AppSettings::GlobalVersion),
    version(option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0")),
    about(option_env!("CARGO_PKG_DESCRIPTION").unwrap_or("")),
    // Cargo passes in the subcommand name to the invoked executable. Use a
    // hidden, optional positional argument to deal with it.
    arg(structopt::clap::Arg::with_name("dummy")
        .possible_value("fuzz")
        .required(false)
        .hidden(true)),
)]
enum Command {
    /// Initialize the fuzz directory
    Init(options::Init),

    /// Add a new fuzz target
    Add(options::Add),

    #[structopt(
        template(LONG_ABOUT_TEMPLATE),
        before_help(BUILD_BEFORE_HELP),
        after_help(BUILD_AFTER_HELP)
    )]
    /// Build fuzz targets
    Build(options::Build),

    /// Print the `std::fmt::Debug` output for an input
    Fmt(options::Fmt),

    /// List all the existing fuzz targets
    List(options::List),

    #[structopt(
        template(LONG_ABOUT_TEMPLATE),
        before_help(RUN_BEFORE_HELP),
        after_help(RUN_AFTER_HELP)
    )]
    /// Run a fuzz target
    Run(options::Run),

    /// Minify a corpus
    Cmin(options::Cmin),

    /// Minify a test case
    Tmin(options::Tmin),

    /// Run program on the generated corpus and generate coverage information
    Coverage(options::Coverage),
}

impl RunCommand for Command {
    fn run_command(&mut self) -> Result<()> {
        match self {
            Command::Init(x) => x.run_command(),
            Command::Add(x) => x.run_command(),
            Command::Build(x) => x.run_command(),
            Command::List(x) => x.run_command(),
            Command::Fmt(x) => x.run_command(),
            Command::Run(x) => x.run_command(),
            Command::Cmin(x) => x.run_command(),
            Command::Tmin(x) => x.run_command(),
            Command::Coverage(x) => x.run_command(),
        }
    }
}

fn main() -> Result<()> {
    Command::from_args().run_command()
}
<commit_msg>Fix warning about multiple newlines being escaped<commit_after>// Copyright 2016 rust-fuzz developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use anyhow::Result;
use structopt::StructOpt;

#[macro_use]
mod templates;
mod options;
mod project;
mod utils;

static FUZZ_TARGETS_DIR_OLD: &str = "fuzzers";
static FUZZ_TARGETS_DIR: &str = "fuzz_targets";

// It turns out that `clap`'s `long_about()` makes `cargo fuzz --help`
// unreadable, and its `before_help()` injects our long about text before the
// version, so change the default template slightly.
const LONG_ABOUT_TEMPLATE: &str = "\
{bin} {version}
{about}

USAGE:
    {usage}

{before-help}

{all-args}

{after-help}";

const RUN_BEFORE_HELP: &str = "\
The fuzz target name is the same as the name of the fuzz target script in
fuzz/fuzz_targets/, i.e. the name picked when running `cargo fuzz add`.

This will run the script inside the fuzz target with varying inputs until it
finds a crash, at which point it will save the crash input to the artifact
directory, print some output, and exit. Unless you configure it otherwise (see
libFuzzer options below), this will run indefinitely.

By default fuzz targets are built with optimizations equivalent to
`cargo build --release`, but with debug assertions and overflow checks enabled.
Address Sanitizer is also enabled by default.";

const RUN_AFTER_HELP: &str = "\
A full list of libFuzzer options can be found at
http://llvm.org/docs/LibFuzzer.html#options

You can also get this by running `cargo fuzz run fuzz_target -- -help=1`

Some useful options (to be used as `cargo fuzz run fuzz_target -- <options>`)
include:

  * `-max_len=<len>`: Will limit the length of the input string to `<len>`

  * `-runs=<number>`: Will limit the number of tries (runs) before it gives up

  * `-max_total_time=<time>`: Will limit the amount of time to fuzz before it
    gives up

  * `-timeout=<time>`: Will limit the amount of time for a single run before it
    considers that run a failure

  * `-only_ascii`: Only provide ASCII input

  * `-dict=<file>`: Use a keyword dictionary from specified file. See
    http://llvm.org/docs/LibFuzzer.html#dictionaries\
";

const BUILD_BEFORE_HELP: &str = "\
By default fuzz targets are built with optimizations equivalent to
`cargo build --release`, but with debug assertions and overflow checks enabled.
Address Sanitizer is also enabled by default.";

const BUILD_AFTER_HELP: &str = "\
Sanitizers perform checks necessary for detecting bugs in unsafe code
at the cost of some performance. For more information on sanitizers see
https://doc.rust-lang.org/unstable-book/compiler-flags/sanitizer.html\
";

/// A trait for running our various commands.
trait RunCommand {
    /// Run this command!
    fn run_command(&mut self) -> Result<()>;
}

#[derive(Clone, Debug, StructOpt)]
#[structopt(
    setting(structopt::clap::AppSettings::SubcommandRequiredElseHelp),
    setting(structopt::clap::AppSettings::GlobalVersion),
    version(option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0")),
    about(option_env!("CARGO_PKG_DESCRIPTION").unwrap_or("")),
    // Cargo passes in the subcommand name to the invoked executable. Use a
    // hidden, optional positional argument to deal with it.
    arg(structopt::clap::Arg::with_name("dummy")
        .possible_value("fuzz")
        .required(false)
        .hidden(true)),
)]
enum Command {
    /// Initialize the fuzz directory
    Init(options::Init),

    /// Add a new fuzz target
    Add(options::Add),

    #[structopt(
        template(LONG_ABOUT_TEMPLATE),
        before_help(BUILD_BEFORE_HELP),
        after_help(BUILD_AFTER_HELP)
    )]
    /// Build fuzz targets
    Build(options::Build),

    /// Print the `std::fmt::Debug` output for an input
    Fmt(options::Fmt),

    /// List all the existing fuzz targets
    List(options::List),

    #[structopt(
        template(LONG_ABOUT_TEMPLATE),
        before_help(RUN_BEFORE_HELP),
        after_help(RUN_AFTER_HELP)
    )]
    /// Run a fuzz target
    Run(options::Run),

    /// Minify a corpus
    Cmin(options::Cmin),

    /// Minify a test case
    Tmin(options::Tmin),

    /// Run program on the generated corpus and generate coverage information
    Coverage(options::Coverage),
}

impl RunCommand for Command {
    fn run_command(&mut self) -> Result<()> {
        match self {
            Command::Init(x) => x.run_command(),
            Command::Add(x) => x.run_command(),
            Command::Build(x) => x.run_command(),
            Command::List(x) => x.run_command(),
            Command::Fmt(x) => x.run_command(),
            Command::Run(x) => x.run_command(),
            Command::Cmin(x) => x.run_command(),
            Command::Tmin(x) => x.run_command(),
            Command::Coverage(x) => x.run_command(),
        }
    }
}

fn main() -> Result<()> {
    Command::from_args().run_command()
}
<|endoftext|>


<commit_before>use std::fmt;
use std::fmt::Display;
use std::iter;

struct Tower {
    weight : usize,
}

impl Tower {
    pub fn new(weight: usize) -> Tower {
        Tower { weight : weight }
    }

    pub fn weight(&self) -> usize {
        self.weight
    }
}

impl Display for Tower {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        f.write_fmt(format_args!("[{0}]", iter::repeat("#").take(self.weight()).collect::<String>()))
    }
}

struct Rod {
    stack : Vec<Tower>,
}

impl Rod {
    pub fn new(sizes : &[usize]) -> Rod {
        Rod { stack : sizes.iter().map(|x| Tower::new(*x) ).collect::<Vec<_>>() }
    }

    fn can_take_tower(&self, tower : &Tower) -> bool {
        match self.stack.last() {
            None => true,
            Some(t) => t.weight() > tower.weight(),
        }
    }

    pub fn can_take(&self, other: &Rod) -> bool {
        false // self.can_take_tower(&other.stack.last())
    }

    pub fn swap(& mut self, other: &mut Rod) -> &mut Self {
        self
    }

}

struct Desk {
    rods : Vec<Rod>,
}

impl Desk {
    pub fn new(rods: &[&[usize]]) -> Desk {
        Desk { rods : rods.iter().map(|x| Rod::new(*x) ).collect::<Vec<_>>() }
    }
}

impl Display for Desk {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        let max_weight : usize =
            self.rods.iter()
                .map(|r| r.stack.iter().map(|s| s.weight()).max().unwrap_or(0))
                .max().unwrap_or(0);
        let height = max_weight + 1;

        let dup = |s: &str, n: usize| iter::repeat(s).take(n).collect::<String>();
        let mut display : Vec<String> = Vec::new();

        for i in 0..height {
            let mut s : String = "  ".to_string();

            for rod in &self.rods {
                let stower: Option<&Tower> = rod.stack.get(i);
                let tmp = match stower {
                    None => format!("{0}|{0}", dup(" ", max_weight + 1)),
                    Some(tower) => format!("{1}[{0}|{0}]{1}", dup("#", tower.weight()), dup(" ", max_weight - tower.weight())),
                };
                s.push_str(" ");
                s.push_str(&tmp);
            }

            s.push_str("  ");
            s.push_str("\n");
            display.push(s);
        }

        display.reverse();
        f.write_fmt(format_args!("{0}", display.connect("")))
    }
}

fn main() {
    let desk1 = Desk::new(&[
        &[4, 3],
        &[2, 1]
    ]);
    println!("{0}", desk1);

}
<commit_msg>Fix terminology: tower -> disk<commit_after>use std::fmt;
use std::fmt::Display;
use std::iter;

struct Disk {
    weight : usize,
}

impl Disk {
    pub fn new(weight: usize) -> Disk {
        Disk { weight : weight }
    }

    pub fn weight(&self) -> usize {
        self.weight
    }
}

impl Display for Disk {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        f.write_fmt(format_args!("[{0}]", iter::repeat("#").take(self.weight()).collect::<String>()))
    }
}

struct Rod {
    stack : Vec<Disk>,
}

impl Rod {
    pub fn new(sizes : &[usize]) -> Rod {
        Rod { stack : sizes.iter().map(|x| Disk::new(*x) ).collect::<Vec<_>>() }
    }

    fn can_move_disk(&self, disk : &Disk) -> bool {
        match self.stack.last() {
            None => true,
            Some(t) => t.weight() > disk.weight(),
        }
    }

    pub fn can_move(&self, other: &Rod) -> bool {
        false // self.can_take_disk(&other.stack.last())
    }

    pub fn swap(& mut self, other: &mut Rod) -> &mut Self {
        self
    }

}

struct Desk {
    rods : Vec<Rod>,
}

impl Desk {
    pub fn new(rods: &[&[usize]]) -> Desk {
        Desk { rods : rods.iter().map(|x| Rod::new(*x) ).collect::<Vec<_>>() }
    }
}

impl Display for Desk {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        let max_weight : usize =
            self.rods.iter()
                .map(|r| r.stack.iter().map(|s| s.weight()).max().unwrap_or(0))
                .max().unwrap_or(0);
        let height = max_weight + 1;

        let dup = |s: &str, n: usize| iter::repeat(s).take(n).collect::<String>();
        let mut display : Vec<String> = Vec::new();

        for i in 0..height {
            let mut s : String = "  ".to_string();

            for rod in &self.rods {
                let tmp = match rod.stack.get(i) {
                    None => format!("{0}|{0}", dup(" ", max_weight + 1)),
                    Some(disk) => format!("{1}[{0}|{0}]{1}", dup("#", disk.weight()), dup(" ", max_weight - disk.weight())),
                };
                s.push_str(" ");
                s.push_str(&tmp);
            }

            s.push_str("  ");
            s.push_str("\n");
            display.push(s);
        }

        display.reverse();
        f.write_fmt(format_args!("{0}", display.connect("")))
    }
}

fn main() {
    let desk1 = Desk::new(&[
        &[4, 3],
        &[2, 1]
    ]);
    println!("{0}", desk1);

}
<|endoftext|>