<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.
#[cfg(windows)]
use std::path::Path;

#[cfg(unix)]
use uucore::fsext::statfs;
use uucore::fsext::{FsUsage, MountInfo};

#[derive(Debug, Clone)]
pub(crate) struct Filesystem {
    pub mount_info: MountInfo,
    pub usage: FsUsage,
}

impl Filesystem {
    // TODO: resolve uuid in `mount_info.dev_name` if exists
    pub(crate) fn new(mount_info: MountInfo) -> Option<Self> {
        let _stat_path = if !mount_info.mount_dir.is_empty() {
            mount_info.mount_dir.clone()
        } else {
            #[cfg(unix)]
            {
                mount_info.dev_name.clone()
            }
            #[cfg(windows)]
            {
                // On windows, we expect the volume id
                mount_info.dev_id.clone()
            }
        };
        #[cfg(unix)]
        let usage = FsUsage::new(statfs(_stat_path).ok()?);
        #[cfg(windows)]
        let usage = FsUsage::new(Path::new(&_stat_path));
        Some(Self { mount_info, usage })
    }
}
<commit_msg>df: add documentation comments for filesystem.rs<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.
//! Provides a summary representation of a filesystem.
//!
//! A [`Filesystem`] struct represents a device containing a
//! filesystem mounted at a particular directory. It also includes
//! information on amount of space available and amount of space used.
#[cfg(windows)]
use std::path::Path;

#[cfg(unix)]
use uucore::fsext::statfs;
use uucore::fsext::{FsUsage, MountInfo};

/// Summary representation of a filesystem.
///
/// A [`Filesystem`] struct represents a device containing a
/// filesystem mounted at a particular directory. The
/// [`Filesystem::mount_info`] field exposes that information. The
/// [`Filesystem::usage`] field provides information on the amount of
/// space available on the filesystem and the amount of space used.
#[derive(Debug, Clone)]
pub(crate) struct Filesystem {
    /// Information about the mounted device, mount directory, and related options.
    pub mount_info: MountInfo,

    /// Information about the amount of space used on the filesystem.
    pub usage: FsUsage,
}

impl Filesystem {
    // TODO: resolve uuid in `mount_info.dev_name` if exists
    pub(crate) fn new(mount_info: MountInfo) -> Option<Self> {
        let _stat_path = if !mount_info.mount_dir.is_empty() {
            mount_info.mount_dir.clone()
        } else {
            #[cfg(unix)]
            {
                mount_info.dev_name.clone()
            }
            #[cfg(windows)]
            {
                // On windows, we expect the volume id
                mount_info.dev_id.clone()
            }
        };
        #[cfg(unix)]
        let usage = FsUsage::new(statfs(_stat_path).ok()?);
        #[cfg(windows)]
        let usage = FsUsage::new(Path::new(&_stat_path));
        Some(Self { mount_info, usage })
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Sunrin SHIMURA
// Collaborator: Jian Zeng
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (paths) GPGHome

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use uucore::display::{println_verbatim, Quotable};
use uucore::error::{FromIo, UError, UResult};

use std::env;
use std::error::Error;
use std::fmt::Display;
use std::iter;
use std::path::{is_separator, PathBuf};

use rand::Rng;
use tempfile::Builder;

static ABOUT: &str = "create a temporary file or directory.";

static DEFAULT_TEMPLATE: &str = "tmp.XXXXXXXXXX";

static OPT_DIRECTORY: &str = "directory";
static OPT_DRY_RUN: &str = "dry-run";
static OPT_QUIET: &str = "quiet";
static OPT_SUFFIX: &str = "suffix";
static OPT_TMPDIR: &str = "tmpdir";
static OPT_T: &str = "t";

static ARG_TEMPLATE: &str = "template";

fn usage() -> String {
    format!("{0} [OPTION]... [TEMPLATE]", uucore::execution_phrase())
}

#[derive(Debug)]
enum MkTempError {
    PersistError(PathBuf),
    MustEndInX(String),
    TooFewXs(String),
    ContainsDirSeparator(String),
    InvalidTemplate(String),
}

impl UError for MkTempError {}

impl Error for MkTempError {}

impl Display for MkTempError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use MkTempError::*;
        match self {
            PersistError(p) => write!(f, "could not persist file {}", p.quote()),
            MustEndInX(s) => write!(f, "with --suffix, template {} must end in X", s.quote()),
            TooFewXs(s) => write!(f, "too few X's in template {}", s.quote()),
            ContainsDirSeparator(s) => {
                write!(
                    f,
                    "invalid suffix {}, contains directory separator",
                    s.quote()
                )
            }
            InvalidTemplate(s) => write!(
                f,
                "invalid template, {}; with --tmpdir, it may not be absolute",
                s.quote()
            ),
        }
    }
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let template = matches.value_of(ARG_TEMPLATE).unwrap();
    let tmpdir = matches.value_of(OPT_TMPDIR).unwrap_or_default();

    let (template, mut tmpdir) = if matches.is_present(OPT_TMPDIR)
        && !PathBuf::from(tmpdir).is_dir() // if a temp dir is provided, it must be an actual path
        && tmpdir.contains("XXX")
    // If this is a template, it has to contain at least 3 X
        && template == DEFAULT_TEMPLATE
    // That means that clap does not think we provided a template
    {
        // Special case to workaround a limitation of clap when doing
        // mktemp --tmpdir apt-key-gpghome.XXX
        // The behavior should be
        // mktemp --tmpdir $TMPDIR apt-key-gpghome.XX
        // As --tmpdir is empty
        //
        // Fixed in clap 3
        // See https://github.com/clap-rs/clap/pull/1587
        let tmp = env::temp_dir();
        (tmpdir, tmp)
    } else if !matches.is_present(OPT_TMPDIR) {
        let tmp = env::temp_dir();
        (template, tmp)
    } else {
        (template, PathBuf::from(tmpdir))
    };

    let make_dir = matches.is_present(OPT_DIRECTORY);
    let dry_run = matches.is_present(OPT_DRY_RUN);
    let suppress_file_err = matches.is_present(OPT_QUIET);

    let (prefix, rand, suffix) = parse_template(template, matches.value_of(OPT_SUFFIX))?;

    if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() {
        return Err(MkTempError::InvalidTemplate(template.into()).into());
    }

    if matches.is_present(OPT_T) {
        tmpdir = env::temp_dir()
    }

    let res = if dry_run {
        dry_exec(tmpdir, prefix, rand, suffix)
    } else {
        exec(tmpdir, prefix, rand, suffix, make_dir)
    };

    if suppress_file_err {
        // Mapping all UErrors to ExitCodes prevents the errors from being printed
        res.map_err(|e| e.code().into())
    } else {
        res
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(OPT_DIRECTORY)
                .short("d")
                .long(OPT_DIRECTORY)
                .help("Make a directory instead of a file"),
        )
        .arg(
            Arg::with_name(OPT_DRY_RUN)
                .short("u")
                .long(OPT_DRY_RUN)
                .help("do not create anything; merely print a name (unsafe)"),
        )
        .arg(
            Arg::with_name(OPT_QUIET)
                .short("q")
                .long("quiet")
                .help("Fail silently if an error occurs."),
        )
        .arg(
            Arg::with_name(OPT_SUFFIX)
                .long(OPT_SUFFIX)
                .help(
                    "append SUFFIX to TEMPLATE; SUFFIX must not contain a path separator. \
                     This option is implied if TEMPLATE does not end with X.",
                )
                .value_name("SUFFIX"),
        )
        .arg(
            Arg::with_name(OPT_TMPDIR)
                .short("p")
                .long(OPT_TMPDIR)
                .help(
                    "interpret TEMPLATE relative to DIR; if DIR is not specified, use \
                     $TMPDIR ($TMP on windows) if set, else /tmp. With this option, TEMPLATE must not \
                     be an absolute name; unlike with -t, TEMPLATE may contain \
                     slashes, but mktemp creates only the final component",
                )
                .value_name("DIR"),
        )
        .arg(Arg::with_name(OPT_T).short(OPT_T).help(
            "Generate a template (using the supplied prefix and TMPDIR (TMP on windows) if set) \
             to create a filename template [deprecated]",
        ))
        .arg(
            Arg::with_name(ARG_TEMPLATE)
                .multiple(false)
                .takes_value(true)
                .max_values(1)
                .default_value(DEFAULT_TEMPLATE),
        )
}

fn parse_template<'a>(
    temp: &'a str,
    suffix: Option<&'a str>,
) -> UResult<(&'a str, usize, &'a str)> {
    let right = match temp.rfind('X') {
        Some(r) => r + 1,
        None => return Err(MkTempError::TooFewXs(temp.into()).into()),
    };
    let left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1);
    let prefix = &temp[..left];
    let rand = right - left;

    if rand < 3 {
        return Err(MkTempError::TooFewXs(temp.into()).into());
    }

    let mut suf = &temp[right..];

    if let Some(s) = suffix {
        if suf.is_empty() {
            suf = s;
        } else {
            return Err(MkTempError::MustEndInX(temp.into()).into());
        }
    };

    if suf.chars().any(is_separator) {
        return Err(MkTempError::ContainsDirSeparator(suf.into()).into());
    }

    Ok((prefix, rand, suf))
}

pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> UResult<()> {
    let len = prefix.len() + suffix.len() + rand;
    let mut buf = String::with_capacity(len);
    buf.push_str(prefix);
    buf.extend(iter::repeat('X').take(rand));
    buf.push_str(suffix);

    // Randomize.
    unsafe {
        // We guarantee utf8.
        let bytes = &mut buf.as_mut_vec()[prefix.len()..prefix.len() + rand];
        rand::thread_rng().fill(bytes);
        for byte in bytes.iter_mut() {
            *byte = match *byte % 62 {
                v @ 0..=9 => (v + b'0'),
                v @ 10..=35 => (v - 10 + b'a'),
                v @ 36..=61 => (v - 36 + b'A'),
                _ => unreachable!(),
            }
        }
    }
    tmpdir.push(buf);
    println_verbatim(tmpdir).map_err_context(|| "failed to print directory name".to_owned())
}

fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> {
    let context = || {
        format!(
            "failed to create file via template '{}{}{}'",
            prefix,
            "X".repeat(rand),
            suffix
        )
    };

    let mut builder = Builder::new();
    builder.prefix(prefix).rand_bytes(rand).suffix(suffix);

    let path = if make_dir {
        builder
            .tempdir_in(&dir)
            .map_err_context(context)?
            .into_path() // `into_path` consumes the TempDir without removing it
    } else {
        builder
            .tempfile_in(&dir)
            .map_err_context(context)?
            .keep() // `keep` ensures that the file is not deleted
            .map_err(|e| MkTempError::PersistError(e.file.path().to_path_buf()))?
            .1
    };
    println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned())
}
<commit_msg>mktemp: Do not use unsafe<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Sunrin SHIMURA
// Collaborator: Jian Zeng
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (paths) GPGHome

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg};
use uucore::display::{println_verbatim, Quotable};
use uucore::error::{FromIo, UError, UResult};

use std::env;
use std::error::Error;
use std::fmt::Display;
use std::iter;
use std::path::{is_separator, PathBuf};

use rand::Rng;
use tempfile::Builder;

static ABOUT: &str = "create a temporary file or directory.";

static DEFAULT_TEMPLATE: &str = "tmp.XXXXXXXXXX";

static OPT_DIRECTORY: &str = "directory";
static OPT_DRY_RUN: &str = "dry-run";
static OPT_QUIET: &str = "quiet";
static OPT_SUFFIX: &str = "suffix";
static OPT_TMPDIR: &str = "tmpdir";
static OPT_T: &str = "t";

static ARG_TEMPLATE: &str = "template";

fn usage() -> String {
    format!("{0} [OPTION]... [TEMPLATE]", uucore::execution_phrase())
}

#[derive(Debug)]
enum MkTempError {
    PersistError(PathBuf),
    MustEndInX(String),
    TooFewXs(String),
    ContainsDirSeparator(String),
    InvalidTemplate(String),
}

impl UError for MkTempError {}

impl Error for MkTempError {}

impl Display for MkTempError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use MkTempError::*;
        match self {
            PersistError(p) => write!(f, "could not persist file {}", p.quote()),
            MustEndInX(s) => write!(f, "with --suffix, template {} must end in X", s.quote()),
            TooFewXs(s) => write!(f, "too few X's in template {}", s.quote()),
            ContainsDirSeparator(s) => {
                write!(
                    f,
                    "invalid suffix {}, contains directory separator",
                    s.quote()
                )
            }
            InvalidTemplate(s) => write!(
                f,
                "invalid template, {}; with --tmpdir, it may not be absolute",
                s.quote()
            ),
        }
    }
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let template = matches.value_of(ARG_TEMPLATE).unwrap();
    let tmpdir = matches.value_of(OPT_TMPDIR).unwrap_or_default();

    let (template, mut tmpdir) = if matches.is_present(OPT_TMPDIR)
        && !PathBuf::from(tmpdir).is_dir() // if a temp dir is provided, it must be an actual path
        && tmpdir.contains("XXX")
    // If this is a template, it has to contain at least 3 X
        && template == DEFAULT_TEMPLATE
    // That means that clap does not think we provided a template
    {
        // Special case to workaround a limitation of clap when doing
        // mktemp --tmpdir apt-key-gpghome.XXX
        // The behavior should be
        // mktemp --tmpdir $TMPDIR apt-key-gpghome.XX
        // As --tmpdir is empty
        //
        // Fixed in clap 3
        // See https://github.com/clap-rs/clap/pull/1587
        let tmp = env::temp_dir();
        (tmpdir, tmp)
    } else if !matches.is_present(OPT_TMPDIR) {
        let tmp = env::temp_dir();
        (template, tmp)
    } else {
        (template, PathBuf::from(tmpdir))
    };

    let make_dir = matches.is_present(OPT_DIRECTORY);
    let dry_run = matches.is_present(OPT_DRY_RUN);
    let suppress_file_err = matches.is_present(OPT_QUIET);

    let (prefix, rand, suffix) = parse_template(template, matches.value_of(OPT_SUFFIX))?;

    if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() {
        return Err(MkTempError::InvalidTemplate(template.into()).into());
    }

    if matches.is_present(OPT_T) {
        tmpdir = env::temp_dir()
    }

    let res = if dry_run {
        dry_exec(tmpdir, prefix, rand, suffix)
    } else {
        exec(tmpdir, prefix, rand, suffix, make_dir)
    };

    if suppress_file_err {
        // Mapping all UErrors to ExitCodes prevents the errors from being printed
        res.map_err(|e| e.code().into())
    } else {
        res
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .arg(
            Arg::with_name(OPT_DIRECTORY)
                .short("d")
                .long(OPT_DIRECTORY)
                .help("Make a directory instead of a file"),
        )
        .arg(
            Arg::with_name(OPT_DRY_RUN)
                .short("u")
                .long(OPT_DRY_RUN)
                .help("do not create anything; merely print a name (unsafe)"),
        )
        .arg(
            Arg::with_name(OPT_QUIET)
                .short("q")
                .long("quiet")
                .help("Fail silently if an error occurs."),
        )
        .arg(
            Arg::with_name(OPT_SUFFIX)
                .long(OPT_SUFFIX)
                .help(
                    "append SUFFIX to TEMPLATE; SUFFIX must not contain a path separator. \
                     This option is implied if TEMPLATE does not end with X.",
                )
                .value_name("SUFFIX"),
        )
        .arg(
            Arg::with_name(OPT_TMPDIR)
                .short("p")
                .long(OPT_TMPDIR)
                .help(
                    "interpret TEMPLATE relative to DIR; if DIR is not specified, use \
                     $TMPDIR ($TMP on windows) if set, else /tmp. With this option, TEMPLATE must not \
                     be an absolute name; unlike with -t, TEMPLATE may contain \
                     slashes, but mktemp creates only the final component",
                )
                .value_name("DIR"),
        )
        .arg(Arg::with_name(OPT_T).short(OPT_T).help(
            "Generate a template (using the supplied prefix and TMPDIR (TMP on windows) if set) \
             to create a filename template [deprecated]",
        ))
        .arg(
            Arg::with_name(ARG_TEMPLATE)
                .multiple(false)
                .takes_value(true)
                .max_values(1)
                .default_value(DEFAULT_TEMPLATE),
        )
}

fn parse_template<'a>(
    temp: &'a str,
    suffix: Option<&'a str>,
) -> UResult<(&'a str, usize, &'a str)> {
    let right = match temp.rfind('X') {
        Some(r) => r + 1,
        None => return Err(MkTempError::TooFewXs(temp.into()).into()),
    };
    let left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1);
    let prefix = &temp[..left];
    let rand = right - left;

    if rand < 3 {
        return Err(MkTempError::TooFewXs(temp.into()).into());
    }

    let mut suf = &temp[right..];

    if let Some(s) = suffix {
        if suf.is_empty() {
            suf = s;
        } else {
            return Err(MkTempError::MustEndInX(temp.into()).into());
        }
    };

    if suf.chars().any(is_separator) {
        return Err(MkTempError::ContainsDirSeparator(suf.into()).into());
    }

    Ok((prefix, rand, suf))
}

pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> UResult<()> {
    let len = prefix.len() + suffix.len() + rand;
    let mut buf = Vec::with_capacity(len);
    buf.extend(prefix.as_bytes());
    buf.extend(iter::repeat(b'X').take(rand));
    buf.extend(suffix.as_bytes());

    // Randomize.
    let bytes = &mut buf[prefix.len()..prefix.len() + rand];
    rand::thread_rng().fill(bytes);
    for byte in bytes.iter_mut() {
        *byte = match *byte % 62 {
            v @ 0..=9 => (v + b'0'),
            v @ 10..=35 => (v - 10 + b'a'),
            v @ 36..=61 => (v - 36 + b'A'),
            _ => unreachable!(),
        }
    }
    // We guarantee utf8.
    let buf = String::from_utf8(buf).unwrap();
    tmpdir.push(buf);
    println_verbatim(tmpdir).map_err_context(|| "failed to print directory name".to_owned())
}

fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> {
    let context = || {
        format!(
            "failed to create file via template '{}{}{}'",
            prefix,
            "X".repeat(rand),
            suffix
        )
    };

    let mut builder = Builder::new();
    builder.prefix(prefix).rand_bytes(rand).suffix(suffix);

    let path = if make_dir {
        builder
            .tempdir_in(&dir)
            .map_err_context(context)?
            .into_path() // `into_path` consumes the TempDir without removing it
    } else {
        builder
            .tempfile_in(&dir)
            .map_err_context(context)?
            .keep() // `keep` ensures that the file is not deleted
            .map_err(|e| MkTempError::PersistError(e.file.path().to_path_buf()))?
            .1
    };
    println_verbatim(path).map_err_context(|| "failed to print directory name".to_owned())
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Yury Krivopalov <ykrivopalov@yandex.ru>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore N'th M'th

use crate::errors::*;
use crate::format::format_and_print;
use crate::options::*;
use crate::units::{Result, Unit};
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::io::{BufRead, Write};
use uucore::display::Quotable;
use uucore::error::UResult;
use uucore::ranges::Range;

pub mod errors;
pub mod format;
pub mod options;
mod units;

static ABOUT: &str = "Convert numbers from/to human-readable strings";
static LONG_HELP: &str = "UNIT options:
   none   no auto-scaling is done; suffixes will trigger an error

   auto   accept optional single/two letter suffix:

          1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576,

   si     accept optional single letter suffix:

          1K = 1000, 1M = 1000000, ...

   iec    accept optional single letter suffix:

          1K = 1024, 1M = 1048576, ...

   iec-i  accept optional two-letter suffix:

          1Ki = 1024, 1Mi = 1048576, ...

FIELDS supports cut(1) style field ranges:
  N    N'th field, counted from 1
  N-   from N'th field, to end of line
  N-M  from N'th to M'th field (inclusive)
  -M   from first to M'th field (inclusive)
  -    all fields
Multiple fields/ranges can be separated with commas
";

fn usage() -> String {
    format!("{0} [OPTION]... [NUMBER]...", uucore::execution_phrase())
}

fn handle_args<'a>(args: impl Iterator<Item = &'a str>, options: NumfmtOptions) -> UResult<()> {
    for l in args {
        match format_and_print(l, &options) {
            Ok(_) => Ok(()),
            Err(e) => Err(NumfmtError::FormattingError(e.to_string())),
        }?;
    }

    Ok(())
}

fn handle_stdin(options: NumfmtOptions) -> UResult<()> {
    let stdin = std::io::stdin();
    let locked_stdin = stdin.lock();

    let mut lines = locked_stdin.lines();
    for (idx, line) in lines.by_ref().enumerate() {
        match line {
            Ok(l) if idx < options.header => {
                println!("{}", l);
                Ok(())
            }
            Ok(l) => match format_and_print(&l, &options) {
                Ok(_) => Ok(()),
                Err(e) => Err(NumfmtError::FormattingError(e.to_string())),
            },
            Err(e) => Err(NumfmtError::IoError(e.to_string())),
        }?;
    }
    Ok(())
}

fn parse_unit(s: &str) -> Result<Unit> {
    match s {
        "auto" => Ok(Unit::Auto),
        "si" => Ok(Unit::Si),
        "iec" => Ok(Unit::Iec(false)),
        "iec-i" => Ok(Unit::Iec(true)),
        "none" => Ok(Unit::None),
        _ => Err("Unsupported unit is specified".to_owned()),
    }
}

fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
    let from = parse_unit(args.value_of(options::FROM).unwrap())?;
    let to = parse_unit(args.value_of(options::TO).unwrap())?;

    let transform = TransformOptions { from, to };

    let padding = match args.value_of(options::PADDING) {
        Some(s) => s.parse::<isize>().map_err(|err| err.to_string()),
        None => Ok(0),
    }?;

    let header = match args.occurrences_of(options::HEADER) {
        0 => Ok(0),
        _ => {
            let value = args.value_of(options::HEADER).unwrap();

            value
                .parse::<usize>()
                .map_err(|_| value)
                .and_then(|n| match n {
                    0 => Err(value),
                    _ => Ok(n),
                })
                .map_err(|value| format!("invalid header value {}", value.quote()))
        }
    }?;

    let fields = match args.value_of(options::FIELD).unwrap() {
        "-" => vec![Range {
            low: 1,
            high: std::usize::MAX,
        }],
        v => Range::from_list(v)?,
    };

    let delimiter = args.value_of(options::DELIMITER).map_or(Ok(None), |arg| {
        if arg.len() == 1 {
            Ok(Some(arg.to_string()))
        } else {
            Err("the delimiter must be a single character".to_string())
        }
    })?;

    // unwrap is fine because the argument has a default value
    let round = match args.value_of(options::ROUND).unwrap() {
        "up" => RoundMethod::Up,
        "down" => RoundMethod::Down,
        "from-zero" => RoundMethod::FromZero,
        "towards-zero" => RoundMethod::TowardsZero,
        "nearest" => RoundMethod::Nearest,
        _ => unreachable!("Should be restricted by clap"),
    };

    let suffix = args.value_of(options::SUFFIX).map(|s| s.to_owned());

    Ok(NumfmtOptions {
        transform,
        padding,
        header,
        fields,
        delimiter,
        round,
        suffix,
    })
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let options = parse_options(&matches).map_err(NumfmtError::IllegalArgument)?;

    let result = match matches.values_of(options::NUMBER) {
        Some(values) => handle_args(values, options),
        None => handle_stdin(options),
    };

    match result {
        Err(e) => {
            std::io::stdout().flush().expect("error flushing stdout");
            Err(e)
        }
        _ => Ok(()),
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help(LONG_HELP)
        .setting(AppSettings::AllowNegativeNumbers)
        .arg(
            Arg::with_name(options::DELIMITER)
                .short("d")
                .long(options::DELIMITER)
                .value_name("X")
                .help("use X instead of whitespace for field delimiter"),
        )
        .arg(
            Arg::with_name(options::FIELD)
                .long(options::FIELD)
                .help("replace the numbers in these input fields (default=1) see FIELDS below")
                .value_name("FIELDS")
                .default_value(options::FIELD_DEFAULT),
        )
        .arg(
            Arg::with_name(options::FROM)
                .long(options::FROM)
                .help("auto-scale input numbers to UNITs; see UNIT below")
                .value_name("UNIT")
                .default_value(options::FROM_DEFAULT),
        )
        .arg(
            Arg::with_name(options::TO)
                .long(options::TO)
                .help("auto-scale output numbers to UNITs; see UNIT below")
                .value_name("UNIT")
                .default_value(options::TO_DEFAULT),
        )
        .arg(
            Arg::with_name(options::PADDING)
                .long(options::PADDING)
                .help(
                    "pad the output to N characters; positive N will \
                     right-align; negative N will left-align; padding is \
                     ignored if the output is wider than N; the default is \
                     to automatically pad if a whitespace is found",
                )
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::HEADER)
                .long(options::HEADER)
                .help(
                    "print (without converting) the first N header lines; \
                     N defaults to 1 if not specified",
                )
                .value_name("N")
                .default_value(options::HEADER_DEFAULT)
                .hide_default_value(true),
        )
        .arg(
            Arg::with_name(options::ROUND)
                .long(options::ROUND)
                .help(
                    "use METHOD for rounding when scaling; METHOD can be: up,\
                    down, from-zero (default), towards-zero, nearest",
                )
                .value_name("METHOD")
                .default_value("from-zero")
                .possible_values(&["up", "down", "from-zero", "towards-zero", "nearest"]),
        )
        .arg(
            Arg::with_name(options::SUFFIX)
                .long(options::SUFFIX)
                .help(
                    "print SUFFIX after each formatted number, and accept \
                    inputs optionally ending with SUFFIX",
                )
                .value_name("SUFFIX"),
        )
        .arg(Arg::with_name(options::NUMBER).hidden(true).multiple(true))
}
<commit_msg>numfmt: add unit test for io error<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Yury Krivopalov <ykrivopalov@yandex.ru>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore N'th M'th

use crate::errors::*;
use crate::format::format_and_print;
use crate::options::*;
use crate::units::{Result, Unit};
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
use std::io::{BufRead, Write};
use uucore::display::Quotable;
use uucore::error::UResult;
use uucore::ranges::Range;

pub mod errors;
pub mod format;
pub mod options;
mod units;

static ABOUT: &str = "Convert numbers from/to human-readable strings";
static LONG_HELP: &str = "UNIT options:
   none   no auto-scaling is done; suffixes will trigger an error

   auto   accept optional single/two letter suffix:

          1K = 1000, 1Ki = 1024, 1M = 1000000, 1Mi = 1048576,

   si     accept optional single letter suffix:

          1K = 1000, 1M = 1000000, ...

   iec    accept optional single letter suffix:

          1K = 1024, 1M = 1048576, ...

   iec-i  accept optional two-letter suffix:

          1Ki = 1024, 1Mi = 1048576, ...

FIELDS supports cut(1) style field ranges:
  N    N'th field, counted from 1
  N-   from N'th field, to end of line
  N-M  from N'th to M'th field (inclusive)
  -M   from first to M'th field (inclusive)
  -    all fields
Multiple fields/ranges can be separated with commas
";

fn usage() -> String {
    format!("{0} [OPTION]... [NUMBER]...", uucore::execution_phrase())
}

fn handle_args<'a>(args: impl Iterator<Item = &'a str>, options: NumfmtOptions) -> UResult<()> {
    for l in args {
        match format_and_print(l, &options) {
            Ok(_) => Ok(()),
            Err(e) => Err(NumfmtError::FormattingError(e.to_string())),
        }?;
    }

    Ok(())
}

fn handle_buffer<R>(input: R, options: NumfmtOptions) -> UResult<()>
where
    R: BufRead,
{
    let mut lines = input.lines();
    for (idx, line) in lines.by_ref().enumerate() {
        match line {
            Ok(l) if idx < options.header => {
                println!("{}", l);
                Ok(())
            }
            Ok(l) => match format_and_print(&l, &options) {
                Ok(_) => Ok(()),
                Err(e) => Err(NumfmtError::FormattingError(e.to_string())),
            },
            Err(e) => Err(NumfmtError::IoError(e.to_string())),
        }?;
    }
    Ok(())
}

fn parse_unit(s: &str) -> Result<Unit> {
    match s {
        "auto" => Ok(Unit::Auto),
        "si" => Ok(Unit::Si),
        "iec" => Ok(Unit::Iec(false)),
        "iec-i" => Ok(Unit::Iec(true)),
        "none" => Ok(Unit::None),
        _ => Err("Unsupported unit is specified".to_owned()),
    }
}

fn parse_options(args: &ArgMatches) -> Result<NumfmtOptions> {
    let from = parse_unit(args.value_of(options::FROM).unwrap())?;
    let to = parse_unit(args.value_of(options::TO).unwrap())?;

    let transform = TransformOptions { from, to };

    let padding = match args.value_of(options::PADDING) {
        Some(s) => s.parse::<isize>().map_err(|err| err.to_string()),
        None => Ok(0),
    }?;

    let header = match args.occurrences_of(options::HEADER) {
        0 => Ok(0),
        _ => {
            let value = args.value_of(options::HEADER).unwrap();

            value
                .parse::<usize>()
                .map_err(|_| value)
                .and_then(|n| match n {
                    0 => Err(value),
                    _ => Ok(n),
                })
                .map_err(|value| format!("invalid header value {}", value.quote()))
        }
    }?;

    let fields = match args.value_of(options::FIELD).unwrap() {
        "-" => vec![Range {
            low: 1,
            high: std::usize::MAX,
        }],
        v => Range::from_list(v)?,
    };

    let delimiter = args.value_of(options::DELIMITER).map_or(Ok(None), |arg| {
        if arg.len() == 1 {
            Ok(Some(arg.to_string()))
        } else {
            Err("the delimiter must be a single character".to_string())
        }
    })?;

    // unwrap is fine because the argument has a default value
    let round = match args.value_of(options::ROUND).unwrap() {
        "up" => RoundMethod::Up,
        "down" => RoundMethod::Down,
        "from-zero" => RoundMethod::FromZero,
        "towards-zero" => RoundMethod::TowardsZero,
        "nearest" => RoundMethod::Nearest,
        _ => unreachable!("Should be restricted by clap"),
    };

    let suffix = args.value_of(options::SUFFIX).map(|s| s.to_owned());

    Ok(NumfmtOptions {
        transform,
        padding,
        header,
        fields,
        delimiter,
        round,
        suffix,
    })
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();

    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    let options = parse_options(&matches).map_err(NumfmtError::IllegalArgument)?;

    let result = match matches.values_of(options::NUMBER) {
        Some(values) => handle_args(values, options),
        None => {
            let stdin = std::io::stdin();
            let mut locked_stdin = stdin.lock();
            handle_buffer(&mut locked_stdin, options)
        }
    };

    match result {
        Err(e) => {
            std::io::stdout().flush().expect("error flushing stdout");
            Err(e)
        }
        _ => Ok(()),
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help(LONG_HELP)
        .setting(AppSettings::AllowNegativeNumbers)
        .arg(
            Arg::with_name(options::DELIMITER)
                .short("d")
                .long(options::DELIMITER)
                .value_name("X")
                .help("use X instead of whitespace for field delimiter"),
        )
        .arg(
            Arg::with_name(options::FIELD)
                .long(options::FIELD)
                .help("replace the numbers in these input fields (default=1) see FIELDS below")
                .value_name("FIELDS")
                .default_value(options::FIELD_DEFAULT),
        )
        .arg(
            Arg::with_name(options::FROM)
                .long(options::FROM)
                .help("auto-scale input numbers to UNITs; see UNIT below")
                .value_name("UNIT")
                .default_value(options::FROM_DEFAULT),
        )
        .arg(
            Arg::with_name(options::TO)
                .long(options::TO)
                .help("auto-scale output numbers to UNITs; see UNIT below")
                .value_name("UNIT")
                .default_value(options::TO_DEFAULT),
        )
        .arg(
            Arg::with_name(options::PADDING)
                .long(options::PADDING)
                .help(
                    "pad the output to N characters; positive N will \
                     right-align; negative N will left-align; padding is \
                     ignored if the output is wider than N; the default is \
                     to automatically pad if a whitespace is found",
                )
                .value_name("N"),
        )
        .arg(
            Arg::with_name(options::HEADER)
                .long(options::HEADER)
                .help(
                    "print (without converting) the first N header lines; \
                     N defaults to 1 if not specified",
                )
                .value_name("N")
                .default_value(options::HEADER_DEFAULT)
                .hide_default_value(true),
        )
        .arg(
            Arg::with_name(options::ROUND)
                .long(options::ROUND)
                .help(
                    "use METHOD for rounding when scaling; METHOD can be: up,\
                    down, from-zero (default), towards-zero, nearest",
                )
                .value_name("METHOD")
                .default_value("from-zero")
                .possible_values(&["up", "down", "from-zero", "towards-zero", "nearest"]),
        )
        .arg(
            Arg::with_name(options::SUFFIX)
                .long(options::SUFFIX)
                .help(
                    "print SUFFIX after each formatted number, and accept \
                    inputs optionally ending with SUFFIX",
                )
                .value_name("SUFFIX"),
        )
        .arg(Arg::with_name(options::NUMBER).hidden(true).multiple(true))
}

#[cfg(test)]
mod tests {
    use super::{handle_buffer, NumfmtOptions, Range, RoundMethod, TransformOptions, Unit};
    use std::io::{BufReader, Error, ErrorKind, Read};
    struct MockBuffer {}

    impl Read for MockBuffer {
        fn read(&mut self, _: &mut [u8]) -> Result<usize, Error> {
            Err(Error::new(ErrorKind::BrokenPipe, "broken pipe"))
        }
    }

    fn get_options() -> NumfmtOptions {
        NumfmtOptions {
            transform: TransformOptions {
                from: Unit::Auto,
                to: Unit::Auto,
            },
            padding: 10,
            header: 0,
            fields: vec![Range { low: 0, high: 1 }],
            delimiter: None,
            round: RoundMethod::Nearest,
            suffix: None,
        }
    }

    #[test]
    fn broken_buffer_returns_io_error() {
        let mock_buffer = MockBuffer {};
        let result = handle_buffer(BufReader::new(mock_buffer), get_options())
            .expect_err("returned Ok after receiving IO error");
        let result_debug = format!("{:?}", result);
        assert_eq!(result_debug, "IoError(\"broken pipe\")");
        assert_eq!(result.code(), 1);
    }
}
<|endoftext|>


<commit_before>//! #Introduction
//! This crate focuses on geting system information.
//!
//! For now it supports Linux, Mac OS X and Windows.
//! And now it can get information of kernel/cpu/memory/disk/load/hostname and so on.
//!

use std::ffi;
use std::fmt;
use std::io::{self, Read};
use std::fs::File;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    /// Average load within one minite.
    pub one: f64,
    /// Average load within five minites.
    pub five: f64,
    /// Average load within fifteen minites.
    pub fifteen: f64,
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    /// Total physical memory.
    pub total: u64,
    pub free: u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached: u64,

    /// Total swap memory.
    pub swap_total: u64,
    pub swap_free: u64,
}

/// System momory information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64,
}

/// Error types
#[derive(Debug)]
pub enum Error {
    UnsupportedSystem,
    ExecFailed(io::Error),
    IO(io::Error),
    Unknown,
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;
        match *self {
            UnsupportedSystem => write!(fmt, "System is not supported"),
            ExecFailed(ref e) => write!(fmt, "Execution failed: {}", e),
            IO(ref e) => write!(fmt, "IO error: {}", e),
            Unknown => write!(fmt, "An unknown error occurred"),
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            UnsupportedSystem => "unsupported system",
            ExecFailed(_) => "execution failed",
            IO(_) => "io error",
            Unknown => "unknown error",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        use self::Error::*;
        match *self {
            UnsupportedSystem => None,
            ExecFailed(ref e) => Some(e),
            IO(ref e) => Some(e),
            Unknown => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Error {
        Error::IO(e)
    }
}

extern "C" {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/sys/kernel/ostype")?.read_to_string(&mut s)?;
        s.pop(); // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        let typ = unsafe {
            ffi::CStr::from_ptr(get_os_type()).to_bytes()
        };
        Ok(String::from_utf8_lossy(typ).into_owned())
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/sys/kernel/osrelease")?.read_to_string(&mut s)?;
        s.pop(); // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        let typ = unsafe {
            ffi::CStr::from_ptr(get_os_release()).to_bytes()
        };
        Ok(String::from_utf8_lossy(typ).into_owned())
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, Error> {
    if cfg!(unix) || cfg!(windows) {
        unsafe { Ok(get_cpu_num()) }
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, Error> {
    if cfg!(target_os = "linux") {
        // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
        let mut s = String::new();
        File::open("/proc/cpuinfo")?.read_to_string(&mut s)?;

        s.split('\n')
         .find(|line| line.starts_with("cpu MHz"))
         .and_then(|line| line.split(':').last())
         .and_then(|val| val.trim().parse::<f64>().ok())
         .map(|speed| speed as u64)
         .ok_or(Error::Unknown)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        unsafe { Ok(get_cpu_speed()) }
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/loadavg")?.read_to_string(&mut s)?;
        let loads = s.split(' ')
                     .take(3)
                     .map(|val| val.parse::<f64>().unwrap())
                     .collect::<Vec<f64>>();
        Ok(LoadAvg {
            one: loads[0],
            five: loads[1],
            fifteen: loads[2],
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/loadavg")?.read_to_string(&mut s)?;
        s.split(' ').nth(3)
         .and_then(|val| val.split('/').last())
         .and_then(|val| val.parse::<u64>().ok())
         .ok_or(Error::Unknown)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    line.split_whitespace()
        .nth(1)
        .and_then(|val| val.parse::<u64>().ok())
        .unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/meminfo")?.read_to_string(&mut s)?;
        let info = s.lines()
                    .map(get_mem_num)
                    .collect::<Vec<u64>>();
        Ok(MemInfo {
            total: info[0],
            free: info[1],
            avail: info[2],
            buffers: info[3],
            cached: info[4],
            swap_total: info[14],
            swap_free: info[15],
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, Error> {
    if cfg!(target_os = "linux") || cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_disk_info() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get hostname.
pub fn hostname() -> Result<String, Error> {
    use std::process::Command;
    if cfg!(unix) {
        Command::new("hostname")
            .output()
            .map_err(|e| Error::ExecFailed(e))
            .map(|output| String::from_utf8(output.stdout).unwrap().trim().to_string())
    } else if cfg!(windows) {
        Command::new("hostname")
            .output()
            .map_err(|e| Error::ExecFailed(e))
            .map(|output| String::from_utf8(output.stdout).unwrap().trim().to_string())
    } else {
        Err(Error::UnsupportedSystem)
    }
}
<commit_msg>Fixed copy/pasted comment for DiskInfo<commit_after>//! #Introduction
//! This crate focuses on geting system information.
//!
//! For now it supports Linux, Mac OS X and Windows.
//! And now it can get information of kernel/cpu/memory/disk/load/hostname and so on.
//!

use std::ffi;
use std::fmt;
use std::io::{self, Read};
use std::fs::File;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    /// Average load within one minite.
    pub one: f64,
    /// Average load within five minites.
    pub five: f64,
    /// Average load within fifteen minites.
    pub fifteen: f64,
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    /// Total physical memory.
    pub total: u64,
    pub free: u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached: u64,

    /// Total swap memory.
    pub swap_total: u64,
    pub swap_free: u64,
}

/// Disk information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64,
}

/// Error types
#[derive(Debug)]
pub enum Error {
    UnsupportedSystem,
    ExecFailed(io::Error),
    IO(io::Error),
    Unknown,
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;
        match *self {
            UnsupportedSystem => write!(fmt, "System is not supported"),
            ExecFailed(ref e) => write!(fmt, "Execution failed: {}", e),
            IO(ref e) => write!(fmt, "IO error: {}", e),
            Unknown => write!(fmt, "An unknown error occurred"),
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            UnsupportedSystem => "unsupported system",
            ExecFailed(_) => "execution failed",
            IO(_) => "io error",
            Unknown => "unknown error",
        }
    }

    fn cause(&self) -> Option<&std::error::Error> {
        use self::Error::*;
        match *self {
            UnsupportedSystem => None,
            ExecFailed(ref e) => Some(e),
            IO(ref e) => Some(e),
            Unknown => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Error {
        Error::IO(e)
    }
}

extern "C" {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/sys/kernel/ostype")?.read_to_string(&mut s)?;
        s.pop(); // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        let typ = unsafe {
            ffi::CStr::from_ptr(get_os_type()).to_bytes()
        };
        Ok(String::from_utf8_lossy(typ).into_owned())
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/sys/kernel/osrelease")?.read_to_string(&mut s)?;
        s.pop(); // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        let typ = unsafe {
            ffi::CStr::from_ptr(get_os_release()).to_bytes()
        };
        Ok(String::from_utf8_lossy(typ).into_owned())
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, Error> {
    if cfg!(unix) || cfg!(windows) {
        unsafe { Ok(get_cpu_num()) }
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, Error> {
    if cfg!(target_os = "linux") {
        // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
        let mut s = String::new();
        File::open("/proc/cpuinfo")?.read_to_string(&mut s)?;

        s.split('\n')
         .find(|line| line.starts_with("cpu MHz"))
         .and_then(|line| line.split(':').last())
         .and_then(|val| val.trim().parse::<f64>().ok())
         .map(|speed| speed as u64)
         .ok_or(Error::Unknown)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        unsafe { Ok(get_cpu_speed()) }
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/loadavg")?.read_to_string(&mut s)?;
        let loads = s.split(' ')
                     .take(3)
                     .map(|val| val.parse::<f64>().unwrap())
                     .collect::<Vec<f64>>();
        Ok(LoadAvg {
            one: loads[0],
            five: loads[1],
            fifteen: loads[2],
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/loadavg")?.read_to_string(&mut s)?;
        s.split(' ').nth(3)
         .and_then(|val| val.split('/').last())
         .and_then(|val| val.parse::<u64>().ok())
         .ok_or(Error::Unknown)
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    line.split_whitespace()
        .nth(1)
        .and_then(|val| val.parse::<u64>().ok())
        .unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, Error> {
    if cfg!(target_os = "linux") {
        let mut s = String::new();
        File::open("/proc/meminfo")?.read_to_string(&mut s)?;
        let info = s.lines()
                    .map(get_mem_num)
                    .collect::<Vec<u64>>();
        Ok(MemInfo {
            total: info[0],
            free: info[1],
            avail: info[2],
            buffers: info[3],
            cached: info[4],
            swap_total: info[14],
            swap_free: info[15],
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, Error> {
    if cfg!(target_os = "linux") || cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_disk_info() })
    } else {
        Err(Error::UnsupportedSystem)
    }
}

/// Get hostname.
pub fn hostname() -> Result<String, Error> {
    use std::process::Command;
    if cfg!(unix) {
        Command::new("hostname")
            .output()
            .map_err(|e| Error::ExecFailed(e))
            .map(|output| String::from_utf8(output.stdout).unwrap().trim().to_string())
    } else if cfg!(windows) {
        Command::new("hostname")
            .output()
            .map_err(|e| Error::ExecFailed(e))
            .map(|output| String::from_utf8(output.stdout).unwrap().trim().to_string())
    } else {
        Err(Error::UnsupportedSystem)
    }
}
<|endoftext|>


<commit_before>use context::Context;
use piston_window::{self, PistonWindow};

pub enum InputResult {
    PushScreen(Box<Screen>),
    PopScreen,
}

pub type InputResults = Vec<InputResult>;

pub trait Screen {
    fn on_input(&mut self, input: &piston_window::Input, window: &PistonWindow, context: &mut Context) -> InputResults;
    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &PistonWindow);
    fn on_update(&mut self, args: &piston_window::UpdateArgs);
}
<commit_msg>InputResult::Exit<commit_after>use context::Context;
use piston_window::{self, PistonWindow};

pub enum InputResult {
    PushScreen(Box<Screen>),
    PopScreen,
    Exit,
}

pub type InputResults = Vec<InputResult>;

pub trait Screen {
    fn on_input(&mut self, input: &piston_window::Input, window: &PistonWindow, context: &mut Context) -> InputResults;
    fn on_draw(&mut self, args: &piston_window::RenderArgs, window: &PistonWindow);
    fn on_update(&mut self, args: &piston_window::UpdateArgs);
}
<|endoftext|>


<commit_before>use serde_json::Value;
use std::fmt;

/// `S`ring/`I`nteger
/// a temporary type for strings that are integers
/// if a field with an integer as string is used, just use `.into()`

/// `S`ring/`I`nteger
/// a temporary type for strings that are integers
/// if a field with an integer as string is used, just use `.into()`
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum SI {
	I(i64),
	S(String),
}

impl From<SI> for i64 {
	fn from(si: SI) -> Self
	{
		match si
		{
			SI::I(i) => i,
			SI::S(s) => s.parse::<i64>().unwrap(),
		}
	}
}

impl fmt::Display for SI {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{
		write!(f, "{}", i64::from(self.to_owned()))
	}
}

#[derive(Debug, Clone, Deserialize)]
pub struct Fullentry {
	//#[serde(deserialize_with = "string_as_i64")]
	pub id: SI,
	pub names: Vec<Name>,
	pub genre: String,
	pub fsk: String,
	pub description: String,
	pub medium: String,
	pub count: String,
	pub state: String,
	pub rate_sum: String,
	pub rate_count: String,
	pub clicks: String,
	pub kat: String,
	pub license: String,
	pub tags: Vec<Tag>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct GetEntry {
	pub id: String,
	pub name: String,
	pub genre: String,
	pub fsk: String,
	pub description: String,
	pub medium: String,
	pub count: String,
	pub state: String,
	pub rate_sum: String,
	pub rate_count: String,
	pub clicks: String,
	pub kat: Value,
	pub license: Value,
}

#[derive(Debug, Clone, Deserialize)]
pub struct UserList {
	pub id: i64,
	pub name: String,
	pub description: String,
	pub genre: Vec<String>,
	pub fsk: Vec<String>,
	pub medium: String,
	pub count: i64,
	pub state: i64,
	pub rate_sum: i64,
	pub rate_count: i64,
	pub clicks: i64,
	pub kat: Kat,
	pub license: License,
}

#[derive(Debug, Clone, Deserialize)]
pub enum SpoilerFlag {
	#[serde(rename = "0")]
	NoSpoiler,
	#[serde(rename = "1")]
	Spoiler,
}

#[derive(Debug, Clone, Deserialize)]
pub enum RateFlag {
	#[serde(rename = "0")]
	NoMatch,
	#[serde(rename = "1")]
	Match,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Tag {
	pub info_id: Option<i64>,
	pub id: SI,
	#[serde(rename = "tid")]
	pub tag_id: SI,
	pub timestamp: String,
	pub rate_flag: RateFlag,
	pub spoiler_flag: SpoilerFlag,
	pub tag: String,
	pub description: String,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Name {
	pub id: String,
	pub eid: String,
	#[serde(rename = "type")]
	pub type_: String,
	pub name: String,
}

#[derive(Debug, Clone, Deserialize)]
pub enum WatchState {
	#[serde(rename = "0")]
	Watched,
	#[serde(rename = "1")]
	Watching,
	#[serde(rename = "2")]
	WillWatch,
	#[serde(rename = "3")]
	Cancelled,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Comment {
	pub id: SI,
	#[serde(rename = "tid")]
	pub info_id: SI,
	#[serde(rename = "type")]
	pub comment_type: String, // i have no idea what this is
	pub state: WatchState,
	pub data: String,
	pub comment: String,
	pub rating: SI,
	pub episode: SI,
	pub positive: SI,
	pub timestamp: SI, //Todo: use chrono here
	pub username: String,
	pub uid: SI,
	pub avatar: String,
}

#[derive(Deserialize, Debug, Clone)]
pub struct Userinfo {
	pub uid: i64,
	pub username: String,
	pub avatar: String, // use some sort of uri type here
	pub status: String,
	// status_time is sometimes a negative number. using i64
	pub status_time: i64,
	pub points_upload: i64,
	pub points_anime: i64,
	pub points_manga: i64,
	pub points_info: i64,
	pub points_forum: i64,
	pub points_misc: i64,
}

#[derive(Debug, Clone, Deserialize)]
pub enum Kat {
	Anime,
	Manga,
}

#[derive(Debug, Clone, Deserialize)]
pub enum License {
	Unknown,
	Unlicensed,
	Licensed,
}
<commit_msg>fix types<commit_after>use serde_json::Value;
use std::fmt;

/// `S`ring/`I`nteger
/// a temporary type for strings that are integers
/// if a field with an integer as string is used, just use `.into()`

/// `S`ring/`I`nteger
/// a temporary type for strings that are integers
/// if a field with an integer as string is used, just use `.into()`
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum SI {
	I(i64),
	S(String),
}

impl From<SI> for i64 {
	fn from(si: SI) -> Self
	{
		match si
		{
			SI::I(i) => i,
			SI::S(s) => s.parse::<i64>().unwrap(),
		}
	}
}

impl fmt::Display for SI {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
	{
		write!(f, "{}", i64::from(self.to_owned()))
	}
}

#[derive(Debug, Clone, Deserialize)]
pub struct Fullentry {
	//#[serde(deserialize_with = "string_as_i64")]
	pub id: SI,
	pub names: Vec<Name>,
	pub genre: String,
	pub fsk: String,
	pub description: String,
	pub medium: String,
	pub count: String,
	pub state: String,
	pub rate_sum: String,
	pub rate_count: String,
	pub clicks: String,
	pub kat: String,
	pub license: String,
	pub tags: Vec<Tag>,
}

#[derive(Debug, Clone, Deserialize)]
pub struct GetEntry {
	pub id: String,
	pub name: String,
	pub genre: String,
	pub fsk: String,
	pub description: String,
	pub medium: String,
	pub count: String,
	pub state: String,
	pub rate_sum: String,
	pub rate_count: String,
	pub clicks: String,
	pub kat: Value,
	pub license: Value,
}

#[derive(Debug, Clone, Deserialize)]
pub struct UserList {
	pub id: i64,
	pub name: String,
	pub description: String,
	pub genre: Vec<String>,
	pub fsk: Vec<String>,
	pub medium: String,
	pub count: i64,
	pub state: i64,
	pub rate_sum: i64,
	pub rate_count: i64,
	pub clicks: i64,
	pub kat: Kat,
	pub license: License,
}

#[derive(Debug, Clone, Deserialize)]
pub enum SpoilerFlag {
	#[serde(rename = "0")]
	NoSpoiler,
	#[serde(rename = "1")]
	Spoiler,
}

#[derive(Debug, Clone, Deserialize)]
pub enum RateFlag {
	#[serde(rename = "0")]
	NoMatch,
	#[serde(rename = "1")]
	Match,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Tag {
	pub info_id: Option<i64>,
	pub id: SI,
	#[serde(rename = "tid")]
	pub tag_id: SI,
	pub timestamp: String,
	pub rate_flag: RateFlag,
	pub spoiler_flag: SpoilerFlag,
	pub tag: String,
	pub description: String,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Name {
	pub id: SI,
	pub eid: SI,
	#[serde(rename = "type")]
	pub type_: String,
	pub name: String,
}

#[derive(Debug, Clone, Deserialize)]
pub enum WatchState {
	#[serde(rename = "0")]
	Watched,
	#[serde(rename = "1")]
	Watching,
	#[serde(rename = "2")]
	WillWatch,
	#[serde(rename = "3")]
	Cancelled,
}

#[derive(Debug, Clone, Deserialize)]
pub struct Comment {
	pub id: SI,
	#[serde(rename = "tid")]
	pub info_id: SI,
	#[serde(rename = "type")]
	pub comment_type: String, // i have no idea what this is
	pub state: WatchState,
	pub data: String,
	pub comment: String,
	pub rating: SI,
	pub episode: SI,
	pub positive: SI,
	pub timestamp: SI, //Todo: use chrono here
	pub username: String,
	pub uid: SI,
	pub avatar: String,
}

#[derive(Deserialize, Debug, Clone)]
pub struct Userinfo {
	pub uid: i64,
	pub username: String,
	pub avatar: String, // use some sort of uri type here
	pub status: String,
	// status_time is sometimes a negative number. using i64
	pub status_time: i64,
	pub points_upload: i64,
	pub points_anime: i64,
	pub points_manga: i64,
	pub points_info: i64,
	pub points_forum: i64,
	pub points_misc: i64,
}

#[derive(Debug, Clone, Deserialize)]
pub enum Kat {
	Anime,
	Manga,
}

#[derive(Debug, Clone, Deserialize)]
pub enum License {
	Unknown,
	Unlicensed,
	Licensed,
}
<|endoftext|>


<commit_before>//! The fields that can be used in queries.
//!
//! Some field types can be used for multiple entities, to make it more user
//! friendly the types are reexported to the submodules corresponding to the
//! names of the entities which they can be used to query.
//!
//! Link to [MusicBrainz
//! documentation](https://musicbrainz.org/doc/Indexed_Search_Syntax).

use super::{full_entities};
// use super::query::QueryExpression;
use super::full_entities::{Mbid, PartialDate};
// use super::entities;

pub trait SearchField {
    type Value: ToString;

    fn to_string(&self) -> String;
}

macro_rules! define_fields {
    ( $( $(#[$attr:meta])* f, $type:ident, $value:ty );* ) => {
        $(
            $(#[$attr])*
            pub struct $type ( pub $value );

            impl SearchField for $type {
                type Value = $value;

                fn to_string(&self) -> String {
                    self.0.to_string()
                }
            }
        )*
    }
}

// TODO consider whether we should rename `Comment` to `Disambiguation` or something like that to
// be more consistent with the rest of the crate.
//
// TODO: enums for quality, lang, script, etc
// TODO it's a bit ugly we have f, at the beginning of every line but its a workaround around the
// parsing ambiguity we'd have if we don't.
define_fields!(
    /// Alias of the searched entity's name.
    f, Alias, String;
    f, AreaId, Mbid;
    f, AreaIso, String;
    f, AreaIso1, String;
    f, AreaIso2, String;
    f, AreaIso3, String;
    f, AreaName, String;
    f, AreaType, full_entities::AreaType;
    f, ArtistCredit, String;
    f, ArtistId, Mbid;
    f, ArtistName, String;
    f, ArtistNameAccent, String;
    f, ArtistType, full_entities::ArtistType;
    f, Asin, String;
    /// The barcode of a `Release`.
    f, Barcode, String;
    f, BeginArea, String;
    f, BeginDate, PartialDate;
    f, CatalogNumber, String;
    f, Comment, String;
    f, Country, String;
    f, CreditName, String;
    f, DataQuality, String;
    f, EndArea, String;
    f, EndDate, PartialDate;
    f, Ended, bool;
    /// The gender of an `Artist`.
    f, Gender, String;
    f, IpiCode, String;
    f, LabelId, String;
    f, Language, String;
    f, MediumCount, u32;
    f, MediumFormat, String;
    /// The searched entity's name. (TODO implement for all relevant searches)
    f, Name, String;
    f, NumDiscIds, u32;
    f, NumDiscIdsMedium, u32;
    f, NumTracks, u32;
    f, NumTracksMedium, u32;
    f, PrimaryType, full_entities::ReleaseGroupPrimaryType;
    f, ReleaseDate, full_entities::PartialDate;
    f, ReleaseGroupId, Mbid;
    f, ReleaseGroupName, String;
    f, ReleaseGroupNameAccent, String;
    f, ReleaseId, Mbid;
    /// The name of the `Release`, without special accent characters.
    f, ReleaseName, String;
    /// The name of the `Release`, including special accent characters.
    f, ReleaseNameAccent, String;
    f, ReleaseNumber, u16;
    f, ReleaseStatus, full_entities::ReleaseStatus;
    f, Script, String;
    f, SecondaryType, String;
    f, SortName, String;
    f, Tag, String
);

macro_rules! define_entity_fields {
    (
        $field_trait:ident, $modname:ident;
        $(
             $strname:expr, $field_type:ident
        );*
        ;
    )
        =>
    {
        /// Acceptable fields searching for instances of the entity.
        pub trait $field_trait : SearchField {
            fn name() -> &'static str;
        }

        pub mod $modname {
            pub use super::$field_trait;

            $(
                pub use super::$field_type;

                impl $field_trait for $field_type {
                    fn name() -> &'static str { $strname }
                }
            )*
        }

    }
}

define_entity_fields!(
    AreaSearchField, area;

    "aid", AreaId;
    "alias", Alias;
    "area", AreaName;
    "begin", BeginDate;
    "comment", Comment;
    "end", EndDate;
    "ended", Ended;
    "iso", AreaIso;
    "iso1", AreaIso1;
    "iso2", AreaIso2;
    "iso3", AreaIso3;
    "sortname", SortName;
    "type", AreaType;
);

define_entity_fields!(
    ArtistSearchField, artist;

    "alias", Alias;
    "area", AreaName;
    "arid", ArtistId;
    "artist", ArtistName;
    "artistaccent", ArtistNameAccent;
    "begin", BeginDate;
    "beginarea", BeginArea;
    "comment", Comment;
    "country", Country;
    "end", EndDate;
    "endarea", EndArea;
    "ended", Ended;
    "gender", Gender;
    "ipi", IpiCode;
    "sortname", SortName;
    "tag", Tag;
    "type", ArtistType;
);

// TODO what are puids?
define_entity_fields!(
    ReleaseSearchField, release;

    "arid", ArtistId;
    "artist", ArtistName;
    "asin", Asin;
    "barcode", Barcode;
    "catno", CatalogNumber;
    "comment", Comment;
    "country", Country;
    "creditname", CreditName;
    "date", ReleaseDate;
    "discids", NumDiscIds;
    "discidsmedium", NumDiscIdsMedium;
    "format", MediumFormat;
    "laid", LabelId;
    "lang", Language;
    "mediums", MediumCount;
    "primarytype", PrimaryType;
    "quality", DataQuality;
    "reid", ReleaseId;
    "release", ReleaseName;
    "releaseaccent", ReleaseNameAccent;
    "rgid", ReleaseGroupId;
    "script", Script;
    "secondarytype", SecondaryType;
    "status", ReleaseStatus;
    "tag", Tag;
    "tracks", NumTracks;
    "tracksmedium", NumTracksMedium;
);

define_entity_fields!(
    ReleaseGroupSearchField, release_group;

    "arid", ArtistId;
    "artist", ArtistCredit;
    "artistname", ArtistName;
    "comment", Comment;
    "creditname", CreditName;
    "primarytype", PrimaryType;
    "reid", ReleaseId;
    "release", ReleaseName;
    "releasegroup", ReleaseGroupName;
    "releasegroupaccent", ReleaseGroupNameAccent;
    "releases", ReleaseNumber;
    "rgid", ReleaseGroupId;
    "secondarytype", SecondaryType;
    "status", ReleaseStatus;
    "tag", Tag;
);
<commit_msg>More docs for search fields<commit_after>//! The fields that can be used in queries.
//!
//! Some field types can be used for multiple entities, to make it more user
//! friendly the types are reexported to the submodules corresponding to the
//! names of the entities which they can be used to query.
//!
//! Link to [MusicBrainz
//! documentation](https://musicbrainz.org/doc/Indexed_Search_Syntax).

use super::full_entities;
// use super::query::QueryExpression;
use super::full_entities::{Mbid, PartialDate};
// use super::entities;

pub trait SearchField {
    type Value: ToString;

    fn to_string(&self) -> String;
}

macro_rules! define_fields {
    ( $( $(#[$attr:meta])* f, $type:ident, $value:ty );* ) => {
        $(
            $(#[$attr])*
            pub struct $type ( pub $value );

            impl SearchField for $type {
                type Value = $value;

                fn to_string(&self) -> String {
                    self.0.to_string()
                }
            }
        )*
    }
}

// TODO consider whether we should rename `Comment` to `Disambiguation` or
// something like that to
// be more consistent with the rest of the crate.
//
// TODO: enums for quality, lang, script, etc
// TODO it's a bit ugly we have f, at the beginning of every line but its a
// workaround around the
// parsing ambiguity we'd have if we don't.
define_fields!(
    /// Alias of the searched entity's name.
    f, Alias, String;
    /// The MBID of the `Area`.
    f, AreaMbid, Mbid;
    /// An ISO 3166-1/2/3 code attached to the `Area`.
    f, AreaIso, String;
    /// An ISO 3166-1 code attached to the `Area`.
    f, AreaIso1, String;
    /// An ISO 3166-2 code attached to the `Area`.
    f, AreaIso2, String;
    /// An ISO 3166-3 code attached to the `Area`.
    f, AreaIso3, String;
    /// The name of thea `Area`.
    f, AreaName, String;
    /// The type of the `Area`.
    f, AreaType, full_entities::AreaType;
    f, ArtistCredit, String;
    /// The MBID of the `Artist`.
    f, ArtistMbid, Mbid;
    /// The name of the `Artist` without accented characters.
    f, ArtistName, String;
    /// The name of the `Artist` with accented characters.
    f, ArtistNameAccent, String;
    /// The type of the `Artist`.
    f, ArtistType, full_entities::ArtistType;
    f, Asin, String;
    /// The barcode of a `Release`.
    f, Barcode, String;
    f, BeginArea, String;
    /// Begin date of the searched entity.
    ///
    /// Check the searched entity's documentation for more information what this means concretely.
    f, BeginDate, PartialDate;
    f, CatalogNumber, String;
    /// Disambiguation comment of the searched entity.
    f, Comment, String;
    f, Country, String;
    f, CreditName, String;
    f, DataQuality, String;
    f, EndArea, String;
    /// End date of the searched entity.
    ///
    /// Check the searched entity's documentation for more information what this means concretely.
    f, EndDate, PartialDate;
    /// Whether the searched entity has already ended.
    ///
    /// Check the searched entity's documentation for more information what this means concretely.
    f, Ended, bool;
    /// The gender of an `Artist`.
    f, Gender, String;
    f, IpiCode, String;
    f, LabelId, String;
    f, Language, String;
    f, MediumCount, u32;
    f, MediumFormat, String;
    /// The searched entity's name. (TODO implement for all relevant searches)
    f, Name, String;
    f, NumDiscIds, u32;
    f, NumDiscIdsMedium, u32;
    f, NumTracks, u32;
    f, NumTracksMedium, u32;
    f, PrimaryType, full_entities::ReleaseGroupPrimaryType;
    f, ReleaseDate, full_entities::PartialDate;
    f, ReleaseGroupId, Mbid;
    f, ReleaseGroupName, String;
    f, ReleaseGroupNameAccent, String;
    f, ReleaseId, Mbid;
    /// The name of the `Release`, without special accent characters.
    f, ReleaseName, String;
    /// The name of the `Release`, including special accent characters.
    f, ReleaseNameAccent, String;
    f, ReleaseNumber, u16;
    f, ReleaseStatus, full_entities::ReleaseStatus;
    f, Script, String;
    f, SecondaryType, String;
    /// The sort name of the searched entity.
    f, SortName, String;
    f, Tag, String
);

macro_rules! define_entity_fields {
    (
        $field_trait:ident, $modname:ident;
        $(
             $strname:expr, $field_type:ident
        );*
        ;
    )
        =>
    {
        /// Acceptable fields searching for instances of the entity.
        pub trait $field_trait : SearchField {
            fn name() -> &'static str;
        }

        pub mod $modname {
            pub use super::$field_trait;

            $(
                pub use super::$field_type;

                impl $field_trait for $field_type {
                    fn name() -> &'static str { $strname }
                }
            )*
        }

    }
}

define_entity_fields!(
    AreaSearchField, area;

    "aid", AreaMbid;
    "alias", Alias;
    "area", AreaName;
    "area", Name;
    "begin", BeginDate;
    "comment", Comment;
    "end", EndDate;
    "ended", Ended;
    "iso", AreaIso;
    "iso1", AreaIso1;
    "iso2", AreaIso2;
    "iso3", AreaIso3;
    "sortname", SortName;
    "type", AreaType;
);

define_entity_fields!(
    ArtistSearchField, artist;

    "alias", Alias;
    "area", AreaName;
    "arid", ArtistMbid;
    "artist", ArtistName;
    "artistaccent", ArtistNameAccent;
    "begin", BeginDate;
    "beginarea", BeginArea;
    "comment", Comment;
    "country", Country;
    "end", EndDate;
    "endarea", EndArea;
    "ended", Ended;
    "gender", Gender;
    "ipi", IpiCode;
    "sortname", SortName;
    "tag", Tag;
    "type", ArtistType;
);

// TODO what are puids?
define_entity_fields!(
    ReleaseSearchField, release;

    "arid", ArtistId;
    "artist", ArtistName;
    "asin", Asin;
    "barcode", Barcode;
    "catno", CatalogNumber;
    "comment", Comment;
    "country", Country;
    "creditname", CreditName;
    "date", ReleaseDate;
    "discids", NumDiscIds;
    "discidsmedium", NumDiscIdsMedium;
    "format", MediumFormat;
    "laid", LabelId;
    "lang", Language;
    "mediums", MediumCount;
    "primarytype", PrimaryType;
    "quality", DataQuality;
    "reid", ReleaseId;
    "release", ReleaseName;
    "releaseaccent", ReleaseNameAccent;
    "rgid", ReleaseGroupId;
    "script", Script;
    "secondarytype", SecondaryType;
    "status", ReleaseStatus;
    "tag", Tag;
    "tracks", NumTracks;
    "tracksmedium", NumTracksMedium;
);

define_entity_fields!(
    ReleaseGroupSearchField, release_group;

    "arid", ArtistId;
    "artist", ArtistCredit;
    "artistname", ArtistName;
    "comment", Comment;
    "creditname", CreditName;
    "primarytype", PrimaryType;
    "reid", ReleaseId;
    "release", ReleaseName;
    "releasegroup", ReleaseGroupName;
    "releasegroupaccent", ReleaseGroupNameAccent;
    "releases", ReleaseNumber;
    "rgid", ReleaseGroupId;
    "secondarytype", SecondaryType;
    "status", ReleaseStatus;
    "tag", Tag;
);
<|endoftext|>


<commit_before>use commit::Commit;

pub trait Serializable {
    fn encode(&self) -> Vec<u8>;
    fn encode_body(&self) -> Vec<u8>;
}

pub fn encode_author_info(commit: &Commit) -> Vec<u8> {
    (format!("author {} <{}> {}\n",
             commit.author_name,
             commit.author_email,
             commit.author_date))
    .into_bytes()
}

pub fn encode_commit_info(commit: &Commit) -> Vec<u8> {
    (format!("committer {} <{}> {}\n",
             commit.committer_name,
             commit.committer_email,
             commit.commit_date))
    .into_bytes()
}
<commit_msg>Add dummy date encoding<commit_after>use commit::Commit;

pub trait Serializable {
    fn encode(&self) -> Vec<u8>;
    fn encode_body(&self) -> Vec<u8>;
}

pub fn encode_author_info(commit: &Commit) -> Vec<u8> {
    (format!("author {} <{}> {}\n",
             commit.author_name,
             commit.author_email,
             encode_date(commit.author_date).as_slice()))
    .into_bytes()
}

pub fn encode_commit_info(commit: &Commit) -> Vec<u8> {
    (format!("committer {} <{}> {}\n",
             commit.committer_name,
             commit.committer_email,
             encode_date(commit.commit_date).as_slice()))
    .into_bytes()
}

pub fn encode_date(date: uint) -> String {
    format!("{} +0000", date)
}<|endoftext|>