<commit_before>extern crate clap;
extern crate iron;
extern crate pretty_bytes;
extern crate chrono;
extern crate ansi_term;

use std::env;
use std::fs::{self, File};
use std::path::{PathBuf};
use std::error::Error;
use std::time::{SystemTime, UNIX_EPOCH};
use std::os::unix::ffi::OsStrExt;

use iron::headers;
use iron::status;
use iron::mime;
use iron::{Iron, Request, Response, IronResult, Set, Chain, Handler, AfterMiddleware};
use pretty_bytes::converter::convert;
use chrono::{DateTime, Local, TimeZone};
use ansi_term::Colour::{Red, Green, Yellow, Blue};

fn main() {
    let matches = clap::App::new("Simple HTTP Server")
        .arg(clap::Arg::with_name("root")
             .index(1)
             .validator(|s| {
                 match File::open(s) {
                     Ok(f) => {
                         if f.metadata().unwrap().is_dir() { Ok(()) } else {
                             Err("Not directory".to_owned())
                         }
                     },
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Root directory"))
        .arg(clap::Arg::with_name("index")
             .short("i")
             .long("index")
             .help("Automatic render index page [index.html, index.htm]"))
        .arg(clap::Arg::with_name("port")
             .short("p")
             .long("port")
             .takes_value(true)
             .default_value("8000")
             .validator(|s| {
                 match s.parse::<u16>() {
                     Ok(_) => Ok(()),
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Port number"))
        .arg(clap::Arg::with_name("threads")
             .short("t")
             .long("threads")
             .takes_value(true)
             .default_value("3")
             .validator(|s| {
                 match s.parse::<u8>() {
                     Ok(v) => {
                         if v > 0 { Ok(()) } else {
                             Err("Nagetive threads".to_owned())
                         }
                     }
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("How many http threads"))
        .get_matches();
    let root = matches
        .value_of("root")
        .map(|s| PathBuf::from(s))
        .unwrap_or(env::current_dir().unwrap());
    let index = matches.is_present("index");
    let port = matches
        .value_of("port")
        .unwrap()
        .parse::<u16>()
        .unwrap();
    let threads = matches
        .value_of("threads")
        .unwrap()
        .parse::<u8>()
        .unwrap();

    println!("[Root]: {}", Blue.paint(root.to_str().unwrap()));
    let addr = format!("0.0.0.0:{}", port);
    println!("[Listening on]: {}",
             Blue.paint(format!("http://{}", addr)));
    println!("====== [{}] ({} threads) ======",
             Blue.paint(now_string()),
             Blue.paint(threads.to_string()));

    let mut chain = Chain::new(MainHandler{root: root, index: index});
    chain.link_after(RequestLogger);
    let mut server = Iron::new(chain);
    server.threads = threads as usize;
    server.http(addr).unwrap();
}

struct MainHandler { root: PathBuf, index: bool }
struct RequestLogger;

impl MainHandler {
    fn send_file(&self, mut resp: Response, path: &PathBuf) -> IronResult<Response> {
        resp.set_mut(path.as_path());
        if resp.headers.get::<headers::ContentType>() == Some(
            &headers::ContentType(mime::Mime(mime::TopLevel::Text, mime::SubLevel::Plain, vec![]))
        ) {
            resp.headers.set(headers::ContentDisposition {
                disposition: headers::DispositionType::Attachment,
                parameters: vec![headers::DispositionParam::Filename(
                    headers::Charset::Ext("utf-8".to_owned()), // The character set for the bytes of the filename
                    None, // The optional language tag (see `language-tag` crate)
                    path.file_name().unwrap().as_bytes().to_vec() // the actual bytes of the filename
                )]
            });
            let default_mime: iron::mime::Mime = "application/octet-stream".parse().unwrap();
            resp.headers.set(headers::ContentType(default_mime));
        }
        return Ok(resp)
    }
}

impl Handler for MainHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut path = self.root.clone();
        for part in req.url.path() {
            path.push(part);
        }
        let root_link: &'static str = "[ROOT]";

        match File::open(&path) {
            Ok(f) => {
                let mut resp = Response::with(status::Ok);
                let metadata = f.metadata().unwrap();
                if metadata.is_dir() {
                    let mut rows = Vec::new();

                    let path_prefix = req.url.path()
                        .into_iter()
                        .filter(|s| !s.is_empty())
                        .collect::<Vec<&str>>();
                    let breadcrumb = if path_prefix.len() > 0 {
                        let mut breadcrumb = path_prefix.clone();
                        let mut bread_links: Vec<String> = Vec::new();
                        bread_links.push(breadcrumb.pop().unwrap().to_owned());
                        while breadcrumb.len() > 0 {
                            let link = breadcrumb.join("/");
                            bread_links.push(format!(
                                "{}",
                                link, breadcrumb.pop().unwrap().to_owned(),
                            ));
                        }
                        bread_links.push(root_link.to_owned());
                        bread_links.reverse();
                        bread_links.join(" / ")
                    } else { root_link.to_owned() };

                    if path_prefix.len() > 0 {
                        let mut link = path_prefix.clone();
                        link.pop();
                        rows.push(format!(
                            "
{}
",
                            link.join("/"), "[Parent Directory]"
                        ));
                    } else {
                        rows.push("

".to_owned());
                    }
                    for entry in fs::read_dir(&path).unwrap() {
                        let entry = entry.unwrap();
                        let entry_meta = entry.metadata().unwrap();
                        let file_name = entry.file_name().into_string().unwrap();
                        if self.index {
                            for fname in vec!["index.html", "index.htm"] {
                                if file_name == fname {
                                    path.push(file_name);
                                    return self.send_file(resp, &path);
                                }
                            }
                        }
                        let file_modified = system_time_to_date_time(entry_meta.modified().unwrap())
                            .format("%Y-%m-%d %H:%M:%S").to_string();
                        let file_size = convert(entry_meta.len() as f64);
                        let file_type = entry_meta.file_type();
                        let link_style = if file_type.is_dir() {
                            "style=\"text-decoration: none; font-weight: bold;\"".to_owned()
                        } else {
                            "style=\"text-decoration: none;\"".to_owned()
                        };
                        let mut link = path_prefix.clone();
                        link.push(&file_name);
                        let link = link.join("/");
                        rows.push(format!(
                            "
{}	 [{}]	 <bold>{}</bold>
",
                            link_style, link, file_name, file_modified, file_size
                        ));
                    }
                    resp.headers.set(headers::ContentType::html());
                    resp.set_mut(format!(
                        "{}

{}
",
                        breadcrumb, rows.join("\n")
                    ));
                    Ok(resp)
                } else {
                    self.send_file(resp, &path)
                }
            },
            Err(e) => {
                let mut resp = Response::with((status::NotFound, format!(
                    "{}

[ERROR]: {}
",
                    root_link, e.description().to_string()
                )));
                resp.headers.set(headers::ContentType::html());
                Ok(resp)
            }
        }
    }
}

impl AfterMiddleware for RequestLogger {
    fn after(&self, req: &mut Request, resp: Response) -> IronResult<Response> {
        let status = resp.status.unwrap();
        let status_str = if status.is_success() {
            Green.bold().paint(status.to_u16().to_string())
        } else if status.is_informational() {
            Yellow.bold().paint(status.to_u16().to_string())
        } else {
            Red.bold().paint(status.to_u16().to_string())
        };

        println!(
            // datetime, remote-ip, status, method, url-path
            "[{}] - {} - {} - {} /{}",
            now_string(),
            req.remote_addr.ip(),
            status_str,
            req.method,
            req.url.as_ref().to_string().splitn(4, '/').collect::<Vec<&str>>().pop().unwrap()
        );
        Ok(resp)
    }
}

fn now_string() -> String {
    Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}

fn system_time_to_date_time(t: SystemTime) -> DateTime<Local> {
    let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
        Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()),
        Err(e) => { // unlikely but should be handled
            let dur = e.duration();
            let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos());
            if nsec == 0 {
                (-sec, 0)
            } else {
                (-sec - 1, 1_000_000_000 - nsec)
            }
        },
    };
    Local.timestamp(sec, nsec)
}
<commit_msg>Update log format<commit_after>extern crate clap;
extern crate iron;
extern crate pretty_bytes;
extern crate chrono;
extern crate ansi_term;

use std::env;
use std::fs::{self, File};
use std::path::{PathBuf};
use std::error::Error;
use std::time::{SystemTime, UNIX_EPOCH};
use std::os::unix::ffi::OsStrExt;

use iron::headers;
use iron::status;
use iron::mime;
use iron::{Iron, Request, Response, IronResult, Set, Chain, Handler, AfterMiddleware};
use pretty_bytes::converter::convert;
use chrono::{DateTime, Local, TimeZone};
use ansi_term::Colour::{Red, Green, Yellow, Blue};

fn main() {
    let matches = clap::App::new("Simple HTTP Server")
        .arg(clap::Arg::with_name("root")
             .index(1)
             .validator(|s| {
                 match File::open(s) {
                     Ok(f) => {
                         if f.metadata().unwrap().is_dir() { Ok(()) } else {
                             Err("Not directory".to_owned())
                         }
                     },
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Root directory"))
        .arg(clap::Arg::with_name("index")
             .short("i")
             .long("index")
             .help("Automatic render index page [index.html, index.htm]"))
        .arg(clap::Arg::with_name("port")
             .short("p")
             .long("port")
             .takes_value(true)
             .default_value("8000")
             .validator(|s| {
                 match s.parse::<u16>() {
                     Ok(_) => Ok(()),
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("Port number"))
        .arg(clap::Arg::with_name("threads")
             .short("t")
             .long("threads")
             .takes_value(true)
             .default_value("3")
             .validator(|s| {
                 match s.parse::<u8>() {
                     Ok(v) => {
                         if v > 0 { Ok(()) } else {
                             Err("Nagetive threads".to_owned())
                         }
                     }
                     Err(e) => Err(e.description().to_string())
                 }
             })
             .help("How many http threads"))
        .get_matches();
    let root = matches
        .value_of("root")
        .map(|s| PathBuf::from(s))
        .unwrap_or(env::current_dir().unwrap());
    let index = matches.is_present("index");
    let port = matches
        .value_of("port")
        .unwrap()
        .parse::<u16>()
        .unwrap();
    let threads = matches
        .value_of("threads")
        .unwrap()
        .parse::<u8>()
        .unwrap();

    let addr = format!("0.0.0.0:{}", port);
    println!("   Root: {}", Blue.paint(root.to_str().unwrap()));
    println!("  Index: {}", Blue.paint(index.to_string()));
    println!("Address: {}", Blue.paint(format!("http://{}", addr)));
    println!("======== [{}] ========", Blue.paint(now_string()));

    let mut chain = Chain::new(MainHandler{root: root, index: index});
    chain.link_after(RequestLogger);
    let mut server = Iron::new(chain);
    server.threads = threads as usize;
    server.http(addr).unwrap();
}

struct MainHandler { root: PathBuf, index: bool }
struct RequestLogger;

impl MainHandler {
    fn send_file(&self, mut resp: Response, path: &PathBuf) -> IronResult<Response> {
        resp.set_mut(path.as_path());
        if resp.headers.get::<headers::ContentType>() == Some(
            &headers::ContentType(mime::Mime(mime::TopLevel::Text, mime::SubLevel::Plain, vec![]))
        ) {
            resp.headers.set(headers::ContentDisposition {
                disposition: headers::DispositionType::Attachment,
                parameters: vec![headers::DispositionParam::Filename(
                    headers::Charset::Ext("utf-8".to_owned()), // The character set for the bytes of the filename
                    None, // The optional language tag (see `language-tag` crate)
                    path.file_name().unwrap().as_bytes().to_vec() // the actual bytes of the filename
                )]
            });
            let default_mime: iron::mime::Mime = "application/octet-stream".parse().unwrap();
            resp.headers.set(headers::ContentType(default_mime));
        }
        return Ok(resp)
    }
}

impl Handler for MainHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let mut path = self.root.clone();
        for part in req.url.path() {
            path.push(part);
        }
        let root_link: &'static str = "[ROOT]";

        match File::open(&path) {
            Ok(f) => {
                let mut resp = Response::with(status::Ok);
                let metadata = f.metadata().unwrap();
                if metadata.is_dir() {
                    let mut rows = Vec::new();

                    let path_prefix = req.url.path()
                        .into_iter()
                        .filter(|s| !s.is_empty())
                        .collect::<Vec<&str>>();
                    let breadcrumb = if path_prefix.len() > 0 {
                        let mut breadcrumb = path_prefix.clone();
                        let mut bread_links: Vec<String> = Vec::new();
                        bread_links.push(breadcrumb.pop().unwrap().to_owned());
                        while breadcrumb.len() > 0 {
                            let link = breadcrumb.join("/");
                            bread_links.push(format!(
                                "{}",
                                link, breadcrumb.pop().unwrap().to_owned(),
                            ));
                        }
                        bread_links.push(root_link.to_owned());
                        bread_links.reverse();
                        bread_links.join(" / ")
                    } else { root_link.to_owned() };

                    if path_prefix.len() > 0 {
                        let mut link = path_prefix.clone();
                        link.pop();
                        rows.push(format!(
                            "
{}
",
                            link.join("/"), "[Parent Directory]"
                        ));
                    } else {
                        rows.push("

".to_owned());
                    }
                    for entry in fs::read_dir(&path).unwrap() {
                        let entry = entry.unwrap();
                        let entry_meta = entry.metadata().unwrap();
                        let file_name = entry.file_name().into_string().unwrap();
                        if self.index {
                            for fname in vec!["index.html", "index.htm"] {
                                if file_name == fname {
                                    path.push(file_name);
                                    return self.send_file(resp, &path);
                                }
                            }
                        }
                        let file_modified = system_time_to_date_time(entry_meta.modified().unwrap())
                            .format("%Y-%m-%d %H:%M:%S").to_string();
                        let file_size = convert(entry_meta.len() as f64);
                        let file_type = entry_meta.file_type();
                        let link_style = if file_type.is_dir() {
                            "style=\"text-decoration: none; font-weight: bold;\"".to_owned()
                        } else {
                            "style=\"text-decoration: none;\"".to_owned()
                        };
                        let mut link = path_prefix.clone();
                        link.push(&file_name);
                        let link = link.join("/");
                        rows.push(format!(
                            "
{}	 [{}]	 <bold>{}</bold>
",
                            link_style, link, file_name, file_modified, file_size
                        ));
                    }
                    resp.headers.set(headers::ContentType::html());
                    resp.set_mut(format!(
                        "{}

{}
",
                        breadcrumb, rows.join("\n")
                    ));
                    Ok(resp)
                } else {
                    self.send_file(resp, &path)
                }
            },
            Err(e) => {
                let mut resp = Response::with((status::NotFound, format!(
                    "{}

[ERROR]: {}
",
                    root_link, e.description().to_string()
                )));
                resp.headers.set(headers::ContentType::html());
                Ok(resp)
            }
        }
    }
}

impl AfterMiddleware for RequestLogger {
    fn after(&self, req: &mut Request, resp: Response) -> IronResult<Response> {
        let status = resp.status.unwrap();
        let status_str = if status.is_success() {
            Green.bold().paint(status.to_u16().to_string())
        } else if status.is_informational() {
            Yellow.bold().paint(status.to_u16().to_string())
        } else {
            Red.bold().paint(status.to_u16().to_string())
        };

        println!(
            // datetime, remote-ip, status-code, method, url-path
            "[{}] - {} - {} - {} /{}",
            now_string(),
            req.remote_addr.ip(),
            status_str,
            req.method,
            req.url.as_ref().to_string().splitn(4, '/').collect::<Vec<&str>>().pop().unwrap()
        );
        Ok(resp)
    }
}

fn now_string() -> String {
    Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}

fn system_time_to_date_time(t: SystemTime) -> DateTime<Local> {
    let (sec, nsec) = match t.duration_since(UNIX_EPOCH) {
        Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()),
        Err(e) => { // unlikely but should be handled
            let dur = e.duration();
            let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos());
            if nsec == 0 {
                (-sec, 0)
            } else {
                (-sec - 1, 1_000_000_000 - nsec)
            }
        },
    };
    Local.timestamp(sec, nsec)
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(docopt_macros)]

#![feature(test)]

extern crate jsrs_common;
extern crate jsrs_parser;
extern crate french_press;

extern crate walkdir;
extern crate unescape;
extern crate rustyline;
extern crate rustc_serialize;
extern crate docopt;

extern crate test;

#[macro_use]
mod var;

mod eval;
mod native;
mod number;
mod bench;
mod preprocess;

use std::cell::RefCell;
use std::fs::{File, metadata};
use std::io::{self, BufReader};
use std::io::prelude::*;
use std::path::Path;
use std::process::exit;
use std::rc::Rc;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use walkdir::WalkDir;

use jsrs_common::types::coerce::AsString;
use french_press::{init_gc, ScopeManager};

use eval::eval_string;
use native::add_pervasives;
use preprocess::{clean_string, add_semicolon};
use jsrs_common::js_error::{self, JsError};

docopt!(Args derive Debug, "
js.rs - a javascript interpreter

Usage:
jsrs
jsrs <file>
jsrs --test
");

fn eval_file(filename: String, debug: bool, should_repl: bool,
             scope_manager: Rc<RefCell<ScopeManager>>) -> js_error::Result<()> {
    if debug {
        println!("Reading from \"{}\"", filename);
    }

    let path = Path::new(&filename);
    let file = File::open(&path)
        .expect(&format!("Cannot open \"{}\": no such file or directory", filename));
    let file_buffer = BufReader::new(file);

    // Build lines
    let mut line_builder = String::new();
    let mut braces = Vec::new();
    let mut negative_test = false;

    let mut file_iter = file_buffer.lines();
    while let Some(line) = file_iter.next() {
        let input = String::from(line.expect(&format!("Cannot read from {}", filename)));
        let input = clean_string(input);
        if input.contains("@negative") {
            negative_test = true;
        }

        let mut last = '\0';
        // Match braces to see if we should wait for more input
        for c in input.chars() {
            if c == '(' {
                braces.push('(');
            } else if c == '{' {
                braces.push('{');
            } else if c == '*' && last == '/' {
                braces.push('/');
            } else if c == ')' {
                if braces.pop() != Some('(') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            } else if c == '}' {
                if braces.pop() != Some('{') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            } else if c == '/' && last == '*' {
                if braces.pop() != Some('/') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            }
            last = c;
        }

        line_builder.push_str(&input);

        if braces.len() == 0 {
            let js_string = clean_string(line_builder.clone());
            if js_string == "" {
                continue;
            }

            if debug {
                println!("\n{}", js_string);
            }

            let ret;
            let eval_result = eval_string(&js_string, scope_manager.clone());
            if let Err(JsError::ParseError(_)) = eval_result {
                line_builder.push_str(" ");
                continue;
            }

            if negative_test {
                match eval_result {
                    Ok((var, ptr)) => ret = (var, ptr),
                    Err(e) => {
                        if !e.is_meta_error() {
                            return Ok(());
                        } else {
                            return Err(e);
                        }
                    }
                }
            } else {
                ret = try!(eval_result);
            }

            if debug {
                println!("=> {:?}", ret);
            }

            // Clear line builder for next statement.
            line_builder = String::new();
        }
    }
    if line_builder != "" {
        let ret;
        let js_string = clean_string(line_builder.clone());
        let eval_result = eval_string(&js_string, scope_manager.clone());

        if negative_test {
            match eval_result {
                Ok((var, ptr)) => ret = (var, ptr),
                Err(e) => {
                    if !e.is_meta_error() {
                        return Ok(());
                    } else {
                        return Err(e);
                    }
                }
            }
        } else {
            ret = try!(eval_result);
        }

        if debug {
            println!("=> {:?}", ret);
        }
    }
    if should_repl {
        repl(scope_manager);
    }
    Ok(())
}

fn test_dir(dir_name: String) {
    for entry in WalkDir::new(dir_name) {
        let entry = entry.unwrap();
        if !entry.path().is_dir() {
            let entry_path = entry.path().display().to_string();
            let scope_manager = Rc::new(RefCell::new(init_gc()));
            add_pervasives(scope_manager.clone());
            println!("{}", entry_path);
            match eval_file(entry_path.clone(), false, false, scope_manager.clone()) {
                Ok(_) => {
                    println!("{}: {}", entry_path, "OK");
                }
                Err(e) => {
                    println!("{}: {}", entry_path, e);
                }
            }
        }
    }
}

fn repl(scope_manager: Rc<RefCell<ScopeManager>>) -> i32 {
    let mut rl = Editor::new();
    let mut stderr = io::stderr();

    if metadata(".history").is_ok() && rl.load_history(".history").is_err() {
        writeln!(stderr, "Error: unable to load history on startup").unwrap();
    }

    loop {
        // prompt
        let readline = rl.readline(">> ");

        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let input = add_semicolon(clean_string(String::from(line)));
                if input == "" {
                    continue;
                }
                rl.add_history_entry(&input);

                match eval_string(&input, scope_manager.clone()) {
                    Ok((var, ptr)) => println!("{}", ptr.map(|p| p.as_string()).unwrap_or(var.t.as_string())),
                    Err(e) => println!("{:?}", e),
                }
            },
            Err(ReadlineError::Interrupted) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 1;
            },
            Err(ReadlineError::Eof) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);

                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 3;
            }
        }
    }
    0
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    if args.flag_test {
        let dir_name = "sputnik";

        test_dir(String::from(dir_name))
    } else {
        let scope_manager = Rc::new(RefCell::new(init_gc()));
        add_pervasives(scope_manager.clone());

        if args.arg_file == "" {
            let ret = repl(scope_manager.clone());
            exit(ret)
        } else {
            eval_file(args.arg_file, true, true, scope_manager.clone())
                .expect("Error evaluating file");
        }
    }
}
<commit_msg>remove filename printouts<commit_after>#![feature(plugin)]
#![plugin(docopt_macros)]

#![feature(test)]

extern crate jsrs_common;
extern crate jsrs_parser;
extern crate french_press;

extern crate walkdir;
extern crate unescape;
extern crate rustyline;
extern crate rustc_serialize;
extern crate docopt;

extern crate test;

#[macro_use]
mod var;

mod eval;
mod native;
mod number;
mod bench;
mod preprocess;

use std::cell::RefCell;
use std::fs::{File, metadata};
use std::io::{self, BufReader};
use std::io::prelude::*;
use std::path::Path;
use std::process::exit;
use std::rc::Rc;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use walkdir::WalkDir;

use jsrs_common::types::coerce::AsString;
use french_press::{init_gc, ScopeManager};

use eval::eval_string;
use native::add_pervasives;
use preprocess::{clean_string, add_semicolon};
use jsrs_common::js_error::{self, JsError};

docopt!(Args derive Debug, "
js.rs - a javascript interpreter

Usage:
jsrs
jsrs <file>
jsrs --test
");

fn eval_file(filename: String, debug: bool, should_repl: bool,
             scope_manager: Rc<RefCell<ScopeManager>>) -> js_error::Result<()> {
    if debug {
        println!("Reading from \"{}\"", filename);
    }

    let path = Path::new(&filename);
    let file = File::open(&path)
        .expect(&format!("Cannot open \"{}\": no such file or directory", filename));
    let file_buffer = BufReader::new(file);

    // Build lines
    let mut line_builder = String::new();
    let mut braces = Vec::new();
    let mut negative_test = false;

    let mut file_iter = file_buffer.lines();
    while let Some(line) = file_iter.next() {
        let input = String::from(line.expect(&format!("Cannot read from {}", filename)));
        let input = clean_string(input);
        if input.contains("@negative") {
            negative_test = true;
        }

        let mut last = '\0';
        // Match braces to see if we should wait for more input
        for c in input.chars() {
            if c == '(' {
                braces.push('(');
            } else if c == '{' {
                braces.push('{');
            } else if c == '*' && last == '/' {
                braces.push('/');
            } else if c == ')' {
                if braces.pop() != Some('(') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            } else if c == '}' {
                if braces.pop() != Some('{') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            } else if c == '/' && last == '*' {
                if braces.pop() != Some('/') {
                    return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                }
            }
            last = c;
        }

        line_builder.push_str(&input);

        if braces.len() == 0 {
            let js_string = clean_string(line_builder.clone());
            if js_string == "" {
                continue;
            }

            if debug {
                println!("\n{}", js_string);
            }

            let ret;
            let eval_result = eval_string(&js_string, scope_manager.clone());
            if let Err(JsError::ParseError(_)) = eval_result {
                line_builder.push_str(" ");
                continue;
            }

            if negative_test {
                match eval_result {
                    Ok((var, ptr)) => ret = (var, ptr),
                    Err(e) => {
                        if !e.is_meta_error() {
                            return Ok(());
                        } else {
                            return Err(e);
                        }
                    }
                }
            } else {
                ret = try!(eval_result);
            }

            if debug {
                println!("=> {:?}", ret);
            }

            // Clear line builder for next statement.
            line_builder = String::new();
        }
    }
    if line_builder != "" {
        let ret;
        let js_string = clean_string(line_builder.clone());
        let eval_result = eval_string(&js_string, scope_manager.clone());

        if negative_test {
            match eval_result {
                Ok((var, ptr)) => ret = (var, ptr),
                Err(e) => {
                    if !e.is_meta_error() {
                        return Ok(());
                    } else {
                        return Err(e);
                    }
                }
            }
        } else {
            ret = try!(eval_result);
        }

        if debug {
            println!("=> {:?}", ret);
        }
    }
    if should_repl {
        repl(scope_manager);
    }
    Ok(())
}

fn test_dir(dir_name: String) {
    for entry in WalkDir::new(dir_name) {
        let entry = entry.unwrap();
        if !entry.path().is_dir() {
            let entry_path = entry.path().display().to_string();
            let scope_manager = Rc::new(RefCell::new(init_gc()));
            add_pervasives(scope_manager.clone());
            match eval_file(entry_path.clone(), false, false, scope_manager.clone()) {
                Ok(_) => {
                    println!("{}: {}", entry_path, "OK");
                }
                Err(e) => {
                    println!("{}: {}", entry_path, e);
                }
            }
        }
    }
}

fn repl(scope_manager: Rc<RefCell<ScopeManager>>) -> i32 {
    let mut rl = Editor::new();
    let mut stderr = io::stderr();

    if metadata(".history").is_ok() && rl.load_history(".history").is_err() {
        writeln!(stderr, "Error: unable to load history on startup").unwrap();
    }

    loop {
        // prompt
        let readline = rl.readline(">> ");

        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let input = add_semicolon(clean_string(String::from(line)));
                if input == "" {
                    continue;
                }
                rl.add_history_entry(&input);

                match eval_string(&input, scope_manager.clone()) {
                    Ok((var, ptr)) => println!("{}", ptr.map(|p| p.as_string()).unwrap_or(var.t.as_string())),
                    Err(e) => println!("{:?}", e),
                }
            },
            Err(ReadlineError::Interrupted) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 1;
            },
            Err(ReadlineError::Eof) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);

                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 3;
            }
        }
    }
    0
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    if args.flag_test {
        let dir_name = "sputnik";

        test_dir(String::from(dir_name))
    } else {
        let scope_manager = Rc::new(RefCell::new(init_gc()));
        add_pervasives(scope_manager.clone());

        if args.arg_file == "" {
            let ret = repl(scope_manager.clone());
            exit(ret)
        } else {
            eval_file(args.arg_file, true, true, scope_manager.clone())
                .expect("Error evaluating file");
        }
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;
mod preflight;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

const COMMITTER_ERROR_MESSAGE: &'static str = r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    if github::can_release(&config) {
        logger::stdout("Creating GitHub release");
        github::release(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    } else {
        logger::stdout("Project not hosted on GitHub. Skipping release step");
    }
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }
}

fn get_repo(repository_path: &str) -> git2::Repository {
    match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    }
}

fn get_repository_path(args: &Args) -> String {
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is
                                                            not a directory"));
    let repo_path = path.to_str().unwrap_or_else(|| print_exit!("Path is not valid unicode"));
    repo_path.to_string()
}

fn get_signature<'a>(repository_path: String) -> git2::Signature<'a> {
    let repo = get_repo(&repository_path);
    let signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(COMMITTER_ERROR_MESSAGE);
                process::exit(1);
            }
    };

    signature.to_owned()
}

fn get_user_and_repo(repository_path: &str) -> Option<(String, String)> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            let (user, repo_name) = user_repo_from_url(&url)
                .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

            Some((user, repo_name))
        },
        Err(err) => {
            logger::warn(format!("Could not determine the origin remote url: {:?}", err));
            logger::warn("semantic-rs can't push changes or create a release on GitHub");
            None
        }
    }
}

fn get_github_token(repository_path: &str) -> Option<String> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            if github::is_github_url(&url) {
                env::var("GH_TOKEN").ok()
            } else {
                None
            }
        },
        Err(_) => None
    }
}

fn get_cargo_token() -> Option<String> {
    env::var("CARGO_TOKEN").ok()
}

fn assemble_configuration(args: Args) -> config::Config {
    let mut config_builder = ConfigBuilder::new();

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);
    let repository_path = get_repository_path(&args);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch.clone());
    config_builder.repository_path(repository_path.clone());
    config_builder.signature(get_signature(repository_path.clone()));
    if let Some((user, repo)) = get_user_and_repo(&repository_path) {
        config_builder.user(user);
        config_builder.repository_name(repo);
    }
    if let Some(gh_token)  = get_github_token(&repository_path) {
        config_builder.gh_token(gh_token);
    }
    if let Some(cargo_token) = get_cargo_token() {
        config_builder.cargo_token(cargo_token);
    }
    let repo = get_repo(&repository_path);
    match repo.find_remote("origin") {
        Ok(r) => config_builder.remote(Ok(r.name().unwrap().to_string())),
        Err(err) => config_builder.remote(Err(err.description().to_string()))
    };

    config_builder.repository(repo);
    config_builder.build()
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");
    println!("semantic.rs 🚀");

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

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let config = assemble_configuration(args);

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    //Before we actually start, we do perform some preflight checks
    //Here we check if everything is in place to do a GitHub release and a
    //release on crates.io.
    //The important bit is, if something's missing, we do not abort since the user can still do all
    //other things except publishing

    logger::stdout("Performing preflight checks now");
    let warnings = preflight::check(&config);

    for warning in warnings {
        logger::warn(format!(">> {}", warning));
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed. Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if config.write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
            None => {
                logger::stdout("No version bump. Nothing to do.");
                process::exit(0);
            }
    };

    if !config.write_mode {
        let changelog = generate_changelog(&config.repository_path, &version, &new_version);
        print_changelog(&changelog);
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(&config.repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        write_changelog(&config.repository_path, &version, &new_version);
        package_crate(&config, &config.repository_path, &new_version);

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(&config.repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            push_to_github(&config, &tag_name);
            release_on_github(&config, &tag_message, &tag_name);
            release_on_cratesio(&config);
            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<commit_msg>feat: print message if everything's fine<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;
mod preflight;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

const COMMITTER_ERROR_MESSAGE: &'static str = r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    if github::can_release(&config) {
        logger::stdout("Creating GitHub release");
        github::release(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    } else {
        logger::stdout("Project not hosted on GitHub. Skipping release step");
    }
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }
}

fn get_repo(repository_path: &str) -> git2::Repository {
    match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    }
}

fn get_repository_path(args: &Args) -> String {
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is
                                                            not a directory"));
    let repo_path = path.to_str().unwrap_or_else(|| print_exit!("Path is not valid unicode"));
    repo_path.to_string()
}

fn get_signature<'a>(repository_path: String) -> git2::Signature<'a> {
    let repo = get_repo(&repository_path);
    let signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(COMMITTER_ERROR_MESSAGE);
                process::exit(1);
            }
    };

    signature.to_owned()
}

fn get_user_and_repo(repository_path: &str) -> Option<(String, String)> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            let (user, repo_name) = user_repo_from_url(&url)
                .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

            Some((user, repo_name))
        },
        Err(err) => {
            logger::warn(format!("Could not determine the origin remote url: {:?}", err));
            logger::warn("semantic-rs can't push changes or create a release on GitHub");
            None
        }
    }
}

fn get_github_token(repository_path: &str) -> Option<String> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            if github::is_github_url(&url) {
                env::var("GH_TOKEN").ok()
            } else {
                None
            }
        },
        Err(_) => None
    }
}

fn get_cargo_token() -> Option<String> {
    env::var("CARGO_TOKEN").ok()
}

fn assemble_configuration(args: Args) -> config::Config {
    let mut config_builder = ConfigBuilder::new();

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);
    let repository_path = get_repository_path(&args);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch.clone());
    config_builder.repository_path(repository_path.clone());
    config_builder.signature(get_signature(repository_path.clone()));
    if let Some((user, repo)) = get_user_and_repo(&repository_path) {
        config_builder.user(user);
        config_builder.repository_name(repo);
    }
    if let Some(gh_token)  = get_github_token(&repository_path) {
        config_builder.gh_token(gh_token);
    }
    if let Some(cargo_token) = get_cargo_token() {
        config_builder.cargo_token(cargo_token);
    }
    let repo = get_repo(&repository_path);
    match repo.find_remote("origin") {
        Ok(r) => config_builder.remote(Ok(r.name().unwrap().to_string())),
        Err(err) => config_builder.remote(Err(err.description().to_string()))
    };

    config_builder.repository(repo);
    config_builder.build()
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");
    println!("semantic.rs 🚀");

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

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let config = assemble_configuration(args);

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    //Before we actually start, we do perform some preflight checks
    //Here we check if everything is in place to do a GitHub release and a
    //release on crates.io.
    //The important bit is, if something's missing, we do not abort since the user can still do all
    //other things except publishing

    logger::stdout("Performing preflight checks now");
    let warnings = preflight::check(&config);

    if warnings.len() == 0 {
        logger::stdout("Checks done. Everything is ok");
    }

    for warning in warnings {
        logger::warn(format!(">> {}", warning));
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed. Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if config.write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
            None => {
                logger::stdout("No version bump. Nothing to do.");
                process::exit(0);
            }
    };

    if !config.write_mode {
        let changelog = generate_changelog(&config.repository_path, &version, &new_version);
        print_changelog(&changelog);
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(&config.repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        write_changelog(&config.repository_path, &version, &new_version);
        package_crate(&config, &config.repository_path, &new_version);

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(&config.repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            push_to_github(&config, &tag_name);
            release_on_github(&config, &tag_message, &tag_name);
            release_on_cratesio(&config);
            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<|endoftext|>


<commit_before>extern crate csv;
extern crate prettytable;
extern crate regex;
extern crate rustyline;
extern crate sqlite3;

use std::env;

fn _normalize_col(col: &String) -> String {
    let re = regex::Regex::new(r"\(.*?\)").unwrap();
    return re.replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace(" ", "_")
        .replace(".", "_")
        .replace("?", "");
}

fn _create_table(db: &mut sqlite3::DatabaseConnection, table_name: &str, cols: &Vec<String>) {
    let create_columns =
        cols.iter().map(|c| format!("{} varchar", c)).collect::<Vec<String>>().join(", ");
    db.exec(&format!("CREATE TABLE {} ({})", table_name, create_columns)).unwrap();
}

fn _insert_row(db: &mut sqlite3::DatabaseConnection,
               table_name: &str,
               row: Vec<String>,
               cols: &Vec<String>) {
    let placeholders = cols.iter()
        .enumerate()
        .map(|(idx, _)| format!("${}", idx + 1))
        .collect::<Vec<String>>()
        .join(", ");
    let mut stmt = db.prepare(&format!("INSERT INTO {} VALUES ({})", table_name, placeholders))
        .unwrap();

    for (idx, value) in row.iter().enumerate() {
        stmt.bind_text((idx + 1) as sqlite3::ParamIx, value).unwrap();
    }
    assert!(stmt.execute().step().unwrap().is_none());
}

fn _load_table_from_path(db: &mut sqlite3::DatabaseConnection,
                         table_name: &str,
                         path: String)
                         -> Vec<String> {
    let mut num_rows = 0;
    let mut reader = csv::Reader::from_file(path).unwrap();

    let normalized_cols = reader.headers().unwrap().iter().map(_normalize_col).collect();
    _create_table(db, table_name, &normalized_cols);

    for row in reader.decode() {
        _insert_row(db, table_name, row.unwrap(), &normalized_cols);
        num_rows += 1;
    }

    println!(
        "Loaded {} rows into {}({})",
        num_rows,
        table_name,
        normalized_cols.join(", "),
    );
    return normalized_cols;
}

fn _print_table(conn: &mut sqlite3::DatabaseConnection, line: &str) {
    let mut table = prettytable::Table::new();
    table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
    let mut stmt = match conn.prepare(&line) {
        Ok(stmt) => stmt,
        Err(e) => {
            println!("{}", e.detail.unwrap());
            return;
        }
    };
    let mut results = stmt.execute();
    while let Some(r) = results.step().unwrap() {
        let mut row = prettytable::row::Row::new(vec![]);
        for i in 0..r.column_count() {
            row.add_cell(prettytable::cell::Cell::new(&r.column_text(i).unwrap_or("".to_string())));
        }
        table.add_row(row);
    }
    table.printstd();
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [char; 4] = [' ', '(', ')', ','];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        return SimpleWordCompleter { words: words };
    }
}

impl rustyline::completion::Completer for SimpleWordCompleter {
    fn complete(&self, line: &str, pos: usize) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) =
            rustyline::completion::extract_word(line, pos, &BREAK_CHARS.iter().cloned().collect());

        let matches = self.words.iter().filter(|w| w.starts_with(word)).cloned().collect();
        return Ok((start, matches));
    }
}

fn main() {
    let mut paths = env::args().skip(1);

    let mut conn = sqlite3::DatabaseConnection::in_memory().unwrap();

    let mut base_words = vec!["distinct", "select", "from", "group", "by", "order", "count"]
        .iter()
        .map(|s| s.to_string())
        .collect::<Vec<String>>();

    if paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", paths.next().unwrap());
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in paths.enumerate() {
            let mut col_names = _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path);
            base_words.append(&mut col_names);
        }
    }

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_completer(Some(completer));
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                rl.add_history_entry(&line);
                _print_table(&mut conn, &line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
}
<commit_msg>where is good too<commit_after>extern crate csv;
extern crate prettytable;
extern crate regex;
extern crate rustyline;
extern crate sqlite3;

use std::env;

fn _normalize_col(col: &String) -> String {
    let re = regex::Regex::new(r"\(.*?\)").unwrap();
    return re.replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace(" ", "_")
        .replace(".", "_")
        .replace("?", "");
}

fn _create_table(db: &mut sqlite3::DatabaseConnection, table_name: &str, cols: &Vec<String>) {
    let create_columns =
        cols.iter().map(|c| format!("{} varchar", c)).collect::<Vec<String>>().join(", ");
    db.exec(&format!("CREATE TABLE {} ({})", table_name, create_columns)).unwrap();
}

fn _insert_row(db: &mut sqlite3::DatabaseConnection,
               table_name: &str,
               row: Vec<String>,
               cols: &Vec<String>) {
    let placeholders = cols.iter()
        .enumerate()
        .map(|(idx, _)| format!("${}", idx + 1))
        .collect::<Vec<String>>()
        .join(", ");
    let mut stmt = db.prepare(&format!("INSERT INTO {} VALUES ({})", table_name, placeholders))
        .unwrap();

    for (idx, value) in row.iter().enumerate() {
        stmt.bind_text((idx + 1) as sqlite3::ParamIx, value).unwrap();
    }
    assert!(stmt.execute().step().unwrap().is_none());
}

fn _load_table_from_path(db: &mut sqlite3::DatabaseConnection,
                         table_name: &str,
                         path: String)
                         -> Vec<String> {
    let mut num_rows = 0;
    let mut reader = csv::Reader::from_file(path).unwrap();

    let normalized_cols = reader.headers().unwrap().iter().map(_normalize_col).collect();
    _create_table(db, table_name, &normalized_cols);

    for row in reader.decode() {
        _insert_row(db, table_name, row.unwrap(), &normalized_cols);
        num_rows += 1;
    }

    println!(
        "Loaded {} rows into {}({})",
        num_rows,
        table_name,
        normalized_cols.join(", "),
    );
    return normalized_cols;
}

fn _print_table(conn: &mut sqlite3::DatabaseConnection, line: &str) {
    let mut table = prettytable::Table::new();
    table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
    let mut stmt = match conn.prepare(&line) {
        Ok(stmt) => stmt,
        Err(e) => {
            println!("{}", e.detail.unwrap());
            return;
        }
    };
    let mut results = stmt.execute();
    while let Some(r) = results.step().unwrap() {
        let mut row = prettytable::row::Row::new(vec![]);
        for i in 0..r.column_count() {
            row.add_cell(prettytable::cell::Cell::new(&r.column_text(i).unwrap_or("".to_string())));
        }
        table.add_row(row);
    }
    table.printstd();
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [char; 4] = [' ', '(', ')', ','];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        return SimpleWordCompleter { words: words };
    }
}

impl rustyline::completion::Completer for SimpleWordCompleter {
    fn complete(&self, line: &str, pos: usize) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) =
            rustyline::completion::extract_word(line, pos, &BREAK_CHARS.iter().cloned().collect());

        let matches = self.words.iter().filter(|w| w.starts_with(word)).cloned().collect();
        return Ok((start, matches));
    }
}

fn main() {
    let mut paths = env::args().skip(1);

    let mut conn = sqlite3::DatabaseConnection::in_memory().unwrap();

    let mut base_words = vec!["distinct", "select", "from", "group", "by", "order", "where",
                              "count"]
        .iter()
        .map(|s| s.to_string())
        .collect::<Vec<String>>();

    if paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", paths.next().unwrap());
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in paths.enumerate() {
            let mut col_names = _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path);
            base_words.append(&mut col_names);
        }
    }

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_completer(Some(completer));
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                rl.add_history_entry(&line);
                _print_table(&mut conn, &line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>#![feature(iter_arith)] // sum is not stable?

use std::io;
use std::fs::File;
use std::io::prelude::*;

#[macro_use]
extern crate scan_fmt;

fn main() {
    try_main().unwrap()
}

fn try_main() -> io::Result<()> {
    let s = try!(run_from_file("input_1.txt", day1::main));
    s.iter().map(|s| println!("{}", s)).collect::<Vec<()>>();

    let s = try!(run_from_file("input_2.txt", day2::main));
    s.iter().map(|s| println!("{}", s)).collect::<Vec<()>>();

    Ok(())
}

fn run_from_file<F>(file: &str, it: F) -> io::Result<Vec<String>>
    where F: Fn(&str) -> Vec<String>
{
    let mut f = try!(File::open(file));
    let mut input = String::new();
    try!(f.read_to_string(&mut input));
    Ok(it(&input))
}

#[test]
fn verify_my_answers() {
    assert!(run_from_file("input_1.txt", day1::main).unwrap() == vec!["74", "1795"]);
    assert!(run_from_file("input_2.txt", day2::main).unwrap() == vec!["1586300", "3737498"]);
}

mod day1 {
    pub fn main(input: &str) -> Vec<String> {
        let s1 = adv_one_one(&input);
        let s2 = adv_one_two(&input, -1);
        vec![s1.to_string(), s2.to_string()]
    }

    fn inmap(c: char) -> i32 {
        match c {
            '(' => 1,
            ')' => -1,
            _ => panic!("invalid input"),
        }
    }

    fn adv_one_one(input: &str) -> i32 {
        input.chars().map(inmap).sum()
    }

    #[test]
    fn one_one_test() {
        assert!(adv_one_one("(())") == 0);
        assert!(adv_one_one("()()") == 0);
        assert!(adv_one_one("(((") == 3);
        assert!(adv_one_one("(()(()(") == 3);
        assert!(adv_one_one("))(((((") == 3);
        assert!(adv_one_one("())") == -1);
        assert!(adv_one_one("))(") == -1);
        assert!(adv_one_one(")))") == -3);
        assert!(adv_one_one(")())())") == -3);
    }

    fn adv_one_two(input: &str, target: i32) -> i32 {
        input.chars()
             .map(inmap)
             .scan(0, |floor, direction| {
                 *floor += direction;
                 Some(*floor)
             })
             .position(|floor| floor == target)
             .unwrap() as i32 + 1
    }

    #[test]
    fn one_two_test() {
        assert!(adv_one_two(")", -1) == 1);
        assert!(adv_one_two("()())", -1) == 5);
    }
}

mod day2 {
    pub fn main(input: &str) -> Vec<String> {
        let s1 = adv_two_one(&input);
        let s2 = adv_two_two(&input);
        vec![s1.to_string(), s2.to_string()]
    }

    fn adv_two_one(input: &str) -> u32 {
        fn pkg(l: u32, w: u32, h: u32) -> u32 {
            let sides = vec![l * w, w * h, h * l];
            let extra = sides.iter().min().unwrap();
            2 * l * w + 2 * w * h + 2 * h * l + extra
        }

        input.lines()
             .map(|line| {
                 let (l, w, h) = scan_fmt!(line, "{d}x{d}x{d}", u32, u32, u32);
                 pkg(l.unwrap(), w.unwrap(), h.unwrap())
             })
             .sum()
    }

    #[test]
    fn two_one_test() {
        assert!(adv_two_one("2x3x4") == 58);
        assert!(adv_two_one("1x1x10") == 43);
    }

    fn adv_two_two(input: &str) -> u32 {
        fn pkg(l: u32, w: u32, h: u32) -> u32 {
            let sides = vec![2 * l + 2 * w, 2 * w + 2 * h, 2 * h + 2 * l];
            let ribbon = sides.iter().min().unwrap();
            let bow = l * w * h;
            ribbon + bow
        }

        input.lines()
             .map(|line| {
                 let (l, w, h) = scan_fmt!(line, "{d}x{d}x{d}", u32, u32, u32);
                 pkg(l.unwrap(), w.unwrap(), h.unwrap())
             })
             .sum()
    }

    #[test]
    fn two_two_test() {
        assert!(adv_two_two("2x3x4") == 34);
        assert!(adv_two_two("1x1x10") == 14);
    }
}
<commit_msg>use assert_eq!<commit_after>#![feature(iter_arith)] // sum is not stable?

use std::io;
use std::fs::File;
use std::io::prelude::*;

#[macro_use]
extern crate scan_fmt;

fn main() {
    try_main().unwrap()
}

fn try_main() -> io::Result<()> {
    let s = try!(run_from_file("input_1.txt", day1::main));
    s.iter().map(|s| println!("{}", s)).collect::<Vec<()>>();

    let s = try!(run_from_file("input_2.txt", day2::main));
    s.iter().map(|s| println!("{}", s)).collect::<Vec<()>>();

    Ok(())
}

fn run_from_file<F>(file: &str, it: F) -> io::Result<Vec<String>>
    where F: Fn(&str) -> Vec<String>
{
    let mut f = try!(File::open(file));
    let mut input = String::new();
    try!(f.read_to_string(&mut input));
    Ok(it(&input))
}

#[test]
fn verify_my_answers() {
    assert_eq!(run_from_file("input_1.txt", day1::main).unwrap(), vec!["74", "1795"]);
    assert_eq!(run_from_file("input_2.txt", day2::main).unwrap(), vec!["1586300", "3737498"]);
}

mod day1 {
    pub fn main(input: &str) -> Vec<String> {
        let s1 = adv_one_one(&input);
        let s2 = adv_one_two(&input, -1);
        vec![s1.to_string(), s2.to_string()]
    }

    fn inmap(c: char) -> i32 {
        match c {
            '(' => 1,
            ')' => -1,
            _ => panic!("invalid input"),
        }
    }

    fn adv_one_one(input: &str) -> i32 {
        input.chars().map(inmap).sum()
    }

    #[test]
    fn one_one_test() {
        assert_eq!(adv_one_one("(())"), 0);
        assert_eq!(adv_one_one("()()"), 0);
        assert_eq!(adv_one_one("((("), 3);
        assert_eq!(adv_one_one("(()(()("), 3);
        assert_eq!(adv_one_one("))((((("), 3);
        assert_eq!(adv_one_one("())"), -1);
        assert_eq!(adv_one_one("))("), -1);
        assert_eq!(adv_one_one(")))"), -3);
        assert_eq!(adv_one_one(")())())"), -3);
    }

    fn adv_one_two(input: &str, target: i32) -> i32 {
        input.chars()
             .map(inmap)
             .scan(0, |floor, direction| {
                 *floor += direction;
                 Some(*floor)
             })
             .position(|floor| floor == target)
             .unwrap() as i32 + 1
    }

    #[test]
    fn one_two_test() {
        assert_eq!(adv_one_two(")", -1), 1);
        assert_eq!(adv_one_two("()())", -1), 5);
    }
}

mod day2 {
    pub fn main(input: &str) -> Vec<String> {
        let s1 = adv_two_one(&input);
        let s2 = adv_two_two(&input);
        vec![s1.to_string(), s2.to_string()]
    }

    fn adv_two_one(input: &str) -> u32 {
        fn pkg(l: u32, w: u32, h: u32) -> u32 {
            let sides = vec![l * w, w * h, h * l];
            let extra = sides.iter().min().unwrap();
            2 * l * w + 2 * w * h + 2 * h * l + extra
        }

        input.lines()
             .map(|line| {
                 let (l, w, h) = scan_fmt!(line, "{d}x{d}x{d}", u32, u32, u32);
                 pkg(l.unwrap(), w.unwrap(), h.unwrap())
             })
             .sum()
    }

    #[test]
    fn two_one_test() {
        assert_eq!(adv_two_one("2x3x4"), 58);
        assert_eq!(adv_two_one("1x1x10"), 43);
    }

    fn adv_two_two(input: &str) -> u32 {
        fn pkg(l: u32, w: u32, h: u32) -> u32 {
            let sides = vec![2 * l + 2 * w, 2 * w + 2 * h, 2 * h + 2 * l];
            let ribbon = sides.iter().min().unwrap();
            let bow = l * w * h;
            ribbon + bow
        }

        input.lines()
             .map(|line| {
                 let (l, w, h) = scan_fmt!(line, "{d}x{d}x{d}", u32, u32, u32);
                 pkg(l.unwrap(), w.unwrap(), h.unwrap())
             })
             .sum()
    }

    #[test]
    fn two_two_test() {
        assert_eq!(adv_two_two("2x3x4"), 34);
        assert_eq!(adv_two_two("1x1x10"), 14);
    }
}
<|endoftext|>


<commit_before>mod error;

// My IP address providers
mod myip;

// DNS providers
mod dns;

// Configuration
mod config;

#[macro_use]
extern crate clap;

#[macro_use]
extern crate log;
extern crate env_logger;

extern crate xmlrpc;
extern crate rustc_serialize;
extern crate regex;
extern crate ip;
extern crate hyper;

use clap::{Arg, App};
use std::env;

use config::Config;
use dns::*;
use error::Result;
use myip::*;
use std::process;

fn main() {
    let config = build_config();

    match main_with_errors(&config) {
        Ok(_) => info!("Process ends with success"),
        Err(err) => {
            error!("Process failed with result: {}", err);
            process::exit(-1);
        }
    }
}

fn build_config() -> Config {
    let matches = App::new("gdu")
        .version(&crate_version!()[..])
        .author("Damien Lecan <dev@dlecan.com>")
        .about("Gandi DNS updater, useful to reflect your dynamic IP address to your Gandi DNS zone file.\nIP address is read from stdin.")
        .args_from_usage(
            "-a --apikey=[apikey] 'Your API key provided by Gandi'
            -d --domain=[domain] 'The domain name whose active zonefile will be updated, e.g. \"domain.com\"'
            -n --dry-run 'Dry run, don't really update Gandi zone file'
            -f --force 'Force new zonefile creation even if IP address isn\'t modified'
            -r --record-name=[record_name] 'Name of the A record to update or create (without domain)'
            [verbose]... -v 'Verbose mode'")
        .arg(Arg::with_name("ip_provider")
            .help("IP address provider name to use to get IP address.")
            .short("i")
            .long("ip-provider")
            .takes_value(true)
            .multiple(false))
        .get_matches();

    // Init logging to DEBUG only if user requires it
    match matches.occurrences_of("verbose") {
        0 => (),
        1 => {
            println!("Verbose mode");
            env::set_var("RUST_LOG", "DEBUG");
        }
        2 | _=> {
            println!("More verbose mode");
            env::set_var("RUST_LOG", "TRACE");
        }
    }
    env_logger::init().unwrap();

    let apikey = matches.value_of("apikey").unwrap();
    debug!("Using apikey: {}", apikey);

    let domain = matches.value_of("domain").unwrap();
    debug!("Using domain: {}", domain);

    let record_name = matches.value_of("record_name").unwrap();
    debug!("Using record name: {}", record_name);

    let dry_run = matches.is_present("dry-run");
    debug!("Dry run: {}", dry_run);

    let force = matches.is_present("force");
    debug!("Force: {}", force);

    let ip_provider = value_t_or_exit!(matches.value_of("ip_provider"), HttpIpProviders);
    debug!("IP address provider: {:?}", ip_provider);

    Config {
        apikey: apikey.to_owned(),
        domain: domain.to_owned(),
        record_name: record_name.to_owned(),
        force: force,
        ip_provider: ip_provider,
    }
}

fn main_with_errors(config: &Config) -> Result<()> {

    // let ip_provider: MyIPAddressProvider = match config.ip_provider {
    //     Stdin => StdinIpProvider,
    //     SfrLaBoxFibre => HttpIpProvider,
    // };

    let ip_provider = HttpIpProvider;

    let expected_ip_addr = try!(ip_provider.get_my_ip_addr());

    debug!("Found IP address: {}", expected_ip_addr);

    // Force Gandi DNS provider for now
    let mut dns_provider = dns::GandiDNSProvider::new(&config.apikey);

    try!(dns_provider.init(&config.domain));

    match expected_ip_addr {
        ip::IpAddr::V6(_)
            if !dns_provider.handle_ipv6_addr() => panic!("You cannot use IP v6 addresses with the selected DNS provider"),
        _ => (),
    }

    let maybe_checked = try!(dns_provider.is_record_already_declared(&config.record_name));

    match maybe_checked {
        Some(ip_addr) => {
            debug!("Record already declared, with IP address: {}", &ip_addr);

            if !config.force && (&ip_addr == &expected_ip_addr) {
                debug!("IP address not modified, no record to update");
                Ok(())
            } else {
                debug!("Update record '{}' with IP address '{}'", &config.record_name, &expected_ip_addr);
                Ok(try!(dns_provider.update_record(&config.record_name, &expected_ip_addr)))
            }
        }
        None => Ok(try!(dns_provider.create_record(&config.record_name, &expected_ip_addr)))
    }
}
<commit_msg>Enable INFO logging for default execution<commit_after>mod error;

// My IP address providers
mod myip;

// DNS providers
mod dns;

// Configuration
mod config;

#[macro_use]
extern crate clap;

#[macro_use]
extern crate log;
extern crate env_logger;

extern crate xmlrpc;
extern crate rustc_serialize;
extern crate regex;
extern crate ip;
extern crate hyper;

use clap::{Arg, App};
use std::env;

use config::Config;
use dns::*;
use error::Result;
use myip::*;
use std::process;

fn main() {
    let config = build_config();

    match main_with_errors(&config) {
        Ok(_) => info!("Process ends with success"),
        Err(err) => {
            error!("Process failed with result: {}", err);
            process::exit(-1);
        }
    }
}

fn build_config() -> Config {
    let matches = App::new("gdu")
        .version(&crate_version!()[..])
        .author("Damien Lecan <dev@dlecan.com>")
        .about("Gandi DNS updater, useful to reflect your dynamic IP address to your Gandi DNS zone file.\nIP address is read from stdin.")
        .args_from_usage(
            "-a --apikey=[apikey] 'Your API key provided by Gandi'
            -d --domain=[domain] 'The domain name whose active zonefile will be updated, e.g. \"domain.com\"'
            -n --dry-run 'Dry run, don't really update Gandi zone file'
            -f --force 'Force new zonefile creation even if IP address isn\'t modified'
            -r --record-name=[record_name] 'Name of the A record to update or create (without domain)'
            [verbose]... -v 'Verbose mode'")
        .arg(Arg::with_name("ip_provider")
            .help("IP address provider name to use to get IP address.")
            .short("i")
            .long("ip-provider")
            .takes_value(true)
            .multiple(false))
        .get_matches();

    // Init logging to DEBUG only if user requires it
    match matches.occurrences_of("verbose") {
        0 => env::set_var("RUST_LOG", "INFO"),
        1 => {
            println!("Verbose mode");
            env::set_var("RUST_LOG", "DEBUG");
        }
        2 | _=> {
            println!("More verbose mode");
            env::set_var("RUST_LOG", "TRACE");
        }
    }
    env_logger::init().unwrap();

    let apikey = matches.value_of("apikey").unwrap();
    debug!("Using apikey: {}", apikey);

    let domain = matches.value_of("domain").unwrap();
    debug!("Using domain: {}", domain);

    let record_name = matches.value_of("record_name").unwrap();
    debug!("Using record name: {}", record_name);

    let dry_run = matches.is_present("dry-run");
    debug!("Dry run: {}", dry_run);

    let force = matches.is_present("force");
    debug!("Force: {}", force);

    let ip_provider = value_t_or_exit!(matches.value_of("ip_provider"), HttpIpProviders);
    debug!("IP address provider: {:?}", ip_provider);

    Config {
        apikey: apikey.to_owned(),
        domain: domain.to_owned(),
        record_name: record_name.to_owned(),
        force: force,
        ip_provider: ip_provider,
    }
}

fn main_with_errors(config: &Config) -> Result<()> {

    // let ip_provider: MyIPAddressProvider = match config.ip_provider {
    //     Stdin => StdinIpProvider,
    //     SfrLaBoxFibre => HttpIpProvider,
    // };

    let ip_provider = HttpIpProvider;

    let expected_ip_addr = try!(ip_provider.get_my_ip_addr());

    debug!("Found IP address: {}", expected_ip_addr);

    // Force Gandi DNS provider for now
    let mut dns_provider = dns::GandiDNSProvider::new(&config.apikey);

    try!(dns_provider.init(&config.domain));

    match expected_ip_addr {
        ip::IpAddr::V6(_)
            if !dns_provider.handle_ipv6_addr() => panic!("You cannot use IP v6 addresses with the selected DNS provider"),
        _ => (),
    }

    let maybe_checked = try!(dns_provider.is_record_already_declared(&config.record_name));

    match maybe_checked {
        Some(ip_addr) => {
            debug!("Record already declared, with IP address: {}", &ip_addr);

            if !config.force && (&ip_addr == &expected_ip_addr) {
                debug!("IP address not modified, no record to update");
                Ok(())
            } else {
                debug!("Update record '{}' with IP address '{}'", &config.record_name, &expected_ip_addr);
                Ok(try!(dns_provider.update_record(&config.record_name, &expected_ip_addr)))
            }
        }
        None => Ok(try!(dns_provider.create_record(&config.record_name, &expected_ip_addr)))
    }
}
<|endoftext|>


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

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

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

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

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

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

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

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

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

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

    let mut cluster = 1;

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

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

struct FileEntry {
    inode: u64,
    dev:  u64,
    path : String,
}

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

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

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

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

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

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

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

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

    let mut cluster = 1;

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

        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                println!("{} files in cluster {} (size: {}, digest: {})",
                         vec.len(), cluster, key, hash);
                // for_each becomes stable v1.22.0 onwards. Should uncomment then.
                //vec.iter().for_each(|f| println!("{}", i.path));
                for file_entry in vec {
                    println!("{}", file_entry.path);
                }
                cluster += 1;
            }
        }
    }
}
<|endoftext|>