<commit_before>#![crate_name = "uu_du"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate time;

#[macro_use]
extern crate uucore;

use std::fs;
use std::iter;
use std::io::{stderr, Write};
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use time::Timespec;

const NAME: &'static str = "du";
const SUMMARY: &'static str = "estimate file space usage";
const LONG_HELP: &'static str = "
 Display  values  are  in  units  of  the  first  available  SIZE from
 --block-size,  and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environ‐
 ment variables.  Otherwise, units default to  1024  bytes  (or  512  if
 POSIXLY_CORRECT is set).

 SIZE  is  an  integer and optional unit (example: 10M is 10*1024*1024).
 Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB, ...  (pow‐
 ers of 1000).
";

struct Options {
    all: bool,
    program_name: String,
    max_depth: Option<usize>,
    total: bool,
    separate_dirs: bool,
}

struct Stat {
    path: PathBuf,
    is_dir: bool,
    size: u64,
    blocks: u64,
    nlink: u64,
    created: u64,
    accessed: u64,
    modified: u64,
}

impl Stat {
    fn new(path: PathBuf) -> Stat {
        let metadata = safe_unwrap!(fs::symlink_metadata(&path));
        Stat {
            path: path,
            is_dir: metadata.is_dir(),
            size: metadata.len(),
            blocks: metadata.blocks() as u64,
            nlink: metadata.nlink() as u64,
            created: metadata.mtime() as u64,
            accessed: metadata.atime() as u64,
            modified: metadata.mtime() as u64,
        }
    }
}

// this takes `my_stat` to avoid having to stat files multiple times.
// XXX: this should use the impl Trait return type when it is stabilized
fn du(mut my_stat: Stat, options: &Options, depth: usize) -> Box<DoubleEndedIterator<Item = Stat>> {
    let mut stats = vec![];
    let mut futures = vec![];

    if my_stat.is_dir {
        let read = match fs::read_dir(&my_stat.path) {
            Ok(read) => read,
            Err(e) => {
                safe_writeln!(
                    stderr(),
                    "{}: cannot read directory ‘{}‘: {}",
                    options.program_name,
                    my_stat.path.display(),
                    e
                );
                return Box::new(iter::once(my_stat));
            }
        };

        for f in read.into_iter() {
            match f {
                Ok(entry) => match fs::symlink_metadata(entry.path()) {
                    Ok(_) => {
                        let this_stat = Stat::new(entry.path());
                        if this_stat.is_dir {
                            futures.push(du(this_stat, options, depth + 1));
                        } else {
                            my_stat.size += this_stat.size;
                            my_stat.blocks += this_stat.blocks;
                            if options.all {
                                stats.push(this_stat);
                            }
                        }
                    }
                    Err(error) => show_error!("{}", error),
                },
                Err(error) => show_error!("{}", error),
            }
        }
    }

    stats.extend(
        futures
            .into_iter()
            .flat_map(|val| val)
            .rev()
            .filter_map(|stat| {
                if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
                    my_stat.size += stat.size;
                    my_stat.blocks += stat.blocks;
                }
                if options.max_depth == None || depth < options.max_depth.unwrap() {
                    Some(stat)
                } else {
                    None
                }
            }),
    );
    stats.push(my_stat);
    Box::new(stats.into_iter())
}

pub fn uumain(args: Vec<String>) -> i32 {
    let syntax = format!(
        "[OPTION]... [FILE]...
 {0} [OPTION]... --files0-from=F",
        NAME
    );
    let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP)
    // In task
        .optflag("a", "all", " write counts for all files, not just directories")
    // In main
        .optflag("", "apparent-size", "print apparent sizes,  rather  than  disk  usage
            although  the apparent  size is usually smaller, it may be larger due to holes
            in ('sparse') files, internal  fragmentation,  indirect  blocks, and the like")
    // In main
        .optopt("B", "block-size", "scale sizes  by  SIZE before printing them.
            E.g., '-BM' prints sizes in units of 1,048,576 bytes.  See SIZE format below.",
            "SIZE")
    // In main
        .optflag("b", "bytes", "equivalent to '--apparent-size --block-size=1'")
    // In main
        .optflag("c", "total", "produce a grand total")
    // In task
    // opts.optflag("D", "dereference-args", "dereference only symlinks that are listed
    //     on the command line"),
    // In main
    // opts.optopt("", "files0-from", "summarize disk usage of the NUL-terminated file
    //                   names specified in file F;
    //                   If F is - then read names from standard input", "F"),
    // // In task
    // opts.optflag("H", "", "equivalent to --dereference-args (-D)"),
    // In main
        .optflag("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)")
    // In main
        .optflag("", "si", "like -h, but use powers of 1000 not 1024")
    // In main
        .optflag("k", "", "like --block-size=1K")
    // In task
        .optflag("l", "count-links", "count sizes many times if hard linked")
    // // In main
        .optflag("m", "", "like --block-size=1M")
    // // In task
    // opts.optflag("L", "dereference", "dereference all symbolic links"),
    // // In task
    // opts.optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"),
    // // In main
        .optflag("0", "null", "end each output line with 0 byte rather than newline")
    // In main
        .optflag("S", "separate-dirs", "do not include size of subdirectories")
    // In main
        .optflag("s", "summarize", "display only a total for each argument")
    // // In task
    // opts.optflag("x", "one-file-system", "skip directories on different file systems"),
    // // In task
    // opts.optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"),
    // // In task
    // opts.optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"),
    // In main
        .optopt("d", "max-depth", "print the total for a directory (or file, with --all)
            only if it is N or fewer levels below the command
            line argument;  --max-depth=0 is the same as --summarize", "N")
    // In main
        .optflagopt("", "time", "show time of the last modification of any file in the
            directory, or any of its subdirectories.  If WORD is given, show time as WORD instead
            of modification time: atime, access, use, ctime or status", "WORD")
    // In main
        .optopt("", "time-style", "show times using style STYLE:
            full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE")
        .parse(args);

    let summarize = matches.opt_present("summarize");

    let max_depth_str = matches.opt_str("max-depth");
    let max_depth = max_depth_str.as_ref().and_then(|s| s.parse::<usize>().ok());
    match (max_depth_str, max_depth) {
        (Some(ref s), _) if summarize => {
            show_error!("summarizing conflicts with --max-depth={}", *s);
            return 1;
        }
        (Some(ref s), None) => {
            show_error!("invalid maximum depth '{}'", *s);
            return 1;
        }
        (Some(_), Some(_)) | (None, _) => { /* valid */ }
    }

    let options = Options {
        all: matches.opt_present("all"),
        program_name: NAME.to_owned(),
        max_depth: max_depth,
        total: matches.opt_present("total"),
        separate_dirs: matches.opt_present("S"),
    };

    let strs = if matches.free.is_empty() {
        vec!["./".to_owned()]
    } else {
        matches.free.clone()
    };

    let mb = if matches.opt_present("si") {
        1000 * 1000
    } else {
        1024 * 1024
    };
    let kb = if matches.opt_present("si") {
        1000
    } else {
        1024
    };

    let block_size = match matches.opt_str("block-size") {
        Some(s) => {
            let mut found_number = false;
            let mut found_letter = false;
            let mut numbers = String::new();
            let mut letters = String::new();
            for c in s.chars() {
                if found_letter && c.is_digit(10) || !found_number && !c.is_digit(10) {
                    show_error!("invalid --block-size argument '{}'", s);
                    return 1;
                } else if c.is_digit(10) {
                    found_number = true;
                    numbers.push(c);
                } else if c.is_alphabetic() {
                    found_letter = true;
                    letters.push(c);
                }
            }
            let number = numbers.parse::<u64>().unwrap();
            let multiple = match &letters[..] {
                "K" => 1024u64.pow(1),
                "M" => 1024u64.pow(2),
                "G" => 1024u64.pow(3),
                "T" => 1024u64.pow(4),
                "P" => 1024u64.pow(5),
                "E" => 1024u64.pow(6),
                "Z" => 1024u64.pow(7),
                "Y" => 1024u64.pow(8),
                "KB" => 1000u64.pow(1),
                "MB" => 1000u64.pow(2),
                "GB" => 1000u64.pow(3),
                "TB" => 1000u64.pow(4),
                "PB" => 1000u64.pow(5),
                "EB" => 1000u64.pow(6),
                "ZB" => 1000u64.pow(7),
                "YB" => 1000u64.pow(8),
                _ => {
                    show_error!("invalid --block-size argument '{}'", s);
                    return 1;
                }
            };
            number * multiple
        }
        None => 1024,
    };

    let convert_size = |size: u64| -> String {
        if matches.opt_present("human-readable") || matches.opt_present("si") {
            if size >= mb {
                format!("{:.1}M", (size as f64) / (mb as f64))
            } else if size >= kb {
                format!("{:.1}K", (size as f64) / (kb as f64))
            } else {
                format!("{}B", size)
            }
        } else if matches.opt_present("k") {
            format!("{}", ((size as f64) / (kb as f64)).ceil())
        } else if matches.opt_present("m") {
            format!("{}", ((size as f64) / (mb as f64)).ceil())
        } else {
            format!("{}", ((size as f64) / (block_size as f64)).ceil())
        }
    };

    let time_format_str = match matches.opt_str("time-style") {
        Some(s) => match &s[..] {
            "full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
            "long-iso" => "%Y-%m-%d %H:%M",
            "iso" => "%Y-%m-%d",
            _ => {
                show_error!(
                    "invalid argument '{}' for 'time style'
Valid arguments are:
- 'full-iso'
- 'long-iso'
- 'iso'
Try '{} --help' for more information.",
                    s,
                    NAME
                );
                return 1;
            }
        },
        None => "%Y-%m-%d %H:%M",
    };

    let line_separator = if matches.opt_present("0") { "\0" } else { "\n" };

    let mut grand_total = 0;
    for path_str in strs.into_iter() {
        let path = PathBuf::from(path_str);
        let iter = du(Stat::new(path), &options, 0).into_iter();
        let (_, len) = iter.size_hint();
        let len = len.unwrap();
        for (index, stat) in iter.enumerate() {
            let size = if matches.opt_present("apparent-size") {
                stat.nlink * stat.size
            } else {
                // C's stat is such that each block is assume to be 512 bytes
                // See: http://linux.die.net/man/2/stat
                stat.blocks * 512
            };
            if matches.opt_present("time") {
                let tm = {
                    let (secs, nsecs) = {
                        let time = match matches.opt_str("time") {
                            Some(s) => match &s[..] {
                                "accessed" => stat.accessed,
                                "created" => stat.created,
                                "modified" => stat.modified,
                                _ => {
                                    show_error!(
                                        "invalid argument 'modified' for '--time'
    Valid arguments are:
      - 'accessed', 'created', 'modified'
    Try '{} --help' for more information.",
                                        NAME
                                    );
                                    return 1;
                                }
                            },
                            None => stat.modified,
                        };
                        ((time / 1000) as i64, (time % 1000 * 1000000) as i32)
                    };
                    time::at(Timespec::new(secs, nsecs))
                };
                if !summarize || (summarize && index == len - 1) {
                    let time_str = tm.strftime(time_format_str).unwrap();
                    print!(
                        "{}\t{}\t{}{}",
                        convert_size(size),
                        time_str,
                        stat.path.display(),
                        line_separator
                    );
                }
            } else {
                if !summarize || (summarize && index == len - 1) {
                    print!(
                        "{}\t{}{}",
                        convert_size(size),
                        stat.path.display(),
                        line_separator
                    );
                }
            }
            if options.total && index == (len - 1) {
                // The last element will be the total size of the the path under
                // path_str.  We add it to the grand total.
                grand_total += size;
            }
        }
    }

    if options.total {
        print!("{}\ttotal", convert_size(grand_total));
        print!("{}", line_separator);
    }

    0
}
<commit_msg>Refactor Stat::new to return Result<commit_after>#![crate_name = "uu_du"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate time;

#[macro_use]
extern crate uucore;

use std::fs;
use std::iter;
use std::io::{stderr, Result, Write};
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use time::Timespec;

const NAME: &'static str = "du";
const SUMMARY: &'static str = "estimate file space usage";
const LONG_HELP: &'static str = "
 Display  values  are  in  units  of  the  first  available  SIZE from
 --block-size,  and the DU_BLOCK_SIZE, BLOCK_SIZE and BLOCKSIZE environ‐
 ment variables.  Otherwise, units default to  1024  bytes  (or  512  if
 POSIXLY_CORRECT is set).

 SIZE  is  an  integer and optional unit (example: 10M is 10*1024*1024).
 Units are K, M, G, T, P, E, Z, Y (powers of 1024) or KB, MB, ...  (pow‐
 ers of 1000).
";

struct Options {
    all: bool,
    program_name: String,
    max_depth: Option<usize>,
    total: bool,
    separate_dirs: bool,
}

struct Stat {
    path: PathBuf,
    is_dir: bool,
    size: u64,
    blocks: u64,
    nlink: u64,
    created: u64,
    accessed: u64,
    modified: u64,
}

impl Stat {
    fn new(path: PathBuf) -> Result<Stat> {
        match fs::symlink_metadata(&path) {
            Ok(metadata) => {
                return Ok(Stat {
                    path: path,
                    is_dir: metadata.is_dir(),
                    size: metadata.len(),
                    blocks: metadata.blocks() as u64,
                    nlink: metadata.nlink() as u64,
                    created: metadata.mtime() as u64,
                    accessed: metadata.atime() as u64,
                    modified: metadata.mtime() as u64,
                })
            }
            Err(e) => Err(e),
        }
    }
}

// this takes `my_stat` to avoid having to stat files multiple times.
// XXX: this should use the impl Trait return type when it is stabilized
fn du(mut my_stat: Stat, options: &Options, depth: usize) -> Box<DoubleEndedIterator<Item = Stat>> {
    let mut stats = vec![];
    let mut futures = vec![];

    if my_stat.is_dir {
        let read = match fs::read_dir(&my_stat.path) {
            Ok(read) => read,
            Err(e) => {
                safe_writeln!(
                    stderr(),
                    "{}: cannot read directory ‘{}‘: {}",
                    options.program_name,
                    my_stat.path.display(),
                    e
                );
                return Box::new(iter::once(my_stat));
            }
        };

        for f in read.into_iter() {
            match f {
                Ok(entry) => match Stat::new(entry.path()) {
                    Ok(this_stat) => {
                        if this_stat.is_dir {
                            futures.push(du(this_stat, options, depth + 1));
                        } else {
                            my_stat.size += this_stat.size;
                            my_stat.blocks += this_stat.blocks;
                            if options.all {
                                stats.push(this_stat);
                            }
                        }
                    }
                    Err(error) => show_error!("{}", error),
                },
                Err(error) => show_error!("{}", error),
            }
        }
    }

    stats.extend(
        futures
            .into_iter()
            .flat_map(|val| val)
            .rev()
            .filter_map(|stat| {
                if !options.separate_dirs && stat.path.parent().unwrap() == my_stat.path {
                    my_stat.size += stat.size;
                    my_stat.blocks += stat.blocks;
                }
                if options.max_depth == None || depth < options.max_depth.unwrap() {
                    Some(stat)
                } else {
                    None
                }
            }),
    );
    stats.push(my_stat);
    Box::new(stats.into_iter())
}

pub fn uumain(args: Vec<String>) -> i32 {
    let syntax = format!(
        "[OPTION]... [FILE]...
 {0} [OPTION]... --files0-from=F",
        NAME
    );
    let matches = new_coreopts!(&syntax, SUMMARY, LONG_HELP)
    // In task
        .optflag("a", "all", " write counts for all files, not just directories")
    // In main
        .optflag("", "apparent-size", "print apparent sizes,  rather  than  disk  usage
            although  the apparent  size is usually smaller, it may be larger due to holes
            in ('sparse') files, internal  fragmentation,  indirect  blocks, and the like")
    // In main
        .optopt("B", "block-size", "scale sizes  by  SIZE before printing them.
            E.g., '-BM' prints sizes in units of 1,048,576 bytes.  See SIZE format below.",
            "SIZE")
    // In main
        .optflag("b", "bytes", "equivalent to '--apparent-size --block-size=1'")
    // In main
        .optflag("c", "total", "produce a grand total")
    // In task
    // opts.optflag("D", "dereference-args", "dereference only symlinks that are listed
    //     on the command line"),
    // In main
    // opts.optopt("", "files0-from", "summarize disk usage of the NUL-terminated file
    //                   names specified in file F;
    //                   If F is - then read names from standard input", "F"),
    // // In task
    // opts.optflag("H", "", "equivalent to --dereference-args (-D)"),
    // In main
        .optflag("h", "human-readable", "print sizes in human readable format (e.g., 1K 234M 2G)")
    // In main
        .optflag("", "si", "like -h, but use powers of 1000 not 1024")
    // In main
        .optflag("k", "", "like --block-size=1K")
    // In task
        .optflag("l", "count-links", "count sizes many times if hard linked")
    // // In main
        .optflag("m", "", "like --block-size=1M")
    // // In task
    // opts.optflag("L", "dereference", "dereference all symbolic links"),
    // // In task
    // opts.optflag("P", "no-dereference", "don't follow any symbolic links (this is the default)"),
    // // In main
        .optflag("0", "null", "end each output line with 0 byte rather than newline")
    // In main
        .optflag("S", "separate-dirs", "do not include size of subdirectories")
    // In main
        .optflag("s", "summarize", "display only a total for each argument")
    // // In task
    // opts.optflag("x", "one-file-system", "skip directories on different file systems"),
    // // In task
    // opts.optopt("X", "exclude-from", "exclude files that match any pattern in FILE", "FILE"),
    // // In task
    // opts.optopt("", "exclude", "exclude files that match PATTERN", "PATTERN"),
    // In main
        .optopt("d", "max-depth", "print the total for a directory (or file, with --all)
            only if it is N or fewer levels below the command
            line argument;  --max-depth=0 is the same as --summarize", "N")
    // In main
        .optflagopt("", "time", "show time of the last modification of any file in the
            directory, or any of its subdirectories.  If WORD is given, show time as WORD instead
            of modification time: atime, access, use, ctime or status", "WORD")
    // In main
        .optopt("", "time-style", "show times using style STYLE:
            full-iso, long-iso, iso, +FORMAT FORMAT is interpreted like 'date'", "STYLE")
        .parse(args);

    let summarize = matches.opt_present("summarize");

    let max_depth_str = matches.opt_str("max-depth");
    let max_depth = max_depth_str.as_ref().and_then(|s| s.parse::<usize>().ok());
    match (max_depth_str, max_depth) {
        (Some(ref s), _) if summarize => {
            show_error!("summarizing conflicts with --max-depth={}", *s);
            return 1;
        }
        (Some(ref s), None) => {
            show_error!("invalid maximum depth '{}'", *s);
            return 1;
        }
        (Some(_), Some(_)) | (None, _) => { /* valid */ }
    }

    let options = Options {
        all: matches.opt_present("all"),
        program_name: NAME.to_owned(),
        max_depth: max_depth,
        total: matches.opt_present("total"),
        separate_dirs: matches.opt_present("S"),
    };

    let strs = if matches.free.is_empty() {
        vec!["./".to_owned()]
    } else {
        matches.free.clone()
    };

    let mb = if matches.opt_present("si") {
        1000 * 1000
    } else {
        1024 * 1024
    };
    let kb = if matches.opt_present("si") {
        1000
    } else {
        1024
    };

    let block_size = match matches.opt_str("block-size") {
        Some(s) => {
            let mut found_number = false;
            let mut found_letter = false;
            let mut numbers = String::new();
            let mut letters = String::new();
            for c in s.chars() {
                if found_letter && c.is_digit(10) || !found_number && !c.is_digit(10) {
                    show_error!("invalid --block-size argument '{}'", s);
                    return 1;
                } else if c.is_digit(10) {
                    found_number = true;
                    numbers.push(c);
                } else if c.is_alphabetic() {
                    found_letter = true;
                    letters.push(c);
                }
            }
            let number = numbers.parse::<u64>().unwrap();
            let multiple = match &letters[..] {
                "K" => 1024u64.pow(1),
                "M" => 1024u64.pow(2),
                "G" => 1024u64.pow(3),
                "T" => 1024u64.pow(4),
                "P" => 1024u64.pow(5),
                "E" => 1024u64.pow(6),
                "Z" => 1024u64.pow(7),
                "Y" => 1024u64.pow(8),
                "KB" => 1000u64.pow(1),
                "MB" => 1000u64.pow(2),
                "GB" => 1000u64.pow(3),
                "TB" => 1000u64.pow(4),
                "PB" => 1000u64.pow(5),
                "EB" => 1000u64.pow(6),
                "ZB" => 1000u64.pow(7),
                "YB" => 1000u64.pow(8),
                _ => {
                    show_error!("invalid --block-size argument '{}'", s);
                    return 1;
                }
            };
            number * multiple
        }
        None => 1024,
    };

    let convert_size = |size: u64| -> String {
        if matches.opt_present("human-readable") || matches.opt_present("si") {
            if size >= mb {
                format!("{:.1}M", (size as f64) / (mb as f64))
            } else if size >= kb {
                format!("{:.1}K", (size as f64) / (kb as f64))
            } else {
                format!("{}B", size)
            }
        } else if matches.opt_present("k") {
            format!("{}", ((size as f64) / (kb as f64)).ceil())
        } else if matches.opt_present("m") {
            format!("{}", ((size as f64) / (mb as f64)).ceil())
        } else {
            format!("{}", ((size as f64) / (block_size as f64)).ceil())
        }
    };

    let time_format_str = match matches.opt_str("time-style") {
        Some(s) => match &s[..] {
            "full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
            "long-iso" => "%Y-%m-%d %H:%M",
            "iso" => "%Y-%m-%d",
            _ => {
                show_error!(
                    "invalid argument '{}' for 'time style'
Valid arguments are:
- 'full-iso'
- 'long-iso'
- 'iso'
Try '{} --help' for more information.",
                    s,
                    NAME
                );
                return 1;
            }
        },
        None => "%Y-%m-%d %H:%M",
    };

    let line_separator = if matches.opt_present("0") { "\0" } else { "\n" };

    let mut grand_total = 0;
    for path_str in strs.into_iter() {
        let path = PathBuf::from(path_str);
        match Stat::new(path) {
            Ok(stat) => {
                let iter = du(stat, &options, 0).into_iter();
                let (_, len) = iter.size_hint();
                let len = len.unwrap();
                for (index, stat) in iter.enumerate() {
                    let size = if matches.opt_present("apparent-size") {
                        stat.nlink * stat.size
                    } else {
                        // C's stat is such that each block is assume to be 512 bytes
                        // See: http://linux.die.net/man/2/stat
                        stat.blocks * 512
                    };
                    if matches.opt_present("time") {
                        let tm = {
                            let (secs, nsecs) = {
                                let time = match matches.opt_str("time") {
                                    Some(s) => match &s[..] {
                                        "accessed" => stat.accessed,
                                        "created" => stat.created,
                                        "modified" => stat.modified,
                                        _ => {
                                            show_error!(
                                                "invalid argument 'modified' for '--time'
    Valid arguments are:
      - 'accessed', 'created', 'modified'
    Try '{} --help' for more information.",
                                                NAME
                                            );
                                            return 1;
                                        }
                                    },
                                    None => stat.modified,
                                };
                                ((time / 1000) as i64, (time % 1000 * 1000000) as i32)
                            };
                            time::at(Timespec::new(secs, nsecs))
                        };
                        if !summarize || (summarize && index == len - 1) {
                            let time_str = tm.strftime(time_format_str).unwrap();
                            print!(
                                "{}\t{}\t{}{}",
                                convert_size(size),
                                time_str,
                                stat.path.display(),
                                line_separator
                            );
                        }
                    } else {
                        if !summarize || (summarize && index == len - 1) {
                            print!(
                                "{}\t{}{}",
                                convert_size(size),
                                stat.path.display(),
                                line_separator
                            );
                        }
                    }
                    if options.total && index == (len - 1) {
                        // The last element will be the total size of the the path under
                        // path_str.  We add it to the grand total.
                        grand_total += size;
                    }
                }
            }
            Err(error) => show_error!("{}", error),
        }
    }

    if options.total {
        print!("{}\ttotal", convert_size(grand_total));
        print!("{}", line_separator);
    }

    0
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io;
use std::error;
use std::string::FromUtf8Error;

use hyper;
use rustc_serialize;

/// slack::Error represents errors that can happen while using the RtmClient
#[derive(Debug)]
pub enum Error {
    /// Http client error
    Http(hyper::Error),
    /// Error decoding websocket text frame Utf8
    Utf8(FromUtf8Error),
    /// Error parsing url
    Url(hyper::Error),
    /// Error decoding Json
    JsonDecode(rustc_serialize::json::DecoderError),
    /// Error parsing Json
    JsonParse(rustc_serialize::json::ParserError),
    /// Error encoding Json
    JsonEncode(rustc_serialize::json::EncoderError),
    /// Slack Api Error
    Api(String),
    /// Errors that do not fit under the other types, Internal is for EG channel errors.
    Internal(String),
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Error {
        match err {
            hyper::Error::Uri(_) => Error::Url(err),
            _ => Error::Http(err)
        }
    }
}

impl From<rustc_serialize::json::DecoderError> for Error {
    fn from(err: rustc_serialize::json::DecoderError) -> Error {
        Error::JsonDecode(err)
    }
}

impl From<rustc_serialize::json::ParserError> for Error {
    fn from(err: rustc_serialize::json::ParserError) -> Error {
        Error::JsonParse(err)
    }
}

impl From<rustc_serialize::json::EncoderError> for Error {
    fn from(err: rustc_serialize::json::EncoderError) -> Error {
        Error::JsonEncode(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Internal(format!("{:?}", err))
    }
}

impl From<FromUtf8Error> for Error {
    fn from(err: FromUtf8Error) -> Error {
        Error::Utf8(err)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Http(ref e) => write!(f, "Http (hyper) Error: {:?}", e),
            Error::Utf8(ref e) => write!(f, "Utf8 decode Error: {:?}", e),
            Error::Url(ref e) => write!(f, "Url Error: {:?}", e),
            Error::JsonDecode(ref e) => write!(f, "Json Decode Error: {:?}", e),
            Error::JsonParse(ref e) => write!(f, "Json Parse Error: {:?}", e),
            Error::JsonEncode(ref e) => write!(f, "Json Encode Error: {:?}", e),
            Error::Api(ref st) => write!(f, "Slack Api Error: {:?}", st),
            Error::Internal(ref st) => write!(f, "Internal Error: {:?}", st)
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Http(ref e) => e.description(),
            Error::Utf8(ref e) => e.description(),
            Error::Url(ref e) => e.description(),
            Error::JsonDecode(ref e) => e.description(),
            Error::JsonParse(ref e) => e.description(),
            Error::JsonEncode(ref e) => e.description(),
            Error::Api(ref st) => st,
            Error::Internal(ref st) => st
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Http(ref e) => Some(e),
            Error::Utf8(ref e) => Some(e),
            Error::Url(ref e) => Some(e),
            Error::JsonDecode(ref e) => Some(e),
            Error::JsonParse(ref e) => Some(e),
            Error::JsonEncode(ref e) => Some(e),
            Error::Api(_) => None,
            Error::Internal(_) => None
        }
    }
}<commit_msg>Remove unused UTF8 error<commit_after>use std::fmt;
use std::io;
use std::error;

use hyper;
use rustc_serialize;

/// slack::Error represents errors that can happen while using the RtmClient
#[derive(Debug)]
pub enum Error {
    /// Http client error
    Http(hyper::Error),
    /// Error parsing url
    Url(hyper::Error),
    /// Error decoding Json
    JsonDecode(rustc_serialize::json::DecoderError),
    /// Error parsing Json
    JsonParse(rustc_serialize::json::ParserError),
    /// Error encoding Json
    JsonEncode(rustc_serialize::json::EncoderError),
    /// Slack Api Error
    Api(String),
    /// Errors that do not fit under the other types, Internal is for EG channel errors.
    Internal(String),
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Error {
        match err {
            hyper::Error::Uri(_) => Error::Url(err),
            _ => Error::Http(err)
        }
    }
}

impl From<rustc_serialize::json::DecoderError> for Error {
    fn from(err: rustc_serialize::json::DecoderError) -> Error {
        Error::JsonDecode(err)
    }
}

impl From<rustc_serialize::json::ParserError> for Error {
    fn from(err: rustc_serialize::json::ParserError) -> Error {
        Error::JsonParse(err)
    }
}

impl From<rustc_serialize::json::EncoderError> for Error {
    fn from(err: rustc_serialize::json::EncoderError) -> Error {
        Error::JsonEncode(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Internal(format!("{:?}", err))
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Http(ref e) => write!(f, "Http (hyper) Error: {:?}", e),
            Error::Url(ref e) => write!(f, "Url Error: {:?}", e),
            Error::JsonDecode(ref e) => write!(f, "Json Decode Error: {:?}", e),
            Error::JsonParse(ref e) => write!(f, "Json Parse Error: {:?}", e),
            Error::JsonEncode(ref e) => write!(f, "Json Encode Error: {:?}", e),
            Error::Api(ref st) => write!(f, "Slack Api Error: {:?}", st),
            Error::Internal(ref st) => write!(f, "Internal Error: {:?}", st)
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Http(ref e) => e.description(),
            Error::Url(ref e) => e.description(),
            Error::JsonDecode(ref e) => e.description(),
            Error::JsonParse(ref e) => e.description(),
            Error::JsonEncode(ref e) => e.description(),
            Error::Api(ref st) => st,
            Error::Internal(ref st) => st
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Http(ref e) => Some(e),
            Error::Url(ref e) => Some(e),
            Error::JsonDecode(ref e) => Some(e),
            Error::JsonParse(ref e) => Some(e),
            Error::JsonEncode(ref e) => Some(e),
            Error::Api(_) => None,
            Error::Internal(_) => None
        }
    }
}<|endoftext|>


<commit_before>use std::{error::Error as StdError, fmt, io, sync::PoisonError};

pub type Result<T> = ::std::result::Result<T, Error>;

pub enum Error {
    Canonicalization(String, io::Error),
    Clap(clap::Error),
    Glob(globset::Error),
    Io(io::Error),
    Notify(notify::Error),
    PoisonedLock,
}

impl StdError for Error {
    fn description(&self) -> &str {
        // This method is soft-deprecated and shouldn't be used,
        // see Display for the actual description.
        "a watchexec error"
    }
}

impl From<clap::Error> for Error {
    fn from(err: clap::Error) -> Self {
        Self::Clap(err)
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Self {
        Self::Glob(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Self::Io(err)
    }
}

impl From<notify::Error> for Error {
    fn from(err: notify::Error) -> Self {
        match err {
            notify::Error::Io(err) => Self::Io(err),
            other => Self::Notify(other),
        }
    }
}

impl<'a, T> From<PoisonError<T>> for Error {
    fn from(_err: PoisonError<T>) -> Self {
        Self::PoisonedLock
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (error_type, error) = match self {
            Self::Canonicalization(path, err) => (
                "Path",
                format!("couldn't canonicalize '{}':\n{}", path, err),
            ),
            Self::Clap(err) => ("Argument", err.to_string()),
            Self::Glob(err) => ("Globset", err.to_string()),
            Self::Io(err) => ("I/O", err.to_string()),
            Self::Notify(err) => ("Notify", err.to_string()),
            Self::PoisonedLock => ("Internal", "poisoned lock".to_string()),
        };

        write!(f, "{} error: {}", error_type, error)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}
<commit_msg>wrap ioerror when it represents oserror E2BIG<commit_after>use std::{error::Error as StdError, fmt, io, sync::PoisonError};

pub type Result<T> = ::std::result::Result<T, Error>;

pub enum Error {
    Canonicalization(String, io::Error),
    Clap(clap::Error),
    Glob(globset::Error),
    Io(io::Error),
    Notify(notify::Error),
    PoisonedLock,
}

impl StdError for Error {
    fn description(&self) -> &str {
        // This method is soft-deprecated and shouldn't be used,
        // see Display for the actual description.
        "a watchexec error"
    }
}

impl From<clap::Error> for Error {
    fn from(err: clap::Error) -> Self {
        Self::Clap(err)
    }
}

impl From<globset::Error> for Error {
    fn from(err: globset::Error) -> Self {
        Self::Glob(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Self::Io(
            match err.raw_os_error() {
                Some(os_err) => match os_err {
                    7 => {
                        let msg = "There are so many changed files that the environment variables of the child process have been overrun. Try running with --no-meta or --no-environment.";
                        io::Error::new(io::ErrorKind::Other, msg)
                    },
                    _ => err,
                }
                None => err,
            }
        )
    }
}

impl From<notify::Error> for Error {
    fn from(err: notify::Error) -> Self {
        match err {
            notify::Error::Io(err) => Self::Io(err),
            other => Self::Notify(other),
        }
    }
}

impl<'a, T> From<PoisonError<T>> for Error {
    fn from(_err: PoisonError<T>) -> Self {
        Self::PoisonedLock
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (error_type, error) = match self {
            Self::Canonicalization(path, err) => (
                "Path",
                format!("couldn't canonicalize '{}':\n{}", path, err),
            ),
            Self::Clap(err) => ("Argument", err.to_string()),
            Self::Glob(err) => ("Globset", err.to_string()),
            Self::Io(err) => ("I/O", err.to_string()),
            Self::Notify(err) => ("Notify", err.to_string()),
            Self::PoisonedLock => ("Internal", "poisoned lock".to_string()),
        };

        write!(f, "{} error: {}", error_type, error)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}
<|endoftext|>


<commit_before>//! Protobuf encoding and decoding errors.

use std::borrow::Cow;
use std::error;
use std::fmt;
use std::io;

/// A Protobuf message decoding error.
///
/// `DecodeError` indicates that the input buffer does not caontain a valid
/// Protobuf message. The error details should be considered 'best effort': in
/// general it is not possible to exactly pinpoint why data is malformed.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DecodeError {
    /// A 'best effort' root cause description.
    description: Cow<'static, str>,
    /// A stack of (message, field) name pairs, which identify the specific
    /// message type and field where decoding failed. The stack contains an
    /// entry per level of nesting.
    stack: Vec<(&'static str, &'static str)>,
}

impl DecodeError {
    /// Creates a new `DecodeError` with a 'best effort' root cause description.
    ///
    /// Meant to be used only by `Message` implementations.
    #[doc(hidden)]
    pub fn new<S>(description: S) -> DecodeError
    where
        S: Into<Cow<'static, str>>,
    {
        DecodeError {
            description: description.into(),
            stack: Vec::new(),
        }
    }

    /// Pushes a (message, field) name location pair on to the location stack.
    ///
    /// Meant to be used only by `Message` implementations.
    #[doc(hidden)]
    pub fn push(&mut self, message: &'static str, field: &'static str) {
        self.stack.push((message, field));
    }
}

impl fmt::Display for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("failed to decode Protobuf message: ")?;
        for &(message, field) in &self.stack {
            write!(f, "{}.{}: ", message, field)?;
        }
        f.write_str(&self.description)
    }
}

impl error::Error for DecodeError {
    fn description(&self) -> &str {
        &self.description
    }
}

impl From<DecodeError> for io::Error {
    fn from(error: DecodeError) -> io::Error {
        io::Error::new(io::ErrorKind::InvalidData, error)
    }
}

/// A Protobuf message encoding error.
///
/// `EncodeError` always indicates that a message failed to encode because the
/// provided buffer had insufficient capacity. Message encoding is otherwise
/// infallible.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct EncodeError {
    required: usize,
    remaining: usize,
}

impl EncodeError {
    /// Creates a new `EncodeError`.
    pub(crate) fn new(required: usize, remaining: usize) -> EncodeError {
        EncodeError {
            required,
            remaining,
        }
    }

    /// Returns the required buffer capacity to encode the message.
    pub fn required_capacity(&self) -> usize {
        self.required
    }

    /// Returns the remaining length in the provided buffer at the time of encoding.
    pub fn remaining(&self) -> usize {
        self.remaining
    }
}

impl fmt::Display for EncodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(error::Error::description(self))?;
        write!(
            f,
            " (required: {}, remaining: {})",
            self.required, self.remaining
        )
    }
}

impl error::Error for EncodeError {
    fn description(&self) -> &str {
        "failed to encode Protobuf message: insufficient buffer capacity"
    }
}

impl From<EncodeError> for io::Error {
    fn from(error: EncodeError) -> io::Error {
        io::Error::new(io::ErrorKind::InvalidInput, error)
    }
}
<commit_msg>remove deprecated error description methods<commit_after>//! Protobuf encoding and decoding errors.

use std::borrow::Cow;
use std::error;
use std::fmt;
use std::io;

/// A Protobuf message decoding error.
///
/// `DecodeError` indicates that the input buffer does not caontain a valid
/// Protobuf message. The error details should be considered 'best effort': in
/// general it is not possible to exactly pinpoint why data is malformed.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DecodeError {
    /// A 'best effort' root cause description.
    description: Cow<'static, str>,
    /// A stack of (message, field) name pairs, which identify the specific
    /// message type and field where decoding failed. The stack contains an
    /// entry per level of nesting.
    stack: Vec<(&'static str, &'static str)>,
}

impl DecodeError {
    /// Creates a new `DecodeError` with a 'best effort' root cause description.
    ///
    /// Meant to be used only by `Message` implementations.
    #[doc(hidden)]
    pub fn new<S>(description: S) -> DecodeError
    where
        S: Into<Cow<'static, str>>,
    {
        DecodeError {
            description: description.into(),
            stack: Vec::new(),
        }
    }

    /// Pushes a (message, field) name location pair on to the location stack.
    ///
    /// Meant to be used only by `Message` implementations.
    #[doc(hidden)]
    pub fn push(&mut self, message: &'static str, field: &'static str) {
        self.stack.push((message, field));
    }
}

impl fmt::Display for DecodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("failed to decode Protobuf message: ")?;
        for &(message, field) in &self.stack {
            write!(f, "{}.{}: ", message, field)?;
        }
        f.write_str(&self.description)
    }
}

impl error::Error for DecodeError {}

impl From<DecodeError> for io::Error {
    fn from(error: DecodeError) -> io::Error {
        io::Error::new(io::ErrorKind::InvalidData, error)
    }
}

/// A Protobuf message encoding error.
///
/// `EncodeError` always indicates that a message failed to encode because the
/// provided buffer had insufficient capacity. Message encoding is otherwise
/// infallible.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct EncodeError {
    required: usize,
    remaining: usize,
}

impl EncodeError {
    /// Creates a new `EncodeError`.
    pub(crate) fn new(required: usize, remaining: usize) -> EncodeError {
        EncodeError {
            required,
            remaining,
        }
    }

    /// Returns the required buffer capacity to encode the message.
    pub fn required_capacity(&self) -> usize {
        self.required
    }

    /// Returns the remaining length in the provided buffer at the time of encoding.
    pub fn remaining(&self) -> usize {
        self.remaining
    }
}

impl fmt::Display for EncodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "failed to encode Protobuf messsage; insufficient buffer capacity (required: {}, remaining: {})",
            self.required, self.remaining
        )
    }
}

impl error::Error for EncodeError {}

impl From<EncodeError> for io::Error {
    fn from(error: EncodeError) -> io::Error {
        io::Error::new(io::ErrorKind::InvalidInput, error)
    }
}
<|endoftext|>


<commit_before>//! A Status encapsulates the result of an operation.
//!
//! It may indicate success,
//! or it may indicate an error with an associated error message.
//!
//! Multiple threads can invoke const methods on a Status without
//! external synchronization, but if any of the threads may call a
//! non-const method, all threads accessing the same Status must use
//! external synchronization.

use std::fmt;
use std::mem;
use std::ffi::CStr;
use std::str;

use rocks_sys as ll;

use to_raw::{ToRaw, FromRaw};

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Code {
    _Ok = 0,                    // will never be available
    NotFound = 1,
    Corruption = 2,
    NotSupported = 3,
    InvalidArgument = 4,
    IOError = 5,
    MergeInProgress = 6,
    Incomplete = 7,
    ShutdownInProgress = 8,
    TimedOut = 9,
    Aborted = 10,
    Busy = 11,
    Expired = 12,
    TryAgain = 13,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum SubCode {
    None = 0,
    MutexTimeout = 1,
    LockTimeout = 2,
    LockLimit = 3,
    NoSpace = 4,
    Deadlock = 5,
    StaleFile = 6,
    MemoryLimit = 7,
}

#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Status {
    raw: *mut ll::rocks_status_t,
}

impl ToRaw<ll::rocks_status_t> for Status {
    fn raw(&self) -> *mut ll::rocks_status_t {
        self.raw
    }
}

impl FromRaw<ll::rocks_status_t> for Result<(), Status> {
    unsafe fn from_ll(raw: *mut ll::rocks_status_t) -> Result<(), Status> {
        if raw.is_null() || ll::rocks_status_code(raw) == 0 {
            Ok(())
        } else {
            Err(Status {
                raw: raw,
            })
        }
    }
}

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

impl Status {
    pub fn is_not_found(&self) -> bool {
        self.code() == Code::NotFound
    }

    pub fn code(&self) -> Code {
        unsafe {
            mem::transmute(ll::rocks_status_code(self.raw))
        }
    }

    pub fn subcode(&self) -> SubCode {
        unsafe {
            mem::transmute(ll::rocks_status_subcode(self.raw))
        }
    }

    /// string indicating the message of the Status
    pub fn state(&self) -> &str {
        unsafe {
            let ptr = ll::rocks_status_get_state(self.raw);
            CStr::from_ptr(ptr).to_str().unwrap_or("")
        }
    }

    pub fn from_ll(raw: *mut ll::rocks_status_t) -> Result<(), Status> {
        if raw.is_null() || unsafe { ll::rocks_status_code(raw) } == 0 {
            Ok(())
        } else {
            Err(Status {
                raw: raw,
            })
        }
    }
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Status({:?}, {:?}, {})", self.code(), self.subcode(), self.state())
    }
}

impl fmt::Debug for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}({:?}, {:?})", self.code(), self.subcode(), self.state())
    }
}

<commit_msg>error: fix null pointer bug<commit_after>//! A Status encapsulates the result of an operation.
//!
//! It may indicate success,
//! or it may indicate an error with an associated error message.
//!
//! Multiple threads can invoke const methods on a Status without
//! external synchronization, but if any of the threads may call a
//! non-const method, all threads accessing the same Status must use
//! external synchronization.

use std::fmt;
use std::mem;
use std::ffi::CStr;
use std::str;

use rocks_sys as ll;

use to_raw::{ToRaw, FromRaw};

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Code {
    _Ok = 0,                    // will never be available
    NotFound = 1,
    Corruption = 2,
    NotSupported = 3,
    InvalidArgument = 4,
    IOError = 5,
    MergeInProgress = 6,
    Incomplete = 7,
    ShutdownInProgress = 8,
    TimedOut = 9,
    Aborted = 10,
    Busy = 11,
    Expired = 12,
    TryAgain = 13,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum SubCode {
    None = 0,
    MutexTimeout = 1,
    LockTimeout = 2,
    LockLimit = 3,
    NoSpace = 4,
    Deadlock = 5,
    StaleFile = 6,
    MemoryLimit = 7,
}

#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Status {
    raw: *mut ll::rocks_status_t,
}

impl ToRaw<ll::rocks_status_t> for Status {
    fn raw(&self) -> *mut ll::rocks_status_t {
        self.raw
    }
}

impl FromRaw<ll::rocks_status_t> for Result<(), Status> {
    unsafe fn from_ll(raw: *mut ll::rocks_status_t) -> Result<(), Status> {
        if raw.is_null() || ll::rocks_status_code(raw) == 0 {
            Ok(())
        } else {
            Err(Status {
                raw: raw,
            })
        }
    }
}

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

impl Status {
    pub fn is_not_found(&self) -> bool {
        self.code() == Code::NotFound
    }

    pub fn code(&self) -> Code {
        unsafe {
            mem::transmute(ll::rocks_status_code(self.raw))
        }
    }

    pub fn subcode(&self) -> SubCode {
        unsafe {
            mem::transmute(ll::rocks_status_subcode(self.raw))
        }
    }

    /// string indicating the message of the Status
    pub fn state(&self) -> &str {
        unsafe {
            let ptr = ll::rocks_status_get_state(self.raw);
            ptr.as_ref().and_then(|s| CStr::from_ptr(s).to_str().ok()).unwrap_or("")
        }
    }

    pub fn from_ll(raw: *mut ll::rocks_status_t) -> Result<(), Status> {
        if raw.is_null() || unsafe { ll::rocks_status_code(raw) } == 0 {
            Ok(())
        } else {
            Err(Status {
                raw: raw,
            })
        }
    }
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Status({:?}, {:?}, {})", self.code(), self.subcode(), self.state())
    }
}

impl fmt::Debug for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}({:?}, {:?})", self.code(), self.subcode(), self.state())
    }
}

<|endoftext|>


<commit_before>extern crate num;

/// The default epsilon value used for floating point comparisons.
pub static EPSILON:f64 = 1.0E-8;

pub trait Float: num::Float{
  /// Tells if the two floating-point values `self` and `y` are considered equal
  /// within the specified `absolute == relative` tolerence value.
  ///
  /// The method of comparison used is described at:
  ///
  /// http://realtimecollisiondetection.net/blog/?p=89
  ///
  /// Also consider using the `equal` method.
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert!(0.9.almost_equal(1.0, 0.1000001));
  /// assert!(0.9.almost_equal(1.0, 0.1));
  /// ```
  fn almost_equal(self, y: Self, abs_tol: Self) -> bool;

  /// equal is short-hand for `self.almost_equal(y, fiz_math::EPSILON)`.
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert!(1.00000001.equal(1.0));
  /// assert!(1.0.equal(1.0));
  /// assert!(!0.9.equal(1.0));
  /// ```
  fn equal(self, y: Self) -> bool;

  /// clamp returns the value v clamped to the range of [min, max].
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert_eq!(1.1.clamp(0.0, 1.0), 1.0);
  /// assert_eq!(2.0.clamp(0.0, 1.5), 1.5);
  /// ```
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert_eq!((-1.0).clamp(0.0, 1.0), 0.0);
  /// assert_eq!(0.0.clamp(0.1, 1.0), 0.1);
  /// ```
  fn clamp(self, min: Self, max: Self) -> Self;
}

impl<T: num::Float> Float for T {
  fn almost_equal(self, y: T, abs_tol: T) -> bool {
    let r = T::from(1.0).unwrap().max(self.abs().max(y.abs()));
    self == y || ((self-y).abs() <= abs_tol * r)
  }

  fn equal(self, y: T) -> bool {
    self.almost_equal(y, T::from(EPSILON).unwrap())
  }

  fn clamp(self, min: Self, max: Self) -> Self {
    self.min(max).max(min)
  }
}
<commit_msg>add conversion methods for radians and degrees to the Float trait<commit_after>extern crate num;

use std::f64;

/// The default epsilon value used for floating point comparisons.
pub static EPSILON:f64 = 1.0E-8;

pub trait Float: num::Float{
  /// Tells if the two floating-point values `self` and `y` are considered equal
  /// within the specified `absolute == relative` tolerence value.
  ///
  /// The method of comparison used is described at:
  ///
  /// http://realtimecollisiondetection.net/blog/?p=89
  ///
  /// Also consider using the `equal` method.
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert!(0.9.almost_equal(1.0, 0.1000001));
  /// assert!(0.9.almost_equal(1.0, 0.1));
  /// ```
  fn almost_equal(self, y: Self, abs_tol: Self) -> bool;

  /// equal is short-hand for `self.almost_equal(y, fiz_math::EPSILON)`.
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert!(1.00000001.equal(1.0));
  /// assert!(1.0.equal(1.0));
  /// assert!(!0.9.equal(1.0));
  /// ```
  fn equal(self, y: Self) -> bool;

  /// clamp returns the value v clamped to the range of [min, max].
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert_eq!(1.1.clamp(0.0, 1.0), 1.0);
  /// assert_eq!(2.0.clamp(0.0, 1.5), 1.5);
  /// ```
  ///
  /// ```
  /// use fiz_math::Float;
  ///
  /// assert_eq!((-1.0).clamp(0.0, 1.0), 0.0);
  /// assert_eq!(0.0.clamp(0.1, 1.0), 0.1);
  /// ```
  fn clamp(self, min: Self, max: Self) -> Self;

  /// radians converts the value `self` (presumed to be in degrees) to radians.
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  /// use std::f64::consts::PI;
  ///
  /// assert_eq!(180.0.radians(), PI);
  /// assert_eq!(360.0.radians(), PI * 2.0);
  /// ```
  fn radians(self) -> Self;

  /// degrees converts the value `self` (presumed to be in radians) to degrees.
  ///
  /// # Examples
  ///
  /// ```
  /// use fiz_math::Float;
  /// use std::f64::consts::PI;
  ///
  /// assert_eq!(PI.degrees(), 180.0);
  /// assert_eq!((PI*2.0).degrees(), 360.0);
  /// ```
  fn degrees(self) -> Self;
}

impl<T: num::Float> Float for T {
  fn almost_equal(self, y: T, abs_tol: T) -> bool {
    let r = T::from(1.0).unwrap().max(self.abs().max(y.abs()));
    self == y || ((self-y).abs() <= abs_tol * r)
  }

  fn equal(self, y: T) -> bool {
    self.almost_equal(y, T::from(EPSILON).unwrap())
  }

  fn clamp(self, min: Self, max: Self) -> Self {
    self.min(max).max(min)
  }

  fn radians(self) -> Self {
    T::from(f64::consts::PI).unwrap() * self / T::from(180.0).unwrap()
  }

  fn degrees(self) -> Self {
    self * T::from(180.0 / f64::consts::PI).unwrap()
  }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::SystemTime;

use anyhow::{bail, Result};
use chrono::prelude::*;
use log::{debug, error, info};
use serde_derive::{Deserialize, Serialize};

use crate::settings::Settings;
use crate::task::{Task, TaskResult, TaskStatus};

pub type SharedState = Arc<Mutex<State>>;

/// Represents the current status of a group.
/// Each group acts as a queue and can be managed individually.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum GroupStatus {
    Running,
    Paused,
}

/// This is the full representation of the current state of the Pueue daemon.
///
/// This includes
/// - The currently used settings.
/// - The full task list
/// - The current status of all tasks
/// - All known groups.
///
/// However, the State does NOT include:
/// - Information about child processes
/// - Handles to child processes
///
/// That information is saved in the daemon's TaskHandler.
///
/// Most functions implemented on the state shouldn't be used by third party software.
/// The daemon is constantly changing and persisting the state. \
/// Any changes applied to a state and saved to disk, will most likely be overwritten
/// after a short time.
///
///
/// The daemon uses the state as a piece of shared memory between it's threads.
/// It's wrapped in a MutexGuard, which allows us to guarantee sequential access to any crucial
/// information, such as status changes and incoming commands by the client.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct State {
    /// The current settings used by the daemon.
    pub settings: Settings,
    /// All tasks currently managed by the daemon.
    pub tasks: BTreeMap<usize, Task>,
    /// All groups
    pub groups: BTreeMap<String, GroupStatus>,
    config_path: Option<PathBuf>,
}

impl State {
    /// Create a new default state.
    pub fn new(settings: &Settings, config_path: Option<PathBuf>) -> State {
        // Create a default group state.
        let mut groups = BTreeMap::new();
        for group in settings.daemon.groups.keys() {
            groups.insert(group.into(), GroupStatus::Running);
        }

        let mut state = State {
            settings: settings.clone(),
            tasks: BTreeMap::new(),
            groups,
            config_path,
        };
        state.create_group("default");
        state
    }

    /// Add a new task
    pub fn add_task(&mut self, mut task: Task) -> usize {
        let next_id = match self.tasks.keys().max() {
            None => 0,
            Some(id) => id + 1,
        };
        task.id = next_id;
        self.tasks.insert(next_id, task);
        self.save();

        next_id
    }

    /// A small helper to change the status of a specific task.
    pub fn change_status(&mut self, id: usize, new_status: TaskStatus) {
        if let Some(ref mut task) = self.tasks.get_mut(&id) {
            task.status = new_status;
            self.save();
        };
    }

    /// Set the time a specific task should be enqueued at.
    pub fn set_enqueue_at(&mut self, id: usize, enqueue_at: Option<DateTime<Local>>) {
        if let Some(ref mut task) = self.tasks.get_mut(&id) {
            task.enqueue_at = enqueue_at;
        }
    }

    /// Add a new group to the daemon. \
    /// This also check if the given group already exists.
    /// Create a state.group entry and a settings.group entry, if it doesn't.
    pub fn create_group(&mut self, group: &str) {
        if self.settings.daemon.groups.get(group).is_none() {
            self.settings.daemon.groups.insert(group.into(), 1);
        }
        if self.groups.get(group).is_none() {
            self.groups.insert(group.into(), GroupStatus::Running);
        }
    }

    /// Remove a group.
    /// This also iterates through all tasks and sets any tasks' group
    /// to the `default` group if it matches the deleted group.
    pub fn remove_group(&mut self, group: &str) -> Result<()> {
        if group.eq("default") {
            bail!("You cannot remove the default group.");
        }

        self.settings.daemon.groups.remove(group);
        self.groups.remove(group);

        // Reset all tasks with removed group to the default.
        for (_, task) in self.tasks.iter_mut() {
            if task.group.eq(group) {
                task.set_default_group();
            }
        }

        self.save();
        self.save_settings()
    }

    /// Set the group status (running/paused) for all groups including the default queue.
    pub fn set_status_for_all_groups(&mut self, status: GroupStatus) {
        let keys = self.groups.keys().cloned().collect::<Vec<String>>();
        for key in keys {
            self.groups.insert(key, status.clone());
        }
        self.save()
    }

    /// Get all ids of task with a specific state inside a specific group.
    pub fn task_ids_in_group_with_stati(&self, group: &str, stati: Vec<TaskStatus>) -> Vec<usize> {
        self.tasks
            .iter()
            .filter(|(_, task)| stati.contains(&task.status))
            .filter(|(_, task)| task.group.eq(group))
            .map(|(id, _)| *id)
            .collect()
    }

    /// Get all ids of task inside a specific group.
    pub fn task_ids_in_group(&self, group: &str) -> Vec<usize> {
        self.tasks
            .iter()
            .filter(|(_, task)| task.group.eq(group))
            .map(|(id, _)| *id)
            .collect()
    }

    /// This checks, whether some tasks have one of the specified statuses. \
    /// The first result is the list of task_ids that match these statuses. \
    /// The second result is the list of task_ids that don't match these statuses. \
    ///
    /// By default, this checks all tasks in the current state. If a list of task_ids is
    /// provided as the third parameter, only those tasks will be checked.
    pub fn tasks_in_statuses(
        &self,
        statuses: Vec<TaskStatus>,
        task_ids: Option<Vec<usize>>,
    ) -> (Vec<usize>, Vec<usize>) {
        let task_ids = match task_ids {
            Some(ids) => ids,
            None => self.tasks.keys().cloned().collect(),
        };

        let mut matching = Vec::new();
        let mut mismatching = Vec::new();

        // Filter all task id's that match the provided statuses.
        for task_id in task_ids.iter() {
            // Check whether the task exists and save all non-existing task ids.
            match self.tasks.get(&task_id) {
                None => {
                    mismatching.push(*task_id);
                    continue;
                }
                Some(task) => {
                    // Check whether the task status matches the specified statuses.
                    if statuses.contains(&task.status) {
                        matching.push(*task_id);
                    } else {
                        mismatching.push(*task_id);
                    }
                }
            };
        }

        (matching, mismatching)
    }

    /// Check if a task can be deleted. \
    /// We have to check all dependant tasks, that haven't finished yet.
    /// This is necessary to prevent deletion of tasks which are specified as a dependency.
    ///
    /// `to_delete` A list of task ids, which should also be deleted.
    ///             This allows to remove dependency tasks as well as their dependants.
    pub fn is_task_removable(&self, task_id: &usize, to_delete: &[usize]) -> bool {
        // Get all task ids of any dependant tasks.
        let dependants: Vec<usize> = self
            .tasks
            .iter()
            .filter(|(_, task)| {
                task.dependencies.contains(&task_id) && task.status != TaskStatus::Done
            })
            .map(|(_, task)| task.id)
            .collect();

        if dependants.is_empty() {
            return true;
        }

        // Check if the dependants are supposed to be deleted as well.
        let should_delete_dependants = dependants.iter().all(|task_id| to_delete.contains(task_id));
        if !should_delete_dependants {
            return false;
        }

        // Lastly, do a recursive check if there are any dependants on our dependants
        dependants
            .iter()
            .all(|task_id| self.is_task_removable(task_id, to_delete))
    }

    /// A small helper for handling task failures. \
    /// Users can specify whether they want to pause the task's group or the
    /// whole daemon on a failed tasks. This function wraps that logic and decides if anything should be
    /// paused depending on the current settings.
    ///
    /// `group` should be the name of the failed task.
    pub fn handle_task_failure(&mut self, group: String) {
        if self.settings.daemon.pause_group_on_failure {
            self.groups.insert(group, GroupStatus::Paused);
        } else if self.settings.daemon.pause_all_on_failure {
            self.set_status_for_all_groups(GroupStatus::Paused);
        }
    }

    /// Do a full reset of the state.
    /// This doesn't reset any processes!
    pub fn reset(&mut self) {
        self.backup();
        self.tasks = BTreeMap::new();
        self.set_status_for_all_groups(GroupStatus::Running);
    }

    /// A small convenience wrapper for saving the settings to a file.
    pub fn save_settings(&self) -> Result<()> {
        self.settings.save(&self.config_path)
    }

    /// Convenience wrapper around save_to_file.
    pub fn save(&self) {
        self.save_to_file(false);
    }

    /// Save the current current state in a file with a timestamp.
    /// At the same time remove old state logs from the log directory.
    /// This function is called, when large changes to the state are applied, e.g. clean/reset.
    pub fn backup(&self) {
        self.save_to_file(true);
        if let Err(error) = self.rotate() {
            error!("Failed to rotate files: {:?}", error);
        };
    }

    /// Save the current state to disk. \
    /// We do this to restore in case of a crash. \
    /// If log == true, the file will be saved with a time stamp.
    ///
    /// In comparison to the daemon -> client communication, the state is saved
    /// as JSON for better readability and debug purposes.
    fn save_to_file(&self, log: bool) {
        let serialized = serde_json::to_string(&self);
        if let Err(error) = serialized {
            error!("Failed to serialize state: {:?}", error);
            return;
        }

        let serialized = serialized.unwrap();
        let path = self.settings.shared.pueue_directory();
        let (temp, real) = if log {
            let path = path.join("log");
            let now: DateTime<Utc> = Utc::now();
            let time = now.format("%Y-%m-%d_%H-%M-%S");
            (
                path.join(format!("{}_state.json.partial", time)),
                path.join(format!("{}_state.json", time)),
            )
        } else {
            (path.join("state.json.partial"), path.join("state.json"))
        };

        // Write to temporary log file first, to prevent loss due to crashes.
        if let Err(error) = fs::write(&temp, serialized) {
            error!(
                "Failed to write log to directory. File permissions? Error: {:?}",
                error
            );
            return;
        }

        // Overwrite the original with the temp file, if everything went fine.
        if let Err(error) = fs::rename(&temp, &real) {
            error!(
                "Failed to overwrite old log file. File permissions? Error: {:?}",
                error
            );
            return;
        }

        if log {
            debug!("State backup created at: {:?}", real);
        } else {
            debug!("State saved at: {:?}", real);
        }
    }

    /// Restore the last state from a previous session. \
    /// The state is stored as json in the log directory.
    pub fn restore(&mut self) {
        let path = Path::new(&self.settings.shared.pueue_directory()).join("state.json");

        // Ignore if the file doesn't exist. It doesn't have to.
        if !path.exists() {
            info!(
                "Couldn't find state from previous session at location: {:?}",
                path
            );
            return;
        }
        info!("Start restoring state");

        // Try to load the file.
        let data = fs::read_to_string(&path);
        if let Err(error) = data {
            error!("Failed to read previous state log: {:?}", error);
            return;
        }
        let data = data.unwrap();

        // Try to deserialize the state file.
        let deserialized: Result<State, serde_json::error::Error> = serde_json::from_str(&data);
        if let Err(error) = deserialized {
            error!("Failed to deserialize previous state log: {:?}", error);
            return;
        }
        let mut state = deserialized.unwrap();

        // Copy group statuses from the previous state.
        for (group, _) in state.settings.daemon.groups {
            if let Some(status) = state.groups.get(&group) {
                self.groups.insert(group.clone(), status.clone());
            }
        }

        // Restore all tasks.
        // While restoring the tasks, check for any invalid/broken stati.
        for (task_id, task) in state.tasks.iter_mut() {
            // Handle ungraceful shutdowns while executing tasks.
            if task.status == TaskStatus::Running || task.status == TaskStatus::Paused {
                info!(
                    "Setting task {} with previous status {:?} to new status {:?}",
                    task.id,
                    task.status,
                    TaskResult::Killed
                );
                task.status = TaskStatus::Done;
                task.result = Some(TaskResult::Killed);
            }

            // Handle crash during editing of the task command.
            if task.status == TaskStatus::Locked {
                task.status = TaskStatus::Stashed;
            }

            // Go trough all tasks and set all groups that are no longer
            // listed in the configuration file to the default.
            if !self.settings.daemon.groups.contains_key(&task.group) {
                task.set_default_group();
            }

            // If there are any queued tasks, pause the group.
            // This should prevent any unwanted execution of tasks due to a system crash.
            if task.status == TaskStatus::Queued {
                info!(
                    "Pausing group {} to prevent unwanted execution of previous tasks",
                    &task.group
                );
                self.groups.insert(task.group.clone(), GroupStatus::Paused);
            }

            self.tasks.insert(*task_id, task.clone());
        }
    }

    /// Remove old logs that aren't needed any longer.
    fn rotate(&self) -> Result<()> {
        let path = self.settings.shared.pueue_directory().join("log");

        // Get all log files in the directory with their respective system time.
        let mut entries: BTreeMap<SystemTime, PathBuf> = BTreeMap::new();
        let mut directory_list = fs::read_dir(path)?;
        while let Some(Ok(entry)) = directory_list.next() {
            let path = entry.path();

            let metadata = entry.metadata()?;
            let time = metadata.modified()?;
            entries.insert(time, path);
        }

        // Remove all files above the threshold.
        // Old files are removed first (implictly by the BTree order).
        let mut number_entries = entries.len();
        let mut iter = entries.iter();
        while number_entries > 10 {
            if let Some((_, path)) = iter.next() {
                fs::remove_file(path)?;
                number_entries -= 1;
            }
        }

        Ok(())
    }
}
<commit_msg>Better debug message for failed deserialization<commit_after>use std::collections::BTreeMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use std::time::SystemTime;

use anyhow::{bail, Result};
use chrono::prelude::*;
use log::{debug, error, info, warn};
use serde_derive::{Deserialize, Serialize};

use crate::settings::Settings;
use crate::task::{Task, TaskResult, TaskStatus};

pub type SharedState = Arc<Mutex<State>>;

/// Represents the current status of a group.
/// Each group acts as a queue and can be managed individually.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum GroupStatus {
    Running,
    Paused,
}

/// This is the full representation of the current state of the Pueue daemon.
///
/// This includes
/// - The currently used settings.
/// - The full task list
/// - The current status of all tasks
/// - All known groups.
///
/// However, the State does NOT include:
/// - Information about child processes
/// - Handles to child processes
///
/// That information is saved in the daemon's TaskHandler.
///
/// Most functions implemented on the state shouldn't be used by third party software.
/// The daemon is constantly changing and persisting the state. \
/// Any changes applied to a state and saved to disk, will most likely be overwritten
/// after a short time.
///
///
/// The daemon uses the state as a piece of shared memory between it's threads.
/// It's wrapped in a MutexGuard, which allows us to guarantee sequential access to any crucial
/// information, such as status changes and incoming commands by the client.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct State {
    /// The current settings used by the daemon.
    pub settings: Settings,
    /// All tasks currently managed by the daemon.
    pub tasks: BTreeMap<usize, Task>,
    /// All groups
    pub groups: BTreeMap<String, GroupStatus>,
    config_path: Option<PathBuf>,
}

impl State {
    /// Create a new default state.
    pub fn new(settings: &Settings, config_path: Option<PathBuf>) -> State {
        // Create a default group state.
        let mut groups = BTreeMap::new();
        for group in settings.daemon.groups.keys() {
            groups.insert(group.into(), GroupStatus::Running);
        }

        let mut state = State {
            settings: settings.clone(),
            tasks: BTreeMap::new(),
            groups,
            config_path,
        };
        state.create_group("default");
        state
    }

    /// Add a new task
    pub fn add_task(&mut self, mut task: Task) -> usize {
        let next_id = match self.tasks.keys().max() {
            None => 0,
            Some(id) => id + 1,
        };
        task.id = next_id;
        self.tasks.insert(next_id, task);
        self.save();

        next_id
    }

    /// A small helper to change the status of a specific task.
    pub fn change_status(&mut self, id: usize, new_status: TaskStatus) {
        if let Some(ref mut task) = self.tasks.get_mut(&id) {
            task.status = new_status;
            self.save();
        };
    }

    /// Set the time a specific task should be enqueued at.
    pub fn set_enqueue_at(&mut self, id: usize, enqueue_at: Option<DateTime<Local>>) {
        if let Some(ref mut task) = self.tasks.get_mut(&id) {
            task.enqueue_at = enqueue_at;
        }
    }

    /// Add a new group to the daemon. \
    /// This also check if the given group already exists.
    /// Create a state.group entry and a settings.group entry, if it doesn't.
    pub fn create_group(&mut self, group: &str) {
        if self.settings.daemon.groups.get(group).is_none() {
            self.settings.daemon.groups.insert(group.into(), 1);
        }
        if self.groups.get(group).is_none() {
            self.groups.insert(group.into(), GroupStatus::Running);
        }
    }

    /// Remove a group.
    /// This also iterates through all tasks and sets any tasks' group
    /// to the `default` group if it matches the deleted group.
    pub fn remove_group(&mut self, group: &str) -> Result<()> {
        if group.eq("default") {
            bail!("You cannot remove the default group.");
        }

        self.settings.daemon.groups.remove(group);
        self.groups.remove(group);

        // Reset all tasks with removed group to the default.
        for (_, task) in self.tasks.iter_mut() {
            if task.group.eq(group) {
                task.set_default_group();
            }
        }

        self.save();
        self.save_settings()
    }

    /// Set the group status (running/paused) for all groups including the default queue.
    pub fn set_status_for_all_groups(&mut self, status: GroupStatus) {
        let keys = self.groups.keys().cloned().collect::<Vec<String>>();
        for key in keys {
            self.groups.insert(key, status.clone());
        }
        self.save()
    }

    /// Get all ids of task with a specific state inside a specific group.
    pub fn task_ids_in_group_with_stati(&self, group: &str, stati: Vec<TaskStatus>) -> Vec<usize> {
        self.tasks
            .iter()
            .filter(|(_, task)| stati.contains(&task.status))
            .filter(|(_, task)| task.group.eq(group))
            .map(|(id, _)| *id)
            .collect()
    }

    /// Get all ids of task inside a specific group.
    pub fn task_ids_in_group(&self, group: &str) -> Vec<usize> {
        self.tasks
            .iter()
            .filter(|(_, task)| task.group.eq(group))
            .map(|(id, _)| *id)
            .collect()
    }

    /// This checks, whether some tasks have one of the specified statuses. \
    /// The first result is the list of task_ids that match these statuses. \
    /// The second result is the list of task_ids that don't match these statuses. \
    ///
    /// By default, this checks all tasks in the current state. If a list of task_ids is
    /// provided as the third parameter, only those tasks will be checked.
    pub fn tasks_in_statuses(
        &self,
        statuses: Vec<TaskStatus>,
        task_ids: Option<Vec<usize>>,
    ) -> (Vec<usize>, Vec<usize>) {
        let task_ids = match task_ids {
            Some(ids) => ids,
            None => self.tasks.keys().cloned().collect(),
        };

        let mut matching = Vec::new();
        let mut mismatching = Vec::new();

        // Filter all task id's that match the provided statuses.
        for task_id in task_ids.iter() {
            // Check whether the task exists and save all non-existing task ids.
            match self.tasks.get(&task_id) {
                None => {
                    mismatching.push(*task_id);
                    continue;
                }
                Some(task) => {
                    // Check whether the task status matches the specified statuses.
                    if statuses.contains(&task.status) {
                        matching.push(*task_id);
                    } else {
                        mismatching.push(*task_id);
                    }
                }
            };
        }

        (matching, mismatching)
    }

    /// Check if a task can be deleted. \
    /// We have to check all dependant tasks, that haven't finished yet.
    /// This is necessary to prevent deletion of tasks which are specified as a dependency.
    ///
    /// `to_delete` A list of task ids, which should also be deleted.
    ///             This allows to remove dependency tasks as well as their dependants.
    pub fn is_task_removable(&self, task_id: &usize, to_delete: &[usize]) -> bool {
        // Get all task ids of any dependant tasks.
        let dependants: Vec<usize> = self
            .tasks
            .iter()
            .filter(|(_, task)| {
                task.dependencies.contains(&task_id) && task.status != TaskStatus::Done
            })
            .map(|(_, task)| task.id)
            .collect();

        if dependants.is_empty() {
            return true;
        }

        // Check if the dependants are supposed to be deleted as well.
        let should_delete_dependants = dependants.iter().all(|task_id| to_delete.contains(task_id));
        if !should_delete_dependants {
            return false;
        }

        // Lastly, do a recursive check if there are any dependants on our dependants
        dependants
            .iter()
            .all(|task_id| self.is_task_removable(task_id, to_delete))
    }

    /// A small helper for handling task failures. \
    /// Users can specify whether they want to pause the task's group or the
    /// whole daemon on a failed tasks. This function wraps that logic and decides if anything should be
    /// paused depending on the current settings.
    ///
    /// `group` should be the name of the failed task.
    pub fn handle_task_failure(&mut self, group: String) {
        if self.settings.daemon.pause_group_on_failure {
            self.groups.insert(group, GroupStatus::Paused);
        } else if self.settings.daemon.pause_all_on_failure {
            self.set_status_for_all_groups(GroupStatus::Paused);
        }
    }

    /// Do a full reset of the state.
    /// This doesn't reset any processes!
    pub fn reset(&mut self) {
        self.backup();
        self.tasks = BTreeMap::new();
        self.set_status_for_all_groups(GroupStatus::Running);
    }

    /// A small convenience wrapper for saving the settings to a file.
    pub fn save_settings(&self) -> Result<()> {
        self.settings.save(&self.config_path)
    }

    /// Convenience wrapper around save_to_file.
    pub fn save(&self) {
        self.save_to_file(false);
    }

    /// Save the current current state in a file with a timestamp.
    /// At the same time remove old state logs from the log directory.
    /// This function is called, when large changes to the state are applied, e.g. clean/reset.
    pub fn backup(&self) {
        self.save_to_file(true);
        if let Err(error) = self.rotate() {
            error!("Failed to rotate files: {:?}", error);
        };
    }

    /// Save the current state to disk. \
    /// We do this to restore in case of a crash. \
    /// If log == true, the file will be saved with a time stamp.
    ///
    /// In comparison to the daemon -> client communication, the state is saved
    /// as JSON for better readability and debug purposes.
    fn save_to_file(&self, log: bool) {
        let serialized = serde_json::to_string(&self);
        if let Err(error) = serialized {
            error!("Failed to serialize state: {:?}", error);
            return;
        }

        let serialized = serialized.unwrap();
        let path = self.settings.shared.pueue_directory();
        let (temp, real) = if log {
            let path = path.join("log");
            let now: DateTime<Utc> = Utc::now();
            let time = now.format("%Y-%m-%d_%H-%M-%S");
            (
                path.join(format!("{}_state.json.partial", time)),
                path.join(format!("{}_state.json", time)),
            )
        } else {
            (path.join("state.json.partial"), path.join("state.json"))
        };

        // Write to temporary log file first, to prevent loss due to crashes.
        if let Err(error) = fs::write(&temp, serialized) {
            error!(
                "Failed to write log to directory. File permissions? Error: {:?}",
                error
            );
            return;
        }

        // Overwrite the original with the temp file, if everything went fine.
        if let Err(error) = fs::rename(&temp, &real) {
            error!(
                "Failed to overwrite old log file. File permissions? Error: {:?}",
                error
            );
            return;
        }

        if log {
            debug!("State backup created at: {:?}", real);
        } else {
            debug!("State saved at: {:?}", real);
        }
    }

    /// Restore the last state from a previous session. \
    /// The state is stored as json in the log directory.
    pub fn restore(&mut self) {
        let path = Path::new(&self.settings.shared.pueue_directory()).join("state.json");

        // Ignore if the file doesn't exist. It doesn't have to.
        if !path.exists() {
            info!(
                "Couldn't find state from previous session at location: {:?}",
                path
            );
            return;
        }
        info!("Start restoring state");

        // Try to load the file.
        let data = fs::read_to_string(&path);
        if let Err(error) = data {
            error!("Failed to read previous state log: {:?}", error);
            return;
        }
        let data = data.unwrap();

        // Try to deserialize the state file.
        let deserialized: Result<State, serde_json::error::Error> = serde_json::from_str(&data);
        if let Err(error) = deserialized {
            warn!("Failed to deserialize previous state log: {:?}", error);
            warn!("Discarding previous state. This is probably due to a new version.");
            return;
        }
        let mut state = deserialized.unwrap();

        // Copy group statuses from the previous state.
        for (group, _) in state.settings.daemon.groups {
            if let Some(status) = state.groups.get(&group) {
                self.groups.insert(group.clone(), status.clone());
            }
        }

        // Restore all tasks.
        // While restoring the tasks, check for any invalid/broken stati.
        for (task_id, task) in state.tasks.iter_mut() {
            // Handle ungraceful shutdowns while executing tasks.
            if task.status == TaskStatus::Running || task.status == TaskStatus::Paused {
                info!(
                    "Setting task {} with previous status {:?} to new status {:?}",
                    task.id,
                    task.status,
                    TaskResult::Killed
                );
                task.status = TaskStatus::Done;
                task.result = Some(TaskResult::Killed);
            }

            // Handle crash during editing of the task command.
            if task.status == TaskStatus::Locked {
                task.status = TaskStatus::Stashed;
            }

            // Go trough all tasks and set all groups that are no longer
            // listed in the configuration file to the default.
            if !self.settings.daemon.groups.contains_key(&task.group) {
                task.set_default_group();
            }

            // If there are any queued tasks, pause the group.
            // This should prevent any unwanted execution of tasks due to a system crash.
            if task.status == TaskStatus::Queued {
                info!(
                    "Pausing group {} to prevent unwanted execution of previous tasks",
                    &task.group
                );
                self.groups.insert(task.group.clone(), GroupStatus::Paused);
            }

            self.tasks.insert(*task_id, task.clone());
        }
    }

    /// Remove old logs that aren't needed any longer.
    fn rotate(&self) -> Result<()> {
        let path = self.settings.shared.pueue_directory().join("log");

        // Get all log files in the directory with their respective system time.
        let mut entries: BTreeMap<SystemTime, PathBuf> = BTreeMap::new();
        let mut directory_list = fs::read_dir(path)?;
        while let Some(Ok(entry)) = directory_list.next() {
            let path = entry.path();

            let metadata = entry.metadata()?;
            let time = metadata.modified()?;
            entries.insert(time, path);
        }

        // Remove all files above the threshold.
        // Old files are removed first (implictly by the BTree order).
        let mut number_entries = entries.len();
        let mut iter = entries.iter();
        while number_entries > 10 {
            if let Some((_, path)) = iter.next() {
                fs::remove_file(path)?;
                number_entries -= 1;
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>///! This mod specifies RedlockResult as an alias for Result and the various Error enum types

use std::result;

/// Redlock Error
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Error {
    /// Failed to acquire lock
    CannotObtainLock,
    /// Error communicating with 1 or more Redis masters
    RedlockConn,
    /// Failed to connect to enough Redis masters
    NotEnoughMasters,
    /// Lock wasn't valid (may have expired)
    InvalidLock,
    /// Unlock instance failed
    UnlockFailed,
}

pub type RedlockResult<T> = result::Result<T, Error>;
<commit_msg>types.rs module comment<commit_after>//! This mod specifies RedlockResult as an alias for Result and the various Error enum types

use std::result;

/// Redlock Error
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Error {
    /// Failed to acquire lock
    CannotObtainLock,
    /// Error communicating with 1 or more Redis masters
    RedlockConn,
    /// Failed to connect to enough Redis masters
    NotEnoughMasters,
    /// Lock wasn't valid (may have expired)
    InvalidLock,
    /// Unlock instance failed
    UnlockFailed,
}

pub type RedlockResult<T> = result::Result<T, Error>;
<|endoftext|>


<commit_before>#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Key {
	Up, Down, Left, Right, A, B, Select, Start
}

fn get_index(key: Key) -> usize {
	match key {
		Key::Up => 0,
		Key::Down => 1,
		Key::Left => 2,
		Key::Right => 3,
		Key::A => 4,
		Key::B => 5,
		Key::Select => 6,
		Key::Start => 7,
	}
}

#[derive(Serialize, Deserialize)]
pub struct Joypad {
	keys: [bool; 8],
	select_button_keys: bool,
	select_direction_keys: bool,
}

impl Joypad {
	pub fn new() -> Joypad {
		Joypad {
			keys: [false; 8],
			select_button_keys: false,
			select_direction_keys: false,
		}
	}

	///Keydown event
	pub fn keydown(&mut self, key: Key) {
		let index = get_index(key);
		self.keys[index] = true;
	}

	///Keyup event
	pub fn keyup(&mut self, key: Key) {
		let index = get_index(key);
		self.keys[index] = false;
	}

	///Query the state of a button
	pub fn key_state(&self, key: Key) -> bool {
		let index = get_index(key);
		self.keys[index]
	}

	///Used to select buttons/dpad
	///only bits 4 and 5 are writeable
	///bit 5: p15 = select button keys (0 = select)
	///bit 4: p14 = select dpad (0 = select)
	//TODO: what happens when they're both selected?
	pub fn write_joyp(&mut self, value: u8) {
		self.select_button_keys = value & 32 == 0;
		self.select_direction_keys = value & 16 == 0;
	}

	pub fn read_joyp(&self) -> u8 {
		let mut high: u8 = 0;
		if self.select_button_keys {
			high |= 32;
		}
		if self.select_direction_keys {
			high |= 16;
		}

		let mut low = 0;
		if self.select_button_keys {
			if self.keys[get_index(Key::Start)] {
				low |= 8;
			}
			if self.keys[get_index(Key::Select)] {
				low |= 4;
			}
			if self.keys[get_index(Key::A)] {
				low |= 2;
			}
			if self.keys[get_index(Key::B)] {
				low |= 1;
			}
		}
		else if self.select_direction_keys {
			if self.keys[get_index(Key::Down)] {
				low |= 8;
			}
			if self.keys[get_index(Key::Up)] {
				low |= 4;
			}
			if self.keys[get_index(Key::Left)] {
				low |= 2;
			}
			if self.keys[get_index(Key::Right)] {
				low |= 1;
			}
		}

		//Convert to active low and return
		(!((high & 0xF0) | (low & 0x0F))) & 0xCF
	}
}
<commit_msg>The bits in joyp for the A and B key were in the wrong order.<commit_after>#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum Key {
	Up = 0, Down = 1, Left = 2, Right = 3, B = 4, A = 5, Select = 6, Start = 7
}

#[derive(Serialize, Deserialize)]
pub struct Joypad {
	keys: [bool; 8],
	select_button_keys: bool,
	select_direction_keys: bool,
}

impl Joypad {
	pub fn new() -> Joypad {
		Joypad {
			keys: [false; 8],
			select_button_keys: false,
			select_direction_keys: false,
		}
	}

	///Keydown event
	pub fn keydown(&mut self, key: Key) {
		self.keys[key as usize] = true;
	}

	///Keyup event
	pub fn keyup(&mut self, key: Key) {
		self.keys[key as usize] = false;
	}

	///Query the state of a button
	pub fn key_state(&self, key: Key) -> bool {
		self.keys[key as usize]
	}

	///Used to select buttons/dpad
	///only bits 4 and 5 are writeable
	///bit 5: p15 = select button keys (0 = select)
	///bit 4: p14 = select dpad (0 = select)
	//TODO: what happens when they're both selected?
	pub fn write_joyp(&mut self, value: u8) {
		self.select_button_keys = value & 32 == 0;
		self.select_direction_keys = value & 16 == 0;
	}

	pub fn read_joyp(&self) -> u8 {
		let mut high: u8 = 0;
		if self.select_button_keys {
			high |= 32;
		}
		if self.select_direction_keys {
			high |= 16;
		}

		let mut low = 0;
		if self.select_button_keys {
			if self.keys[Key::Start as usize] {
				low |= 8;
			}
			if self.keys[Key::Select as usize] {
				low |= 4;
			}
			if self.keys[Key::B as usize] {
				low |= 2;
			}
			if self.keys[Key::A as usize] {
				low |= 1;
			}
		}
		else if self.select_direction_keys {
			if self.keys[Key::Down as usize] {
				low |= 8;
			}
			if self.keys[Key::Up as usize] {
				low |= 4;
			}
			if self.keys[Key::Left as usize] {
				low |= 2;
			}
			if self.keys[Key::Right as usize] {
				low |= 1;
			}
		}

		//Convert to active low and return
		(!((high & 0xF0) | (low & 0x0F))) & 0xCF
	}
}
<|endoftext|>