<commit_before>// Copyright 2017-2021 Lukas Pustina <lukas@pustina.de>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std::collections::HashMap;
use std::fmt;
use std::time::Duration;

use serde::Serialize;
use yansi::Paint;

use crate::app::output::styles::{
    self, ATTENTION_PREFIX, CAPTION_PREFIX, ERROR_PREFIX, FINISHED_PREFIX, INFO_PREFIX, ITEMAZATION_PREFIX, OK_PREFIX,
};
use crate::app::output::summary::SummaryFormatter;
use crate::app::output::OutputConfig;
use crate::app::{output, AppConfig};
use crate::error::Errors;
use crate::estimate::Estimate;
use crate::resolver::{self, MultiQuery, ResolverGroup, ResolverGroupOpts, ResolverOpts};
use crate::services::server_lists::ServerListSpec;
use crate::services::whois;
use crate::statistics::Statistics;

#[derive(Debug)]
pub struct ConsoleOpts {
    quiet: bool,
    show_errors: bool,
    partial_results: bool,
}

impl ConsoleOpts {
    pub fn with_partial_results(self, partial_results: bool) -> ConsoleOpts {
        ConsoleOpts {
            partial_results,
            ..self
        }
    }
}

impl Default for ConsoleOpts {
    fn default() -> ConsoleOpts {
        ConsoleOpts {
            quiet: false,
            show_errors: false,
            partial_results: false,
        }
    }
}

impl From<&AppConfig> for ConsoleOpts {
    fn from(app_config: &AppConfig) -> Self {
        ConsoleOpts {
            quiet: app_config.quiet,
            show_errors: app_config.show_errors,
            partial_results: false,
        }
    }
}

#[derive(Debug)]
pub struct Console {
    opts: ConsoleOpts,
}

impl Console {
    pub fn new(opts: ConsoleOpts) -> Console {
        Console { opts }
    }

    pub fn print_resolver_opts(&self, group_opts: &ResolverGroupOpts, opts: &ResolverOpts) {
        if self.not_quiet() {
            self.caption(format!(
            "{}: mode={}, concurrent nameservers={}, max. nameservers={}, concurrent requests={}, retries={}, timeout={}s, ndots={}{}{}{}",
            Fmt::emph("Options"),
            group_opts.mode,
            group_opts.max_concurrent,
            group_opts.limit.unwrap(), // Safe unwrap, because of Clap's default value
            opts.max_concurrent_requests,
            opts.retries,
            opts.timeout.as_secs(),
            opts.ndots,
            if opts.expects_multiple_responses {
                ", wait for additional responses"
            } else {
                ""
            },
            if opts.abort_on_error { ", abort on error" } else { "" },
            if opts.abort_on_timeout {
                ", abort on timeout"
            } else {
                ""
            },
        ));
        }
    }

    pub fn print_lookup_estimates(&self, resolvers: &ResolverGroup, query: &resolver::MultiQuery) {
        let num_servers = resolvers.opts.limit.unwrap().min(resolvers.len()); // Safe unwrap, because of Clap's default value
        let num_names = query.num_names();
        let num_record_types = query.num_record_types();
        let estimate = resolvers.estimate(query);

        let queries_str = if estimate.min_requests == estimate.max_requests {
            format!(
                "{} {}",
                estimate.min_requests,
                if estimate.min_requests > 1 {
                    "requests"
                } else {
                    "request"
                }
            )
        } else {
            format!(
                "between {} and {} requests",
                estimate.min_requests, estimate.max_requests
            )
        };
        let namesservers_str = if num_servers > 1 {
            format!("{} nameservers", num_servers)
        } else {
            "1 nameserver".to_string()
        };
        let record_types_str = if num_record_types > 1 {
            format!("{} record types", num_record_types)
        } else {
            "1 record type".to_string()
        };
        let names_str = if num_names > 1 {
            format!("{} names", num_names)
        } else {
            "1 name".to_string()
        };

        self.info(format!(
            "Sending {} to {} for {} of {}.",
            queries_str, namesservers_str, record_types_str, names_str
        ));
    }

    pub fn print_download_estimates(&self, server_list_specs: &[ServerListSpec]) {
        self.info(format!("Downloading {} server lists.", server_list_specs.len()));
    }

    pub fn print_whois_estimates(&self, query: &whois::MultiQuery) {
        let num_resources = query.resources().len();
        let num_queries = query.query_types().len();
        let num_calls = num_resources * num_queries;

        println!(
            "{} Sending up to {} requests for {} query types of {} resources.",
            &*INFO_PREFIX, num_calls, num_queries, num_resources
        );
    }

    pub fn print_partial_headers(&self, caption: &str, resolvers: &ResolverGroup, query: &MultiQuery) {
        if self.show_partial_headers() {
            self.caption(caption);
            self.print_lookup_estimates(resolvers, &query);
        }
    }

    /// Print partial results in case they are not muted and output-type is not JSON
    pub fn print_partial_results<'a, T>(
        &self,
        output_config: &OutputConfig,
        results: &'a T,
        run_time: Duration,
    ) -> anyhow::Result<()>
    where
        T: Serialize + SummaryFormatter + Statistics<'a> + Errors,
        <T as Statistics<'a>>::StatsOut: std::fmt::Display,
    {
        if self.show_partial_headers() {
            self.print_statistics(results, run_time);
        }
        if self.show_partial_results() && !matches!(output_config, OutputConfig::Json {..}) {
            output::output(output_config, results)?;
        }
        if self.show_errors() {
            self.print_error_counts(results);
        }

        Ok(())
    }

    pub fn print_error_counts<E: Errors>(&self, results: &E) {
        let mut counts: HashMap<String, usize> = HashMap::new();

        for err in results.errors() {
            let key = format!("{:?}", err);
            let val = counts.entry(key).or_insert(0);
            *val += 1;
        }

        self.info("Error counts");
        if counts.is_empty() {
            self.ok("No errors occurred.");
        } else {
            for (k, v) in counts.iter() {
                self.itemize(format!("Err {} occurred {} times", k, v));
            }
        }
    }

    pub fn print_statistics<'a, T: Statistics<'a>>(&self, data: &'a T, total_run_time: Duration)
    where
        <T as Statistics<'a>>::StatsOut: fmt::Display,
    {
        let statistics = data.statistics();
        self.info(format!(
            "Received {} within {} ms of total run time.",
            statistics,
            total_run_time.as_millis()
        ));
    }

    pub fn print_finished(&self) {
        if self.not_quiet() {
            self.finished();
        }
    }

    pub fn emphasize<T: fmt::Display>(&self, item: T) {
        println!("{}", Fmt::emph(item))
    }

    pub fn info<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", &*INFO_PREFIX, str.as_ref());
    }

    pub fn attention<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", Fmt::attention(&*ATTENTION_PREFIX), str.as_ref());
    }

    pub fn finished(&self) {
        self.emphasize(format!("{} Finished.", &*FINISHED_PREFIX));
    }

    pub fn caption<T: AsRef<str>>(&self, str: T) {
        self.emphasize(format!("{} {}", &*CAPTION_PREFIX, str.as_ref()));
    }

    pub fn failed<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", Fmt::error(&*ERROR_PREFIX), str.as_ref());
    }

    pub fn error<T: AsRef<str>>(&self, str: T) {
        eprintln!("{} {}", Fmt::error(&*ERROR_PREFIX), str.as_ref());
    }

    pub fn ok<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", Fmt::ok(&*OK_PREFIX), str.as_ref());
    }

    pub fn itemize<T: AsRef<str>>(&self, str: T) {
        println!(" {} {}", &*ITEMAZATION_PREFIX, str.as_ref());
    }

    pub fn not_quiet(&self) -> bool {
        !self.opts.quiet
    }

    /** Check if partial results should be printed
     *
     * This is true, `partial_results` is set, independent of `quiet`
     */
    pub fn show_partial_results(&self) -> bool {
        self.opts.partial_results
    }

    /** Check if headers and footers for partial steps should be printed
     *
     * This is true, if `quiet` is not set and `partial_results` is set.
     */
    pub fn show_partial_headers(&self) -> bool {
        !self.opts.quiet && self.opts.partial_results
    }

    /** Check if detailed error counts should be printed
     *
     * This is true, if `quiet` is not set and `show_errors` is set.
     */
    pub fn show_errors(&self) -> bool {
        !self.opts.quiet && self.opts.show_errors
    }
}

pub struct Fmt {}

impl Fmt {
    pub fn emph<T: fmt::Display>(item: T) -> Paint<T> {
        styles::EMPH.paint(item)
    }

    pub fn attention<T: fmt::Display>(item: T) -> Paint<T> {
        styles::ATTENTION.paint(item)
    }

    pub fn error<T: fmt::Display>(item: T) -> Paint<T> {
        styles::ERROR.paint(item)
    }

    pub fn ok<T: fmt::Display>(item: T) -> Paint<T> {
        styles::OK.paint(item)
    }
}
<commit_msg>rustfmt<commit_after>// Copyright 2017-2021 Lukas Pustina <lukas@pustina.de>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std::collections::HashMap;
use std::fmt;
use std::time::Duration;

use serde::Serialize;
use yansi::Paint;

use crate::app::output::styles::{
    self, ATTENTION_PREFIX, CAPTION_PREFIX, ERROR_PREFIX, FINISHED_PREFIX, INFO_PREFIX, ITEMAZATION_PREFIX, OK_PREFIX,
};
use crate::app::output::summary::SummaryFormatter;
use crate::app::output::OutputConfig;
use crate::app::{output, AppConfig};
use crate::error::Errors;
use crate::estimate::Estimate;
use crate::resolver::{self, MultiQuery, ResolverGroup, ResolverGroupOpts, ResolverOpts};
use crate::services::server_lists::ServerListSpec;
use crate::services::whois;
use crate::statistics::Statistics;

#[derive(Debug)]
pub struct ConsoleOpts {
    quiet: bool,
    show_errors: bool,
    partial_results: bool,
}

impl ConsoleOpts {
    pub fn with_partial_results(self, partial_results: bool) -> ConsoleOpts {
        ConsoleOpts {
            partial_results,
            ..self
        }
    }
}

impl Default for ConsoleOpts {
    fn default() -> ConsoleOpts {
        ConsoleOpts {
            quiet: false,
            show_errors: false,
            partial_results: false,
        }
    }
}

impl From<&AppConfig> for ConsoleOpts {
    fn from(app_config: &AppConfig) -> Self {
        ConsoleOpts {
            quiet: app_config.quiet,
            show_errors: app_config.show_errors,
            partial_results: false,
        }
    }
}

#[derive(Debug)]
pub struct Console {
    opts: ConsoleOpts,
}

impl Console {
    pub fn new(opts: ConsoleOpts) -> Console {
        Console { opts }
    }

    pub fn print_resolver_opts(&self, group_opts: &ResolverGroupOpts, opts: &ResolverOpts) {
        if self.not_quiet() {
            self.caption(format!(
            "{}: mode={}, concurrent nameservers={}, max. nameservers={}, concurrent requests={}, retries={}, timeout={}s, ndots={}{}{}{}",
            Fmt::emph("Options"),
            group_opts.mode,
            group_opts.max_concurrent,
            group_opts.limit.unwrap(), // Safe unwrap, because of Clap's default value
            opts.max_concurrent_requests,
            opts.retries,
            opts.timeout.as_secs(),
            opts.ndots,
            if opts.expects_multiple_responses {
                ", wait for additional responses"
            } else {
                ""
            },
            if opts.abort_on_error { ", abort on error" } else { "" },
            if opts.abort_on_timeout {
                ", abort on timeout"
            } else {
                ""
            },
        ));
        }
    }

    pub fn print_lookup_estimates(&self, resolvers: &ResolverGroup, query: &resolver::MultiQuery) {
        let num_servers = resolvers.opts.limit.unwrap().min(resolvers.len()); // Safe unwrap, because of Clap's default value
        let num_names = query.num_names();
        let num_record_types = query.num_record_types();
        let estimate = resolvers.estimate(query);

        let queries_str = if estimate.min_requests == estimate.max_requests {
            format!(
                "{} {}",
                estimate.min_requests,
                if estimate.min_requests > 1 {
                    "requests"
                } else {
                    "request"
                }
            )
        } else {
            format!(
                "between {} and {} requests",
                estimate.min_requests, estimate.max_requests
            )
        };
        let namesservers_str = if num_servers > 1 {
            format!("{} nameservers", num_servers)
        } else {
            "1 nameserver".to_string()
        };
        let record_types_str = if num_record_types > 1 {
            format!("{} record types", num_record_types)
        } else {
            "1 record type".to_string()
        };
        let names_str = if num_names > 1 {
            format!("{} names", num_names)
        } else {
            "1 name".to_string()
        };

        self.info(format!(
            "Sending {} to {} for {} of {}.",
            queries_str, namesservers_str, record_types_str, names_str
        ));
    }

    pub fn print_download_estimates(&self, server_list_specs: &[ServerListSpec]) {
        self.info(format!("Downloading {} server lists.", server_list_specs.len()));
    }

    pub fn print_whois_estimates(&self, query: &whois::MultiQuery) {
        let num_resources = query.resources().len();
        let num_queries = query.query_types().len();
        let num_calls = num_resources * num_queries;

        println!(
            "{} Sending up to {} requests for {} query types of {} resources.",
            &*INFO_PREFIX, num_calls, num_queries, num_resources
        );
    }

    pub fn print_partial_headers(&self, caption: &str, resolvers: &ResolverGroup, query: &MultiQuery) {
        if self.show_partial_headers() {
            self.caption(caption);
            self.print_lookup_estimates(resolvers, &query);
        }
    }

    /// Print partial results in case they are not muted and output-type is not JSON
    pub fn print_partial_results<'a, T>(
        &self,
        output_config: &OutputConfig,
        results: &'a T,
        run_time: Duration,
    ) -> anyhow::Result<()>
    where
        T: Serialize + SummaryFormatter + Statistics<'a> + Errors,
        <T as Statistics<'a>>::StatsOut: std::fmt::Display,
    {
        if self.show_partial_headers() {
            self.print_statistics(results, run_time);
        }
        if self.show_partial_results() && !matches!(output_config, OutputConfig::Json { .. }) {
            output::output(output_config, results)?;
        }
        if self.show_errors() {
            self.print_error_counts(results);
        }

        Ok(())
    }

    pub fn print_error_counts<E: Errors>(&self, results: &E) {
        let mut counts: HashMap<String, usize> = HashMap::new();

        for err in results.errors() {
            let key = format!("{:?}", err);
            let val = counts.entry(key).or_insert(0);
            *val += 1;
        }

        self.info("Error counts");
        if counts.is_empty() {
            self.ok("No errors occurred.");
        } else {
            for (k, v) in counts.iter() {
                self.itemize(format!("Err {} occurred {} times", k, v));
            }
        }
    }

    pub fn print_statistics<'a, T: Statistics<'a>>(&self, data: &'a T, total_run_time: Duration)
    where
        <T as Statistics<'a>>::StatsOut: fmt::Display,
    {
        let statistics = data.statistics();
        self.info(format!(
            "Received {} within {} ms of total run time.",
            statistics,
            total_run_time.as_millis()
        ));
    }

    pub fn print_finished(&self) {
        if self.not_quiet() {
            self.finished();
        }
    }

    pub fn emphasize<T: fmt::Display>(&self, item: T) {
        println!("{}", Fmt::emph(item))
    }

    pub fn info<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", &*INFO_PREFIX, str.as_ref());
    }

    pub fn attention<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", Fmt::attention(&*ATTENTION_PREFIX), str.as_ref());
    }

    pub fn finished(&self) {
        self.emphasize(format!("{} Finished.", &*FINISHED_PREFIX));
    }

    pub fn caption<T: AsRef<str>>(&self, str: T) {
        self.emphasize(format!("{} {}", &*CAPTION_PREFIX, str.as_ref()));
    }

    pub fn failed<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", Fmt::error(&*ERROR_PREFIX), str.as_ref());
    }

    pub fn error<T: AsRef<str>>(&self, str: T) {
        eprintln!("{} {}", Fmt::error(&*ERROR_PREFIX), str.as_ref());
    }

    pub fn ok<T: AsRef<str>>(&self, str: T) {
        println!("{} {}", Fmt::ok(&*OK_PREFIX), str.as_ref());
    }

    pub fn itemize<T: AsRef<str>>(&self, str: T) {
        println!(" {} {}", &*ITEMAZATION_PREFIX, str.as_ref());
    }

    pub fn not_quiet(&self) -> bool {
        !self.opts.quiet
    }

    /** Check if partial results should be printed
     *
     * This is true, `partial_results` is set, independent of `quiet`
     */
    pub fn show_partial_results(&self) -> bool {
        self.opts.partial_results
    }

    /** Check if headers and footers for partial steps should be printed
     *
     * This is true, if `quiet` is not set and `partial_results` is set.
     */
    pub fn show_partial_headers(&self) -> bool {
        !self.opts.quiet && self.opts.partial_results
    }

    /** Check if detailed error counts should be printed
     *
     * This is true, if `quiet` is not set and `show_errors` is set.
     */
    pub fn show_errors(&self) -> bool {
        !self.opts.quiet && self.opts.show_errors
    }
}

pub struct Fmt {}

impl Fmt {
    pub fn emph<T: fmt::Display>(item: T) -> Paint<T> {
        styles::EMPH.paint(item)
    }

    pub fn attention<T: fmt::Display>(item: T) -> Paint<T> {
        styles::ATTENTION.paint(item)
    }

    pub fn error<T: fmt::Display>(item: T) -> Paint<T> {
        styles::ERROR.paint(item)
    }

    pub fn ok<T: fmt::Display>(item: T) -> Paint<T> {
        styles::OK.paint(item)
    }
}
<|endoftext|>


<commit_before>#![feature(convert)]
#![feature(io)]

extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;
extern crate libc;
extern crate cronparse;

use cronparse::{CrontabFile, CrontabFileError};
use cronparse::crontab::UserCrontabEntry;
use tempfile::NamedTempFile;
use docopt::Docopt;
use libc::{uid_t, gid_t};
use users::User;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read, self};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ffi::CString;

include!(concat!(env!("OUT_DIR"), "/config.rs"));

extern "C" {
    fn chown(path: *const libc::c_char, owner: libc::uid_t, group: libc::gid_t) -> libc::c_int;
}

fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), io::Error> {
    match unsafe { chown(CString::new(path.as_ref().as_os_str().to_bytes().unwrap()).unwrap().as_ptr(), owner, group) } {
        0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
        _ => unreachable!()
    }
}

static USAGE: &'static str = r#"
Usage: crontab [-u <user>] -l
       crontab [-u <user>] -e [<file>]
       crontab [-u <user>] -s
       crontab [-u <user>] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help                Show this help message and exit.
  -u <user>, --user <user>  It specifies the name of the user whose crontab is to
                            be tweaked. If this option is not given, crontab
                            examines "your" crontab, i.e., the crontab of the
                            person executing the command. Note that su(8) can
                            confuse crontab and that if you are running inside of
                            su(8) you should always use the -u option for safety's
                            sake. The first form of this command is used to
                            install a new crontab from some named file or standard
                            input if the pseudo-filename "-" is given.
  -l, --list                The current crontab will be displayed on standard
                            output.
  -r, --remove              The current crontab will be removed.
  -e, --edit                This option is used to edit the current crontab using
                            the editor specified by the VISUAL or EDITOR
                            environment variables. After you exit from the editor,
                            the modified crontab will be installed automatically.
  -s, --show                Show all users who have a crontab.
  -i, --ask                 This option modifies the -r option to prompt the user
                            for a 'y/Y' response before actually removing the
                            crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_file: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .find(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|file| file.tee(stdout()).bytes().count()) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", cron_user.name),
            _ => println!("failed to read {}: {}", cron_file.display(), e),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", cron_user.name),
                _ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e)
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(_cron_file: &Path, _cron_user: &User, _args: &Args) -> i32 {
    let mut stderr = stderr();

    if let Ok(dir) = fs::read_dir(USERS_CRONTAB_DIR) {
        for item in dir {
            if let Ok(entry) = item {
                if let Some(name) = entry.file_name().to_str() {
                    if users::get_user_by_name(name).is_some() {
                        println!("{}", name);
                    } else {
                        writeln!(stderr, "WARNING: crontab found with no matching user: {}", name).unwrap();
                    }
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found").unwrap();
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|file| file.tee(&mut tmpfile).bytes().count()) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "error copying crontab file {}: {}", cron_file.display(), e).unwrap();
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    {
        change_owner(tmpfile.path(), cron_user.uid, cron_user.primary_group).unwrap();
        let _guard = users::switch_user_group(cron_user.uid, cron_user.primary_group);
        match Command::new(editor).arg(tmpfile.path()).status() {
            Ok(status) if status.success() => (),
            _ => {
                writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display()).unwrap();
                return 1;
            }
        }
    }

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(err) = tmpfile.persist(cron_file) {
        writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display()).unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();
    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    match args.arg_file {
        Some(ref name) if &**name == "-" => { stdin().tee(&mut tmpfile).bytes().count(); },
        Some(ref name) => { File::open(&**name).unwrap().tee(&mut tmpfile).bytes().count(); },
        None => unreachable!()
    }

    tmpfile.flush().unwrap();

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(e) = tmpfile.persist(cron_file) {
        writeln!(stderr, "error renaming {} to {}: {}", e.file.path().display(), cron_file.display(), e.error).unwrap();
        return 1;
    }

    change_owner(cron_file, cron_user.uid, cron_user.primary_group).unwrap();

    0
}

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

    let cron_user = match args.flag_user {
        Some(_) if users::get_current_uid() != 0 => {
            writeln!(stderr, "must be privileged to use -u").unwrap();
            exit(1);
        },
        Some(ref user) => match users::get_user_by_name(&**user) {
            Some(user) => user,
            None => {
                writeln!(stderr, "unknown user: {}", user).unwrap();
                exit(1);
            }
        },
        None => users::get_user_by_uid(users::get_current_uid()).unwrap(),
    };

    match fs::metadata(USERS_CRONTAB_DIR) {
        Ok(ref meta) if !meta.is_dir() => {
            writeln!(stderr, "{} is not a directory!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        Err(_) => if let Err(_) = fs::create_dir_all(USERS_CRONTAB_DIR) {
            writeln!(stderr, "{} doesn't exist!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        _ => ()
    }

    let cron_file = PathBuf::from(USERS_CRONTAB_DIR).join(cron_user.name.clone());

    exit(match args {
        Args { flag_show: true, .. } => show(&*cron_file, &cron_user, &args),
        Args { flag_list: true, .. } => list(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, .. } => replace(&*cron_file, &cron_user, &args),
        Args { flag_remove: true, .. } => remove(&*cron_file, &cron_user, &args),
        _ => unreachable!()
    })
}

fn check_crontab_syntax<P: AsRef<Path>>(path: P) -> Result<(), CrontabFileError> {
    match try!(CrontabFile::<UserCrontabEntry>::new(path)).find(Result::is_err) {
        Some(Err(err)) => Err(err),
        _ => Ok(())
    }
}
<commit_msg>crontab: add newline to basic template<commit_after>#![feature(convert)]
#![feature(io)]

extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;
extern crate libc;
extern crate cronparse;

use cronparse::{CrontabFile, CrontabFileError};
use cronparse::crontab::UserCrontabEntry;
use tempfile::NamedTempFile;
use docopt::Docopt;
use libc::{uid_t, gid_t};
use users::User;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read, self};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ffi::CString;

include!(concat!(env!("OUT_DIR"), "/config.rs"));

extern "C" {
    fn chown(path: *const libc::c_char, owner: libc::uid_t, group: libc::gid_t) -> libc::c_int;
}

fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), io::Error> {
    match unsafe { chown(CString::new(path.as_ref().as_os_str().to_bytes().unwrap()).unwrap().as_ptr(), owner, group) } {
        0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
        _ => unreachable!()
    }
}

static USAGE: &'static str = r#"
Usage: crontab [-u <user>] -l
       crontab [-u <user>] -e [<file>]
       crontab [-u <user>] -s
       crontab [-u <user>] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help                Show this help message and exit.
  -u <user>, --user <user>  It specifies the name of the user whose crontab is to
                            be tweaked. If this option is not given, crontab
                            examines "your" crontab, i.e., the crontab of the
                            person executing the command. Note that su(8) can
                            confuse crontab and that if you are running inside of
                            su(8) you should always use the -u option for safety's
                            sake. The first form of this command is used to
                            install a new crontab from some named file or standard
                            input if the pseudo-filename "-" is given.
  -l, --list                The current crontab will be displayed on standard
                            output.
  -r, --remove              The current crontab will be removed.
  -e, --edit                This option is used to edit the current crontab using
                            the editor specified by the VISUAL or EDITOR
                            environment variables. After you exit from the editor,
                            the modified crontab will be installed automatically.
  -s, --show                Show all users who have a crontab.
  -i, --ask                 This option modifies the -r option to prompt the user
                            for a 'y/Y' response before actually removing the
                            crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_file: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .find(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|file| file.tee(stdout()).bytes().count()) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", cron_user.name),
            _ => println!("failed to read {}: {}", cron_file.display(), e),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", cron_user.name),
                _ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e)
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(_cron_file: &Path, _cron_user: &User, _args: &Args) -> i32 {
    let mut stderr = stderr();

    if let Ok(dir) = fs::read_dir(USERS_CRONTAB_DIR) {
        for item in dir {
            if let Ok(entry) = item {
                if let Some(name) = entry.file_name().to_str() {
                    if users::get_user_by_name(name).is_some() {
                        println!("{}", name);
                    } else {
                        writeln!(stderr, "WARNING: crontab found with no matching user: {}", name).unwrap();
                    }
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found").unwrap();
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|file| file.tee(&mut tmpfile).bytes().count()) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command\n".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "error copying crontab file {}: {}", cron_file.display(), e).unwrap();
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    {
        change_owner(tmpfile.path(), cron_user.uid, cron_user.primary_group).unwrap();
        let _guard = users::switch_user_group(cron_user.uid, cron_user.primary_group);
        match Command::new(editor).arg(tmpfile.path()).status() {
            Ok(status) if status.success() => (),
            _ => {
                writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display()).unwrap();
                return 1;
            }
        }
    }

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(err) = tmpfile.persist(cron_file) {
        writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display()).unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();
    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    match args.arg_file {
        Some(ref name) if &**name == "-" => { stdin().tee(&mut tmpfile).bytes().count(); },
        Some(ref name) => { File::open(&**name).unwrap().tee(&mut tmpfile).bytes().count(); },
        None => unreachable!()
    }

    tmpfile.flush().unwrap();

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(e) = tmpfile.persist(cron_file) {
        writeln!(stderr, "error renaming {} to {}: {}", e.file.path().display(), cron_file.display(), e.error).unwrap();
        return 1;
    }

    change_owner(cron_file, cron_user.uid, cron_user.primary_group).unwrap();

    0
}

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

    let cron_user = match args.flag_user {
        Some(_) if users::get_current_uid() != 0 => {
            writeln!(stderr, "must be privileged to use -u").unwrap();
            exit(1);
        },
        Some(ref user) => match users::get_user_by_name(&**user) {
            Some(user) => user,
            None => {
                writeln!(stderr, "unknown user: {}", user).unwrap();
                exit(1);
            }
        },
        None => users::get_user_by_uid(users::get_current_uid()).unwrap(),
    };

    match fs::metadata(USERS_CRONTAB_DIR) {
        Ok(ref meta) if !meta.is_dir() => {
            writeln!(stderr, "{} is not a directory!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        Err(_) => if let Err(_) = fs::create_dir_all(USERS_CRONTAB_DIR) {
            writeln!(stderr, "{} doesn't exist!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        _ => ()
    }

    let cron_file = PathBuf::from(USERS_CRONTAB_DIR).join(cron_user.name.clone());

    exit(match args {
        Args { flag_show: true, .. } => show(&*cron_file, &cron_user, &args),
        Args { flag_list: true, .. } => list(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, .. } => replace(&*cron_file, &cron_user, &args),
        Args { flag_remove: true, .. } => remove(&*cron_file, &cron_user, &args),
        _ => unreachable!()
    })
}

fn check_crontab_syntax<P: AsRef<Path>>(path: P) -> Result<(), CrontabFileError> {
    match try!(CrontabFile::<UserCrontabEntry>::new(path)).find(Result::is_err) {
        Some(Err(err)) => Err(err),
        _ => Ok(())
    }
}
<|endoftext|>


<commit_before>// This file is part of Twig (ported to Rust).
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Node of compiled templates (forming an Abstract-Syntax-Tree)
///
/// @author Colin Kiegel <kiegel@gmx.de>

//////////////
// imports  //
//////////////

use runtime::{Runtime, Job};
use std::fmt::Debug;

/////////////
// exports //
/////////////

pub trait Execute : Debug {
    fn execute(&self, runtime: &Runtime, job: &mut Job);
}
<commit_msg>runtime::api::Execute - run()<commit_after>// This file is part of Twig (ported to Rust).
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Node of compiled templates (forming an Abstract-Syntax-Tree)
///
/// @author Colin Kiegel <kiegel@gmx.de>

//////////////
// imports  //
//////////////

use runtime::{Runtime, Job};
use std::fmt::Debug;

/////////////
// exports //
/////////////

pub trait Execute : Debug {
    fn execute(&self, runtime: &Runtime, job: &mut Job);

    // NOTE: This function is *not* object safe, because
    //      runtime.run() requires a cast of self to object type `Execute`
    //      This limitation of rust seems awkward, because except for the
    //      type erasure this function would be object safe.
    //
    //      It feels like type erasure should be 'idempotent', i.e.
    //      type erasure on a trait object should just return the trait
    //      object. Thus type erasure should *not* require Self:Sized
    //
    // TODO: report to upstream rust
    fn run(&self, runtime: &Runtime) -> String where
        Self: Sized
    {
        runtime.run(self)
    }
}
<|endoftext|>


<commit_before>use libc::c_char;
use std::ffi::{CString, CStr, NulError};
use std::fmt;
use std::path::Path;
use rwops::RWops;

use GameControllerSubsystem;
use get_error;
use joystick;
use common::{validate_int, IntegerOrSdlError};

use sys::controller as ll;
use sys::event::{SDL_QUERY, SDL_ENABLE};

#[derive(Debug)]
pub enum AddMappingError {
    InvalidMapping(NulError),
    InvalidFilePath(String),
    SdlError(String),
}

impl fmt::Display for AddMappingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::AddMappingError::*;

        match *self {
            InvalidMapping(ref e) => write!(f, "Null error: {}", e),
            InvalidFilePath(ref value) => write!(f, "Invalid file path ({})", value),
            SdlError(ref e) => write!(f, "SDL error: {}", e)
        }
    }
}

impl GameControllerSubsystem {
    /// Retreive the total number of attached joysticks *and* controllers identified by SDL.
    pub fn num_joysticks(&self) -> Result<u32, String> {
        let result = unsafe { ::sys::joystick::SDL_NumJoysticks() };

        if result >= 0 {
            Ok(result as u32)
        } else {
            Err(get_error())
        }
    }

    /// Return true if the joystick at index `id` is a game controller.
    #[inline]
    pub fn is_game_controller(&self, id: u32) -> bool {
        match validate_int(id, "id") {
            Ok(id) => unsafe { ll::SDL_IsGameController(id) != 0 },
            Err(_) => false
        }
    }

    /// Attempt to open the controller number `id` and return
    /// it. Controller IDs are the same as joystick IDs and the
    /// maximum number can be retreived using the `SDL_NumJoysticks`
    /// function.
    pub fn open(&self, id: u32) -> Result<GameController, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let id = try!(validate_int(id, "id"));
        let controller = unsafe { ll::SDL_GameControllerOpen(id) };

        if controller.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(GameController {
                subsystem: self.clone(),
                raw: controller
            })
        }
    }

    /// Return the name of the controller at the given index.
    pub fn name_for_index(&self, index: u32) -> Result<String, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let index = try!(validate_int(index, "index"));
        let c_str = unsafe { ll::SDL_GameControllerNameForIndex(index) };

        if c_str.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(unsafe {
                CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
            })
        }
    }

    /// If state is `true` controller events are processed, otherwise
    /// they're ignored.
    pub fn set_event_state(&self, state: bool) {
        unsafe { ll::SDL_GameControllerEventState(state as i32) };
    }

    /// Return `true` if controller events are processed.
    pub fn event_state(&self) -> bool {
        unsafe { ll::SDL_GameControllerEventState(SDL_QUERY as i32)
                 == SDL_ENABLE as i32 }
    }

    /// Add a new mapping from a mapping string
    pub fn add_mapping(&self, mapping: &str)
            -> Result<MappingStatus, AddMappingError> {
        use self::AddMappingError::*;
        let mapping = match CString::new(mapping) {
            Ok(s) => s,
            Err(err) => return Err(InvalidMapping(err)),
        };

        let result = unsafe { ll::SDL_GameControllerAddMapping(mapping.as_ptr() as *const c_char) };

        match result {
            1 => Ok(MappingStatus::Added),
            0 => Ok(MappingStatus::Updated),
            _ => Err(SdlError(get_error())),
        }
    }

    /// Load mappings from a file
    pub fn load_mappings<P: AsRef<Path>>(&self, path: P)
            -> Result<i32, AddMappingError> {
        use self::AddMappingError::*;

        let file = match RWops::from_file(path, "r") {
            Ok(f) => f,
            Err(s) => return Err(InvalidFilePath(s))
        };

        let result = unsafe { ll::SDL_GameControllerAddMappingsFromRW(file.raw(), 0) };

        match result {
            -1 => Err(SdlError(get_error())),
            _ => Ok(result)
        }
    }

    pub fn mapping_for_guid(&self, guid: joystick::Guid) -> Result<String, String> {
        let c_str = unsafe { ll::SDL_GameControllerMappingForGUID(guid.raw()) };

        c_str_to_string_or_err(c_str)
    }

    #[inline]
    /// Force controller update when not using the event loop
    pub fn update(&self) {
        unsafe { ll::SDL_GameControllerUpdate() };
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Axis {
    LeftX        = ll::SDL_CONTROLLER_AXIS_LEFTX,
    LeftY        = ll::SDL_CONTROLLER_AXIS_LEFTY,
    RightX       = ll::SDL_CONTROLLER_AXIS_RIGHTX,
    RightY       = ll::SDL_CONTROLLER_AXIS_RIGHTY,
    TriggerLeft  = ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TriggerRight = ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
}

impl Axis {
    /// Return the Axis from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(axis: &str) -> Option<Axis> {
        let id = match CString::new(axis) {
            Ok(axis) => unsafe { ll::SDL_GameControllerGetAxisFromString(axis.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_AXIS_INVALID
        };

        Axis::from_ll(id)
    }

    /// Return a string for a given axis in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let axis = self as ll::SDL_GameControllerAxis;

        let string = unsafe { ll::SDL_GameControllerGetStringForAxis(axis) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerAxis) -> Option<Axis> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_AXIS_INVALID      => return None,
            ll::SDL_CONTROLLER_AXIS_LEFTX        => Axis::LeftX,
            ll::SDL_CONTROLLER_AXIS_LEFTY        => Axis::LeftY,
            ll::SDL_CONTROLLER_AXIS_RIGHTX       => Axis::RightX,
            ll::SDL_CONTROLLER_AXIS_RIGHTY       => Axis::RightY,
            ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT  => Axis::TriggerLeft,
            ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => Axis::TriggerRight,
            _ => panic!("unhandled controller axis")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerAxis {
        match *self {
            Axis::LeftX => ll::SDL_CONTROLLER_AXIS_LEFTX,
            Axis::LeftY => ll::SDL_CONTROLLER_AXIS_LEFTY,
            Axis::RightX => ll::SDL_CONTROLLER_AXIS_RIGHTX,
            Axis::RightY => ll::SDL_CONTROLLER_AXIS_RIGHTY,
            Axis::TriggerLeft => ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
            Axis::TriggerRight => ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Button {
    A             = ll::SDL_CONTROLLER_BUTTON_A,
    B             = ll::SDL_CONTROLLER_BUTTON_B,
    X             = ll::SDL_CONTROLLER_BUTTON_X,
    Y             = ll::SDL_CONTROLLER_BUTTON_Y,
    Back          = ll::SDL_CONTROLLER_BUTTON_BACK,
    Guide         = ll::SDL_CONTROLLER_BUTTON_GUIDE,
    Start         = ll::SDL_CONTROLLER_BUTTON_START,
    LeftStick     = ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RightStick    = ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LeftShoulder  = ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RightShoulder = ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPadUp        = ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPadDown      = ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPadLeft      = ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPadRight     = ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
}

impl Button {
    /// Return the Button from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(button: &str) -> Option<Button> {
        let id = match CString::new(button) {
            Ok(button) => unsafe { ll::SDL_GameControllerGetButtonFromString(button.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_BUTTON_INVALID
        };

        Button::from_ll(id)
    }

    /// Return a string for a given button in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let button = self as ll::SDL_GameControllerButton;

        let string = unsafe { ll::SDL_GameControllerGetStringForButton(button) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerButton) -> Option<Button> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_BUTTON_INVALID       => return None,
            ll::SDL_CONTROLLER_BUTTON_A             => Button::A,
            ll::SDL_CONTROLLER_BUTTON_B             => Button::B,
            ll::SDL_CONTROLLER_BUTTON_X             => Button::X,
            ll::SDL_CONTROLLER_BUTTON_Y             => Button::Y,
            ll::SDL_CONTROLLER_BUTTON_BACK          => Button::Back,
            ll::SDL_CONTROLLER_BUTTON_GUIDE         => Button::Guide,
            ll::SDL_CONTROLLER_BUTTON_START         => Button::Start,
            ll::SDL_CONTROLLER_BUTTON_LEFTSTICK     => Button::LeftStick,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK    => Button::RightStick,
            ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER  => Button::LeftShoulder,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => Button::RightShoulder,
            ll::SDL_CONTROLLER_BUTTON_DPAD_UP       => Button::DPadUp,
            ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN     => Button::DPadDown,
            ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT     => Button::DPadLeft,
            ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT    => Button::DPadRight,
            _ => panic!("unhandled controller button")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerButton {
        match *self {
            Button::A             => ll::SDL_CONTROLLER_BUTTON_A,
            Button::B             => ll::SDL_CONTROLLER_BUTTON_B,
            Button::X             => ll::SDL_CONTROLLER_BUTTON_X,
            Button::Y             => ll::SDL_CONTROLLER_BUTTON_Y,
            Button::Back          => ll::SDL_CONTROLLER_BUTTON_BACK,
            Button::Guide         => ll::SDL_CONTROLLER_BUTTON_GUIDE,
            Button::Start         => ll::SDL_CONTROLLER_BUTTON_START,
            Button::LeftStick     => ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
            Button::RightStick    => ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
            Button::LeftShoulder  => ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
            Button::RightShoulder => ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
            Button::DPadUp        => ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
            Button::DPadDown      => ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
            Button::DPadLeft      => ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
            Button::DPadRight     => ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
        }
    }
}

/// Possible return values for `add_mapping`
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum MappingStatus {
    Added   = 1,
    Updated = 0,
}

/// Wrapper around the SDL_GameController object
pub struct GameController {
    subsystem: GameControllerSubsystem,
    raw: *mut ll::SDL_GameController
}

impl GameController {
    #[inline]
    pub fn subsystem(&self) -> &GameControllerSubsystem { &self.subsystem }

    /// Return the name of the controller or an empty string if no
    /// name is found.
    pub fn name(&self) -> String {
        let name = unsafe { ll::SDL_GameControllerName(self.raw) };

        c_str_to_string(name)
    }

    /// Return a String describing the controller's button and axis
    /// mappings
    pub fn mapping(&self) -> String {
        let mapping = unsafe { ll::SDL_GameControllerMapping(self.raw) };

        c_str_to_string(mapping)
    }

    /// Return true if the controller has been opened and currently
    /// connected.
    pub fn attached(&self) -> bool {
        unsafe { ll::SDL_GameControllerGetAttached(self.raw) != 0 }
    }

    /// Return the joystick id of this controller
    pub fn instance_id(&self) -> i32 {
        let result = unsafe {
          let joystick = ll::SDL_GameControllerGetJoystick(self.raw);
          ::sys::joystick::SDL_JoystickInstanceID(joystick)
        };

        if result < 0 {
            // Should only fail if the joystick is NULL.
            panic!(get_error())
        } else {
            result
        }
    }

    /// Get the position of the given `axis`
    pub fn axis(&self, axis: Axis) -> i16 {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let axis = axis as ll::SDL_GameControllerAxis;

        unsafe { ll::SDL_GameControllerGetAxis(self.raw, axis) }
    }

    /// Returns `true` if `button` is pressed.
    pub fn button(&self, button: Button) -> bool {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let button = button as ll::SDL_GameControllerButton;

        unsafe { ll::SDL_GameControllerGetButton(self.raw, button) != 0 }
    }
}

impl Drop for GameController {
    fn drop(&mut self) {
        unsafe { ll::SDL_GameControllerClose(self.raw) }
    }
}

/// Convert C string `c_str` to a String. Return an empty string if
/// c_str is NULL.
fn c_str_to_string(c_str: *const c_char) -> String {
    if c_str.is_null() {
        String::new()
    } else {
        unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        }
    }
}

/// Convert C string `c_str` to a String. Return an SDL error if
/// `c_str` is NULL.
fn c_str_to_string_or_err(c_str: *const c_char) -> Result<String, String> {
    if c_str.is_null() {
        Err(get_error())
    } else {
        Ok(unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        })
    }
}
<commit_msg>Implement Error for AddMappingError.<commit_after>use libc::c_char;
use std::error;
use std::ffi::{CString, CStr, NulError};
use std::fmt;
use std::path::Path;
use rwops::RWops;

use GameControllerSubsystem;
use get_error;
use joystick;
use common::{validate_int, IntegerOrSdlError};

use sys::controller as ll;
use sys::event::{SDL_QUERY, SDL_ENABLE};

#[derive(Debug)]
pub enum AddMappingError {
    InvalidMapping(NulError),
    InvalidFilePath(String),
    SdlError(String),
}

impl fmt::Display for AddMappingError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::AddMappingError::*;

        match *self {
            InvalidMapping(ref e) => write!(f, "Null error: {}", e),
            InvalidFilePath(ref value) => write!(f, "Invalid file path ({})", value),
            SdlError(ref e) => write!(f, "SDL error: {}", e)
        }
    }
}

impl error::Error for AddMappingError {
    fn description(&self) -> &str {
        use self::AddMappingError::*;

        match *self {
            InvalidMapping(_) => "invalid mapping",
            InvalidFilePath(_) => "invalid file path",
            SdlError(ref e) => e,
        }
    }
}

impl GameControllerSubsystem {
    /// Retreive the total number of attached joysticks *and* controllers identified by SDL.
    pub fn num_joysticks(&self) -> Result<u32, String> {
        let result = unsafe { ::sys::joystick::SDL_NumJoysticks() };

        if result >= 0 {
            Ok(result as u32)
        } else {
            Err(get_error())
        }
    }

    /// Return true if the joystick at index `id` is a game controller.
    #[inline]
    pub fn is_game_controller(&self, id: u32) -> bool {
        match validate_int(id, "id") {
            Ok(id) => unsafe { ll::SDL_IsGameController(id) != 0 },
            Err(_) => false
        }
    }

    /// Attempt to open the controller number `id` and return
    /// it. Controller IDs are the same as joystick IDs and the
    /// maximum number can be retreived using the `SDL_NumJoysticks`
    /// function.
    pub fn open(&self, id: u32) -> Result<GameController, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let id = try!(validate_int(id, "id"));
        let controller = unsafe { ll::SDL_GameControllerOpen(id) };

        if controller.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(GameController {
                subsystem: self.clone(),
                raw: controller
            })
        }
    }

    /// Return the name of the controller at the given index.
    pub fn name_for_index(&self, index: u32) -> Result<String, IntegerOrSdlError> {
        use common::IntegerOrSdlError::*;
        let index = try!(validate_int(index, "index"));
        let c_str = unsafe { ll::SDL_GameControllerNameForIndex(index) };

        if c_str.is_null() {
            Err(SdlError(get_error()))
        } else {
            Ok(unsafe {
                CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
            })
        }
    }

    /// If state is `true` controller events are processed, otherwise
    /// they're ignored.
    pub fn set_event_state(&self, state: bool) {
        unsafe { ll::SDL_GameControllerEventState(state as i32) };
    }

    /// Return `true` if controller events are processed.
    pub fn event_state(&self) -> bool {
        unsafe { ll::SDL_GameControllerEventState(SDL_QUERY as i32)
                 == SDL_ENABLE as i32 }
    }

    /// Add a new mapping from a mapping string
    pub fn add_mapping(&self, mapping: &str)
            -> Result<MappingStatus, AddMappingError> {
        use self::AddMappingError::*;
        let mapping = match CString::new(mapping) {
            Ok(s) => s,
            Err(err) => return Err(InvalidMapping(err)),
        };

        let result = unsafe { ll::SDL_GameControllerAddMapping(mapping.as_ptr() as *const c_char) };

        match result {
            1 => Ok(MappingStatus::Added),
            0 => Ok(MappingStatus::Updated),
            _ => Err(SdlError(get_error())),
        }
    }

    /// Load mappings from a file
    pub fn load_mappings<P: AsRef<Path>>(&self, path: P)
            -> Result<i32, AddMappingError> {
        use self::AddMappingError::*;

        let file = match RWops::from_file(path, "r") {
            Ok(f) => f,
            Err(s) => return Err(InvalidFilePath(s))
        };

        let result = unsafe { ll::SDL_GameControllerAddMappingsFromRW(file.raw(), 0) };

        match result {
            -1 => Err(SdlError(get_error())),
            _ => Ok(result)
        }
    }

    pub fn mapping_for_guid(&self, guid: joystick::Guid) -> Result<String, String> {
        let c_str = unsafe { ll::SDL_GameControllerMappingForGUID(guid.raw()) };

        c_str_to_string_or_err(c_str)
    }

    #[inline]
    /// Force controller update when not using the event loop
    pub fn update(&self) {
        unsafe { ll::SDL_GameControllerUpdate() };
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Axis {
    LeftX        = ll::SDL_CONTROLLER_AXIS_LEFTX,
    LeftY        = ll::SDL_CONTROLLER_AXIS_LEFTY,
    RightX       = ll::SDL_CONTROLLER_AXIS_RIGHTX,
    RightY       = ll::SDL_CONTROLLER_AXIS_RIGHTY,
    TriggerLeft  = ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TriggerRight = ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
}

impl Axis {
    /// Return the Axis from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(axis: &str) -> Option<Axis> {
        let id = match CString::new(axis) {
            Ok(axis) => unsafe { ll::SDL_GameControllerGetAxisFromString(axis.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_AXIS_INVALID
        };

        Axis::from_ll(id)
    }

    /// Return a string for a given axis in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let axis = self as ll::SDL_GameControllerAxis;

        let string = unsafe { ll::SDL_GameControllerGetStringForAxis(axis) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerAxis) -> Option<Axis> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_AXIS_INVALID      => return None,
            ll::SDL_CONTROLLER_AXIS_LEFTX        => Axis::LeftX,
            ll::SDL_CONTROLLER_AXIS_LEFTY        => Axis::LeftY,
            ll::SDL_CONTROLLER_AXIS_RIGHTX       => Axis::RightX,
            ll::SDL_CONTROLLER_AXIS_RIGHTY       => Axis::RightY,
            ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT  => Axis::TriggerLeft,
            ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => Axis::TriggerRight,
            _ => panic!("unhandled controller axis")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerAxis {
        match *self {
            Axis::LeftX => ll::SDL_CONTROLLER_AXIS_LEFTX,
            Axis::LeftY => ll::SDL_CONTROLLER_AXIS_LEFTY,
            Axis::RightX => ll::SDL_CONTROLLER_AXIS_RIGHTX,
            Axis::RightY => ll::SDL_CONTROLLER_AXIS_RIGHTY,
            Axis::TriggerLeft => ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
            Axis::TriggerRight => ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
        }
    }
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Button {
    A             = ll::SDL_CONTROLLER_BUTTON_A,
    B             = ll::SDL_CONTROLLER_BUTTON_B,
    X             = ll::SDL_CONTROLLER_BUTTON_X,
    Y             = ll::SDL_CONTROLLER_BUTTON_Y,
    Back          = ll::SDL_CONTROLLER_BUTTON_BACK,
    Guide         = ll::SDL_CONTROLLER_BUTTON_GUIDE,
    Start         = ll::SDL_CONTROLLER_BUTTON_START,
    LeftStick     = ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RightStick    = ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LeftShoulder  = ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RightShoulder = ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPadUp        = ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPadDown      = ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPadLeft      = ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPadRight     = ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
}

impl Button {
    /// Return the Button from a string description in the same format
    /// used by the game controller mapping strings.
    pub fn from_string(button: &str) -> Option<Button> {
        let id = match CString::new(button) {
            Ok(button) => unsafe { ll::SDL_GameControllerGetButtonFromString(button.as_ptr() as *const c_char) },
            // string contains a nul byte - it won't match anything.
            Err(_) => ll::SDL_CONTROLLER_BUTTON_INVALID
        };

        Button::from_ll(id)
    }

    /// Return a string for a given button in the same format using by
    /// the game controller mapping strings
    pub fn string(self) -> String {
        let button = self as ll::SDL_GameControllerButton;

        let string = unsafe { ll::SDL_GameControllerGetStringForButton(button) };

        c_str_to_string(string)
    }

    pub fn from_ll(bitflags: ll::SDL_GameControllerButton) -> Option<Button> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_BUTTON_INVALID       => return None,
            ll::SDL_CONTROLLER_BUTTON_A             => Button::A,
            ll::SDL_CONTROLLER_BUTTON_B             => Button::B,
            ll::SDL_CONTROLLER_BUTTON_X             => Button::X,
            ll::SDL_CONTROLLER_BUTTON_Y             => Button::Y,
            ll::SDL_CONTROLLER_BUTTON_BACK          => Button::Back,
            ll::SDL_CONTROLLER_BUTTON_GUIDE         => Button::Guide,
            ll::SDL_CONTROLLER_BUTTON_START         => Button::Start,
            ll::SDL_CONTROLLER_BUTTON_LEFTSTICK     => Button::LeftStick,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK    => Button::RightStick,
            ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER  => Button::LeftShoulder,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => Button::RightShoulder,
            ll::SDL_CONTROLLER_BUTTON_DPAD_UP       => Button::DPadUp,
            ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN     => Button::DPadDown,
            ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT     => Button::DPadLeft,
            ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT    => Button::DPadRight,
            _ => panic!("unhandled controller button")
        })
    }

    pub fn to_ll(&self) -> ll::SDL_GameControllerButton {
        match *self {
            Button::A             => ll::SDL_CONTROLLER_BUTTON_A,
            Button::B             => ll::SDL_CONTROLLER_BUTTON_B,
            Button::X             => ll::SDL_CONTROLLER_BUTTON_X,
            Button::Y             => ll::SDL_CONTROLLER_BUTTON_Y,
            Button::Back          => ll::SDL_CONTROLLER_BUTTON_BACK,
            Button::Guide         => ll::SDL_CONTROLLER_BUTTON_GUIDE,
            Button::Start         => ll::SDL_CONTROLLER_BUTTON_START,
            Button::LeftStick     => ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
            Button::RightStick    => ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
            Button::LeftShoulder  => ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
            Button::RightShoulder => ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
            Button::DPadUp        => ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
            Button::DPadDown      => ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
            Button::DPadLeft      => ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
            Button::DPadRight     => ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
        }
    }
}

/// Possible return values for `add_mapping`
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum MappingStatus {
    Added   = 1,
    Updated = 0,
}

/// Wrapper around the SDL_GameController object
pub struct GameController {
    subsystem: GameControllerSubsystem,
    raw: *mut ll::SDL_GameController
}

impl GameController {
    #[inline]
    pub fn subsystem(&self) -> &GameControllerSubsystem { &self.subsystem }

    /// Return the name of the controller or an empty string if no
    /// name is found.
    pub fn name(&self) -> String {
        let name = unsafe { ll::SDL_GameControllerName(self.raw) };

        c_str_to_string(name)
    }

    /// Return a String describing the controller's button and axis
    /// mappings
    pub fn mapping(&self) -> String {
        let mapping = unsafe { ll::SDL_GameControllerMapping(self.raw) };

        c_str_to_string(mapping)
    }

    /// Return true if the controller has been opened and currently
    /// connected.
    pub fn attached(&self) -> bool {
        unsafe { ll::SDL_GameControllerGetAttached(self.raw) != 0 }
    }

    /// Return the joystick id of this controller
    pub fn instance_id(&self) -> i32 {
        let result = unsafe {
          let joystick = ll::SDL_GameControllerGetJoystick(self.raw);
          ::sys::joystick::SDL_JoystickInstanceID(joystick)
        };

        if result < 0 {
            // Should only fail if the joystick is NULL.
            panic!(get_error())
        } else {
            result
        }
    }

    /// Get the position of the given `axis`
    pub fn axis(&self, axis: Axis) -> i16 {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let axis = axis as ll::SDL_GameControllerAxis;

        unsafe { ll::SDL_GameControllerGetAxis(self.raw, axis) }
    }

    /// Returns `true` if `button` is pressed.
    pub fn button(&self, button: Button) -> bool {
        // This interface is a bit messed up: 0 is a valid position
        // but can also mean that an error occured.
        // Fortunately, an error can only occur if the controller pointer is NULL.
        // There should be no apparent reason for this to change in the future.

        let button = button as ll::SDL_GameControllerButton;

        unsafe { ll::SDL_GameControllerGetButton(self.raw, button) != 0 }
    }
}

impl Drop for GameController {
    fn drop(&mut self) {
        unsafe { ll::SDL_GameControllerClose(self.raw) }
    }
}

/// Convert C string `c_str` to a String. Return an empty string if
/// c_str is NULL.
fn c_str_to_string(c_str: *const c_char) -> String {
    if c_str.is_null() {
        String::new()
    } else {
        unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        }
    }
}

/// Convert C string `c_str` to a String. Return an SDL error if
/// `c_str` is NULL.
fn c_str_to_string_or_err(c_str: *const c_char) -> Result<String, String> {
    if c_str.is_null() {
        Err(get_error())
    } else {
        Ok(unsafe {
            CStr::from_ptr(c_str as *const _).to_str().unwrap().to_owned()
        })
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CString, NulError};
use std::fmt;
use std::ptr;
use std::os::raw::{c_char,c_int};

use video::WindowRef;
use get_error;

use sys::messagebox as ll;

bitflags! {
    pub flags MessageBoxFlag: u32 {
        const MESSAGEBOX_ERROR =
            ::sys::messagebox::SDL_MessageBoxFlags::SDL_MESSAGEBOX_ERROR as u32,
        const MESSAGEBOX_WARNING =
            ::sys::messagebox::SDL_MessageBoxFlags::SDL_MESSAGEBOX_WARNING as u32,
        const MESSAGEBOX_INFORMATION =
            ::sys::messagebox::SDL_MessageBoxFlags::SDL_MESSAGEBOX_INFORMATION as u32
    }
}

bitflags! {
    pub flags MessageBoxButtonFlag: u32 {
        const MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT =
            ::sys::messagebox::SDL_MessageBoxButtonFlags::SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT as u32,
        const MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT =
            ::sys::messagebox::SDL_MessageBoxButtonFlags::SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT as u32,
        const MESSAGEBOX_BUTTON_NOTHING = 0
    }
}

#[derive(Debug)]
pub struct MessageBoxColorScheme {
    pub background:(u8,u8,u8),
    pub text:(u8,u8,u8),
    pub button_border:(u8,u8,u8),
    pub button_background:(u8,u8,u8),
    pub button_selected:(u8,u8,u8)
}

/// button_id is the integer that will be returned
/// by show_message_box. It is not sed by SDL2,
/// and should only be used to know which button has been triggered
#[derive(Debug)]
pub struct ButtonData<'a> {
    pub flags:MessageBoxButtonFlag,
    pub button_id:i32,
    pub text:&'a str
}

#[derive(Debug)]
pub enum ClickedButton<'a> {
    CloseButton,
    CustomButton(&'a ButtonData<'a>)
}

impl From<MessageBoxColorScheme> for [ll::SDL_MessageBoxColor ; 5] {
    fn from(scheme:MessageBoxColorScheme) -> [ll::SDL_MessageBoxColor ; 5] {
        fn to_message_box_color(t:(u8,u8,u8)) -> ll::SDL_MessageBoxColor {
            ll::SDL_MessageBoxColor {
                r:t.0,
                g:t.1,
                b:t.2
            }
        };
        [to_message_box_color(scheme.background),
        to_message_box_color(scheme.text),
        to_message_box_color(scheme.button_border),
        to_message_box_color(scheme.button_background),
        to_message_box_color(scheme.button_selected)]
    }
}

#[derive(Debug)]
pub enum ShowMessageError {
    InvalidTitle(NulError),
    InvalidMessage(NulError),
    /// Second argument of the tuple (i32) corresponds to the
    /// first button_id having an error
    InvalidButton(NulError,i32),
    SdlError(String),
}

impl fmt::Display for ShowMessageError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ShowMessageError::*;

        match *self {
            InvalidTitle(ref e) => write!(f, "Invalid title: {}", e),
            InvalidMessage(ref e) => write!(f, "Invalid message: {}", e),
            InvalidButton(ref e, value) => write!(f,
                "Invalid button ({}): {}", value, e),
            SdlError(ref e) => write!(f, "SDL error: {}", e)
        }
    }
}

/// Show a simple message box, meant to be informative only.
///
/// There is no way to know if the user clicked "Ok" or closed the message box,
/// If you want to retrieve which button was clicked and customize a bit more
/// your message box, use `show_message_box` instead.
pub fn show_simple_message_box(flags: MessageBoxFlag, title: &str,
        message: &str, window: Option<&WindowRef>)
        -> Result<(), ShowMessageError> {
    use self::ShowMessageError::*;
    let result = unsafe {
        let title = match CString::new(title) {
            Ok(s) => s,
            Err(err) => return Err(InvalidTitle(err)),
        };
        let message = match CString::new(message) {
            Ok(s) => s,
            Err(err) => return Err(InvalidMessage(err)),
        };
        ll::SDL_ShowSimpleMessageBox(
            flags.bits(),
            title.as_ptr() as *const c_char,
            message.as_ptr() as *const c_char,
            window.map_or(ptr::null_mut(), |win| win.raw())
        )
    } == 0;

    if result {
        Ok(())
    } else {
        Err(SdlError(get_error()))
    }
}

/// Show a customizable message box.
///
/// An array of buttons is required for it to work. The array can be empty,
/// but it will have no button beside the close button.
///
/// On success, it will return either the button clicked or the close button.
/// Note that the variant of the `ClickedButton` enum will also be returned if the message box
/// has been forcefully closed (Alt-F4, ...)
///
pub fn show_message_box<'a>(flags:MessageBoxFlag, buttons:&'a [ButtonData], title:&str,
    message:&str, window:Option<&WindowRef>, scheme:Option<MessageBoxColorScheme>)
    -> Result<ClickedButton<'a>,ShowMessageError> {
    use self::ShowMessageError::*;
    let mut button_id : c_int = 0;
    let title = match CString::new(title) {
        Ok(s) => s,
        Err(err) => return Err(InvalidTitle(err)),
    };
    let message = match CString::new(message) {
        Ok(s) => s,
        Err(err) => return Err(InvalidMessage(err)),
    };
    let button_texts : Result<Vec<_>,(_,i32)> = buttons.iter().map(|b|{
        CString::new(b.text).map_err(|e|(e,b.button_id))
    }).collect(); // Create CString for every button; and catch any CString Error
    let button_texts = match button_texts {
        Ok(b) => b,
        Err(e) => return Err(InvalidButton(e.0,e.1))
    };
    let raw_buttons : Vec<ll::SDL_MessageBoxButtonData> =
        buttons.iter().zip(button_texts.iter()).map(|(b,b_text)|{
        ll::SDL_MessageBoxButtonData {
            flags:b.flags.bits(),
            buttonid:b.button_id as c_int,
            text:b_text.as_ptr()
        }
    }).collect();
    let result = unsafe {
        let msg_box_data = ll::SDL_MessageBoxData {
            flags:flags.bits(),
            window:window.map_or(ptr::null_mut(), |win| win.raw()),
            title: title.as_ptr() as *const c_char,
            message: message.as_ptr() as *const c_char,
            numbuttons: raw_buttons.len() as c_int,
            buttons: raw_buttons.as_ptr(),
            color_scheme: if let Some(scheme) = scheme {
                &ll::SDL_MessageBoxColorScheme {
                    colors:From::from(scheme)
                } as *const _
            } else {
                ptr::null()
            }
        };
        ll::SDL_ShowMessageBox(
            &msg_box_data as *const _,
            &mut button_id as &mut _
        )
    } == 0;
    if result {
        match button_id {
            -1 => Ok(ClickedButton::CloseButton),
            id => {
                let button = buttons.iter().find(|b| b.button_id == id);
                Ok(ClickedButton::CustomButton(button.unwrap()))
            }
        }
    } else {
        Err(SdlError(get_error()))
    }
}
<commit_msg>Implement Error for ShowMessageError.<commit_after>use std::error;
use std::ffi::{CString, NulError};
use std::fmt;
use std::ptr;
use std::os::raw::{c_char,c_int};

use video::WindowRef;
use get_error;

use sys::messagebox as ll;

bitflags! {
    pub flags MessageBoxFlag: u32 {
        const MESSAGEBOX_ERROR =
            ::sys::messagebox::SDL_MessageBoxFlags::SDL_MESSAGEBOX_ERROR as u32,
        const MESSAGEBOX_WARNING =
            ::sys::messagebox::SDL_MessageBoxFlags::SDL_MESSAGEBOX_WARNING as u32,
        const MESSAGEBOX_INFORMATION =
            ::sys::messagebox::SDL_MessageBoxFlags::SDL_MESSAGEBOX_INFORMATION as u32
    }
}

bitflags! {
    pub flags MessageBoxButtonFlag: u32 {
        const MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT =
            ::sys::messagebox::SDL_MessageBoxButtonFlags::SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT as u32,
        const MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT =
            ::sys::messagebox::SDL_MessageBoxButtonFlags::SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT as u32,
        const MESSAGEBOX_BUTTON_NOTHING = 0
    }
}

#[derive(Debug)]
pub struct MessageBoxColorScheme {
    pub background:(u8,u8,u8),
    pub text:(u8,u8,u8),
    pub button_border:(u8,u8,u8),
    pub button_background:(u8,u8,u8),
    pub button_selected:(u8,u8,u8)
}

/// button_id is the integer that will be returned
/// by show_message_box. It is not sed by SDL2,
/// and should only be used to know which button has been triggered
#[derive(Debug)]
pub struct ButtonData<'a> {
    pub flags:MessageBoxButtonFlag,
    pub button_id:i32,
    pub text:&'a str
}

#[derive(Debug)]
pub enum ClickedButton<'a> {
    CloseButton,
    CustomButton(&'a ButtonData<'a>)
}

impl From<MessageBoxColorScheme> for [ll::SDL_MessageBoxColor ; 5] {
    fn from(scheme:MessageBoxColorScheme) -> [ll::SDL_MessageBoxColor ; 5] {
        fn to_message_box_color(t:(u8,u8,u8)) -> ll::SDL_MessageBoxColor {
            ll::SDL_MessageBoxColor {
                r:t.0,
                g:t.1,
                b:t.2
            }
        };
        [to_message_box_color(scheme.background),
        to_message_box_color(scheme.text),
        to_message_box_color(scheme.button_border),
        to_message_box_color(scheme.button_background),
        to_message_box_color(scheme.button_selected)]
    }
}

#[derive(Debug)]
pub enum ShowMessageError {
    InvalidTitle(NulError),
    InvalidMessage(NulError),
    /// Second argument of the tuple (i32) corresponds to the
    /// first button_id having an error
    InvalidButton(NulError,i32),
    SdlError(String),
}

impl fmt::Display for ShowMessageError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ShowMessageError::*;

        match *self {
            InvalidTitle(ref e) => write!(f, "Invalid title: {}", e),
            InvalidMessage(ref e) => write!(f, "Invalid message: {}", e),
            InvalidButton(ref e, value) => write!(f,
                "Invalid button ({}): {}", value, e),
            SdlError(ref e) => write!(f, "SDL error: {}", e)
        }
    }
}

impl error::Error for ShowMessageError {
    fn description(&self) -> &str {
        use self::ShowMessageError::*;

        match *self {
            InvalidTitle(_) => "invalid title",
            InvalidMessage(_) => "invalid message",
            InvalidButton(..) => "invalid button",
            SdlError(ref e) => e
        }
    }
}

/// Show a simple message box, meant to be informative only.
///
/// There is no way to know if the user clicked "Ok" or closed the message box,
/// If you want to retrieve which button was clicked and customize a bit more
/// your message box, use `show_message_box` instead.
pub fn show_simple_message_box(flags: MessageBoxFlag, title: &str,
        message: &str, window: Option<&WindowRef>)
        -> Result<(), ShowMessageError> {
    use self::ShowMessageError::*;
    let result = unsafe {
        let title = match CString::new(title) {
            Ok(s) => s,
            Err(err) => return Err(InvalidTitle(err)),
        };
        let message = match CString::new(message) {
            Ok(s) => s,
            Err(err) => return Err(InvalidMessage(err)),
        };
        ll::SDL_ShowSimpleMessageBox(
            flags.bits(),
            title.as_ptr() as *const c_char,
            message.as_ptr() as *const c_char,
            window.map_or(ptr::null_mut(), |win| win.raw())
        )
    } == 0;

    if result {
        Ok(())
    } else {
        Err(SdlError(get_error()))
    }
}

/// Show a customizable message box.
///
/// An array of buttons is required for it to work. The array can be empty,
/// but it will have no button beside the close button.
///
/// On success, it will return either the button clicked or the close button.
/// Note that the variant of the `ClickedButton` enum will also be returned if the message box
/// has been forcefully closed (Alt-F4, ...)
///
pub fn show_message_box<'a>(flags:MessageBoxFlag, buttons:&'a [ButtonData], title:&str,
    message:&str, window:Option<&WindowRef>, scheme:Option<MessageBoxColorScheme>)
    -> Result<ClickedButton<'a>,ShowMessageError> {
    use self::ShowMessageError::*;
    let mut button_id : c_int = 0;
    let title = match CString::new(title) {
        Ok(s) => s,
        Err(err) => return Err(InvalidTitle(err)),
    };
    let message = match CString::new(message) {
        Ok(s) => s,
        Err(err) => return Err(InvalidMessage(err)),
    };
    let button_texts : Result<Vec<_>,(_,i32)> = buttons.iter().map(|b|{
        CString::new(b.text).map_err(|e|(e,b.button_id))
    }).collect(); // Create CString for every button; and catch any CString Error
    let button_texts = match button_texts {
        Ok(b) => b,
        Err(e) => return Err(InvalidButton(e.0,e.1))
    };
    let raw_buttons : Vec<ll::SDL_MessageBoxButtonData> =
        buttons.iter().zip(button_texts.iter()).map(|(b,b_text)|{
        ll::SDL_MessageBoxButtonData {
            flags:b.flags.bits(),
            buttonid:b.button_id as c_int,
            text:b_text.as_ptr()
        }
    }).collect();
    let result = unsafe {
        let msg_box_data = ll::SDL_MessageBoxData {
            flags:flags.bits(),
            window:window.map_or(ptr::null_mut(), |win| win.raw()),
            title: title.as_ptr() as *const c_char,
            message: message.as_ptr() as *const c_char,
            numbuttons: raw_buttons.len() as c_int,
            buttons: raw_buttons.as_ptr(),
            color_scheme: if let Some(scheme) = scheme {
                &ll::SDL_MessageBoxColorScheme {
                    colors:From::from(scheme)
                } as *const _
            } else {
                ptr::null()
            }
        };
        ll::SDL_ShowMessageBox(
            &msg_box_data as *const _,
            &mut button_id as &mut _
        )
    } == 0;
    if result {
        match button_id {
            -1 => Ok(ClickedButton::CloseButton),
            id => {
                let button = buttons.iter().find(|b| b.button_id == id);
                Ok(ClickedButton::CustomButton(button.unwrap()))
            }
        }
    } else {
        Err(SdlError(get_error()))
    }
}
<|endoftext|>


<commit_before>use std::io;

use raw;

/// A successful record registration
///
/// Releases the record when dropped (unless it is a
/// [`Registration::get_default_txt_record`]
/// (struct.Registration.html#method.get_default_txt_record) or a
/// [`Register::get_default_txt_record`]
/// (struct.Register.html#method.get_default_txt_record))
///
/// Also keeps the underlying [`Registration`](struct.Registration.html)
/// or [`Connection`](struct.Connection.html) alive.
pub struct Record(raw::DNSRecord);

impl Record {
	/// Type of the record
	pub fn rr_type(&self) -> u16 {
		self.0.rr_type()
	}

	/// Update record
	///
	/// Cannot change type or class of record.
	///
	/// See [`DNSServiceUpdateRecord`](https://developer.apple.com/documentation/dnssd/1804739-dnsserviceupdaterecord).
	pub fn update_raw_record(
		&self,
		rdata: &[u8],
		ttl: u32
	) -> io::Result<()> {
		self.0.update_record(
			0, /* no flags */
			rdata,
			ttl
		)?;
		Ok(())
	}

	/// Keep record alive for as long as the underlying
	/// [`Registration`](struct.Registration.html) or
	/// [`Connection`](struct.Connection.html) lives
	pub fn keep(self) {
		self.0.keep()
	}
}

pub fn new_record(r: raw::DNSRecord) -> Record {
	Record(r)
}
<commit_msg>[doc] fix some links<commit_after>use std::io;

use raw;

/// A successful record registration
///
/// Releases the record when dropped (unless it is a
/// [`Registration::get_default_txt_record`] or a
/// [`Register::get_default_txt_record`])
///
/// Also keeps the underlying [`Registration`] or [`Connection`] alive.
///
/// [`Registration::get_default_txt_record`]: struct.Registration.html#method.get_default_txt_record
/// [`Register::get_default_txt_record`]: struct.Register.html#method.get_default_txt_record
/// [`Registration`]: struct.Registration.html
/// [`Connection`]: struct.Connection.html
pub struct Record(raw::DNSRecord);

impl Record {
	/// Type of the record
	pub fn rr_type(&self) -> u16 {
		self.0.rr_type()
	}

	/// Update record
	///
	/// Cannot change type or class of record.
	///
	/// See [`DNSServiceUpdateRecord`](https://developer.apple.com/documentation/dnssd/1804739-dnsserviceupdaterecord).
	pub fn update_raw_record(
		&self,
		rdata: &[u8],
		ttl: u32
	) -> io::Result<()> {
		self.0.update_record(
			0, /* no flags */
			rdata,
			ttl
		)?;
		Ok(())
	}

	/// Keep record alive for as long as the underlying
	/// [`Registration`](struct.Registration.html) or
	/// [`Connection`](struct.Connection.html) lives
	pub fn keep(self) {
		self.0.keep()
	}
}

pub fn new_record(r: raw::DNSRecord) -> Record {
	Record(r)
}
<|endoftext|>


<commit_before>use std::iter::repeat;
use set1::challenge2::fixed_xor;
use set1::utils::hex_to_bytes;

fn chi_squared(text: &str) -> f32 {
    let mut chars_count = vec![0.0; 26];
    let mut ignored = 0;
    for byte in text.bytes() {
        if (byte >= 65 && byte <= 90) || (byte >= 97 && byte <= 122) {
            chars_count[if byte >= 97 {
                (byte - 97) as usize
            } else {
                (byte - 65) as usize
            }] += 1.0;
        }
        // space
        else if byte != 32 {
            ignored += 1;
        }
    }
    // ascii only hence len usage
    let length = (text.len() - ignored) as f32;
    vec![0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153,
         0.00772, 0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056,
         0.02758, 0.00978, 0.02360, 0.00150, 0.01974, 0.00074]
        .iter()
        .zip(chars_count.iter())
        .map(|(x, y)| (y - x * length).powf(2.0) / (x * length))
        .sum()
}

fn find_original(text: &str) -> String {
    let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    let strings_for_xor: Vec<String> =
        alphabet.chars().map(|x| repeat(x).take(text.len()).collect::<String>()).collect();

    strings_for_xor.iter()
        .map(|x| {
            let xor = fixed_xor(&hex_to_bytes(text), &x.as_bytes());
            let string = match String::from_utf8(xor) {
                Ok(v) => v,
                Err(e) => String::new(),
            };
            (chi_squared(&string), string)
        })
        .min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
        .unwrap().1

}

#[test]
fn test_chi_squared_A() {
    assert_eq!(chi_squared("A"), 11.244388)
}

#[test]
fn test_chi_squared_a() {
    assert_eq!(chi_squared("a"), 11.244388)
}

#[test]
fn test_find_original() {
    assert_eq!(find_original("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"),
               "Cooking MC\'s like a pound of bacon")
}
<commit_msg>set1: Edit chi_squared to lower score for invalid chars<commit_after>use set1::challenge2::fixed_xor;
use set1::utils::hex_to_bytes;

pub fn chi_squared(text: &str) -> f32 {
    let mut chars_count = vec![0.0; 26];
    let mut ignored = 0;
    for byte in text.bytes() {
        if (byte >= 65 && byte <= 90) || (byte >= 97 && byte <= 122) {
            chars_count[if byte >= 97 {
                (byte - 97) as usize
            } else {
                (byte - 65) as usize
            }] += 1.0;
        }
        // space
        else if byte != 32 {
            ignored += 1;
        }
    }
    // ascii only hence len usage
    let length = (text.len() - ignored) as f32;
    vec![0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153,
         0.00772, 0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056,
         0.02758, 0.00978, 0.02360, 0.00150, 0.01974, 0.00074]
        .iter()
        .zip(chars_count.iter())
        .map(|(x, y)| (y - x * (text.len() as f32 + ignored as f32)).powf(2.0) / (x * length))
        .sum()
}

pub fn find_original(text: &str) -> Option<(f32, String)>{
    (0..255).filter_map(|x| {
            let cipher = vec![x; text.len()];
            let xor = fixed_xor(&hex_to_bytes(text), &cipher);
            String::from_utf8(xor).ok()
        })
        .map(|x| (chi_squared(&x), x))
        .filter(|&(x, _)| x.is_finite())
        .min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
}

#[test]
fn test_chi_squared_upper_a() {
    assert_eq!(chi_squared("A"), 11.244388)
}

#[test]
fn test_chi_squared_a() {
    assert_eq!(chi_squared("a"), 11.244388)
}

#[test]
fn test_find_original() {
    assert_eq!(find_original("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736").unwrap().1,
               String::from("Cooking MC\'s like a pound of bacon"))
}
<|endoftext|>


<commit_before>use std::process::{Command, Output};
use std::fs;
use std::io::prelude::*;
use std::fs::File;
use platform;

#[derive(Clone)]
pub struct SystemServices;

pub enum OSType {
    Redhat,
    Unknown
}

pub trait SystemInterface {
    fn file_exists(&self, path: &String) -> bool;
    fn read_file(&self, path: &String) -> Option<String>;
    fn is_package_installed(&self, package_name: &String) -> bool;
    fn install_package(&self, package: &String) -> Output;
    fn get_hostname(&self) -> Option<String>;
    fn set_hostname(&self, new_hostname: &String) -> bool;
    fn os_type(&self) -> OSType;
}

impl SystemInterface for SystemServices {
     fn file_exists(&self, path: &String) -> bool {
        let metadata = fs::metadata(path);

        match metadata {
            Ok(md) => md.is_dir() || md.is_file(),
            Err(_) => false
        }
    }

     fn read_file(&self, path: &String) -> Option<String> {
        let mut file_handle = File::open(path);
        let mut s = String::new();
        match file_handle {
            Ok(mut handle) => {
                handle.read_to_string(&mut s);
                Some(s)
            },
            Err(err)   => None
        }
    }

     fn is_package_installed(&self, package_name: &String) -> bool {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let command_str = format!("{} {}", platform.package_installed_command, package_name);
        let cmd_list :Vec<&str> = command_str.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());

        match Command::new(cmd_name).args(&cmd_args[..]).output() {
            Ok(output) => output.status.success(),
            Err(err)   => false
        }
    }

     fn install_package(&self, package: &String) -> Output {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let cmd_list :Vec<&str> = platform.install_command.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());
        cmd_args.push(package.to_string());

        Command::new(cmd_name)
            .args(&cmd_args[..])
            .output()
            .unwrap()
    }

     fn get_hostname(&self) -> Option<String> {
        let output = Command::new("hostname").output().unwrap();
        if output.status.success() {
            let hostname_with_newline = String::from_utf8(output.stdout).unwrap();
            Some(hostname_with_newline.replace("\n", ""))
        } else {
            None
        }
    }

     fn set_hostname(&self, new_hostname: &String) -> bool {
        let output = Command::new("hostname")
            .arg(new_hostname)
            .output()
            .unwrap();
        output.status.success()
    }

    fn os_type(&self) -> OSType {
        if self.file_exists(&"/etc/redhat-release".to_string()) ||
            self.file_exists(&"/etc/centos-release".to_string()){
                OSType::Redhat
            } else {
                OSType::Unknown
            }
    }
}
<commit_msg>remove mut modifier for file_handle<commit_after>use std::process::{Command, Output};
use std::fs;
use std::io::prelude::*;
use std::fs::File;
use platform;

#[derive(Clone)]
pub struct SystemServices;

pub enum OSType {
    Redhat,
    Unknown
}

pub trait SystemInterface {
    fn file_exists(&self, path: &String) -> bool;
    fn read_file(&self, path: &String) -> Option<String>;
    fn is_package_installed(&self, package_name: &String) -> bool;
    fn install_package(&self, package: &String) -> Output;
    fn get_hostname(&self) -> Option<String>;
    fn set_hostname(&self, new_hostname: &String) -> bool;
    fn os_type(&self) -> OSType;
}

impl SystemInterface for SystemServices {
     fn file_exists(&self, path: &String) -> bool {
        let metadata = fs::metadata(path);

        match metadata {
            Ok(md) => md.is_dir() || md.is_file(),
            Err(_) => false
        }
    }

     fn read_file(&self, path: &String) -> Option<String> {
        let file_handle = File::open(path);
        let mut s = String::new();
        match file_handle {
            Ok(mut handle) => {
                handle.read_to_string(&mut s);
                Some(s)
            },
            Err(err)   => None
        }
    }

     fn is_package_installed(&self, package_name: &String) -> bool {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let command_str = format!("{} {}", platform.package_installed_command, package_name);
        let cmd_list :Vec<&str> = command_str.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());

        match Command::new(cmd_name).args(&cmd_args[..]).output() {
            Ok(output) => output.status.success(),
            Err(err)   => false
        }
    }

     fn install_package(&self, package: &String) -> Output {
        let platform = platform::for_os_type(self.os_type()).unwrap();
        let cmd_list :Vec<&str> = platform.install_command.split_whitespace().collect();
        let cmd_name :String = cmd_list.first().unwrap().to_string();
        let mut cmd_args = Vec::new();
        cmd_args.extend(cmd_list.iter().skip(1).map(|s| s.to_string()).collect::<Vec<_>>());
        cmd_args.push(package.to_string());

        Command::new(cmd_name)
            .args(&cmd_args[..])
            .output()
            .unwrap()
    }

     fn get_hostname(&self) -> Option<String> {
        let output = Command::new("hostname").output().unwrap();
        if output.status.success() {
            let hostname_with_newline = String::from_utf8(output.stdout).unwrap();
            Some(hostname_with_newline.replace("\n", ""))
        } else {
            None
        }
    }

     fn set_hostname(&self, new_hostname: &String) -> bool {
        let output = Command::new("hostname")
            .arg(new_hostname)
            .output()
            .unwrap();
        output.status.success()
    }

    fn os_type(&self) -> OSType {
        if self.file_exists(&"/etc/redhat-release".to_string()) ||
            self.file_exists(&"/etc/centos-release".to_string()){
                OSType::Redhat
            } else {
                OSType::Unknown
            }
    }
}
<|endoftext|>