<commit_before>// Copyright 2015-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/)

#![cfg(not(test))]
#![deny(warnings)]

extern crate getopts;
extern crate serde_json as json;

use std::env;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, ExitStatus};
use std::str;
use std::collections::HashSet;
use std::iter::FromIterator;

use json::Value;

use getopts::{Options, Matches};

fn main() {
    let exit_status = execute();
    std::io::stdout().flush().unwrap();
    std::process::exit(exit_status);
}

fn execute() -> i32 {
    let success = 0;
    let failure = 1;

    let mut opts = getopts::Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("q", "quiet", "no output printed to stdout");
    opts.optflag("v", "verbose", "use verbose output");
    opts.optmulti(
        "p",
        "package",
        "specify package to format (only usable in workspaces)",
        "<package>",
    );
    opts.optflag("", "all", "format all packages (only usable in workspaces)");

    let matches = match opts.parse(env::args().skip(1).take_while(|a| a != "--")) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return failure;
        }
    };

    let verbosity = match (matches.opt_present("v"), matches.opt_present("q")) {
        (false, false) => Verbosity::Normal,
        (false, true) => Verbosity::Quiet,
        (true, false) => Verbosity::Verbose,
        (true, true) => {
            print_usage(&opts, "quiet mode and verbose mode are not compatible");
            return failure;
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
        return success;
    }

    let workspace_hitlist = WorkspaceHitlist::from_matches(&matches);

    match format_crate(verbosity, workspace_hitlist) {
        Err(e) => {
            print_usage(&opts, &e.to_string());
            failure
        }
        Ok(status) => {
            if status.success() {
                success
            } else {
                status.code().unwrap_or(failure)
            }
        }
    }
}

fn print_usage(opts: &Options, reason: &str) {
    let msg = format!("{}\nusage: cargo fmt [options]", reason);
    println!(
        "{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \
         Arguments after `--` are passed to rustfmt.",
        opts.usage(&msg)
    );
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Verbosity {
    Verbose,
    Normal,
    Quiet,
}

fn format_crate(
    verbosity: Verbosity,
    workspace_hitlist: WorkspaceHitlist,
) -> Result<ExitStatus, std::io::Error> {
    let targets = get_targets(workspace_hitlist)?;

    // Currently only bin and lib files get formatted
    let files: Vec<_> = targets
        .into_iter()
        .filter(|t| t.kind.should_format())
        .inspect(|t| if verbosity == Verbosity::Verbose {
            println!("[{:?}] {:?}", t.kind, t.path)
        })
        .map(|t| t.path)
        .collect();

    format_files(&files, &get_fmt_args(), verbosity)
}

fn get_fmt_args() -> Vec<String> {
    // All arguments after -- are passed to rustfmt
    env::args().skip_while(|a| a != "--").skip(1).collect()
}

#[derive(Debug)]
enum TargetKind {
    Lib, // dylib, staticlib, lib
    Bin, // bin
    Example, // example file
    Test, // test file
    Bench, // bench file
    CustomBuild, // build script
    Other, // plugin,...
}

impl TargetKind {
    fn should_format(&self) -> bool {
        match *self {
            TargetKind::Lib | TargetKind::Bin | TargetKind::Example | TargetKind::Test |
            TargetKind::Bench | TargetKind::CustomBuild => true,
            _ => false,
        }
    }
}

#[derive(Debug)]
pub struct Target {
    path: PathBuf,
    kind: TargetKind,
}

#[derive(Debug, PartialEq, Eq)]
pub enum WorkspaceHitlist {
    All,
    Some(Vec<String>),
    None,
}

impl WorkspaceHitlist {
    pub fn get_some<'a>(&'a self) -> Option<&'a [String]> {
        if let &WorkspaceHitlist::Some(ref hitlist) = self {
            Some(&hitlist)
        } else {
            None
        }
    }

    pub fn from_matches(matches: &Matches) -> WorkspaceHitlist {
        match (matches.opt_present("all"), matches.opt_present("p")) {
            (false, false) => WorkspaceHitlist::None,
            (true, _) => WorkspaceHitlist::All,
            (false, true) => WorkspaceHitlist::Some(matches.opt_strs("p")),
        }
    }
}

// Returns a vector of all compile targets of a crate
fn get_targets(workspace_hitlist: WorkspaceHitlist) -> Result<Vec<Target>, std::io::Error> {
    let mut targets: Vec<Target> = vec![];
    if workspace_hitlist == WorkspaceHitlist::None {
        let output = Command::new("cargo").arg("read-manifest").output()?;
        if output.status.success() {
            // None of the unwraps should fail if output of `cargo read-manifest` is correct
            let data = &String::from_utf8(output.stdout).unwrap();
            let json: Value = json::from_str(data).unwrap();
            let json_obj = json.as_object().unwrap();
            let jtargets = json_obj.get("targets").unwrap().as_array().unwrap();
            for jtarget in jtargets {
                targets.push(target_from_json(jtarget));
            }

            return Ok(targets);
        }
        return Err(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            str::from_utf8(&output.stderr).unwrap(),
        ));
    }
    // This happens when cargo-fmt is not used inside a crate or
    // is used inside a workspace.
    // To ensure backward compatability, we only use `cargo metadata` for workspaces.
    // TODO: Is it possible only use metadata or read-manifest
    let output = Command::new("cargo")
        .arg("metadata")
        .arg("--no-deps")
        .output()?;
    if output.status.success() {
        let data = &String::from_utf8(output.stdout).unwrap();
        let json: Value = json::from_str(data).unwrap();
        let json_obj = json.as_object().unwrap();
        let mut hitlist: HashSet<&String> = if workspace_hitlist != WorkspaceHitlist::All {
            HashSet::from_iter(workspace_hitlist.get_some().unwrap())
        } else {
            HashSet::new() // Unused
        };
        let members: Vec<&Value> = json_obj
            .get("packages")
            .unwrap()
            .as_array()
            .unwrap()
            .into_iter()
            .filter(|member| if workspace_hitlist == WorkspaceHitlist::All {
                true
            } else {
                let member_obj = member.as_object().unwrap();
                let member_name = member_obj.get("name").unwrap().as_str().unwrap();
                hitlist.take(&member_name.to_string()).is_some()
            })
            .collect();
        if hitlist.len() != 0 {
            // Mimick cargo of only outputting one <package> spec.
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!(
                    "package `{}` is not a member of the workspace",
                    hitlist.iter().next().unwrap()
                ),
            ));
        }
        for member in members {
            let member_obj = member.as_object().unwrap();
            let jtargets = member_obj.get("targets").unwrap().as_array().unwrap();
            for jtarget in jtargets {
                targets.push(target_from_json(jtarget));
            }
        }
        return Ok(targets);
    }
    Err(std::io::Error::new(
        std::io::ErrorKind::NotFound,
        str::from_utf8(&output.stderr).unwrap(),
    ))

}

fn target_from_json(jtarget: &Value) -> Target {
    let jtarget = jtarget.as_object().unwrap();
    let path = PathBuf::from(jtarget.get("src_path").unwrap().as_str().unwrap());
    let kinds = jtarget.get("kind").unwrap().as_array().unwrap();
    let kind = match kinds[0].as_str().unwrap() {
        "bin" => TargetKind::Bin,
        "lib" | "dylib" | "staticlib" | "cdylib" | "rlib" => TargetKind::Lib,
        "test" => TargetKind::Test,
        "example" => TargetKind::Example,
        "bench" => TargetKind::Bench,
        "custom-build" => TargetKind::CustomBuild,
        _ => TargetKind::Other,
    };

    Target {
        path: path,
        kind: kind,
    }
}

fn format_files(
    files: &[PathBuf],
    fmt_args: &[String],
    verbosity: Verbosity,
) -> Result<ExitStatus, std::io::Error> {
    let stdout = if verbosity == Verbosity::Quiet {
        std::process::Stdio::null()
    } else {
        std::process::Stdio::inherit()
    };
    if verbosity == Verbosity::Verbose {
        print!("rustfmt");
        for a in fmt_args.iter() {
            print!(" {}", a);
        }
        for f in files.iter() {
            print!(" {}", f.display());
        }
        println!("");
    }
    let mut command = Command::new("rustfmt")
        .stdout(stdout)
        .args(files)
        .args(fmt_args)
        .spawn()
        .map_err(|e| match e.kind() {
            std::io::ErrorKind::NotFound => {
                std::io::Error::new(
                    std::io::ErrorKind::Other,
                    "Could not run rustfmt, please make sure it is in your PATH.",
                )
            }
            _ => e,
        })?;
    command.wait()
}
<commit_msg>Do not run cargo fmt if there are unnecessary arguments<commit_after>// Copyright 2015-2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Inspired by Paul Woolcock's cargo-fmt (https://github.com/pwoolcoc/cargo-fmt/)

#![cfg(not(test))]
#![deny(warnings)]

extern crate getopts;
extern crate serde_json as json;

use std::env;
use std::io::Write;
use std::path::PathBuf;
use std::process::{Command, ExitStatus};
use std::str;
use std::collections::HashSet;
use std::iter::FromIterator;

use json::Value;

use getopts::{Options, Matches};

fn main() {
    let exit_status = execute();
    std::io::stdout().flush().unwrap();
    std::process::exit(exit_status);
}

fn execute() -> i32 {
    let success = 0;
    let failure = 1;

    let mut opts = getopts::Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optflag("q", "quiet", "no output printed to stdout");
    opts.optflag("v", "verbose", "use verbose output");
    opts.optmulti(
        "p",
        "package",
        "specify package to format (only usable in workspaces)",
        "<package>",
    );
    opts.optflag("", "all", "format all packages (only usable in workspaces)");

    // If there is any invalid argument passed to `cargo fmt`, return without formatting.
    if let Some(arg) = env::args()
        .skip(2)
        .take_while(|a| a != "--")
        .find(|a| !a.starts_with('-'))
    {
        print_usage(&opts, &format!("Invalid argument: `{}`.", arg));
        return failure;
    }

    let matches = match opts.parse(env::args().skip(1).take_while(|a| a != "--")) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return failure;
        }
    };

    let verbosity = match (matches.opt_present("v"), matches.opt_present("q")) {
        (false, false) => Verbosity::Normal,
        (false, true) => Verbosity::Quiet,
        (true, false) => Verbosity::Verbose,
        (true, true) => {
            print_usage(&opts, "quiet mode and verbose mode are not compatible");
            return failure;
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
        return success;
    }

    let workspace_hitlist = WorkspaceHitlist::from_matches(&matches);

    match format_crate(verbosity, workspace_hitlist) {
        Err(e) => {
            print_usage(&opts, &e.to_string());
            failure
        }
        Ok(status) => {
            if status.success() {
                success
            } else {
                status.code().unwrap_or(failure)
            }
        }
    }
}

fn print_usage(opts: &Options, reason: &str) {
    let msg = format!("{}\nusage: cargo fmt [options]", reason);
    println!(
        "{}\nThis utility formats all bin and lib files of the current crate using rustfmt. \
         Arguments after `--` are passed to rustfmt.",
        opts.usage(&msg)
    );
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Verbosity {
    Verbose,
    Normal,
    Quiet,
}

fn format_crate(
    verbosity: Verbosity,
    workspace_hitlist: WorkspaceHitlist,
) -> Result<ExitStatus, std::io::Error> {
    let targets = get_targets(workspace_hitlist)?;

    // Currently only bin and lib files get formatted
    let files: Vec<_> = targets
        .into_iter()
        .filter(|t| t.kind.should_format())
        .inspect(|t| if verbosity == Verbosity::Verbose {
            println!("[{:?}] {:?}", t.kind, t.path)
        })
        .map(|t| t.path)
        .collect();

    format_files(&files, &get_fmt_args(), verbosity)
}

fn get_fmt_args() -> Vec<String> {
    // All arguments after -- are passed to rustfmt
    env::args().skip_while(|a| a != "--").skip(1).collect()
}

#[derive(Debug)]
enum TargetKind {
    Lib, // dylib, staticlib, lib
    Bin, // bin
    Example, // example file
    Test, // test file
    Bench, // bench file
    CustomBuild, // build script
    Other, // plugin,...
}

impl TargetKind {
    fn should_format(&self) -> bool {
        match *self {
            TargetKind::Lib | TargetKind::Bin | TargetKind::Example | TargetKind::Test |
            TargetKind::Bench | TargetKind::CustomBuild => true,
            _ => false,
        }
    }
}

#[derive(Debug)]
pub struct Target {
    path: PathBuf,
    kind: TargetKind,
}

#[derive(Debug, PartialEq, Eq)]
pub enum WorkspaceHitlist {
    All,
    Some(Vec<String>),
    None,
}

impl WorkspaceHitlist {
    pub fn get_some<'a>(&'a self) -> Option<&'a [String]> {
        if let &WorkspaceHitlist::Some(ref hitlist) = self {
            Some(&hitlist)
        } else {
            None
        }
    }

    pub fn from_matches(matches: &Matches) -> WorkspaceHitlist {
        match (matches.opt_present("all"), matches.opt_present("p")) {
            (false, false) => WorkspaceHitlist::None,
            (true, _) => WorkspaceHitlist::All,
            (false, true) => WorkspaceHitlist::Some(matches.opt_strs("p")),
        }
    }
}

// Returns a vector of all compile targets of a crate
fn get_targets(workspace_hitlist: WorkspaceHitlist) -> Result<Vec<Target>, std::io::Error> {
    let mut targets: Vec<Target> = vec![];
    if workspace_hitlist == WorkspaceHitlist::None {
        let output = Command::new("cargo").arg("read-manifest").output()?;
        if output.status.success() {
            // None of the unwraps should fail if output of `cargo read-manifest` is correct
            let data = &String::from_utf8(output.stdout).unwrap();
            let json: Value = json::from_str(data).unwrap();
            let json_obj = json.as_object().unwrap();
            let jtargets = json_obj.get("targets").unwrap().as_array().unwrap();
            for jtarget in jtargets {
                targets.push(target_from_json(jtarget));
            }

            return Ok(targets);
        }
        return Err(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            str::from_utf8(&output.stderr).unwrap(),
        ));
    }
    // This happens when cargo-fmt is not used inside a crate or
    // is used inside a workspace.
    // To ensure backward compatability, we only use `cargo metadata` for workspaces.
    // TODO: Is it possible only use metadata or read-manifest
    let output = Command::new("cargo")
        .arg("metadata")
        .arg("--no-deps")
        .output()?;
    if output.status.success() {
        let data = &String::from_utf8(output.stdout).unwrap();
        let json: Value = json::from_str(data).unwrap();
        let json_obj = json.as_object().unwrap();
        let mut hitlist: HashSet<&String> = if workspace_hitlist != WorkspaceHitlist::All {
            HashSet::from_iter(workspace_hitlist.get_some().unwrap())
        } else {
            HashSet::new() // Unused
        };
        let members: Vec<&Value> = json_obj
            .get("packages")
            .unwrap()
            .as_array()
            .unwrap()
            .into_iter()
            .filter(|member| if workspace_hitlist == WorkspaceHitlist::All {
                true
            } else {
                let member_obj = member.as_object().unwrap();
                let member_name = member_obj.get("name").unwrap().as_str().unwrap();
                hitlist.take(&member_name.to_string()).is_some()
            })
            .collect();
        if hitlist.len() != 0 {
            // Mimick cargo of only outputting one <package> spec.
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!(
                    "package `{}` is not a member of the workspace",
                    hitlist.iter().next().unwrap()
                ),
            ));
        }
        for member in members {
            let member_obj = member.as_object().unwrap();
            let jtargets = member_obj.get("targets").unwrap().as_array().unwrap();
            for jtarget in jtargets {
                targets.push(target_from_json(jtarget));
            }
        }
        return Ok(targets);
    }
    Err(std::io::Error::new(
        std::io::ErrorKind::NotFound,
        str::from_utf8(&output.stderr).unwrap(),
    ))

}

fn target_from_json(jtarget: &Value) -> Target {
    let jtarget = jtarget.as_object().unwrap();
    let path = PathBuf::from(jtarget.get("src_path").unwrap().as_str().unwrap());
    let kinds = jtarget.get("kind").unwrap().as_array().unwrap();
    let kind = match kinds[0].as_str().unwrap() {
        "bin" => TargetKind::Bin,
        "lib" | "dylib" | "staticlib" | "cdylib" | "rlib" => TargetKind::Lib,
        "test" => TargetKind::Test,
        "example" => TargetKind::Example,
        "bench" => TargetKind::Bench,
        "custom-build" => TargetKind::CustomBuild,
        _ => TargetKind::Other,
    };

    Target {
        path: path,
        kind: kind,
    }
}

fn format_files(
    files: &[PathBuf],
    fmt_args: &[String],
    verbosity: Verbosity,
) -> Result<ExitStatus, std::io::Error> {
    let stdout = if verbosity == Verbosity::Quiet {
        std::process::Stdio::null()
    } else {
        std::process::Stdio::inherit()
    };
    if verbosity == Verbosity::Verbose {
        print!("rustfmt");
        for a in fmt_args.iter() {
            print!(" {}", a);
        }
        for f in files.iter() {
            print!(" {}", f.display());
        }
        println!("");
    }
    let mut command = Command::new("rustfmt")
        .stdout(stdout)
        .args(files)
        .args(fmt_args)
        .spawn()
        .map_err(|e| match e.kind() {
            std::io::ErrorKind::NotFound => {
                std::io::Error::new(
                    std::io::ErrorKind::Other,
                    "Could not run rustfmt, please make sure it is in your PATH.",
                )
            }
            _ => e,
        })?;
    command.wait()
}
<|endoftext|>


<commit_before>use std::ops::Deref;

#[derive(Debug)]
pub struct CloseOnDrop<T: __private::Closable> {
    inner: T,
}

impl<T: __private::Closable> CloseOnDrop<T> {
    pub fn new(inner: T) -> Self {
        Self { inner }
    }
}

impl<T: __private::Closable> Deref for CloseOnDrop<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl<T: __private::Closable> Drop for CloseOnDrop<T> {
    fn drop(&mut self) {
        self.close();
    }
}

pub(crate) mod __private {
    pub trait Closable {
        fn close(&self);
    }
}
<commit_msg>add CloseOnDrop::into_inner<commit_after>use std::ops::Deref;

#[derive(Debug)]
pub struct CloseOnDrop<T: __private::Closable> {
    inner: Option<T>,
}

impl<T: __private::Closable> CloseOnDrop<T> {
    pub fn new(inner: T) -> Self {
        Self { inner: Some(inner) }
    }

    /// Give the underlying object, cancelling the close on drop action
    pub fn into_inner(mut self) -> T {
        self.inner.take().expect("inner should only be None once consumed or dropped")
    }
}

impl<T: __private::Closable> Deref for CloseOnDrop<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        self.inner.as_ref().expect("inner should only be None once consumed or dropped")
    }
}

impl<T: __private::Closable> Drop for CloseOnDrop<T> {
    fn drop(&mut self) {
        if let Some(inner) = self.inner.as_ref() {
            inner.close();
        }
    }
}

pub(crate) mod __private {
    pub trait Closable {
        fn close(&self);
    }
}
<|endoftext|>


<commit_before>use analysis::namespaces;
use case::CaseExt;
use codegen::general::{self, version_condition, version_condition_string};
use config::gobjects::GObject;
use env::Env;
use file_saver;
use library::*;
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::Result;
use std::path::Path;
use traits::*;
use version::Version;

pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
    let path = root_path.join("enums.rs");
    file_saver::save_to_file(path, env.config.make_backup, |w| {
        try!(general::start_comments(w, &env.config));
        try!(writeln!(w, ""));
        try!(writeln!(w, "use ffi;"));
        if env.namespaces.glib_ns_id == namespaces::MAIN {
            try!(writeln!(w, "use ffi as glib_ffi;
use error::ErrorDomain;
use translate::*;"));
        } else {
            try!(writeln!(w, "use glib_ffi;
use glib::error::ErrorDomain;
use glib::translate::*;"));
        }
        try!(writeln!(w, ""));

        let configs = env.config.objects.values()
            .filter(|c| {
                c.status.need_generate() &&
                    c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
            });
        let mut first = true;
        for config in configs {
            if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
                if first {
                    mod_rs.push("\nmod enums;".into());
                    first = false;
                }
                if let Some (cfg) = version_condition_string(env, enum_.version, false, 0) {
                    mod_rs.push(cfg);
                }
                mod_rs.push(format!("pub use self::enums::{};", enum_.name));
                try!(generate_enum(env, w, enum_, config));
            }
        }

        Ok(())
    });
}

fn generate_enum(env: &Env, w: &mut Write, enum_: &Enumeration, config: &GObject) -> Result<()> {
    struct Member {
        name: String,
        c_name: String,
        version: Option<Version>,
    }

    let mut members: Vec<Member> = Vec::new();
    let mut vals: HashSet<String> = HashSet::new();

    for member in &enum_.members {
        let member_config = config.members.matched(&member.name);
        let is_alias = member_config.iter().any(|m| m.alias);
        if is_alias || vals.contains(&member.value) {
            continue
        }
        vals.insert(member.value.clone());
        let version = member_config.iter().filter_map(|m| m.version).next();
        members.push(Member {
            name: member.name.to_camel(),
            c_name: member.c_identifier.clone(),
            version: version,
        });
    }

    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]"));
    try!(writeln!(w, "pub enum {} {{", enum_.name));
    for member in &members {
        try!(writeln!(w, "\t{},", member.name));
    }
    try!(writeln!(w, "{}", "    #[doc(hidden)]
    __Nonexhaustive(()),
}
"));

    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(w, "#[doc(hidden)]
impl ToGlib for {name} {{
    type GlibType = ffi::{ffi_name};

    fn to_glib(&self) -> ffi::{ffi_name} {{
        match *self {{", name = enum_.name, ffi_name = enum_.c_type));
    for member in &members {
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(w, "\t\t\t{}::{} => ffi::{},", enum_.name, member.name, member.c_name));
    }
    try!(writeln!(w, "\t\t\t{}::__Nonexhaustive(_) => panic!(),", enum_.name));
    try!(writeln!(w, "{}",
"        }
    }
}
"));

    let assert = if env.config.generate_safety_asserts {
        "skip_assert_initialized!();\n\t\t"
    } else {
        ""
    };

    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(w, "#[doc(hidden)]
impl FromGlib<ffi::{ffi_name}> for {name} {{
    fn from_glib(value: ffi::{ffi_name}) -> Self {{
        {assert}match value {{", name = enum_.name, ffi_name = enum_.c_type, assert = assert));
    for member in &members {
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(w, "\t\t\tffi::{} => {}::{},", member.c_name, enum_.name, member.name));
    }
    if members.len() == 1 {
        try!(writeln!(w, "\t\t\t_ => {}::__Nonexhaustive(()),", enum_.name));
    }
    try!(writeln!(w, "{}",
"        }
    }
}
"));

    if let Some(ref get_quark) = enum_.error_domain {
        let get_quark = get_quark.replace("-", "_");
        let has_failed_member = members.iter().any(|m| m.name == "Failed");

        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(w, "impl ErrorDomain for {name} {{
    fn domain() -> glib_ffi::GQuark {{
        {assert}unsafe {{ ffi::{get_quark}() }}
    }}

    fn code(self) -> i32 {{
        self.to_glib() as i32
    }}

    fn from(code: i32) -> Option<Self> {{
        {assert}match code {{", name = enum_.name, get_quark = get_quark, assert = assert));

        for member in &members {
            try!(version_condition(w, env, member.version, false, 3));
            try!(writeln!(w, "\t\t\tx if x == ffi::{} as i32 => Some({}::{}),", member.c_name,
                enum_.name, member.name));
        }
        if has_failed_member {
            try!(writeln!(w, "\t\t\t_ => Some({}::Failed),", enum_.name));
        } else {
            try!(writeln!(w, "\t\t\t_ => Some({}::__Nonexhaustive(())),", enum_.name));
        }

        try!(writeln!(w, "{}",
"        }
    }
}
"));
    }

    Ok(())
}
<commit_msg>Enums primary detects error_domain by function "quark"<commit_after>use analysis::namespaces;
use case::CaseExt;
use codegen::general::{self, version_condition, version_condition_string};
use config::gobjects::GObject;
use env::Env;
use file_saver;
use library::*;
use std::collections::HashSet;
use std::io::prelude::*;
use std::io::Result;
use std::path::Path;
use traits::*;
use version::Version;

pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
    let path = root_path.join("enums.rs");
    file_saver::save_to_file(path, env.config.make_backup, |w| {
        try!(general::start_comments(w, &env.config));
        try!(writeln!(w, ""));
        try!(writeln!(w, "use ffi;"));
        if env.namespaces.glib_ns_id == namespaces::MAIN {
            try!(writeln!(w, "use ffi as glib_ffi;
use error::ErrorDomain;
use translate::*;"));
        } else {
            try!(writeln!(w, "use glib_ffi;
use glib::error::ErrorDomain;
use glib::translate::*;"));
        }
        try!(writeln!(w, ""));

        let configs = env.config.objects.values()
            .filter(|c| {
                c.status.need_generate() &&
                    c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
            });
        let mut first = true;
        for config in configs {
            if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
                if first {
                    mod_rs.push("\nmod enums;".into());
                    first = false;
                }
                if let Some (cfg) = version_condition_string(env, enum_.version, false, 0) {
                    mod_rs.push(cfg);
                }
                mod_rs.push(format!("pub use self::enums::{};", enum_.name));
                try!(generate_enum(env, w, enum_, config));
            }
        }

        Ok(())
    });
}

fn generate_enum(env: &Env, w: &mut Write, enum_: &Enumeration, config: &GObject) -> Result<()> {
    struct Member {
        name: String,
        c_name: String,
        version: Option<Version>,
    }

    let mut members: Vec<Member> = Vec::new();
    let mut vals: HashSet<String> = HashSet::new();

    for member in &enum_.members {
        let member_config = config.members.matched(&member.name);
        let is_alias = member_config.iter().any(|m| m.alias);
        if is_alias || vals.contains(&member.value) {
            continue
        }
        vals.insert(member.value.clone());
        let version = member_config.iter().filter_map(|m| m.version).next();
        members.push(Member {
            name: member.name.to_camel(),
            c_name: member.c_identifier.clone(),
            version: version,
        });
    }

    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(w, "#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]"));
    try!(writeln!(w, "pub enum {} {{", enum_.name));
    for member in &members {
        try!(writeln!(w, "\t{},", member.name));
    }
    try!(writeln!(w, "{}", "    #[doc(hidden)]
    __Nonexhaustive(()),
}
"));

    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(w, "#[doc(hidden)]
impl ToGlib for {name} {{
    type GlibType = ffi::{ffi_name};

    fn to_glib(&self) -> ffi::{ffi_name} {{
        match *self {{", name = enum_.name, ffi_name = enum_.c_type));
    for member in &members {
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(w, "\t\t\t{}::{} => ffi::{},", enum_.name, member.name, member.c_name));
    }
    try!(writeln!(w, "\t\t\t{}::__Nonexhaustive(_) => panic!(),", enum_.name));
    try!(writeln!(w, "{}",
"        }
    }
}
"));

    let assert = if env.config.generate_safety_asserts {
        "skip_assert_initialized!();\n\t\t"
    } else {
        ""
    };

    try!(version_condition(w, env, enum_.version, false, 0));
    try!(writeln!(w, "#[doc(hidden)]
impl FromGlib<ffi::{ffi_name}> for {name} {{
    fn from_glib(value: ffi::{ffi_name}) -> Self {{
        {assert}match value {{", name = enum_.name, ffi_name = enum_.c_type, assert = assert));
    for member in &members {
        try!(version_condition(w, env, member.version, false, 3));
        try!(writeln!(w, "\t\t\tffi::{} => {}::{},", member.c_name, enum_.name, member.name));
    }
    if members.len() == 1 {
        try!(writeln!(w, "\t\t\t_ => {}::__Nonexhaustive(()),", enum_.name));
    }
    try!(writeln!(w, "{}",
"        }
    }
}
"));
    let error_domain: Option<String> = enum_.functions.iter()
        .filter(|f| f.name == "quark")
        .next()
        .and_then(|f| f.c_identifier.clone())
        .or_else(|| enum_.error_domain.clone());

    if let Some(ref get_quark) = error_domain {
        let get_quark = get_quark.replace("-", "_");
        let has_failed_member = members.iter().any(|m| m.name == "Failed");

        try!(version_condition(w, env, enum_.version, false, 0));
        try!(writeln!(w, "impl ErrorDomain for {name} {{
    fn domain() -> glib_ffi::GQuark {{
        {assert}unsafe {{ ffi::{get_quark}() }}
    }}

    fn code(self) -> i32 {{
        self.to_glib() as i32
    }}

    fn from(code: i32) -> Option<Self> {{
        {assert}match code {{", name = enum_.name, get_quark = get_quark, assert = assert));

        for member in &members {
            try!(version_condition(w, env, member.version, false, 3));
            try!(writeln!(w, "\t\t\tx if x == ffi::{} as i32 => Some({}::{}),", member.c_name,
                enum_.name, member.name));
        }
        if has_failed_member {
            try!(writeln!(w, "\t\t\t_ => Some({}::Failed),", enum_.name));
        } else {
            try!(writeln!(w, "\t\t\t_ => Some({}::__Nonexhaustive(())),", enum_.name));
        }

        try!(writeln!(w, "{}",
"        }
    }
}
"));
    }

    Ok(())
}
<|endoftext|>


<commit_before>use errors::pool::PoolError;

use services::pool::PoolService;

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

pub enum PoolCommand {
    Create(String, // name
           Option<String>, // config
           Box<Fn(Result<(), PoolError>) + Send>),
    Delete(String, // name
           Box<Fn(Result<(), PoolError>) + Send>),
    Open(String, // name
         Option<String>, // config
         Box<Fn(Result<i32, PoolError>) + Send>),
    OpenAck(i32, // cmd id
            Result<i32 /* pool handle */, PoolError>),
    Close(i32, // pool handle
          Box<Fn(Result<(), PoolError>) + Send>),
    //TODO unify type of result for open/close/refresh? (Fn(Result<i32, PoolError>))
    CloseAck(i32,
             Result<(), PoolError>),
    Refresh(i32, // pool handle
            Box<Fn(Result<(), PoolError>) + Send>),
    RefreshAck(i32,
               Result<(), PoolError>),
}

pub struct PoolCommandExecutor {
    pool_service: Rc<PoolService>,
    pending_cbs_opening: RefCell<HashMap<i32, Box<Fn(Result<i32, PoolError>)>>>,
    //TODO unify type of cb for open/close/refresh or use separate HashMaps
    //pending_cbs_close_refresh: RefCell<HashMap<i32, Box<Fn(Result<(), PoolError>)>>>,
}

impl PoolCommandExecutor {
    pub fn new(pool_service: Rc<PoolService>) -> PoolCommandExecutor {
        PoolCommandExecutor {
            pool_service: pool_service,
            pending_cbs_opening: RefCell::new(HashMap::new()),
        }
    }

    pub fn execute(&self, command: PoolCommand) {
        match command {
            PoolCommand::Create(name, config, cb) => {
                info!(target: "pool_command_executor", "Create command received");
                self.create(&name, config.as_ref().map(String::as_str), cb);
            },
            PoolCommand::Delete(name, cb) => {
                info!(target: "pool_command_executor", "Delete command received");
                self.delete(&name, cb);
            },
            PoolCommand::Open(name, config, cb) => {
                info!(target: "pool_command_executor", "Open command received");
                self.open(&name, config.as_ref().map(String::as_str), cb);
            },
            PoolCommand::OpenAck(handle, result) => {
                info!("OpenAck handle {:?}, result {:?}", handle, result);
                self.pending_cbs_opening.borrow_mut().remove(&handle)
                    .expect("Expect callback to process ack command")
                    (result);
            },
            PoolCommand::Close(handle, cb) => {
                info!(target: "pool_command_executor", "Close command received");
                self.close(handle, cb);
            },
            PoolCommand::CloseAck(handle, result) => {
                unimplemented!();
            },
            PoolCommand::Refresh(handle, cb) => {
                info!(target: "pool_command_executor", "Refresh command received");
                self.close(handle, cb);
            },
            PoolCommand::RefreshAck(handle, result) => {
                unimplemented!();
            }
        };
    }

    fn create(&self, name: &str, config: Option<&str>, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }

    fn delete(&self, name: &str, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }

    fn open(&self, name: &str, config: Option<&str>, cb: Box<Fn(Result<i32, PoolError>) + Send>) {
        let config: String = match config {
            Some(s) => String::from(s),
            None => format!("{{\"genesis_txn\": \"{}.txn\"}}", name),
        };
        match self.pool_service.open(name, config.as_str()) {
            Err(err) => { cb(Err(err)); }
            Ok(id) => { self.pending_cbs_opening.borrow_mut().insert(id, cb); }
        };
    }

    fn close(&self, handle: i32, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }

    fn refresh(&self, handle: i32, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }
}<commit_msg>Remove incorrect default config for poll open, add TODO.<commit_after>use errors::pool::PoolError;

use services::pool::PoolService;

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

pub enum PoolCommand {
    Create(String, // name
           Option<String>, // config
           Box<Fn(Result<(), PoolError>) + Send>),
    Delete(String, // name
           Box<Fn(Result<(), PoolError>) + Send>),
    Open(String, // name
         Option<String>, // config
         Box<Fn(Result<i32, PoolError>) + Send>),
    OpenAck(i32, // cmd id
            Result<i32 /* pool handle */, PoolError>),
    Close(i32, // pool handle
          Box<Fn(Result<(), PoolError>) + Send>),
    //TODO unify type of result for open/close/refresh? (Fn(Result<i32, PoolError>))
    CloseAck(i32,
             Result<(), PoolError>),
    Refresh(i32, // pool handle
            Box<Fn(Result<(), PoolError>) + Send>),
    RefreshAck(i32,
               Result<(), PoolError>),
}

pub struct PoolCommandExecutor {
    pool_service: Rc<PoolService>,
    pending_cbs_opening: RefCell<HashMap<i32, Box<Fn(Result<i32, PoolError>)>>>,
    //TODO unify type of cb for open/close/refresh or use separate HashMaps
    //pending_cbs_close_refresh: RefCell<HashMap<i32, Box<Fn(Result<(), PoolError>)>>>,
}

impl PoolCommandExecutor {
    pub fn new(pool_service: Rc<PoolService>) -> PoolCommandExecutor {
        PoolCommandExecutor {
            pool_service: pool_service,
            pending_cbs_opening: RefCell::new(HashMap::new()),
        }
    }

    pub fn execute(&self, command: PoolCommand) {
        match command {
            PoolCommand::Create(name, config, cb) => {
                info!(target: "pool_command_executor", "Create command received");
                self.create(&name, config.as_ref().map(String::as_str), cb);
            },
            PoolCommand::Delete(name, cb) => {
                info!(target: "pool_command_executor", "Delete command received");
                self.delete(&name, cb);
            },
            PoolCommand::Open(name, config, cb) => {
                info!(target: "pool_command_executor", "Open command received");
                self.open(&name, config.as_ref().map(String::as_str), cb);
            },
            PoolCommand::OpenAck(handle, result) => {
                info!("OpenAck handle {:?}, result {:?}", handle, result);
                self.pending_cbs_opening.borrow_mut().remove(&handle)
                    .expect("Expect callback to process ack command")
                    (result);
            },
            PoolCommand::Close(handle, cb) => {
                info!(target: "pool_command_executor", "Close command received");
                self.close(handle, cb);
            },
            PoolCommand::CloseAck(handle, result) => {
                unimplemented!();
            },
            PoolCommand::Refresh(handle, cb) => {
                info!(target: "pool_command_executor", "Refresh command received");
                self.close(handle, cb);
            },
            PoolCommand::RefreshAck(handle, result) => {
                unimplemented!();
            }
        };
    }

    fn create(&self, name: &str, config: Option<&str>, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }

    fn delete(&self, name: &str, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }

    fn open(&self, name: &str, config: Option<&str>, cb: Box<Fn(Result<i32, PoolError>) + Send>) {
        match self.pool_service.open(name, config.unwrap_or("TODO: default config")) { //TODO
            Err(err) => { cb(Err(err)); }
            Ok(id) => { self.pending_cbs_opening.borrow_mut().insert(id, cb); }
        };
    }

    fn close(&self, handle: i32, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }

    fn refresh(&self, handle: i32, cb: Box<Fn(Result<(), PoolError>) + Send>) {
        // TODO: FIXME: Implement me!!!
        cb(Ok(()));
    }
}<|endoftext|>


<commit_before>use crate::dist::notifications::*;
use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;
use sha2::{Digest, Sha256};
use url::Url;

use std::fs;
use std::ops;
use std::path::{Path, PathBuf};

const UPDATE_HASH_LEN: usize = 20;

#[derive(Copy, Clone)]
pub struct DownloadCfg<'a> {
    pub dist_root: &'a str,
    pub temp_cfg: &'a temp::Cfg,
    pub download_dir: &'a PathBuf,
    pub notify_handler: &'a dyn Fn(Notification<'_>),
}

pub struct File {
    path: PathBuf,
}

impl ops::Deref for File {
    type Target = Path;

    fn deref(&self) -> &Path {
        ops::Deref::deref(&self.path)
    }
}

impl<'a> DownloadCfg<'a> {
    /// Downloads a file, validating its hash, and resuming interrupted downloads
    /// Partial downloads are stored in `self.download_dir`, keyed by hash. If the
    /// target file already exists, then the hash is checked and it is returned
    /// immediately without re-downloading.
    pub fn download(&self, url: &Url, hash: &str) -> Result<File> {
        utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| {
            (self.notify_handler)(n.into())
        })?;
        let target_file = self.download_dir.join(Path::new(hash));

        if target_file.exists() {
            let cached_result = file_hash(&target_file)?;
            if hash == cached_result {
                (self.notify_handler)(Notification::FileAlreadyDownloaded);
                (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));
                return Ok(File { path: target_file });
            } else {
                (self.notify_handler)(Notification::CachedFileChecksumFailed);
                fs::remove_file(&target_file).chain_err(|| "cleaning up previous download")?;
            }
        }

        let partial_file_path = target_file.with_file_name(
            target_file
                .file_name()
                .map(|s| s.to_str().unwrap_or("_"))
                .unwrap_or("_")
                .to_owned()
                + ".partial",
        );

        let mut hasher = Sha256::new();

        utils::download_file_with_resume(
            &url,
            &partial_file_path,
            Some(&mut hasher),
            true,
            &|n| (self.notify_handler)(n.into()),
        )?;

        let actual_hash = format!("{:x}", hasher.result());

        if hash != actual_hash {
            // Incorrect hash
            Err(ErrorKind::ChecksumFailed {
                url: url.to_string(),
                expected: hash.to_string(),
                calculated: actual_hash,
            }
            .into())
        } else {
            (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));

            utils::rename_file("downloaded", &partial_file_path, &target_file)?;
            Ok(File { path: target_file })
        }
    }

    pub fn clean(&self, hashes: &[String]) -> Result<()> {
        for hash in hashes.iter() {
            let used_file = self.download_dir.join(hash);
            if self.download_dir.join(&used_file).exists() {
                fs::remove_file(used_file).chain_err(|| "cleaning up cached downloads")?;
            }
        }
        Ok(())
    }

    fn download_hash(&self, url: &str) -> Result<String> {
        let hash_url = utils::parse_url(&(url.to_owned() + ".sha256"))?;
        let hash_file = self.temp_cfg.new_file()?;

        utils::download_file(&hash_url, &hash_file, None, &|n| {
            (self.notify_handler)(n.into())
        })?;

        Ok(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned())?)
    }

    /// Downloads a file, sourcing its hash from the same url with a `.sha256` suffix.
    /// If `update_hash` is present, then that will be compared to the downloaded hash,
    /// and if they match, the download is skipped.
    pub fn download_and_check(
        &self,
        url_str: &str,
        update_hash: Option<&Path>,
        ext: &str,
    ) -> Result<Option<(temp::File<'a>, String)>> {
        let hash = self.download_hash(url_str)?;
        let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect();

        if let Some(hash_file) = update_hash {
            if utils::is_file(hash_file) {
                if let Ok(contents) = utils::read_file("update hash", hash_file) {
                    if contents == partial_hash {
                        // Skip download, update hash matches
                        return Ok(None);
                    }
                } else {
                    (self.notify_handler)(Notification::CantReadUpdateHash(hash_file));
                }
            } else {
                (self.notify_handler)(Notification::NoUpdateHash(hash_file));
            }
        }

        let url = utils::parse_url(url_str)?;
        let file = self.temp_cfg.new_file_with_ext("", ext)?;

        let mut hasher = Sha256::new();
        utils::download_file(&url, &file, Some(&mut hasher), &|n| {
            (self.notify_handler)(n.into())
        })?;
        let actual_hash = format!("{:x}", hasher.result());

        if hash != actual_hash {
            // Incorrect hash
            return Err(ErrorKind::ChecksumFailed {
                url: url_str.to_owned(),
                expected: hash,
                calculated: actual_hash,
            }
            .into());
        } else {
            (self.notify_handler)(Notification::ChecksumValid(url_str));
        }

        // TODO: Check the signature of the file

        Ok(Some((file, partial_hash)))
    }
}

fn file_hash(path: &Path) -> Result<String> {
    let mut hasher = Sha256::new();
    use std::io::Read;
    let mut downloaded = fs::File::open(&path).chain_err(|| "opening already downloaded file")?;
    let mut buf = vec![0; 32768];
    while let Ok(n) = downloaded.read(&mut buf) {
        if n == 0 {
            break;
        }
        hasher.input(&buf[..n]);
    }

    Ok(format!("{:x}", hasher.result()))
}
<commit_msg>Better IO buffer when validating dist hashes<commit_after>use crate::dist::notifications::*;
use crate::dist::temp;
use crate::errors::*;
use crate::utils::utils;
use sha2::{Digest, Sha256};
use url::Url;

use std::fs;
use std::ops;
use std::path::{Path, PathBuf};

const UPDATE_HASH_LEN: usize = 20;

#[derive(Copy, Clone)]
pub struct DownloadCfg<'a> {
    pub dist_root: &'a str,
    pub temp_cfg: &'a temp::Cfg,
    pub download_dir: &'a PathBuf,
    pub notify_handler: &'a dyn Fn(Notification<'_>),
}

pub struct File {
    path: PathBuf,
}

impl ops::Deref for File {
    type Target = Path;

    fn deref(&self) -> &Path {
        ops::Deref::deref(&self.path)
    }
}

impl<'a> DownloadCfg<'a> {
    /// Downloads a file, validating its hash, and resuming interrupted downloads
    /// Partial downloads are stored in `self.download_dir`, keyed by hash. If the
    /// target file already exists, then the hash is checked and it is returned
    /// immediately without re-downloading.
    pub fn download(&self, url: &Url, hash: &str) -> Result<File> {
        utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| {
            (self.notify_handler)(n.into())
        })?;
        let target_file = self.download_dir.join(Path::new(hash));

        if target_file.exists() {
            let cached_result = file_hash(&target_file, self.notify_handler)?;
            if hash == cached_result {
                (self.notify_handler)(Notification::FileAlreadyDownloaded);
                (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));
                return Ok(File { path: target_file });
            } else {
                (self.notify_handler)(Notification::CachedFileChecksumFailed);
                fs::remove_file(&target_file).chain_err(|| "cleaning up previous download")?;
            }
        }

        let partial_file_path = target_file.with_file_name(
            target_file
                .file_name()
                .map(|s| s.to_str().unwrap_or("_"))
                .unwrap_or("_")
                .to_owned()
                + ".partial",
        );

        let mut hasher = Sha256::new();

        utils::download_file_with_resume(
            &url,
            &partial_file_path,
            Some(&mut hasher),
            true,
            &|n| (self.notify_handler)(n.into()),
        )?;

        let actual_hash = format!("{:x}", hasher.result());

        if hash != actual_hash {
            // Incorrect hash
            Err(ErrorKind::ChecksumFailed {
                url: url.to_string(),
                expected: hash.to_string(),
                calculated: actual_hash,
            }
            .into())
        } else {
            (self.notify_handler)(Notification::ChecksumValid(&url.to_string()));

            utils::rename_file("downloaded", &partial_file_path, &target_file)?;
            Ok(File { path: target_file })
        }
    }

    pub fn clean(&self, hashes: &[String]) -> Result<()> {
        for hash in hashes.iter() {
            let used_file = self.download_dir.join(hash);
            if self.download_dir.join(&used_file).exists() {
                fs::remove_file(used_file).chain_err(|| "cleaning up cached downloads")?;
            }
        }
        Ok(())
    }

    fn download_hash(&self, url: &str) -> Result<String> {
        let hash_url = utils::parse_url(&(url.to_owned() + ".sha256"))?;
        let hash_file = self.temp_cfg.new_file()?;

        utils::download_file(&hash_url, &hash_file, None, &|n| {
            (self.notify_handler)(n.into())
        })?;

        Ok(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned())?)
    }

    /// Downloads a file, sourcing its hash from the same url with a `.sha256` suffix.
    /// If `update_hash` is present, then that will be compared to the downloaded hash,
    /// and if they match, the download is skipped.
    pub fn download_and_check(
        &self,
        url_str: &str,
        update_hash: Option<&Path>,
        ext: &str,
    ) -> Result<Option<(temp::File<'a>, String)>> {
        let hash = self.download_hash(url_str)?;
        let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect();

        if let Some(hash_file) = update_hash {
            if utils::is_file(hash_file) {
                if let Ok(contents) = utils::read_file("update hash", hash_file) {
                    if contents == partial_hash {
                        // Skip download, update hash matches
                        return Ok(None);
                    }
                } else {
                    (self.notify_handler)(Notification::CantReadUpdateHash(hash_file));
                }
            } else {
                (self.notify_handler)(Notification::NoUpdateHash(hash_file));
            }
        }

        let url = utils::parse_url(url_str)?;
        let file = self.temp_cfg.new_file_with_ext("", ext)?;

        let mut hasher = Sha256::new();
        utils::download_file(&url, &file, Some(&mut hasher), &|n| {
            (self.notify_handler)(n.into())
        })?;
        let actual_hash = format!("{:x}", hasher.result());

        if hash != actual_hash {
            // Incorrect hash
            return Err(ErrorKind::ChecksumFailed {
                url: url_str.to_owned(),
                expected: hash,
                calculated: actual_hash,
            }
            .into());
        } else {
            (self.notify_handler)(Notification::ChecksumValid(url_str));
        }

        // TODO: Check the signature of the file

        Ok(Some((file, partial_hash)))
    }
}

fn file_hash(path: &Path, notify_handler: &dyn Fn(Notification<'_>)) -> Result<String> {
    let mut hasher = Sha256::new();
    let notification_converter = |notification: crate::utils::Notification<'_>| {
        notify_handler(notification.into());
    };
    let mut downloaded = utils::FileReaderWithProgress::new_file(&path, &notification_converter)?;
    use std::io::Read;
    let mut buf = vec![0; 32768];
    while let Ok(n) = downloaded.read(&mut buf) {
        if n == 0 {
            break;
        }
        hasher.input(&buf[..n]);
    }

    Ok(format!("{:x}", hasher.result()))
}
<|endoftext|>


<commit_before>
use std::cmp::Ordering;
use std::path::{ Path, PathBuf };
use std::fs;
use std::fs::{ PathExt, File };
use std::io::{ Write };

use rustc_serialize::json;
use rustc_serialize::json::{ ToJson, Json };

use ::file_type::{ create_parent_links, Link, read_file, FileType };

static TYPE_STR: &'static str = "dir";

pub struct DirFactory;

impl ::file_type::FileTypeFactory for DirFactory {
    fn try_create(&self, path: &Path) -> Option<Box<FileType>> {
        if path.is_dir() {
            Some(Box::new(Dir {
                path: PathBuf::from(path),
                type_str: TYPE_STR,
                file_type_manager: ::file_type::FileTypeManager::new()
            }))
        } else { None }
    }

    fn initialize(&self, app_context: &mut ::AppContext) -> Result<(), &'static str> {

        // Validate generic stuff
        let header_hbs_path = app_context.root_source.join("partials/header.hbs");
        if !header_hbs_path.exists() {
            return Err("Missing partials/header.hbs");
        }

        let footer_hbs_path = app_context.root_source.join("partials/footer.hbs");
        if !footer_hbs_path.exists() {
            return Err("Missing partials/footer.hbs");
        }

        // Validate Dir
        let dir_hbs_path = app_context.root_source.join("layouts/dir.hbs");
        if !dir_hbs_path.exists() {
            return Err("Missing /layouts/dir.hbs");
        }

        // Grab generic stuff
        let header_hbs_contents = try!(read_file(header_hbs_path));
        let footer_hbs_contents = try!(read_file(footer_hbs_path));

        // Create Dir
        let dir_template_name = TYPE_STR;
        let dir_hbs_contents = try!(read_file(&dir_hbs_path));

        app_context.handlebars.register_template_string(dir_template_name, format!("{}\n{}\n{}", header_hbs_contents, dir_hbs_contents, footer_hbs_contents))
            .ok().expect("Error registering header|dir|footer template");

        Ok(())
    }
}

pub struct Dir {
    path: PathBuf,
    type_str: &'static str,
    file_type_manager: ::file_type::FileTypeManager
}

impl Dir {
    fn get_children(&self, context: &::AppContext) -> Vec<Child> {
        let mut result: Vec<Child> = Vec::new();

            match fs::read_dir(&self.path) {
                Ok(items) => {
                    for item in items {
                        let item = item.unwrap().path();
                        let child = self.file_type_manager.create_file_type(&item);
                        result.push(Child {
                            name: String::from_str(item.file_stem().unwrap().to_str().unwrap()),
                            url: child.get_url(context),
                            file_type: String::from_str(child.get_type_str())
                        });
                    }
                },
                Err(_) => ()
            }

        (&mut result).sort_by(|a, b| {
            if a.file_type == String::from_str(TYPE_STR) && b.file_type != String::from_str(TYPE_STR) {
                Ordering::Less
            } else {
                a.name.cmp(&b.name)
            }

        });

        result
    }
}

impl FileType for Dir {
    fn get_url(&self, context: &::AppContext) -> String {
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let relative = if relative.to_str().unwrap() == "." { String::new() } else {
            format!("{}/", relative.to_str().unwrap())
        };
        format!("{}{}", context.base_url, relative)
    }

    fn convert(&self, context: &::AppContext) {
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let new_dir = context.root_dest.join(&relative);
        let new_dir_index = new_dir.join("index.html");
        if !new_dir.exists() {
            fs::create_dir(&new_dir).ok().expect("Cannot create destination subdir");
        }
        let children = self.get_children(context);
        let name = match relative.file_name() {
            Some(_) => String::from_str(relative.file_name().unwrap().to_str().unwrap()),
            None => String::from_str("root")
        };
        let parents = create_parent_links(&context.base_url, &relative, true);
        let dir_model = DirModel {
            name: name,
            parents: parents,
            children: children,
            base_url: context.base_url.clone()
        };
        match context.handlebars.render(TYPE_STR, &dir_model) {
            Ok(rendered) => {
                // Create File
                let mut file = File::create(&new_dir_index).ok().expect("Could not create dir index.html file");
                //fs::chmod(&new_dir_index, USER_FILE).ok().expect("Couldn't chmod new file");
                file.write_all(rendered.as_bytes())
                    .ok().expect("Could not write html to file");
            },
            Err(why) => panic!("Error rendering markdown: {:?}", why)
        }
    }

    fn get_type_str(&self) -> &'static str {
        self.type_str
    }
}

#[derive(RustcEncodable, PartialEq)]
struct Child {
    name: String,
    url: String,
    file_type: String
}

#[derive(RustcEncodable)]
struct DirModel {
    name: String,
    parents: Vec<Link>,
    children: Vec<Child>,
    base_url: String
}

impl ToJson for DirModel {
    fn to_json(&self) -> Json {
        Json::from_str(&json::encode(&self).unwrap()).unwrap()
    }
}
<commit_msg>sort by file name not file type<commit_after>
use std::cmp::Ordering;
use std::path::{ Path, PathBuf };
use std::fs;
use std::fs::{ PathExt, File };
use std::io::{ Write };

use rustc_serialize::json;
use rustc_serialize::json::{ ToJson, Json };

use ::file_type::{ create_parent_links, Link, read_file, FileType };

static TYPE_STR: &'static str = "dir";

pub struct DirFactory;

impl ::file_type::FileTypeFactory for DirFactory {
    fn try_create(&self, path: &Path) -> Option<Box<FileType>> {
        if path.is_dir() {
            Some(Box::new(Dir {
                path: PathBuf::from(path),
                type_str: TYPE_STR,
                file_type_manager: ::file_type::FileTypeManager::new()
            }))
        } else { None }
    }

    fn initialize(&self, app_context: &mut ::AppContext) -> Result<(), &'static str> {

        // Validate generic stuff
        let header_hbs_path = app_context.root_source.join("partials/header.hbs");
        if !header_hbs_path.exists() {
            return Err("Missing partials/header.hbs");
        }

        let footer_hbs_path = app_context.root_source.join("partials/footer.hbs");
        if !footer_hbs_path.exists() {
            return Err("Missing partials/footer.hbs");
        }

        // Validate Dir
        let dir_hbs_path = app_context.root_source.join("layouts/dir.hbs");
        if !dir_hbs_path.exists() {
            return Err("Missing /layouts/dir.hbs");
        }

        // Grab generic stuff
        let header_hbs_contents = try!(read_file(header_hbs_path));
        let footer_hbs_contents = try!(read_file(footer_hbs_path));

        // Create Dir
        let dir_template_name = TYPE_STR;
        let dir_hbs_contents = try!(read_file(&dir_hbs_path));

        app_context.handlebars.register_template_string(dir_template_name, format!("{}\n{}\n{}", header_hbs_contents, dir_hbs_contents, footer_hbs_contents))
            .ok().expect("Error registering header|dir|footer template");

        Ok(())
    }
}

pub struct Dir {
    path: PathBuf,
    type_str: &'static str,
    file_type_manager: ::file_type::FileTypeManager
}

impl Dir {
    fn get_children(&self, context: &::AppContext) -> Vec<Child> {
        let mut result: Vec<Child> = Vec::new();

            match fs::read_dir(&self.path) {
                Ok(items) => {
                    for item in items {
                        let item = item.unwrap().path();
                        let child = self.file_type_manager.create_file_type(&item);
                        result.push(Child {
                            name: String::from_str(item.file_stem().unwrap().to_str().unwrap()),
                            url: child.get_url(context),
                            file_type: String::from_str(child.get_type_str())
                        });
                    }
                },
                Err(_) => ()
            }

        (&mut result).sort_by(|a, b| {
            if a.file_type == String::from_str(TYPE_STR) && b.file_type != String::from_str(TYPE_STR) {
                Ordering::Less
            } else if  a.file_type != String::from_str(TYPE_STR) && b.file_type == String::from_str(TYPE_STR) {
                Ordering::Greater
            } else {
                a.name.cmp(&b.name)
            }

        });

        result
    }
}

impl FileType for Dir {
    fn get_url(&self, context: &::AppContext) -> String {
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let relative = if relative.to_str().unwrap() == "." { String::new() } else {
            format!("{}/", relative.to_str().unwrap())
        };
        format!("{}{}", context.base_url, relative)
    }

    fn convert(&self, context: &::AppContext) {
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let new_dir = context.root_dest.join(&relative);
        let new_dir_index = new_dir.join("index.html");
        if !new_dir.exists() {
            fs::create_dir(&new_dir).ok().expect("Cannot create destination subdir");
        }
        let children = self.get_children(context);
        let name = match relative.file_name() {
            Some(_) => String::from_str(relative.file_name().unwrap().to_str().unwrap()),
            None => String::from_str("root")
        };
        let parents = create_parent_links(&context.base_url, &relative, true);
        let dir_model = DirModel {
            name: name,
            parents: parents,
            children: children,
            base_url: context.base_url.clone()
        };
        match context.handlebars.render(TYPE_STR, &dir_model) {
            Ok(rendered) => {
                // Create File
                let mut file = File::create(&new_dir_index).ok().expect("Could not create dir index.html file");
                //fs::chmod(&new_dir_index, USER_FILE).ok().expect("Couldn't chmod new file");
                file.write_all(rendered.as_bytes())
                    .ok().expect("Could not write html to file");
            },
            Err(why) => panic!("Error rendering markdown: {:?}", why)
        }
    }

    fn get_type_str(&self) -> &'static str {
        self.type_str
    }
}

#[derive(RustcEncodable, PartialEq)]
struct Child {
    name: String,
    url: String,
    file_type: String
}

#[derive(RustcEncodable)]
struct DirModel {
    name: String,
    parents: Vec<Link>,
    children: Vec<Child>,
    base_url: String
}

impl ToJson for DirModel {
    fn to_json(&self) -> Json {
        Json::from_str(&json::encode(&self).unwrap()).unwrap()
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};

use chrono::prelude::*;
use serde_derive::{Deserialize, Serialize};
use strum_macros::{Display, EnumString};

use crate::state::{GroupStatus, State};
use crate::task::Task;

/// This is the main message enum. \
/// Everything that's communicated in Pueue can be serialized as this enum.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum Message {
    Add(AddMessage),
    Remove(Vec<usize>),
    Switch(SwitchMessage),
    Stash(Vec<usize>),
    Enqueue(EnqueueMessage),

    Start(StartMessage),
    Restart(RestartMessage),
    Pause(PauseMessage),
    Kill(KillMessage),

    /// Used to send some input to a process's stdin
    Send(SendMessage),

    /// The first part of the three-step protocol to edit a task.
    /// This one requests an edit from the daemon.
    EditRequest(usize),
    /// The daemon locked the task and responds with the task's details.
    EditResponse(EditResponseMessage),
    /// The client sends the edited details to the daemon.
    Edit(EditMessage),

    Group(GroupMessage),
    GroupResponse(GroupResponseMessage),

    Status,
    StatusResponse(Box<State>),
    Log(LogRequestMessage),
    LogResponse(BTreeMap<usize, TaskLogMessage>),

    /// The client requests a continuous stream of a task's log.
    StreamRequest(StreamRequestMessage),
    /// The next chunk of output, that's send to the client.
    Stream(String),

    /// The boolean decides, whether the children should be get a SIGTERM as well.
    Reset(ResetMessage),
    Clean(CleanMessage),
    DaemonShutdown(Shutdown),

    Success(String),
    Failure(String),

    Parallel(ParallelMessage),
}

/// This enum is used to express a selection of tasks.
/// As commands can be executed on various sets of tasks, we need some kind of datastructure to
/// explicitly and unambiguously specify the selection.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum TaskSelection {
    TaskIds(Vec<usize>),
    Group(String),
    All,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct AddMessage {
    pub command: String,
    pub path: String,
    pub envs: HashMap<String, String>,
    pub start_immediately: bool,
    pub stashed: bool,
    pub group: String,
    pub enqueue_at: Option<DateTime<Local>>,
    pub dependencies: Vec<usize>,
    pub label: Option<String>,
    pub print_task_id: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct SwitchMessage {
    pub task_id_1: usize,
    pub task_id_2: usize,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct EnqueueMessage {
    pub task_ids: Vec<usize>,
    pub enqueue_at: Option<DateTime<Local>>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct StartMessage {
    pub tasks: TaskSelection,
    pub children: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct RestartMessage {
    pub tasks: Vec<TasksToRestart>,
    pub start_immediately: bool,
    pub stashed: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct TasksToRestart {
    pub task_id: usize,
    pub command: String,
    pub path: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct PauseMessage {
    pub tasks: TaskSelection,
    pub wait: bool,
    pub children: bool,
}

/// This is a small custom Enum for all currently supported unix signals.
/// Supporting all unix signals would be a mess, since there is a LOT of them.
///
/// This is also needed for usage in clap, since nix's Signal doesn't implement [Display] and
/// [std::str::FromStr].
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize, Display, EnumString)]
pub enum Signal {
    #[strum(serialize = "SigInt", serialize = "sigint", serialize = "2")]
    SigInt,
    #[strum(serialize = "SigKill", serialize = "sigkill", serialize = "9")]
    SigKill,
    #[strum(serialize = "SigTerm", serialize = "sigterm", serialize = "15")]
    SigTerm,
    #[strum(serialize = "SigCont", serialize = "sigcont", serialize = "18")]
    SigCont,
    #[strum(serialize = "SigStop", serialize = "sigstop", serialize = "19")]
    SigStop,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct KillMessage {
    pub tasks: TaskSelection,
    pub children: bool,
    pub signal: Option<Signal>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct SendMessage {
    pub task_id: usize,
    pub input: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct EditMessage {
    pub task_id: usize,
    pub command: String,
    pub path: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct EditResponseMessage {
    pub task_id: usize,
    pub command: String,
    pub path: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum GroupMessage {
    Add(String, Option<usize>),
    Remove(String),
    List,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct GroupResponseMessage {
    pub groups: BTreeMap<String, GroupStatus>,
    pub settings: BTreeMap<String, usize>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct ResetMessage {
    pub children: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct CleanMessage {
    #[serde(default = "false_default")]
    pub successful_only: bool,
}
fn false_default() -> bool {
    false
}

/// Determines which type of shutdown we're dealing with.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum Shutdown {
    /// Emergency is most likely a system unix signal or a CTRL+C in a terminal.
    Emergency,
    /// Graceful is user initiated and expected.
    Graceful,
}

/// `err` decides, whether you should stream stderr or stdout.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct StreamRequestMessage {
    pub task_id: Option<usize>,
    pub err: bool,
}

/// Request logs for specific tasks.
///
/// `task_ids` specifies the requested tasks. If none are given, all tasks are selected.
/// `send_logs` Determines whether tasks should be sent at all.
/// `lines` Determines whether only a few lines of log should be returned.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct LogRequestMessage {
    pub task_ids: Vec<usize>,
    pub send_logs: bool,
    pub lines: Option<usize>,
}

/// Helper struct for sending tasks and their log output to the client.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct TaskLogMessage {
    pub task: Task,
    pub stdout: Option<Vec<u8>>,
    pub stderr: Option<Vec<u8>>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct ParallelMessage {
    pub parallel_tasks: usize,
    pub group: String,
}

pub fn create_success_message<T: ToString>(text: T) -> Message {
    Message::Success(text.to_string())
}

pub fn create_failure_message<T: ToString>(text: T) -> Message {
    Message::Failure(text.to_string())
}
<commit_msg>Name `GroupMessage::Add` fields<commit_after>use std::collections::{BTreeMap, HashMap};

use chrono::prelude::*;
use serde_derive::{Deserialize, Serialize};
use strum_macros::{Display, EnumString};

use crate::state::{GroupStatus, State};
use crate::task::Task;

/// This is the main message enum. \
/// Everything that's communicated in Pueue can be serialized as this enum.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum Message {
    Add(AddMessage),
    Remove(Vec<usize>),
    Switch(SwitchMessage),
    Stash(Vec<usize>),
    Enqueue(EnqueueMessage),

    Start(StartMessage),
    Restart(RestartMessage),
    Pause(PauseMessage),
    Kill(KillMessage),

    /// Used to send some input to a process's stdin
    Send(SendMessage),

    /// The first part of the three-step protocol to edit a task.
    /// This one requests an edit from the daemon.
    EditRequest(usize),
    /// The daemon locked the task and responds with the task's details.
    EditResponse(EditResponseMessage),
    /// The client sends the edited details to the daemon.
    Edit(EditMessage),

    Group(GroupMessage),
    GroupResponse(GroupResponseMessage),

    Status,
    StatusResponse(Box<State>),
    Log(LogRequestMessage),
    LogResponse(BTreeMap<usize, TaskLogMessage>),

    /// The client requests a continuous stream of a task's log.
    StreamRequest(StreamRequestMessage),
    /// The next chunk of output, that's send to the client.
    Stream(String),

    /// The boolean decides, whether the children should be get a SIGTERM as well.
    Reset(ResetMessage),
    Clean(CleanMessage),
    DaemonShutdown(Shutdown),

    Success(String),
    Failure(String),

    Parallel(ParallelMessage),
}

/// This enum is used to express a selection of tasks.
/// As commands can be executed on various sets of tasks, we need some kind of datastructure to
/// explicitly and unambiguously specify the selection.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum TaskSelection {
    TaskIds(Vec<usize>),
    Group(String),
    All,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct AddMessage {
    pub command: String,
    pub path: String,
    pub envs: HashMap<String, String>,
    pub start_immediately: bool,
    pub stashed: bool,
    pub group: String,
    pub enqueue_at: Option<DateTime<Local>>,
    pub dependencies: Vec<usize>,
    pub label: Option<String>,
    pub print_task_id: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct SwitchMessage {
    pub task_id_1: usize,
    pub task_id_2: usize,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct EnqueueMessage {
    pub task_ids: Vec<usize>,
    pub enqueue_at: Option<DateTime<Local>>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct StartMessage {
    pub tasks: TaskSelection,
    pub children: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct RestartMessage {
    pub tasks: Vec<TasksToRestart>,
    pub start_immediately: bool,
    pub stashed: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct TasksToRestart {
    pub task_id: usize,
    pub command: String,
    pub path: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct PauseMessage {
    pub tasks: TaskSelection,
    pub wait: bool,
    pub children: bool,
}

/// This is a small custom Enum for all currently supported unix signals.
/// Supporting all unix signals would be a mess, since there is a LOT of them.
///
/// This is also needed for usage in clap, since nix's Signal doesn't implement [Display] and
/// [std::str::FromStr].
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize, Display, EnumString)]
pub enum Signal {
    #[strum(serialize = "SigInt", serialize = "sigint", serialize = "2")]
    SigInt,
    #[strum(serialize = "SigKill", serialize = "sigkill", serialize = "9")]
    SigKill,
    #[strum(serialize = "SigTerm", serialize = "sigterm", serialize = "15")]
    SigTerm,
    #[strum(serialize = "SigCont", serialize = "sigcont", serialize = "18")]
    SigCont,
    #[strum(serialize = "SigStop", serialize = "sigstop", serialize = "19")]
    SigStop,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct KillMessage {
    pub tasks: TaskSelection,
    pub children: bool,
    pub signal: Option<Signal>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct SendMessage {
    pub task_id: usize,
    pub input: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct EditMessage {
    pub task_id: usize,
    pub command: String,
    pub path: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct EditResponseMessage {
    pub task_id: usize,
    pub command: String,
    pub path: String,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum GroupMessage {
    Add {
        name: String,
        parallel_tasks: Option<usize>
    },
    Remove(String),
    List,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct GroupResponseMessage {
    pub groups: BTreeMap<String, GroupStatus>,
    pub settings: BTreeMap<String, usize>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct ResetMessage {
    pub children: bool,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct CleanMessage {
    #[serde(default = "false_default")]
    pub successful_only: bool,
}
fn false_default() -> bool {
    false
}

/// Determines which type of shutdown we're dealing with.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum Shutdown {
    /// Emergency is most likely a system unix signal or a CTRL+C in a terminal.
    Emergency,
    /// Graceful is user initiated and expected.
    Graceful,
}

/// `err` decides, whether you should stream stderr or stdout.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct StreamRequestMessage {
    pub task_id: Option<usize>,
    pub err: bool,
}

/// Request logs for specific tasks.
///
/// `task_ids` specifies the requested tasks. If none are given, all tasks are selected.
/// `send_logs` Determines whether tasks should be sent at all.
/// `lines` Determines whether only a few lines of log should be returned.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct LogRequestMessage {
    pub task_ids: Vec<usize>,
    pub send_logs: bool,
    pub lines: Option<usize>,
}

/// Helper struct for sending tasks and their log output to the client.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct TaskLogMessage {
    pub task: Task,
    pub stdout: Option<Vec<u8>>,
    pub stderr: Option<Vec<u8>>,
}

#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct ParallelMessage {
    pub parallel_tasks: usize,
    pub group: String,
}

pub fn create_success_message<T: ToString>(text: T) -> Message {
    Message::Success(text.to_string())
}

pub fn create_failure_message<T: ToString>(text: T) -> Message {
    Message::Failure(text.to_string())
}
<|endoftext|>


<commit_before>use std::mem;

use super::point::Point;

#[derive(Debug)]
pub struct Triangle {
   pub a: Point,
   pub b: Point,
   pub c: Point
}

impl Triangle {
   pub fn new(a: Point, b: Point, c: Point) -> Self {
      Triangle {
         a: a,
         b: b,
         c: c
      }
   }

   #[inline]
   pub fn clockwise(&self) -> bool {
      (self.b.x - self.a.x) * (self.c.y - self.a.y) < (self.c.x - self.a.x) * (self.b.y - self.a.y)
   }

   #[inline]
   pub fn change_orientation(&mut self) {
      mem::swap(&mut self.b, &mut self.c);
   }
}
<commit_msg>Triangle::centroid method<commit_after>use std::mem;

use super::point::Point;

use super::number::NumberOps;

#[derive(Debug)]
pub struct Triangle {
   pub a: Point,
   pub b: Point,
   pub c: Point
}

impl Triangle {
   pub fn new(a: Point, b: Point, c: Point) -> Self {
      Triangle {
         a: a,
         b: b,
         c: c
      }
   }

   #[inline]
   pub fn clockwise(&self) -> bool {
      (self.b.x - self.a.x) * (self.c.y - self.a.y) < (self.c.x - self.a.x) * (self.b.y - self.a.y)
   }

   #[inline]
   pub fn change_orientation(&mut self) {
      mem::swap(&mut self.b, &mut self.c);
   }

   #[inline]
   pub fn centroid(&self) -> Point {
      Point::new(
         (self.a.x + self.b.x + self.c.x).rounding_div(3),
         (self.a.y + self.b.y + self.c.y).rounding_div(3)
      )
   }
}
<|endoftext|>


<commit_before>//! Ordered Hashmap
//!
//! Needs to iterate over items in predictable order
//! e.g. for save ini sections and items in the same order as loaded or added

use std::borrow::Borrow;
use std::collections::hash_map::{self, Entry};
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::IntoIterator;

/// Ordered hashmap built on top of std::collections::HashMap
/// Keys are stored in the field `keys` in the order they were added
#[derive(Debug)]
pub struct OrderedHashMap<K, V> {
    #[doc(hidden)]
    base: HashMap<K, V>,
    keys: Vec<K>,
}

impl<K, V> OrderedHashMap<K, V>
where
    K: Eq + Hash + Clone,
{
    /// Creates an empty `OrderedHashMap`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map: OrderedHashMap<&str, i32> = HashMap::new();
    /// ```
    pub fn new() -> OrderedHashMap<K, V> {
        OrderedHashMap { base: HashMap::<K, V>::new(), keys: Vec::<K>::new() }
    }

    /// Returns a reference to the value corresponding to the key.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
    /// the key type.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert(1, "a");
    /// assert_eq!(map.get(&1), Some(&"a"));
    /// assert_eq!(map.get(&2), None);
    /// ```
    pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        self.base.get(k)
    }

    /// Returns a mutable reference to the value corresponding to the key.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
    /// the key type.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use ordered_hashmap::OrderedHashMap;
    ///
    /// let mut map = OrderedHashMap::new();
    /// map.insert(1, "a");
    /// if let Some(x) = map.get_mut(&1) {
    ///     *x = "b";
    /// }
    /// assert_eq!(map[&1], "b");
    /// ```
    pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        self.base.get_mut(k)
    }

    /// Inserts a key-value pair into the map.
    ///
    /// If the map did not have this key present, [`None`] is returned.
    ///
    /// If the map did have this key present, the value is updated, and the old
    /// value is returned. The key is not updated, though; this matters for
    /// types that can be `==` without being identical.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// assert_eq!(map.insert(37, "a"), None);
    /// assert_eq!(map.is_empty(), false);
    ///
    /// map.insert(37, "b");
    /// assert_eq!(map.insert(37, "c"), Some("b"));
    /// assert_eq!(map[&37], "c");
    /// ```
    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        if !self.base.contains_key(&k) {
            self.keys.push(k.clone());
        }
        self.base.insert(k, v)
    }

    /// Removes a key from the map, returning the value at the key if the key
    /// was previously in the map.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert(1, "a");
    /// assert_eq!(map.remove(&1), Some("a"));
    /// assert_eq!(map.remove(&1), None);
    /// ```
    pub fn remove(&mut self, k: &K) -> Option<V> {
        match self.keys.iter().position(|x| x == k) {
            Some(index) => {
                self.keys.swap_remove(index);
                self.base.remove(k)
            }
            None => None,
        }
    }

    /// An iterator visiting all key-value pairs in the order they were added.
    /// The iterator element type is `(&'a K, &'a V)`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert("a", 1);
    /// map.insert("b", 2);
    /// map.insert("c", 3);
    ///
    /// for (key, val) in map.iter() {
    ///     println!("key: {} val: {}", key, val);
    /// }
    /// ```
    pub fn iter(&self) -> Iter<'_, K, V> {
        Iter { base: &self.base, keys_iterator: self.keys.iter() }
    }

    /// An iterator visiting all key-value pairs in the order they were added,
    /// with mutable references to the values.
    /// The iterator element type is `(&'a K, &'a mut V)`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert("a", 1);
    /// map.insert("b", 2);
    /// map.insert("c", 3);
    ///
    /// // Update all values
    /// for (_, val) in map.iter_mut() {
    ///     *val *= 2;
    /// }
    ///
    /// for (key, val) in &map {
    ///     println!("key: {} val: {}", key, val);
    /// }
    /// ```
    pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
        self.base.iter_mut()
    }

    /// An iterator visiting all keys in the order they were added.
    /// The iterator element type is `&'a K`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert("a", 1);
    /// map.insert("b", 2);
    /// map.insert("c", 3);
    ///
    /// for key in map.keys() {
    ///     println!("{}", key);
    /// }
    /// ```
    pub fn keys(&self) -> std::slice::Iter<K> {
        self.keys.iter()
    }

    /// Gets the given key's corresponding entry in the map for in-place manipulation.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut letters = OrderedHashMap::new();
    ///
    /// for ch in "a short treatise on fungi".chars() {
    ///     let counter = letters.entry(ch).or_insert(0);
    ///     *counter += 1;
    /// }
    ///
    /// assert_eq!(letters[&'s'], 2);
    /// assert_eq!(letters[&'t'], 3);
    /// assert_eq!(letters[&'u'], 1);
    /// assert_eq!(letters.get(&'y'), None);
    /// ```
    pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
        if !self.base.contains_key(&key) {
            self.keys.push(key.clone());
        }
        self.base.entry(key)
    }
}

impl<K, V> Default for OrderedHashMap<K, V>
where
    K: Eq + Hash + Clone,
{
    fn default() -> Self {
        Self::new()
    }
}

impl<'a, K, V> IntoIterator for &'a OrderedHashMap<K, V>
where
    K: Eq + Hash,
{
    type Item = (&'a K, &'a V);
    type IntoIter = Iter<'a, K, V>;

    fn into_iter(self) -> Self::IntoIter {
        Iter { base: &self.base, keys_iterator: self.keys.iter() }
    }
}

impl<K, V> IntoIterator for OrderedHashMap<K, V>
where
    K: Eq + Hash,
{
    type Item = (K, V);
    type IntoIter = IntoIter<K, V>;

    fn into_iter(self) -> Self::IntoIter {
        IntoIter { base: self.base, keys_iterator: self.keys.into_iter() }
    }
}

pub struct Iter<'a, K, V> {
    #[doc(hidden)]
    base: &'a HashMap<K, V>,
    keys_iterator: std::slice::Iter<'a, K>,
}

impl<'a, K, V> Iterator for Iter<'a, K, V>
where
    K: Eq + Hash,
{
    type Item = (&'a K, &'a V);
    fn next(&mut self) -> Option<Self::Item> {
        match self.keys_iterator.next() {
            Some(k) => self.base.get_key_value(&k),
            None => None,
        }
    }
}

pub struct IntoIter<K, V> {
    #[doc(hidden)]
    base: HashMap<K, V>,
    keys_iterator: std::vec::IntoIter<K>,
}

impl<K, V> Iterator for IntoIter<K, V>
where
    K: Eq + Hash,
{
    type Item = (K, V);
    fn next(&mut self) -> Option<Self::Item> {
        match self.keys_iterator.next() {
            Some(k) => self.base.remove_entry(&k),
            None => None,
        }
    }
}

pub type IterMut<'a, K, V> = hash_map::IterMut<'a, K, V>;

#[cfg(test)]
mod library_test {
    use super::*;

    #[test]
    fn get() {
        let mut map = OrderedHashMap::new();
        map.insert("a", 1);
        assert_eq!(map.get("a"), Some(&1));
        assert_eq!(map.get("b"), None);
    }
}
<commit_msg>complete documentation of ordered hashmap<commit_after>//! Ordered Hashmap
//!
//! Needs to iterate over items in predictable order
//! e.g. for save ini sections and items in the same order as loaded or added

use std::borrow::Borrow;
use std::collections::hash_map::{self, Entry};
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::IntoIterator;

/// Ordered hashmap built on top of std::collections::HashMap
/// Keys are stored in the field `keys` in the order they were added
#[derive(Debug)]
pub struct OrderedHashMap<K, V> {
    #[doc(hidden)]
    base: HashMap<K, V>,
    keys: Vec<K>,
}

impl<K, V> OrderedHashMap<K, V>
where
    K: Eq + Hash + Clone,
{
    /// Creates an empty `OrderedHashMap`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map: OrderedHashMap<&str, i32> = HashMap::new();
    /// ```
    pub fn new() -> OrderedHashMap<K, V> {
        OrderedHashMap { base: HashMap::<K, V>::new(), keys: Vec::<K>::new() }
    }

    /// Returns a reference to the value corresponding to the key.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
    /// the key type.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert(1, "a");
    /// assert_eq!(map.get(&1), Some(&"a"));
    /// assert_eq!(map.get(&2), None);
    /// ```
    pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        self.base.get(k)
    }

    /// Returns a mutable reference to the value corresponding to the key.
    ///
    /// The key may be any borrowed form of the map's key type, but
    /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
    /// the key type.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// use ordered_hashmap::OrderedHashMap;
    ///
    /// let mut map = OrderedHashMap::new();
    /// map.insert(1, "a");
    /// if let Some(x) = map.get_mut(&1) {
    ///     *x = "b";
    /// }
    /// assert_eq!(map[&1], "b");
    /// ```
    pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
    where
        K: Borrow<Q>,
        Q: Hash + Eq,
    {
        self.base.get_mut(k)
    }

    /// Inserts a key-value pair into the map.
    ///
    /// If the map did not have this key present, [`None`] is returned.
    ///
    /// If the map did have this key present, the value is updated, and the old
    /// value is returned. The key is not updated, though; this matters for
    /// types that can be `==` without being identical.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// assert_eq!(map.insert(37, "a"), None);
    /// assert_eq!(map.is_empty(), false);
    ///
    /// map.insert(37, "b");
    /// assert_eq!(map.insert(37, "c"), Some("b"));
    /// assert_eq!(map[&37], "c");
    /// ```
    pub fn insert(&mut self, k: K, v: V) -> Option<V> {
        if !self.base.contains_key(&k) {
            self.keys.push(k.clone());
        }
        self.base.insert(k, v)
    }

    /// Removes a key from the map, returning the value at the key if the key
    /// was previously in the map.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert(1, "a");
    /// assert_eq!(map.remove(&1), Some("a"));
    /// assert_eq!(map.remove(&1), None);
    /// ```
    pub fn remove(&mut self, k: &K) -> Option<V> {
        match self.keys.iter().position(|x| x == k) {
            Some(index) => {
                self.keys.swap_remove(index);
                self.base.remove(k)
            }
            None => None,
        }
    }

    /// An iterator visiting all key-value pairs in the order they were added.
    /// The iterator element type is `(&'a K, &'a V)`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert("a", 1);
    /// map.insert("b", 2);
    /// map.insert("c", 3);
    ///
    /// for (key, val) in map.iter() {
    ///     println!("key: {} val: {}", key, val);
    /// }
    /// ```
    pub fn iter(&self) -> Iter<'_, K, V> {
        Iter { base: &self.base, keys_iterator: self.keys.iter() }
    }

    /// An iterator visiting all key-value pairs in the order they were added,
    /// with mutable references to the values.
    /// The iterator element type is `(&'a K, &'a mut V)`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert("a", 1);
    /// map.insert("b", 2);
    /// map.insert("c", 3);
    ///
    /// // Update all values
    /// for (_, val) in map.iter_mut() {
    ///     *val *= 2;
    /// }
    ///
    /// for (key, val) in &map {
    ///     println!("key: {} val: {}", key, val);
    /// }
    /// ```
    pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
        self.base.iter_mut()
    }

    /// An iterator visiting all keys in the order they were added.
    /// The iterator element type is `&'a K`.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut map = OrderedHashMap::new();
    /// map.insert("a", 1);
    /// map.insert("b", 2);
    /// map.insert("c", 3);
    ///
    /// for key in map.keys() {
    ///     println!("{}", key);
    /// }
    /// ```
    pub fn keys(&self) -> std::slice::Iter<K> {
        self.keys.iter()
    }

    /// Gets the given key's corresponding entry in the map for in-place manipulation.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// let mut letters = OrderedHashMap::new();
    ///
    /// for ch in "a short treatise on fungi".chars() {
    ///     let counter = letters.entry(ch).or_insert(0);
    ///     *counter += 1;
    /// }
    ///
    /// assert_eq!(letters[&'s'], 2);
    /// assert_eq!(letters[&'t'], 3);
    /// assert_eq!(letters[&'u'], 1);
    /// assert_eq!(letters.get(&'y'), None);
    /// ```
    pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
        if !self.base.contains_key(&key) {
            self.keys.push(key.clone());
        }
        self.base.entry(key)
    }
}

impl<K, V> Default for OrderedHashMap<K, V>
where
    K: Eq + Hash + Clone,
{
    fn default() -> Self {
        Self::new()
    }
}

impl<'a, K, V> IntoIterator for &'a OrderedHashMap<K, V>
where
    K: Eq + Hash,
{
    type Item = (&'a K, &'a V);
    type IntoIter = Iter<'a, K, V>;

    fn into_iter(self) -> Self::IntoIter {
        Iter { base: &self.base, keys_iterator: self.keys.iter() }
    }
}

impl<K, V> IntoIterator for OrderedHashMap<K, V>
where
    K: Eq + Hash,
{
    type Item = (K, V);
    type IntoIter = IntoIter<K, V>;

    fn into_iter(self) -> Self::IntoIter {
        IntoIter { base: self.base, keys_iterator: self.keys.into_iter() }
    }
}

/// An iterator over the entries of a `OrderedHashMap`.
///
/// This `struct` is created by the `iter` method on `OrderedHashMap`.
///
/// # Example
///
/// ```ignore
/// let mut map = OrderedHashMap::new();
/// map.insert("a", 1);
/// let iter = map.iter();
/// ```
pub struct Iter<'a, K, V> {
    #[doc(hidden)]
    base: &'a HashMap<K, V>,
    keys_iterator: std::slice::Iter<'a, K>,
}

impl<'a, K, V> Iterator for Iter<'a, K, V>
where
    K: Eq + Hash,
{
    type Item = (&'a K, &'a V);
    fn next(&mut self) -> Option<Self::Item> {
        match self.keys_iterator.next() {
            Some(k) => self.base.get_key_value(&k),
            None => None,
        }
    }
}

/// An owning iterator over the entries of a `OrderedHashMap`.
///
/// This `struct` is created by the `into_iter` method on `OrderedHashMap`
/// (provided by the `IntoIterator` trait)
///
/// # Example
///
/// ```ignore
/// let mut map = OrderedHashMap::new();
/// map.insert("a", 1);
/// let iter = map.into_iter();
/// ```
pub struct IntoIter<K, V> {
    #[doc(hidden)]
    base: HashMap<K, V>,
    keys_iterator: std::vec::IntoIter<K>,
}

impl<K, V> Iterator for IntoIter<K, V>
where
    K: Eq + Hash,
{
    type Item = (K, V);
    fn next(&mut self) -> Option<Self::Item> {
        match self.keys_iterator.next() {
            Some(k) => self.base.remove_entry(&k),
            None => None,
        }
    }
}

/// A mutable iterator over the entries of a `OrderedHashMap`.
/// Note that it iterates in arbitrary order.
pub type IterMut<'a, K, V> = hash_map::IterMut<'a, K, V>;

#[cfg(test)]
mod library_test {
    use super::*;

    #[test]
    fn get() {
        let mut map = OrderedHashMap::new();
        map.insert("a", 1);
        assert_eq!(map.get("a"), Some(&1));
        assert_eq!(map.get("b"), None);
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fs::File;
use std::io::{ BufRead, BufReader };
use std::path::Path;

use package::pkg::{ Package, Version };

const SEPERATION_SYMBOL: &'static str = "=";

pub fn get_installed_version(pkg: Package, path: &Path) -> Result<Version, String> {
    let file = get!(File::open(path), "An error occured while opening version list.");
    let file = BufReader::new(file);

    for line in file.lines() {
        let line: String = get!(line, "An error occured while trying to unwrap line.");
        if line.contains(&pkg.name) {
            let ver: Vec<&str> = line.trim().split(SEPERATION_SYMBOL).collect();
            if ver.len() == 2 {
                return Ok(try!(Version::from_str(ver[1])))
            } else {
                return Err("Wrong version".to_string())
            }
        }
    }

    return Err("Specified package not found".to_string())
}

pub fn update_installed_version(pkg: Package, path: &Path, version: Version) -> Result<bool, String> {

}<commit_msg>Switch to use JSON to save installed package versions<commit_after>use rustc_serialize::{ json, Encodable };

use std::error::Error;
use std::fs::File;
use std::io::{ Read, Write };
use std::path::Path;

use package::pkg::{ Package, Version };

pub fn get_list(path: &Path) -> Result<Vec<(String, Version)>, String> {
    let mut buffer = String::new();
    let mut file = get!(File::open(path), "An error occured while opening file");

    get!(file.read_to_string(&mut buffer), "An error occured while reading file");
    return Ok(get!(json::decode(&buffer), "An error occured while decoding JSON"))
}

fn raw_update<T: Encodable>(object: &T, path: &Path) -> Result<(), String> {
    let mut file = get!(File::open(path), "An error occured while opening file");
    return match file.write(get!(json::encode(&object), "An error occured while encoding list").as_bytes()) {
        Ok(_) => Ok(()),
        Err(why) => Err(why.description().to_string())
    }
}

pub fn update_list(package: &Package, path: &Path) -> Result<(), String> {
    let mut list: Vec<(String, Version)> = try!(get_list(path));
    for x in 0..(list.len() - 1) {
        if &list[x].0 == &package.name {
            list[x].1 = package.version.clone();
            return raw_update(&list, path)
        }
    };

    list.push((package.name.clone(), package.version.clone()));
    return raw_update(&list, path)
}<|endoftext|>