<commit_before>#![feature(path_ext)]
#![feature(fs_walk)]
#![feature(convert)]

#![allow(dead_code)] // for debug

#[macro_use(log)]
extern crate cronparse;

use std::convert::AsRef;
use std::fs::{walk_dir, PathExt};
use std::path::{Path, PathBuf};
use std::thread::spawn;
use std::collections::BTreeMap;
use std::env;

use cronparse::{CrontabFile, CrontabFileError, CrontabFileErrorKind};
use cronparse::crontab::{UserCrontabEntry, SystemCrontabEntry, AnacrontabEntry, EnvVarEntry, CrontabEntry, ToCrontabEntry};
use cronparse::log::{KernelLogger, ConsoleLogger, AnyLogger, Logger, LogLevel};

fn generate_systemd_units(path: &Path, entry: CrontabEntry, env: &BTreeMap<String, String>) {
    println!("{} => {:?}, {:?}", path.display(), entry, env);
}

static USERS_CRONTAB_DIR: &'static str = "/var/spool/cron";  // UserCrontabEntry
static SYSTEM_CRONTAB_DIR: &'static str = "/etc/cron.d";  // SystemCrontabEntry
static ANACRONTAB_FILE: &'static str = "/etc/anacrontab";  // AnacrontabEntry

fn process_crontab_file<T: ToCrontabEntry, P: AsRef<Path>>(path: P, logger: &mut Logger) {
    CrontabFile::<T>::new(path.as_ref()).map(|crontab| {
        let mut env = BTreeMap::new();
        for entry in crontab {
            match entry {
                Ok(CrontabEntry::EnvVar(EnvVarEntry(name, value))) => { env.insert(name, value); },
                Ok(data) => generate_systemd_units(path.as_ref(), data, &env),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Io(_), .. }) => log!(logger, LogLevel::Warning, "error accessing file {}: {}", path.as_ref().display(), err),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Parse(_), .. }) => log!(logger, LogLevel::Warning, "skipping file {} due to parsing error: {}", path.as_ref().display(), err),
            }
        }
    }).unwrap_or_else(|err| {
        log!(logger, LogLevel::Error, "error parsing file {}: {}", path.as_ref().display(), err);
    });
}

#[inline]
fn is_run_by_systemd() -> bool {
    env::args().len() >= 3
}

fn get_logger() -> AnyLogger {
    if is_run_by_systemd() {
        AnyLogger::Kernel(KernelLogger::new())
    } else {
        AnyLogger::Console(ConsoleLogger::new())
    }
}

fn process_crontab_dir<T: ToCrontabEntry>(dir: &str, logger: &mut Logger) {
    let files = walk_dir(dir).and_then(|fs| fs.map(|r| r.map(|p| p.path()))
                                       .filter(|r| r.as_ref().map(|p| p.is_file()).unwrap_or(true))
                                       .collect::<Result<Vec<PathBuf>, _>>());
    match files {
        Err(err) => log!(logger, LogLevel::Error, "error processing directory {}: {}", dir, err),
        Ok(files) => for file in files {
            process_crontab_file::<T, _>(file, logger as &mut Logger);
        }
    }
}

fn main() {
    let user_thread = spawn(|| process_crontab_dir::<UserCrontabEntry>(USERS_CRONTAB_DIR, &mut get_logger()));
    let system_thread = spawn(|| process_crontab_dir::<SystemCrontabEntry>(SYSTEM_CRONTAB_DIR, &mut get_logger()));
    let anacron_thread = spawn(|| process_crontab_file::<AnacrontabEntry, _>(ANACRONTAB_FILE, &mut get_logger()));

    let _ = user_thread.join();
    let _ = system_thread.join();
    let _ = anacron_thread.join();
}
<commit_msg>don't allow dead code<commit_after>#![feature(path_ext)]
#![feature(fs_walk)]
#![feature(convert)]

#[macro_use(log)]
extern crate cronparse;

use std::convert::AsRef;
use std::fs::{walk_dir, PathExt};
use std::path::{Path, PathBuf};
use std::thread::spawn;
use std::collections::BTreeMap;
use std::env;

use cronparse::{CrontabFile, CrontabFileError, CrontabFileErrorKind};
use cronparse::crontab::{UserCrontabEntry, SystemCrontabEntry, AnacrontabEntry, EnvVarEntry, CrontabEntry, ToCrontabEntry};
use cronparse::log::{KernelLogger, ConsoleLogger, AnyLogger, Logger, LogLevel};

fn generate_systemd_units(path: &Path, entry: CrontabEntry, env: &BTreeMap<String, String>) {
    println!("{} => {:?}, {:?}", path.display(), entry, env);
}

static USERS_CRONTAB_DIR: &'static str = "/var/spool/cron";  // UserCrontabEntry
static SYSTEM_CRONTAB_DIR: &'static str = "/etc/cron.d";  // SystemCrontabEntry
static ANACRONTAB_FILE: &'static str = "/etc/anacrontab";  // AnacrontabEntry

fn process_crontab_file<T: ToCrontabEntry, P: AsRef<Path>>(path: P, logger: &mut Logger) {
    CrontabFile::<T>::new(path.as_ref()).map(|crontab| {
        let mut env = BTreeMap::new();
        for entry in crontab {
            match entry {
                Ok(CrontabEntry::EnvVar(EnvVarEntry(name, value))) => { env.insert(name, value); },
                Ok(data) => generate_systemd_units(path.as_ref(), data, &env),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Io(_), .. }) => log!(logger, LogLevel::Warning, "error accessing file {}: {}", path.as_ref().display(), err),
                Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Parse(_), .. }) => log!(logger, LogLevel::Warning, "skipping file {} due to parsing error: {}", path.as_ref().display(), err),
            }
        }
    }).unwrap_or_else(|err| {
        log!(logger, LogLevel::Error, "error parsing file {}: {}", path.as_ref().display(), err);
    });
}

#[inline]
fn is_run_by_systemd() -> bool {
    env::args().len() >= 3
}

fn get_logger() -> AnyLogger {
    if is_run_by_systemd() {
        AnyLogger::Kernel(KernelLogger::new())
    } else {
        AnyLogger::Console(ConsoleLogger::new())
    }
}

fn process_crontab_dir<T: ToCrontabEntry>(dir: &str, logger: &mut Logger) {
    let files = walk_dir(dir).and_then(|fs| fs.map(|r| r.map(|p| p.path()))
                                       .filter(|r| r.as_ref().map(|p| p.is_file()).unwrap_or(true))
                                       .collect::<Result<Vec<PathBuf>, _>>());
    match files {
        Err(err) => log!(logger, LogLevel::Error, "error processing directory {}: {}", dir, err),
        Ok(files) => for file in files {
            process_crontab_file::<T, _>(file, logger as &mut Logger);
        }
    }
}

fn main() {
    let user_thread = spawn(|| process_crontab_dir::<UserCrontabEntry>(USERS_CRONTAB_DIR, &mut get_logger()));
    let system_thread = spawn(|| process_crontab_dir::<SystemCrontabEntry>(SYSTEM_CRONTAB_DIR, &mut get_logger()));
    let anacron_thread = spawn(|| process_crontab_file::<AnacrontabEntry, _>(ANACRONTAB_FILE, &mut get_logger()));

    let _ = user_thread.join();
    let _ = system_thread.join();
    let _ = anacron_thread.join();
}
<|endoftext|>


<commit_before>extern crate clipboard_win;
extern crate docopt;
extern crate rustc_serialize;
extern crate winapi;
extern crate user32;
extern crate kernel32;

use clipboard_win::WindowsError;
use clipboard_win::wrapper::{get_last_error, open_clipboard, close_clipboard};
use clipboard_win::clipboard_formats::CF_UNICODETEXT;
use docopt::Docopt;
use kernel32::{GlobalLock, GlobalUnlock};
use winapi::winnt::HANDLE;
use user32::GetClipboardData;
use std::io;
use std::io::Read;

const USAGE: &'static str = "
win32yank

Usage:
    win32yank -o [--lf]
    win32yank -i [--crlf]

Options:
    -o          Print clipboard contents to stdout
    -i          Set clipboard from stdin
    --lf        Replace CRLF with LF before printing to stdout
    --crlf      Replace lone LF bytes with CRLF before setting the clipboard
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_o: bool,
    flag_i: bool,
    flag_lf: bool,
    flag_crlf: bool,
}

fn from_wide_ptr(ptr: *const u16) -> String {
    use std::ffi::OsString;
    use std::os::windows::ffi::OsStringExt;

    if ptr.is_null() {
        return String::new();
    }

    unsafe {
        assert!(!ptr.is_null());
        let len = (0..std::isize::MAX).position(|i| *ptr.offset(i) == 0).unwrap();
        let slice = std::slice::from_raw_parts(ptr, len);
        OsString::from_wide(slice).to_string_lossy().into_owned()
    }
}

fn get_clipboard(replace_crlf: bool) -> Result<String, WindowsError> {
    let result: Result<String, WindowsError>;
    try!(open_clipboard());
    unsafe {
        let text_handler: HANDLE = GetClipboardData(CF_UNICODETEXT as u32);

        if text_handler.is_null() {
            result = Err(get_last_error());
        } else {
            let text_p = GlobalLock(text_handler) as *const u16;
            result = Ok(from_wide_ptr(text_p));
            GlobalUnlock(text_handler);
        }
    }
    try!(close_clipboard());

    if replace_crlf {
        result.map(|data| data.replace("\r\n", "\n"))
    } else {
        result
    }
}

fn set_clipboard(content: &str, replace_lf: bool) -> Result<(), WindowsError> {
    // clipboard_win::wrapper::set_clipboard uses CF_UNICODETEXT,
    // which is what we want

    if replace_lf {
        let chunks = content.split("\r\n")
            .map(|item| item.replace("\n", "\r\n"));
        let mut first = true;
        let mut out = String::with_capacity(content.len());
        for chunk in chunks {
            if first {
                first = false;
            } else {
                out.push_str("\r\n");
            }
            out.push_str(&chunk);
        }
        clipboard_win::set_clipboard(&out)
    } else {
        clipboard_win::set_clipboard(content)
    }
}

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

    if args.flag_o {
        let content = get_clipboard(args.flag_lf).unwrap();
        print!("{}", content);
    } else if args.flag_i {
        let mut stdin = io::stdin();
        let mut content = String::new();
        stdin.read_to_string(&mut content).unwrap();
        set_clipboard(&content, args.flag_crlf).unwrap();
    }
}

#[test]
fn test() {
    // Windows dislikes if we lock the clipboard too long
    // sleep for bit
    use std::thread::sleep;
    use std::time::Duration;
    let sleep_time = 300;

    let v = "Hello\nfrom\nwin32yank";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    let v = "Hello\rfrom\rwin32yank";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    let v = "Hello\r\nfrom\r\nwin32yank";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    let v = "\r\nfrom\r\nwin32yank\r\n\n...\\r\n";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    //
    // set_clipboard(true)
    set_clipboard("", true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), "");
    sleep(Duration::from_millis(sleep_time));

    set_clipboard("\n", true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), "\r\n");
    sleep(Duration::from_millis(sleep_time));

    set_clipboard("\r\n", true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), "\r\n");
    sleep(Duration::from_millis(sleep_time));

    let v = "\r\nfrom\r\nwin32yank\r\n\n...\\r\n";
    set_clipboard(v, true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(),
               "\r\nfrom\r\nwin32yank\r\n\r\n...\\r\r\n");
}
<commit_msg>Check clipboard contents before retrieving it<commit_after>extern crate clipboard_win;
extern crate docopt;
extern crate rustc_serialize;
extern crate winapi;
extern crate user32;
extern crate kernel32;

use clipboard_win::WindowsError;
use clipboard_win::wrapper::{get_last_error, open_clipboard, close_clipboard};
use clipboard_win::clipboard_formats::CF_UNICODETEXT;
use docopt::Docopt;
use kernel32::{GlobalLock, GlobalUnlock};
use winapi::winnt::HANDLE;
use user32::{GetClipboardData, EnumClipboardFormats};
use std::io;
use std::io::Read;

const USAGE: &'static str = "
win32yank

Usage:
    win32yank -o [--lf]
    win32yank -i [--crlf]

Options:
    -o          Print clipboard contents to stdout
    -i          Set clipboard from stdin
    --lf        Replace CRLF with LF before printing to stdout
    --crlf      Replace lone LF bytes with CRLF before setting the clipboard
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_o: bool,
    flag_i: bool,
    flag_lf: bool,
    flag_crlf: bool,
}

fn from_wide_ptr(ptr: *const u16) -> String {
    use std::ffi::OsString;
    use std::os::windows::ffi::OsStringExt;

    if ptr.is_null() {
        return String::new();
    }

    unsafe {
        assert!(!ptr.is_null());
        let len = (0..std::isize::MAX).position(|i| *ptr.offset(i) == 0).unwrap();
        let slice = std::slice::from_raw_parts(ptr, len);
        OsString::from_wide(slice).to_string_lossy().into_owned()
    }
}

fn get_clipboard(replace_crlf: bool) -> Result<String, WindowsError> {
    let result: Result<String, WindowsError>;
    try!(open_clipboard());

    // Check clipboard contents, return empty string if unicode text
    // is not available
    let mut enumfmt = 0;
    loop {
        match unsafe {EnumClipboardFormats(enumfmt)} {
            // Either the call failed or there are no more formats
            0 => return Ok(String::new()),
            v if v == CF_UNICODETEXT => break,
            other => enumfmt = other,
        }
    }

    unsafe {
        let text_handler: HANDLE = GetClipboardData(CF_UNICODETEXT as u32);

        if text_handler.is_null() {
            result = Err(get_last_error());
        } else {
            let text_p = GlobalLock(text_handler) as *const u16;
            result = Ok(from_wide_ptr(text_p));
            GlobalUnlock(text_handler);
        }
    }
    try!(close_clipboard());

    if replace_crlf {
        result.map(|data| data.replace("\r\n", "\n"))
    } else {
        result
    }
}

fn set_clipboard(content: &str, replace_lf: bool) -> Result<(), WindowsError> {
    // clipboard_win::wrapper::set_clipboard uses CF_UNICODETEXT,
    // which is what we want

    if replace_lf {
        let chunks = content.split("\r\n")
            .map(|item| item.replace("\n", "\r\n"));
        let mut first = true;
        let mut out = String::with_capacity(content.len());
        for chunk in chunks {
            if first {
                first = false;
            } else {
                out.push_str("\r\n");
            }
            out.push_str(&chunk);
        }
        clipboard_win::set_clipboard(&out)
    } else {
        clipboard_win::set_clipboard(content)
    }
}

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

    if args.flag_o {
        let content = get_clipboard(args.flag_lf).unwrap();
        print!("{}", content);
    } else if args.flag_i {
        let mut stdin = io::stdin();
        let mut content = String::new();
        stdin.read_to_string(&mut content).unwrap();
        set_clipboard(&content, args.flag_crlf).unwrap();
    }
}

#[test]
fn test() {
    // Windows dislikes if we lock the clipboard too long
    // sleep for bit
    use std::thread::sleep;
    use std::time::Duration;
    let sleep_time = 300;

    let v = "Hello\nfrom\nwin32yank";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    let v = "Hello\rfrom\rwin32yank";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    let v = "Hello\r\nfrom\r\nwin32yank";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    let v = "\r\nfrom\r\nwin32yank\r\n\n...\\r\n";
    set_clipboard(v, false).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), v);
    sleep(Duration::from_millis(sleep_time));

    //
    // set_clipboard(true)
    set_clipboard("", true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), "");
    sleep(Duration::from_millis(sleep_time));

    set_clipboard("\n", true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), "\r\n");
    sleep(Duration::from_millis(sleep_time));

    set_clipboard("\r\n", true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(), "\r\n");
    sleep(Duration::from_millis(sleep_time));

    let v = "\r\nfrom\r\nwin32yank\r\n\n...\\r\n";
    set_clipboard(v, true).unwrap();
    assert_eq!(get_clipboard(false).unwrap(),
               "\r\nfrom\r\nwin32yank\r\n\r\n...\\r\r\n");
}
<|endoftext|>


<commit_before>extern crate cargo;
#[macro_use]
extern crate nom;
extern crate rustc_serialize;

use cargo::CliResult;
use cargo::core::Dependency;
use cargo::core::Package;
use cargo::core::PackageId;
use cargo::core::SourceId;
use cargo::core::Workspace;
use cargo::core::dependency::Kind;
use cargo::ops::Packages;
use cargo::ops;
use cargo::util::Cfg;
use cargo::util::ChainError;
use cargo::util::Config;
use cargo::util::ToUrl;
use cargo::util::human;
use std::collections::HashMap;
use std::collections::HashSet;
use std::env;
use std::fs;
use std::io::Write;
use std::fs::File;
use std::path::Path;
use std::str::FromStr;
use std::str;

/** Define parser for --overrides flag */
fn isnt_plus(chr: char) -> bool { chr != '+' }
fn isnt_colon(chr: char) -> bool { chr != ':' }
fn isnt_comma(chr: char) -> bool { chr != ',' }
named!(parse_override( &str ) -> DependencyOverride,
   do_parse!(
     name: map!(take_while_s!(isnt_plus), str::to_owned) >>
     char!('+') >>
     version: map!(take_while_s!(isnt_colon), str::to_owned) >>
     char!(':') >>
     bazel_path: map!(take_while_s!(isnt_comma), str::to_owned) >>
     (DependencyOverride { name: name, version: version, bazel_path: bazel_path })
   )
);
named!(parse_overrides( &str ) -> Vec<DependencyOverride>, separated_list!(char!(','), parse_override));

#[derive(Debug)]
pub struct RazePackage {
  pub id: PackageId,
  pub package: Package,
  pub features: HashSet<String>,
}

#[derive(RustcDecodable)]
struct Options {
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_host: Option<String>,
    flag_color: Option<String>,
    flag_overrides: Option<String>
}

#[derive(Debug)]
struct DependencyOverride {
    pub name: String,
    pub version: String,
    pub bazel_path: String,
}

const USAGE: &'static str = r#"
Generate BUILD files for your pre-vendored Cargo dependencies.

Usage:
    cargo raze [options]

Options:
    -h, --help               Print this message
    -v, --verbose            Use verbose output
    --host HOST              Registry index to sync with
    -q, --quiet              No output printed to stdout
    --color WHEN             Coloring: auto, always, never
    --overrides LIST         Comma separated cargo dependency overrides ["libc+0.2.21:@workspace//path:dep,..."]
"#;

fn main() {
    let config = Config::default().unwrap();
    let args = env::args().collect::<Vec<_>>();
    let result = cargo::call_main_without_stdin(real_main, &config, USAGE, &args, false);

    match result {
        Err(e) => cargo::handle_cli_error(e, &mut *config.shell()),
        Ok(()) => {},
    }
}

fn real_main(options: Options, config: &Config) -> CliResult {
    try!(config.configure(options.flag_verbose,
                          options.flag_quiet,
                          &options.flag_color,
                          /* frozen = */ false,
                          /* locked = */ false));
    let platform_triple = config.rustc()?.host.clone();
    // TODO(acmcarther): Fix unwrap. I'm unwrapping here temporarily because Nom's err is hard to
    // convert to CargoError
    let override_name_and_ver_to_path: HashMap<(String, String), String> = {
        options.flag_overrides.as_ref()
            .map(|f| parse_overrides(f).to_result().unwrap())
            .unwrap_or(Vec::new())
            .into_iter()
            .map(|entry| ((entry.name, entry.version), entry.bazel_path))
            .collect()
    };

    let (packages, resolve) = {
        let lockfile = Path::new("Cargo.lock");
        let manifest_path = lockfile.parent().unwrap().join("Cargo.toml");
        let manifest = env::current_dir().unwrap().join(&manifest_path);
        let ws = try!(Workspace::new(&manifest, config));
        let specs = Packages::All.into_package_id_specs(&ws).chain_error(|| {
          human("failed to find specs? whats a spec?")
        })?;

        ops::resolve_ws_precisely(
                &ws,
                None,
                &[],
                false,
                false,
                &specs).chain_error(|| {
            human("failed to load pkg lockfile")
        })?
    };

    let package_ids = {
        let source_id_from_registry =
          options.flag_host.map(|s| s.to_url().map(|url| SourceId::for_registry(&url)).map_err(human));

        let registry_id = try!(source_id_from_registry.unwrap_or_else(|| SourceId::crates_io(config)));
        try!(fs::metadata("Cargo.lock").chain_error(|| {
          human("failed to find Cargo.lock. Please run `cargo generate-lockfile` first.")
        }));

        let mut package_ids = resolve.iter()
            .filter(|id| *id.source_id() == registry_id)
            .cloned()
            .collect::<Vec<_>>();
        package_ids.sort_by_key(|id| id.name().to_owned());
        package_ids
    };

    let raze_packages = package_ids.iter()
      .filter(|id| !override_name_and_ver_to_path.contains_key(&(id.name().to_owned(), id.version().to_string())))
      .map(|id| RazePackage {
          id: id.clone(),
          package: packages.get(id).unwrap().clone(),
          // TODO(acmcarther): This will break as of cargo commit 50f1c172
          features: resolve.features(id)
            .cloned()
            .unwrap_or(HashSet::new())})
      .collect::<Vec<_>>();

    let mut has_build_dependency = false;

    for pkg in raze_packages.iter() {
        let &RazePackage {ref id, ref features, ref package, ..} = pkg;
        let vendor_dir = format!("./vendor/{}-{}/", id.name(), id.version());

        try!(fs::metadata(&vendor_dir).chain_error(|| {
            human(format!("failed to find {}. Please run `cargo vendor -x` first.", vendor_dir))
        }));

        let vendor_path = Path::new(&vendor_dir);

        let normal_dependencies = package.dependencies().iter().cloned()
            .filter(|dep| {
                if dep.kind() == Kind::Normal {
                    return true;
                }

                println!("WARNING: Crate <{}> is dropping <{:?}> dependency <{}>",
                         id.name(), dep.kind(), dep.name());

                if dep.kind() == Kind::Build {
                  has_build_dependency = true;
                }

                return false;
            })
            .collect::<Vec<_>>();

        let package_dependencies_by_name = normal_dependencies.into_iter()
            .map(|dep| (dep.name().to_owned(), dep))
            .collect::<HashMap<String, Dependency>>();

        let resolved_dependencies_by_name = resolve.deps(id).into_iter()
            .map(|dep| (dep.name().to_owned(), dep.clone()))
            .collect::<HashMap<String, PackageId>>();

        let all_dependencies = package_dependencies_by_name.keys().cloned()
            .chain(resolved_dependencies_by_name.keys().cloned())
            .collect::<HashSet<_>>();

        let mut bazel_dependency_strs = Vec::new();
        for dependency_name in all_dependencies.iter() {
            if !resolved_dependencies_by_name.contains_key(dependency_name) {
                println!("TRACE: Crate <{}> is dropping <{}> because it is unused.",
                         id.name(), dependency_name);
                continue
            }
            if !package_dependencies_by_name.contains_key(dependency_name) {
                // Dropped dependency because it was not a Normal dependency
                continue
            }
            let resolved_dependency = resolved_dependencies_by_name.get(dependency_name).unwrap();
            let package_dependency = package_dependencies_by_name.get(dependency_name).unwrap();

            let dependency_version = resolved_dependency.version();
            let dependency_override = override_name_and_ver_to_path
                .get(&(dependency_name.to_owned(), dependency_version.to_string()));

            let platform_requires_dependency = package_dependency.platform()
              .map(|p| p.matches(&platform_triple, Some(&generic_linux_cfgs())))
              .unwrap_or(true);
            if !platform_requires_dependency {
                println!("INFO: Crate <{}> is dropping <{}> because it is not for this plaform.",
                         id.name(), dependency_name);
                println!("INFO: Dependency <{}>'s target specification is <{:?}>",
                         dependency_name, package_dependency.platform().unwrap());
                continue
            }

            let bazel_dependency_path = match dependency_override {
                Some(override_path) => override_path.clone(),
                None => format!("//vendor/{name}-{crate_version}:{sanitized_name}",
                  name=dependency_name, sanitized_name=dependency_name.replace("-", "_"),
                  crate_version=resolved_dependency.version())
            };
            bazel_dependency_strs.push(bazel_dependency_path);
        }

        let sanitized_crate_name = id.name().replace("-", "_");
        let mut comma_separated_deps = bazel_dependency_strs.into_iter()
            .map(|dep_str| format!("        \"{}\",\n", dep_str))
            .collect::<Vec<String>>();
        comma_separated_deps.sort();
        let mut comma_separated_features = features
            .iter()
            .map(|f| format!("        \"{}\",\n", f))
            .collect::<Vec<String>>();
        comma_separated_features.sort();

        let comma_separated_deps_str =
            comma_separated_deps.into_iter().collect::<String>();
        let comma_separated_features_str =
            comma_separated_features.into_iter().collect::<String>();

        let build_file_content = generate_build_file(
            &sanitized_crate_name,
            &comma_separated_deps_str,
            &comma_separated_features_str);

        let build_file_path = vendor_path.join("BUILD");
        try!(File::create(&build_file_path)
            .and_then(|mut f| f.write_all(build_file_content.as_bytes()))
            .chain_error(|| human(format!("failed to create: `{}`", build_file_path.display()))));
    }
    let workspace_path = Path::new("WORKSPACE");
    if fs::metadata(&workspace_path).is_err() {
      try!(File::create(&workspace_path)
          .chain_error(|| human(format!("failed to create: `{}`", workspace_path.display()))));
    }

    Ok(())
}

fn generic_linux_cfgs() -> Vec<Cfg> {
    // TODO(acmcarther): use output of rustc, similar to
    // cargo::ops::cargo_rustc::context::Context::probe_target_info_kind
    // https://github.com/rust-lang/cargo/blob/f5348cc321a032db95cd18e3129a4392d2e0a892/src/cargo/ops/cargo_rustc/context.rs#L199
    let hardcoded_properties =
r#"debug_assertions
target_arch="x86_64"
target_endian="little"
target_env="gnu"
target_family="unix"
target_feature="sse"
target_feature="sse2"
target_has_atomic="16"
target_has_atomic="32"
target_has_atomic="64"
target_has_atomic="8"
target_has_atomic="ptr"
target_os="linux"
target_pointer_width="64"
target_thread_local
target_vendor="unknown"
unix"#;
    hardcoded_properties.lines()
      .map(Cfg::from_str)
      .map(|cfg| cfg.expect("hardcoded values should be parsable"))
      .collect()
}

pub fn generate_build_file(
        sanitized_crate_name: &str,
        comma_separated_deps: &String,
        comma_separated_features: &String) -> String {
    format!(r#"'''
WARNING: THIS IS GENERATED CODE!
DO NOT MODIFY!
Instead, rerun raze with different arguments.
'''
package(default_visibility = ["//visibility:public"])

licenses(["notice"])

load(
    "@io_bazel_rules_rust//rust:rust.bzl",
    "rust_library",
)

filegroup(
    name = "sources",
    srcs = glob(["lib.rs", "src/**/*.rs"]),
)

rust_library(
    name = "{sanitized_crate_name}",
    srcs = glob(["lib.rs", "src/**/*.rs"]),
    deps = [
{comma_separated_deps}    ],
    rustc_flags = [
        "--cap-lints warn",
    ],
    crate_features = [
{comma_separated_features}    ],
)"#,
        sanitized_crate_name=sanitized_crate_name,
        comma_separated_deps=comma_separated_deps,
        comma_separated_features=comma_separated_features)
}
<commit_msg>Now generating Cargo.bzl files -- leave gen to bazel<commit_after>extern crate cargo;
extern crate rustc_serialize;

use cargo::CliResult;
use cargo::core::Dependency;
use cargo::core::Package;
use cargo::core::PackageId;
use cargo::core::TargetKind;
use cargo::core::SourceId;
use cargo::core::Workspace;
use cargo::core::dependency::Kind;
use cargo::ops::Packages;
use cargo::ops;
use cargo::util::Cfg;
use cargo::util::ChainError;
use cargo::util::Config;
use cargo::util::ToUrl;
use cargo::util::human;
use std::collections::HashMap;
use std::collections::HashSet;
use std::env;
use std::iter;
use std::hash::Hash;
use std::cmp::Eq;
use std::fs;
use std::io::Write;
use std::fs::File;
use std::path::Path;
use std::str::FromStr;
use std::str;

// A basic expr type for bzl files
pub enum BExpr {
  Value(String),
  Array(Vec<BExpr>),
  Struct(Vec<(String, BExpr)>),
}

impl BExpr {
  pub fn pretty_print(&self) -> String {
    self.pretty_print_spaced(4 /* space_count */)
  }

  fn pretty_print_spaced(&self, space_count: usize) -> String {
    assert!(space_count >= 4);
    let less_spaces = iter::repeat(' ')
      .take(if space_count > 0 { space_count - 4 } else { 0 })
      .collect::<String>();
    let spaces = iter::repeat(' ')
      .take(space_count)
      .collect::<String>();
    match self {
      &BExpr::Value(ref s) => format!("\"{}\"", s),
      &BExpr::Array(ref a) if a.len() == 0 => format!("[]"),
      &BExpr::Array(ref a) => format!("[\n{}{}]", a.iter()
                              .map(|i| format!("{}{},\n", spaces, i.pretty_print_spaced(space_count + 4)))
                              .collect::<String>(), less_spaces),
      &BExpr::Struct(ref s) if s.len() == 0 => format!("struct()"),
      &BExpr::Struct(ref s) => format!("struct(\n{}{})", s.iter()
                              .map(|&(ref k, ref v)| format!("{}{} = {},\n", spaces, k, v.pretty_print_spaced(space_count + 4)))
                              .collect::<String>(), less_spaces),
    }
  }
}

// Produces a hashmap-ish Struct BExpr
macro_rules! b_struct {
    ($($key:expr => $value:expr),*) => {
        {
            let mut contents: Vec<(String, BExpr)> = Vec::new();
            $(
              contents.push(($key.to_string(), $value));
            )*
            BExpr::Struct(contents)
        }
    };
}

// Produces an array-ish BExpr
macro_rules! b_array {
    ($($value:expr),*) => {
        {
            let mut contents: Vec<BExpr> = Vec::new();
            $(
              contents.push($value);
            )*
            BExpr::Array(contents)
        }
    };
}

// Produces a string-ish value BExpr
macro_rules! b_value {
    ($value:expr) => {
      BExpr::Value($value.to_string())
    };
}

trait ToBExpr {
  fn to_expr(&self) -> BExpr;
}

impl <T> ToBExpr for Vec<T> where T: ToBExpr {
  fn to_expr(&self) -> BExpr {
    BExpr::Array(self.iter().map(|v| v.to_expr()).collect())
  }
}
impl <T> ToBExpr for HashSet<T> where T: Eq + Hash + ToBExpr {
  fn to_expr(&self) -> BExpr {
    BExpr::Array(self.iter().map(|v| v.to_expr()).collect())
  }
}

impl <T> ToBExpr for HashMap<String, T> where T: ToBExpr {
  fn to_expr(&self) -> BExpr {
    BExpr::Struct(self.iter().map(|(k, v)| (k.clone(), v.to_expr())).collect())
  }
}

impl ToBExpr for String {
  fn to_expr(&self) -> BExpr {
    b_value!(self)
  }
}

#[derive(Debug, Clone)]
pub struct BazelPackage {
  pub id: PackageId,
  pub package: Package,
  pub features: HashSet<String>,
  pub full_name: String,
  pub path: String,
  pub dependencies: Vec<BazelDependency>,
  pub build_dependencies: Vec<BazelDependency>,
  pub dev_dependencies: Vec<BazelDependency>,
  pub targets: Vec<BazelTarget>,
}

impl ToBExpr for BazelPackage {
  fn to_expr(&self) -> BExpr {
    b_struct! {
      "package" => b_struct! {
        "pkg_name" => b_value!(self.id.name()),
        "pkg_version" => b_value!(self.id.version())
      },
      "dependencies" => self.dependencies.to_expr(),
      "build_dependencies" => self.build_dependencies.to_expr(),
      "dev_dependencies" => self.dev_dependencies.to_expr(),
      "features" => self.features.to_expr(),
      "targets" => self.targets.to_expr()
    }
  }
}

#[derive(Debug, Clone)]
pub struct BazelDependency {
  pub name: String,
  pub version: String,
}

impl ToBExpr for BazelDependency {
  fn to_expr(&self) -> BExpr {
    b_struct! {
      "name" => b_value!(self.name),
      "version" => b_value!(self.version)
    }
  }
}

#[derive(Debug, Clone)]
pub struct BazelTarget {
  pub name: String,
  pub kinds: Vec<String>,
  pub path: String,
}

impl ToBExpr for BazelTarget {
  fn to_expr(&self) -> BExpr {
    b_struct! {
      "name" => b_value!(self.name),
      "kinds" => self.kinds.to_expr(),
      "path" => b_value!(self.path)
    }
  }
}

#[derive(RustcDecodable)]
struct Options {
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_host: Option<String>,
    flag_color: Option<String>,
}

const USAGE: &'static str = r#"
Generate Cargo.bzl files for your pre-vendored Cargo dependencies.

Usage:
    cargo raze [options]

Options:
    -h, --help               Print this message
    -v, --verbose            Use verbose output
    --host HOST              Registry index to sync with
    -q, --quiet              No output printed to stdout
    --color WHEN             Coloring: auto, always, never
"#;

fn main() {
    let config = Config::default().unwrap();
    let args = env::args().collect::<Vec<_>>();
    let result = cargo::call_main_without_stdin(real_main, &config, USAGE, &args, false);

    match result {
        Err(e) => cargo::handle_cli_error(e, &mut *config.shell()),
        Ok(()) => {},
    }
}

fn real_main(options: Options, config: &Config) -> CliResult {
    try!(config.configure(options.flag_verbose,
                          options.flag_quiet,
                          &options.flag_color,
                          /* frozen = */ false,
                          /* locked = */ false));
    let platform_triple = config.rustc()?.host.clone();

    let (packages, resolve) = {
        let lockfile = Path::new("Cargo.lock");
        let manifest_path = lockfile.parent().unwrap().join("Cargo.toml");
        let manifest = env::current_dir().unwrap().join(&manifest_path);
        let ws = try!(Workspace::new(&manifest, config));
        let specs = Packages::All.into_package_id_specs(&ws).chain_error(|| {
          human("failed to find specs? whats a spec?")
        })?;

        ops::resolve_ws_precisely(
                &ws,
                None,
                &[],
                false,
                false,
                &specs).chain_error(|| {
            human("failed to load pkg lockfile")
        })?
    };

    let package_ids = {
        let source_id_from_registry =
          options.flag_host.map(|s| s.to_url().map(|url| SourceId::for_registry(&url)).map_err(human));

        let registry_id = try!(source_id_from_registry.unwrap_or_else(|| SourceId::crates_io(config)));
        try!(fs::metadata("Cargo.lock").chain_error(|| {
          human("failed to find Cargo.lock. Please run `cargo generate-lockfile` first.")
        }));

        let mut package_ids = resolve.iter()
            .filter(|id| *id.source_id() == registry_id)
            .cloned()
            .collect::<Vec<_>>();
        package_ids.sort_by_key(|id| id.name().to_owned());
        package_ids
    };

    let mut raze_packages = package_ids.iter()
      .map(|id| BazelPackage {
          id: id.clone(),
          package: packages.get(id).unwrap().clone(),
          full_name: format!("{}-{}", id.name(), id.version()),
          path: format!("./vendor/{}-{}/", id.name(), id.version()),
          // TODO(acmcarther): This will break as of cargo commit 50f1c172
          features: resolve.features(id)
            .cloned()
            .unwrap_or(HashSet::new()),
          dependencies: Vec::new(),
          build_dependencies: Vec::new(),
          dev_dependencies: Vec::new(),
          targets: Vec::new(),
      })
      .collect::<Vec<_>>();

    // Verify that the package is already vendored
    for pkg in raze_packages.iter() {
        try!(fs::metadata(&pkg.path).chain_error(|| {
            human(format!("failed to find {}. Please run `cargo vendor -x` first.", pkg.path))
        }));
    }

    // Determine targets
    for mut pkg in raze_packages.iter_mut() {
        let &mut BazelPackage {
          ref full_name,
          ref package,
          ref mut targets, ..} = pkg;
        let partial_path = format!("{}/", full_name);
        let partial_path_byte_length = partial_path.as_bytes().len();

        for target in package.targets().iter() {
            let target_path_str = target.src_path().to_str()
              .expect("path wasn't unicode")
              .to_owned();
            let crate_name_str_idx = target_path_str.find(&partial_path)
              .expect("target path should have been in vendor directory");
            let local_path_bytes = target_path_str.bytes()
              .skip(crate_name_str_idx + partial_path_byte_length)
              .collect::<Vec<_>>();
            let local_path = String::from_utf8(local_path_bytes)
              .expect("source string was corrupted while slicing");

            targets.push(BazelTarget {
              name: target.name().to_owned(),
              path: local_path,
              kinds: kind_to_kinds(target.kind()),
            });
        }
    }

    let platform_attrs = generic_linux_cfgs();

    // Determine exact dependencies per package
    for mut pkg in raze_packages.iter_mut() {
        let &mut BazelPackage {
          ref id,
          ref package,
          ref mut dependencies,
          ref mut build_dependencies,
          ref mut dev_dependencies, ..} = pkg;

        let concrete_dependencies = package.dependencies().iter()
            .filter(|dep| dep.platform().map(|p| p.matches(&platform_triple, Some(&platform_attrs))).unwrap_or(true))
            .cloned()
            .collect::<Vec<_>>();

        let normal_dependencies_by_name = concrete_dependencies.iter()
            .filter(|dep| dep.kind() == Kind::Normal)
            .map(|dep| (dep.name().to_owned(), dep.clone()))
            .collect::<HashMap<String, Dependency>>();

        let dev_dependencies_by_name = concrete_dependencies.iter()
            .filter(|dep| dep.kind() == Kind::Development)
            .map(|dep| (dep.name().to_owned(), dep.clone()))
            .collect::<HashMap<String, Dependency>>();

        let build_dependencies_by_name = concrete_dependencies.iter()
            .filter(|dep| dep.kind() == Kind::Build)
            .map(|dep| (dep.name().to_owned(), dep.clone()))
            .collect::<HashMap<String, Dependency>>();

        let planned_dependencies_by_name = resolve.deps(id).into_iter()
            .map(|dep| (dep.name().to_owned(), dep.clone()))
            .collect::<HashMap<String, PackageId>>();

        let all_dependency_names = planned_dependencies_by_name.keys().cloned()
            .chain(normal_dependencies_by_name.keys().cloned())
            .chain(dev_dependencies_by_name.keys().cloned())
            .chain(build_dependencies_by_name.keys().cloned())
            .collect::<HashSet<_>>();

        for dependency_name in all_dependency_names.iter() {
            if !planned_dependencies_by_name.contains_key(dependency_name) {
                // TODO(acmcarther): Identify why this is removing most dev dependencies
                //println!("TRACE: Crate <{}> is omitting concrete dependency <{}> because it is unused.",
                //         id.name(), dependency_name);
                continue
            }
            let planned_dependency = planned_dependencies_by_name.get(dependency_name).unwrap();
            let bazel_dependency = BazelDependency {
                name: dependency_name.clone(),
                version: planned_dependency.version().to_string(),
            };

            if let Some(_) = dev_dependencies_by_name.get(dependency_name) {
                dev_dependencies.push(bazel_dependency.clone());
            }

            if let Some(_) = build_dependencies_by_name.get(dependency_name) {
                build_dependencies.push(bazel_dependency.clone());
            }

            if let Some(_) = normal_dependencies_by_name.get(dependency_name) {
                dependencies.push(bazel_dependency);
            }
        }
    }

    let platform_attrs_pretty = platform_attrs.iter().map(cfg_pretty).collect::<Vec<_>>();

    for pkg in raze_packages.into_iter() {
        let file_contents = format!(
r#""""
cargo-raze generated details for {name}.

Generated for:
platform_triple: {platform_triple}
platform_attrs:
{platform_attrs:#?}

DO NOT MODIFY! Instead, add a CargoOverride.bzl mixin.
"""
description = {expr}"
"#,
            name = pkg.full_name,
            platform_triple = platform_triple,
            platform_attrs = platform_attrs_pretty,
            expr = pkg.to_expr().pretty_print());

        let cargo_bzl_path = format!("{}Cargo.bzl", &pkg.path);
        try!(File::create(&cargo_bzl_path)
             .and_then(|mut f| f.write_all(file_contents.as_bytes()))
             .chain_error(|| human(format!("failed to create {}", cargo_bzl_path))));
        println!("Generated {} successfully", cargo_bzl_path);
    }

    println!("All done!");
    Ok(())
}

fn generic_linux_cfgs() -> Vec<Cfg> {
    // TODO(acmcarther): use output of rustc, similar to
    // cargo::ops::cargo_rustc::context::Context::probe_target_info_kind
    // https://github.com/rust-lang/cargo/blob/f5348cc321a032db95cd18e3129a4392d2e0a892/src/cargo/ops/cargo_rustc/context.rs#L199
    let hardcoded_properties =
r#"debug_assertions
target_arch="x86_64"
target_endian="little"
target_env="gnu"
target_family="unix"
target_feature="sse"
target_feature="sse2"
target_has_atomic="16"
target_has_atomic="32"
target_has_atomic="64"
target_has_atomic="8"
target_has_atomic="ptr"
target_os="linux"
target_pointer_width="64"
target_thread_local
target_vendor="unknown"
unix"#;
    hardcoded_properties.lines()
      .map(Cfg::from_str)
      .map(|cfg| cfg.expect("hardcoded values should be parsable"))
      .collect()
}

fn cfg_pretty(cfg: &Cfg) -> String {
    match cfg {
        &Cfg::Name(ref s) => s.clone(),
        &Cfg::KeyPair(ref k, ref v) => format!("{}: {}", k, v)
    }
}

// TODO(acmcarther): Remove this shim from cargo when Cargo is upgraded
fn kind_to_kinds(kind: &TargetKind) -> Vec<String> {
    match kind {
        &TargetKind::Lib(ref kinds) => kinds.iter().map(|k| k.crate_type().to_owned()).collect(),
        &TargetKind::Bin => vec!["bin".to_owned()],
        &TargetKind::ExampleBin | &TargetKind::ExampleLib(_) => vec!["example".to_owned()],
        &TargetKind::Test => vec!["test".to_owned()],
        &TargetKind::CustomBuild => vec!["custom-build".to_owned()],
        &TargetKind::Bench => vec!["bench".to_owned()],
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate loggerv;

#[macro_use]
extern crate clap;

extern crate serde_json;

extern crate time;

extern crate regex;

extern crate colored;

use clap::App;
use colored::*;
use log::LogLevel;
use regex::Regex;
use serde_json::Value;
use std::collections::BTreeMap as DataMap;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
use std::io::Write;
use std::process;
use time::get_time;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
struct MinionResult {
    command: Option<String>,
    retcode: Retcode,
    output: Option<String>,
    result: Option<String>,
    host: String,
}

type MinionResults = Vec<MinionResult>;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Retcode {
    Success,
    Failure,
}

impl Retcode {
    fn is_success(&self) -> bool {
        self == &Retcode::Success
    }
}

impl Default for Retcode {
    fn default() -> Retcode {
        Retcode::Failure
    }
}

impl From<u64> for Retcode {
    fn from(input: u64) -> Self {
        if input == 0 {
            Retcode::Success
        } else {
            Retcode::Failure
        }
    }
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml)
        .version(crate_version!())
        .get_matches();
    trace!("matches: {:?}", matches);

    {
        let loglevel: LogLevel = value_t!(matches, "loglevel", LogLevel)
            .expect("can not parse loglevel from args");
        loggerv::init_with_level(loglevel).expect("can not initialize logger with parsed loglevel");
    }

    let changed = matches.is_present("changed");
    let no_save_file = matches.is_present("no_save_file");

    let (host_data, no_return) = {
        let input = matches.value_of("input").expect("can not get input file from args");

        let input = if input == "-" {
            let mut buffer = String::new();
            io::stdin().read_to_string(&mut buffer).expect("can not read from stdin");
            buffer
        } else {
            let mut file = File::open(input).expect("can not open input file");
            let mut input = String::new();
            file.read_to_string(&mut input).expect("can not read input file to string");
            input
        };

        // match all hosts that have not returned as they are not in the json data
        // format is normally like "Minion pricesearch did not respond. No job will be sent."
        let catch_not_returned_minions =
            Regex::new(r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$")
                .expect("regex for catching not returned minions is not valid");

        let mut no_return = Vec::new();
        for host in catch_not_returned_minions.captures_iter(input.as_str()) {
            no_return.push(host[1].to_string());
        }

        // clean up hosts that have not returned from the json data
        (catch_not_returned_minions.replace_all(input.as_str(), "").into_owned(), no_return)
    };

    let value: Value = serde_json::from_str(host_data.as_str())
        .expect("can not convert input data to value. have you run the salt command with \
                 --static?");

    let results = match get_results(&value, no_return) {
        Ok(r) => r,
        Err(e) => {
            error!("can not get results from serde value: {}", e);
            if !no_save_file {
                write_save_file(host_data.as_str());
            }
            process::exit(1)
        }
    };

    trace!("results: {:#?}", results);

    let compressed = get_compressed(results);
    trace!("compressed: {:#?}", compressed);

    print_compressed(compressed, changed);
}

#[derive(Debug)]
enum ResultError {
    ConvertDiffToString,
    ConvertValueToString,
    ReturnCodeNotNumber,
    RetValueIsNone,
    RetValueIsNull,
    RetValueIsNumber,
    ValueNotAnObject,
}

impl fmt::Display for ResultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultError::ConvertDiffToString => write!(f, "can not convert diff to string"),
            ResultError::ConvertValueToString => write!(f, "can not convert value to string"),
            ResultError::ReturnCodeNotNumber => write!(f, "returncode is not a number"),
            ResultError::RetValueIsNone => write!(f, "ret value is none"),
            ResultError::RetValueIsNull => write!(f, "ret value is null"),
            ResultError::RetValueIsNumber => write!(f, "ret value is number"),
            ResultError::ValueNotAnObject => write!(f, "value it not an object"),
        }
    }
}

fn get_results(value: &Value, no_return: Vec<String>) -> Result<MinionResults, ResultError> {
    if !value.is_object() {
        return Err(ResultError::ValueNotAnObject);
    }

    let mut results: MinionResults = Vec::new();

    for (host, values) in value.as_object().unwrap().iter() {
        trace!("host: {:#?}", host);
        trace!("values: {:#?}", values);

        let retcode: Retcode = match values.get("retcode") {
            Some(o) => {
                match o.as_u64() {
                    Some(v) => v.into(),
                    None => return Err(ResultError::ReturnCodeNotNumber),
                }
            }
            None => {
                warn!("host {} does not have a return code", host);
                Retcode::Failure
            }
        };

        let ret = match values.get("ret") {
            None => return Err(ResultError::RetValueIsNone),
            Some(r) => r,
        };

        match *ret {
            Value::Null => return Err(ResultError::RetValueIsNull),
            Value::Bool(r) => {
                let (command, result, output) = if r {
                    (None, Some("true".to_string()), None)
                } else {
                    (None, Some("false".to_string()), None)
                };

                results.push(MinionResult {
                    command: command,
                    host: host.clone(),
                    output: output,
                    result: result,
                    retcode: retcode,
                });
            }
            Value::Number(_) => return Err(ResultError::RetValueIsNumber),
            Value::String(ref r) => {
                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(r.clone()),
                    retcode: retcode,
                    ..MinionResult::default()
                });
            }

            Value::Array(ref r) => {
                let values: Vec<_> = r.iter()
                    .map(|v| v.as_str().expect("can not convert the array value to a string"))
                    .collect();

                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(values.join("\n").to_string()),
                    retcode: retcode.clone(),
                    ..MinionResult::default()
                });
            }
            Value::Object(ref r) => {
                if r.is_empty() {
                    results.push(MinionResult {
                        host: host.clone(),
                        retcode: retcode.clone(),
                        ..MinionResult::default()
                    });
                }

                for (command, command_result) in r.iter() {
                    trace!("command: {:#?}", command);
                    trace!("command_result: {:#?}", command_result);

                    let result = match command_result.get("comment") {
                        Some(r) => {
                            match r.as_str() {
                                Some(s) => Some(s.to_string()),
                                None => return Err(ResultError::ConvertValueToString),
                            }
                        }
                        None => None,
                    };

                    let output = match command_result.get("changes") {
                        Some(r) => {
                            match r.get("diff") {
                                Some(d) => {
                                    match d.as_str() {
                                        Some(i) => Some(i.to_string()),
                                        None => return Err(ResultError::ConvertDiffToString),
                                    }
                                }
                                None => None,
                            }
                        }
                        None => None,
                    };

                    results.push(MinionResult {
                        command: Some(command.to_string()),
                        host: host.clone(),
                        output: output,
                        result: result,
                        retcode: retcode.clone(),
                    });
                }
            }
        };
    }

    for host in no_return {
        results.push(MinionResult {
            host: host,
            retcode: Retcode::Failure,
            output: Some("Minion did not respond. No job will be sent.".to_string()),
            ..MinionResult::default()
        });
    }

    Ok(results)
}

fn get_compressed(results: MinionResults) -> DataMap<MinionResult, Vec<String>> {
    // compress output by changeing the hostname to the same value for all results and then just
    // adding all hosts with that value to the map.
    let mut compressed: DataMap<MinionResult, Vec<String>> = DataMap::new();
    for result in results {
        let mut result_no_host = result.clone();
        result_no_host.host = String::new();

        compressed.entry(result_no_host).or_insert_with(Vec::new).push(result.host);
    }

    compressed
}

fn print_compressed(compressed: DataMap<MinionResult, Vec<String>>, changed: bool) {
    let mut unchanged = 0;
    for (result, hosts) in compressed {
        // continue if we only want to print out changes and there are none and the command was a
        // success
        // TODO: make this a filter of the map
        if changed && !result.output.is_some() && result.retcode.is_success() {
            unchanged += 1;
            continue;
        }

        println!("");
        println!("{}", "----------".bold());
        println!("");

        // state, command info
        {
            if result.command.is_some() {
                println!("{}", "------".purple());

                if result.command.is_some() {
                    println!("{}",
                             format!("COMMAND: {}", result.command.clone().unwrap()).purple());
                }

                println!("{}\n", "------".purple());
            }
        }

        // hosts
        {
            println!("{}", "------".cyan());
            println!("{}{}", "HOSTS: ".cyan(), hosts.join(", ").as_str());
            println!("{}\n", "------".cyan());
        }

        // output
        {
            println!("{}", "------".yellow());

            match result.retcode {
                Retcode::Success => println!("{}{}", "RETURN CODE: ".yellow(), "Success".green()),
                Retcode::Failure => println!("{}{}", "RETURN CODE: ".yellow(), "Failure".red()),
            }

            if result.result.is_some() {
                println!("{}", "RESULT:".yellow());
                println!("{}\n", result.result.unwrap());
            }

            println!("{}", "OUTPUT:".yellow());
            if result.output.is_some() {
                for line in result.output.unwrap().lines() {
                    if line.starts_with('-') {
                        println!("{}", line.red());
                        continue;
                    }

                    if line.starts_with('+') {
                        println!("{}", line.green());
                        continue;
                    }

                    println!("{}", line);
                }
            } else {
                println!("No changes");
            }
            println!("{}", "------".yellow());
        }
    }

    if changed {
        println!("");
        info!("{} unchanged states", unchanged);
    }
}

fn write_save_file(host_data: &str) {
    let save_filename = format!("/tmp/salt-compressor_{}.json", get_time().sec);
    let mut save_file = File::create(save_filename.clone()).expect("can not create save_file");
    save_file.write_all(host_data.as_bytes()).expect("can not write host data to save_file");
    info!("please send me the save file under {} which contains the json \
           data from salt",
          save_filename);
}
<commit_msg>Made unchanged states logmessage more explicit.<commit_after>#[macro_use]
extern crate log;
extern crate loggerv;

#[macro_use]
extern crate clap;

extern crate serde_json;

extern crate time;

extern crate regex;

extern crate colored;

use clap::App;
use colored::*;
use log::LogLevel;
use regex::Regex;
use serde_json::Value;
use std::collections::BTreeMap as DataMap;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
use std::io::Write;
use std::process;
use time::get_time;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
struct MinionResult {
    command: Option<String>,
    retcode: Retcode,
    output: Option<String>,
    result: Option<String>,
    host: String,
}

type MinionResults = Vec<MinionResult>;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum Retcode {
    Success,
    Failure,
}

impl Retcode {
    fn is_success(&self) -> bool {
        self == &Retcode::Success
    }
}

impl Default for Retcode {
    fn default() -> Retcode {
        Retcode::Failure
    }
}

impl From<u64> for Retcode {
    fn from(input: u64) -> Self {
        if input == 0 {
            Retcode::Success
        } else {
            Retcode::Failure
        }
    }
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let matches = App::from_yaml(yaml)
        .version(crate_version!())
        .get_matches();
    trace!("matches: {:?}", matches);

    {
        let loglevel: LogLevel = value_t!(matches, "loglevel", LogLevel)
            .expect("can not parse loglevel from args");
        loggerv::init_with_level(loglevel).expect("can not initialize logger with parsed loglevel");
    }

    let changed = matches.is_present("changed");
    let no_save_file = matches.is_present("no_save_file");

    let (host_data, no_return) = {
        let input = matches.value_of("input").expect("can not get input file from args");

        let input = if input == "-" {
            let mut buffer = String::new();
            io::stdin().read_to_string(&mut buffer).expect("can not read from stdin");
            buffer
        } else {
            let mut file = File::open(input).expect("can not open input file");
            let mut input = String::new();
            file.read_to_string(&mut input).expect("can not read input file to string");
            input
        };

        // match all hosts that have not returned as they are not in the json data
        // format is normally like "Minion pricesearch did not respond. No job will be sent."
        let catch_not_returned_minions =
            Regex::new(r"(?m)^Minion (\S*) did not respond\. No job will be sent\.$")
                .expect("regex for catching not returned minions is not valid");

        let mut no_return = Vec::new();
        for host in catch_not_returned_minions.captures_iter(input.as_str()) {
            no_return.push(host[1].to_string());
        }

        // clean up hosts that have not returned from the json data
        (catch_not_returned_minions.replace_all(input.as_str(), "").into_owned(), no_return)
    };

    let value: Value = serde_json::from_str(host_data.as_str())
        .expect("can not convert input data to value. have you run the salt command with \
                 --static?");

    let results = match get_results(&value, no_return) {
        Ok(r) => r,
        Err(e) => {
            error!("can not get results from serde value: {}", e);
            if !no_save_file {
                write_save_file(host_data.as_str());
            }
            process::exit(1)
        }
    };

    trace!("results: {:#?}", results);

    let compressed = get_compressed(results);
    trace!("compressed: {:#?}", compressed);

    print_compressed(compressed, changed);
}

#[derive(Debug)]
enum ResultError {
    ConvertDiffToString,
    ConvertValueToString,
    ReturnCodeNotNumber,
    RetValueIsNone,
    RetValueIsNull,
    RetValueIsNumber,
    ValueNotAnObject,
}

impl fmt::Display for ResultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ResultError::ConvertDiffToString => write!(f, "can not convert diff to string"),
            ResultError::ConvertValueToString => write!(f, "can not convert value to string"),
            ResultError::ReturnCodeNotNumber => write!(f, "returncode is not a number"),
            ResultError::RetValueIsNone => write!(f, "ret value is none"),
            ResultError::RetValueIsNull => write!(f, "ret value is null"),
            ResultError::RetValueIsNumber => write!(f, "ret value is number"),
            ResultError::ValueNotAnObject => write!(f, "value it not an object"),
        }
    }
}

fn get_results(value: &Value, no_return: Vec<String>) -> Result<MinionResults, ResultError> {
    if !value.is_object() {
        return Err(ResultError::ValueNotAnObject);
    }

    let mut results: MinionResults = Vec::new();

    for (host, values) in value.as_object().unwrap().iter() {
        trace!("host: {:#?}", host);
        trace!("values: {:#?}", values);

        let retcode: Retcode = match values.get("retcode") {
            Some(o) => {
                match o.as_u64() {
                    Some(v) => v.into(),
                    None => return Err(ResultError::ReturnCodeNotNumber),
                }
            }
            None => {
                warn!("host {} does not have a return code", host);
                Retcode::Failure
            }
        };

        let ret = match values.get("ret") {
            None => return Err(ResultError::RetValueIsNone),
            Some(r) => r,
        };

        match *ret {
            Value::Null => return Err(ResultError::RetValueIsNull),
            Value::Bool(r) => {
                let (command, result, output) = if r {
                    (None, Some("true".to_string()), None)
                } else {
                    (None, Some("false".to_string()), None)
                };

                results.push(MinionResult {
                    command: command,
                    host: host.clone(),
                    output: output,
                    result: result,
                    retcode: retcode,
                });
            }
            Value::Number(_) => return Err(ResultError::RetValueIsNumber),
            Value::String(ref r) => {
                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(r.clone()),
                    retcode: retcode,
                    ..MinionResult::default()
                });
            }

            Value::Array(ref r) => {
                let values: Vec<_> = r.iter()
                    .map(|v| v.as_str().expect("can not convert the array value to a string"))
                    .collect();

                results.push(MinionResult {
                    host: host.clone(),
                    result: Some(values.join("\n").to_string()),
                    retcode: retcode.clone(),
                    ..MinionResult::default()
                });
            }
            Value::Object(ref r) => {
                if r.is_empty() {
                    results.push(MinionResult {
                        host: host.clone(),
                        retcode: retcode.clone(),
                        ..MinionResult::default()
                    });
                }

                for (command, command_result) in r.iter() {
                    trace!("command: {:#?}", command);
                    trace!("command_result: {:#?}", command_result);

                    let result = match command_result.get("comment") {
                        Some(r) => {
                            match r.as_str() {
                                Some(s) => Some(s.to_string()),
                                None => return Err(ResultError::ConvertValueToString),
                            }
                        }
                        None => None,
                    };

                    let output = match command_result.get("changes") {
                        Some(r) => {
                            match r.get("diff") {
                                Some(d) => {
                                    match d.as_str() {
                                        Some(i) => Some(i.to_string()),
                                        None => return Err(ResultError::ConvertDiffToString),
                                    }
                                }
                                None => None,
                            }
                        }
                        None => None,
                    };

                    results.push(MinionResult {
                        command: Some(command.to_string()),
                        host: host.clone(),
                        output: output,
                        result: result,
                        retcode: retcode.clone(),
                    });
                }
            }
        };
    }

    for host in no_return {
        results.push(MinionResult {
            host: host,
            retcode: Retcode::Failure,
            output: Some("Minion did not respond. No job will be sent.".to_string()),
            ..MinionResult::default()
        });
    }

    Ok(results)
}

fn get_compressed(results: MinionResults) -> DataMap<MinionResult, Vec<String>> {
    // compress output by changeing the hostname to the same value for all results and then just
    // adding all hosts with that value to the map.
    let mut compressed: DataMap<MinionResult, Vec<String>> = DataMap::new();
    for result in results {
        let mut result_no_host = result.clone();
        result_no_host.host = String::new();

        compressed.entry(result_no_host).or_insert_with(Vec::new).push(result.host);
    }

    compressed
}

fn print_compressed(compressed: DataMap<MinionResult, Vec<String>>, changed: bool) {
    let mut unchanged = 0;
    for (result, hosts) in compressed {
        // continue if we only want to print out changes and there are none and the command was a
        // success
        // TODO: make this a filter of the map
        if changed && !result.output.is_some() && result.retcode.is_success() {
            unchanged += 1;
            continue;
        }

        println!("");
        println!("{}", "----------".bold());
        println!("");

        // state, command info
        {
            if result.command.is_some() {
                println!("{}", "------".purple());

                if result.command.is_some() {
                    println!("{}",
                             format!("COMMAND: {}", result.command.clone().unwrap()).purple());
                }

                println!("{}\n", "------".purple());
            }
        }

        // hosts
        {
            println!("{}", "------".cyan());
            println!("{}{}", "HOSTS: ".cyan(), hosts.join(", ").as_str());
            println!("{}\n", "------".cyan());
        }

        // output
        {
            println!("{}", "------".yellow());

            match result.retcode {
                Retcode::Success => println!("{}{}", "RETURN CODE: ".yellow(), "Success".green()),
                Retcode::Failure => println!("{}{}", "RETURN CODE: ".yellow(), "Failure".red()),
            }

            if result.result.is_some() {
                println!("{}", "RESULT:".yellow());
                println!("{}\n", result.result.unwrap());
            }

            println!("{}", "OUTPUT:".yellow());
            if result.output.is_some() {
                for line in result.output.unwrap().lines() {
                    if line.starts_with('-') {
                        println!("{}", line.red());
                        continue;
                    }

                    if line.starts_with('+') {
                        println!("{}", line.green());
                        continue;
                    }

                    println!("{}", line);
                }
            } else {
                println!("No changes");
            }
            println!("{}", "------".yellow());
        }
    }

    if changed {
        println!("");
        info!("filtered {} unchanged states", unchanged);
    }
}

fn write_save_file(host_data: &str) {
    let save_filename = format!("/tmp/salt-compressor_{}.json", get_time().sec);
    let mut save_file = File::create(save_filename.clone()).expect("can not create save_file");
    save_file.write_all(host_data.as_bytes()).expect("can not write host data to save_file");
    info!("please send me the save file under {} which contains the json \
           data from salt",
          save_filename);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
#[macro_use]
extern crate arrayref;
extern crate env_logger;

mod arch;
mod utils;
mod loaders;
// use arch::memory::Memory;
// use arch::cpu::CPU;

use log::{LogLevelFilter, SetLoggerError};
use std::env;
use env_logger::LogBuilder;
use std::fs::File;
use std::path::PathBuf;
use loaders::loaders_factory::LoadersFactory;
use arch::rom_structs::Header;

fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    builder.filter(None, LogLevelFilter::Info);
    builder.init()
}

fn load_rom(buf: &[u8]) -> Result<&[u8], String> {
    let header = Header::from_bytes(array_ref![buf[0..16], 0, 16]);

    if &header.header != "NES\x1A".as_bytes() {
        return Err("Invalid header!".to_owned());
    }

    info!("Rom has trainer? {}", header.has_trainer());

    let prg_start = 16 + (if header.has_trainer() { 512 } else { 0 }) as usize;
    let prg_end = prg_start + header.prg_rom_size();

    info!("PRG ROM from {:#X} to {:#X}", prg_start, prg_end);

    info!("Mapper: {}", header.mapping_number());

    let rom: &[u8] = &buf[prg_start..prg_end];

    Ok(rom)
}

pub fn main() {
    if init_logger().is_err() {
        eprintln!("Failed to initialize logger.");
    }

    let args: Vec<_> = env::args().collect();
    let file_path = PathBuf::from(&args[1]);

    let ext = match file_path.extension() {
        Some(ext) => ext.to_str().unwrap_or(""),
        None => "",
    };

    let mut file = File::open(&file_path).expect("Failed to open file");

    let loader = LoadersFactory::decode(ext);

    info!("Selected loader {}", loader.name());

    let buf = loader.load_rom(&mut file).expect("Failed to load ROM");
    match load_rom(&buf) {
        Ok(rom) => {
            info!("ROM size: {}", rom.len());
        }
        Err(e) => {
            eprintln!("Errore {}", e);
        }
    }
}
<commit_msg>Handled no arguments<commit_after>#[macro_use]
extern crate log;
#[macro_use]
extern crate arrayref;
extern crate env_logger;

mod arch;
mod utils;
mod loaders;
// use arch::memory::Memory;
// use arch::cpu::CPU;

use log::{LogLevelFilter, SetLoggerError};
use std::env;
use env_logger::LogBuilder;
use std::fs::File;
use std::path::PathBuf;
use loaders::loaders_factory::LoadersFactory;
use arch::rom_structs::Header;

fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    builder.filter(None, LogLevelFilter::Info);
    builder.init()
}

fn load_rom(buf: &[u8]) -> Result<&[u8], String> {
    let header = Header::from_bytes(array_ref![buf[0..16], 0, 16]);

    if &header.header != "NES\x1A".as_bytes() {
        return Err("Invalid header!".to_owned());
    }

    info!("Rom has trainer? {}", header.has_trainer());

    let prg_start = 16 + (if header.has_trainer() { 512 } else { 0 }) as usize;
    let prg_end = prg_start + header.prg_rom_size();

    info!("PRG ROM from {:#X} to {:#X}", prg_start, prg_end);

    info!("Mapper: {}", header.mapping_number());

    let rom: &[u8] = &buf[prg_start..prg_end];

    Ok(rom)
}

pub fn main() {
    if init_logger().is_err() {
        eprintln!("Failed to initialize logger.");
        return;
    }

    let args: Vec<_> = env::args().collect();

    if args.len() < 2 {
        eprintln!("Not enough arguments!");
        return;
    }

    let file_path = PathBuf::from(&args[1]);

    let ext = match file_path.extension() {
        Some(ext) => ext.to_str().unwrap_or(""),
        None => "",
    };

    let mut file = File::open(&file_path).expect("Failed to open file");

    let loader = LoadersFactory::decode(ext);

    info!("Selected loader {}", loader.name());

    let buf = loader.load_rom(&mut file).expect("Failed to load ROM");
    match load_rom(&buf) {
        Ok(rom) => {
            info!("ROM size: {}", rom.len());
        }
        Err(e) => {
            eprintln!("Errore {}", e);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Mark Lee

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate hyper;
extern crate regex;
extern crate rquery;
extern crate semver;
extern crate serde;
extern crate serde_json;

use hyper::{Client, Get, Server};
use hyper::header::{Accept, ContentType};
use hyper::server::{Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use regex::Regex;
use rquery::Document;
use semver::Version;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{Error, Read};

const DEFAULT_SERVER_PORT: u32 = 9000;
const MIN_BUNDLER_VERSION: &'static str = "1.11.0";
const RUBY_LANGPACK_RELEASES_URL: &'static str = "https://github.\
                                                  com/heroku/heroku-buildpack-ruby/releases.atom";

fn download_url(client: &Client, url: &str) -> Result<String, Error> {
    let mut resp = client.get(url).send().expect("Could not send HTTP request");
    let mut body = String::new();
    match resp.read_to_string(&mut body) {
        Err(error) => Err(error),
        _ => Ok(body),
    }
}

fn latest_buildpack_release(client: &Client) -> String {
    let xml = download_url(&client, RUBY_LANGPACK_RELEASES_URL)
                  .expect("Could not download Atom releases!");
    let atom_doc = Document::new_from_xml_string(&xml[..]).expect("Could not parse Atom releases!");
    let latest_tag = atom_doc.select("entry title")
                             .expect("Could not find latest buildpack tag!");
    latest_tag.text().clone()
}

fn bundler_version_from_ruby_buildpack(client: &Client) -> Option<String> {
    let ruby_langpack_url = format!("https://raw.githubusercontent.com/\
                                     heroku/heroku-buildpack-ruby/{}/lib/language_pack/\
                                     ruby.rb",
                                    latest_buildpack_release(&client));
    let ruby_file = &download_url(&client, &ruby_langpack_url[..])
                         .expect("Could not download ruby.rb!")[..];
    let regex = Regex::new(r#"BUNDLER_VERSION += "(.+?)""#).expect("Invalid regular expression!");
    if regex.is_match(ruby_file) {
        let captures = regex.captures(ruby_file).expect("Could not match?!");
        Some(captures.at(1).expect("Capture not found?!").to_owned())
    } else {
        None
    }
}

fn is_bundler_upgraded(client: &Client) -> bool {
    if let Some(buildpack_bundler_version_str) = bundler_version_from_ruby_buildpack(&client) {
        let min_version = Version::parse(MIN_BUNDLER_VERSION)
                              .expect("Could not parse min version!");
        let new_version = Version::parse(&buildpack_bundler_version_str[..])
                              .expect("Could not parse new version!");
        min_version < new_version
    } else {
        false
    }
}

fn determine_content_type(req: &Request) -> ContentType {
    if let Some(accept) = req.headers.get::<Accept>() {
        let mut use_json = false;
        for qitem in &accept.0 {
            let qitem_str = &format!("{}", qitem)[..];
            if qitem_str == "application/json" {
                use_json = true;
                break;
            }
        }

        if use_json {
            ContentType::json()
        } else {
            ContentType::html()
        }
    } else {
        ContentType::html()
    }
}

fn result_to_json(result: bool) -> String {
    let mut map = HashMap::new();
    map.insert("result".to_owned(), result);
    serde_json::to_string(&map).expect("Could not serialize result!")
}

fn result_to_html(result: bool) -> String {
    let result_str = if result {
        r#"
Yes
"#
    } else {
        r#"
No
"#
    };
    match File::open("index.html") {
        Ok(mut html_file) => {
            let mut html = String::new();
            html_file.read_to_string(&mut html).expect("Could not read HTML file!");
            html.replace("{{ is_bundler_upgraded }}", result_str)
                .replace("{{ MIN_BUNDLER_VERSION }}", MIN_BUNDLER_VERSION)
        }
        Err(error) => format!("HTML NOT FOUND: {}", error),
    }
}

fn server_port() -> String {
    match env::var_os("PORT") {
        Some(val) => {
            match val.into_string() {
                Ok(port) => port,
                _ => format!("{}", DEFAULT_SERVER_PORT),
            }
        }
        None => format!("{}", DEFAULT_SERVER_PORT),
    }
}

fn main() {
    let server = Server::http(&format!("0.0.0.0:{}", server_port())[..])
                     .expect("Could not create server!");
    server.handle(|req: Request, mut res: Response| {
              match req.uri {
                  AbsolutePath(ref path) => {
                      match (&req.method, &path[..]) {
                          (&Get, "/") => {
                              let client = Client::new();
                              let result = is_bundler_upgraded(&client);
                              let content_type = determine_content_type(&req);
                              let data = match &format!("{}", content_type)[..] {
                                  "application/json; charset=utf-8" => result_to_json(result),
                                  _ => result_to_html(result),
                              };
                              res.headers_mut().set(content_type);
                              res.send(data.as_bytes()).expect("Could not set response body!")
                          }
                          _ => {
                              *res.status_mut() = hyper::NotFound;
                          }
                      }
                  }
                  _ => {
                      *res.status_mut() = hyper::BadRequest;
                  }
              }
          })
          .expect("Could not set up HTTP request handler!");
}
<commit_msg>Simpler server_port implementation<commit_after>// Copyright (C) 2016 Mark Lee

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate hyper;
extern crate regex;
extern crate rquery;
extern crate semver;
extern crate serde;
extern crate serde_json;

use hyper::{Client, Get, Server};
use hyper::header::{Accept, ContentType};
use hyper::server::{Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use regex::Regex;
use rquery::Document;
use semver::Version;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::{Error, Read};

const DEFAULT_SERVER_PORT: u32 = 9000;
const MIN_BUNDLER_VERSION: &'static str = "1.11.0";
const RUBY_LANGPACK_RELEASES_URL: &'static str = "https://github.\
                                                  com/heroku/heroku-buildpack-ruby/releases.atom";

fn download_url(client: &Client, url: &str) -> Result<String, Error> {
    let mut resp = client.get(url).send().expect("Could not send HTTP request");
    let mut body = String::new();
    match resp.read_to_string(&mut body) {
        Err(error) => Err(error),
        _ => Ok(body),
    }
}

fn latest_buildpack_release(client: &Client) -> String {
    let xml = download_url(&client, RUBY_LANGPACK_RELEASES_URL)
                  .expect("Could not download Atom releases!");
    let atom_doc = Document::new_from_xml_string(&xml[..]).expect("Could not parse Atom releases!");
    let latest_tag = atom_doc.select("entry title")
                             .expect("Could not find latest buildpack tag!");
    latest_tag.text().clone()
}

fn bundler_version_from_ruby_buildpack(client: &Client) -> Option<String> {
    let ruby_langpack_url = format!("https://raw.githubusercontent.com/\
                                     heroku/heroku-buildpack-ruby/{}/lib/language_pack/\
                                     ruby.rb",
                                    latest_buildpack_release(&client));
    let ruby_file = &download_url(&client, &ruby_langpack_url[..])
                         .expect("Could not download ruby.rb!")[..];
    let regex = Regex::new(r#"BUNDLER_VERSION += "(.+?)""#).expect("Invalid regular expression!");
    if regex.is_match(ruby_file) {
        let captures = regex.captures(ruby_file).expect("Could not match?!");
        Some(captures.at(1).expect("Capture not found?!").to_owned())
    } else {
        None
    }
}

fn is_bundler_upgraded(client: &Client) -> bool {
    if let Some(buildpack_bundler_version_str) = bundler_version_from_ruby_buildpack(&client) {
        let min_version = Version::parse(MIN_BUNDLER_VERSION)
                              .expect("Could not parse min version!");
        let new_version = Version::parse(&buildpack_bundler_version_str[..])
                              .expect("Could not parse new version!");
        min_version < new_version
    } else {
        false
    }
}

fn determine_content_type(req: &Request) -> ContentType {
    if let Some(accept) = req.headers.get::<Accept>() {
        let mut use_json = false;
        for qitem in &accept.0 {
            let qitem_str = &format!("{}", qitem)[..];
            if qitem_str == "application/json" {
                use_json = true;
                break;
            }
        }

        if use_json {
            ContentType::json()
        } else {
            ContentType::html()
        }
    } else {
        ContentType::html()
    }
}

fn result_to_json(result: bool) -> String {
    let mut map = HashMap::new();
    map.insert("result".to_owned(), result);
    serde_json::to_string(&map).expect("Could not serialize result!")
}

fn result_to_html(result: bool) -> String {
    let result_str = if result {
        r#"
Yes
"#
    } else {
        r#"
No
"#
    };
    match File::open("index.html") {
        Ok(mut html_file) => {
            let mut html = String::new();
            html_file.read_to_string(&mut html).expect("Could not read HTML file!");
            html.replace("{{ is_bundler_upgraded }}", result_str)
                .replace("{{ MIN_BUNDLER_VERSION }}", MIN_BUNDLER_VERSION)
        }
        Err(error) => format!("HTML NOT FOUND: {}", error),
    }
}

fn server_port() -> String {
    match env::var("PORT") {
        Ok(port) => port,
        _ => format!("{}", DEFAULT_SERVER_PORT),
    }
}
}

fn main() {
    let server = Server::http(&format!("0.0.0.0:{}", server_port())[..])
                     .expect("Could not create server!");
    server.handle(|req: Request, mut res: Response| {
              match req.uri {
                  AbsolutePath(ref path) => {
                      match (&req.method, &path[..]) {
                          (&Get, "/") => {
                              let client = Client::new();
                              let result = is_bundler_upgraded(&client);
                              let content_type = determine_content_type(&req);
                              let data = match &format!("{}", content_type)[..] {
                                  "application/json; charset=utf-8" => result_to_json(result),
                                  _ => result_to_html(result),
                              };
                              res.headers_mut().set(content_type);
                              res.send(data.as_bytes()).expect("Could not set response body!")
                          }
                          _ => {
                              *res.status_mut() = hyper::NotFound;
                          }
                      }
                  }
                  _ => {
                      *res.status_mut() = hyper::BadRequest;
                  }
              }
          })
          .expect("Could not set up HTTP request handler!");
}
<|endoftext|>