<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Orvar Segerström <orvarsegerstrom@gmail.com>
// (c) Sokovikov Evgeniy  <skv-headless@yandex.ru>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

// spell-checker:ignore (ToDO) sourcepath targetpath

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg, ArgMatches};
use std::env;
use std::fs;
use std::io::{self, stdin};
#[cfg(unix)]
use std::os::unix;
#[cfg(windows)]
use std::os::windows;
use std::path::{Path, PathBuf};
use uucore::backup_control::{self, BackupMode};

use fs_extra::dir::{move_dir, CopyOptions as DirCopyOptions};

pub struct Behavior {
    overwrite: OverwriteMode,
    backup: BackupMode,
    suffix: String,
    update: bool,
    target_dir: Option<String>,
    no_target_dir: bool,
    verbose: bool,
}

#[derive(Clone, Eq, PartialEq)]
pub enum OverwriteMode {
    NoClobber,
    Interactive,
    Force,
}

static ABOUT: &str = "Move SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.";
static LONG_HELP: &str = "";

static OPT_BACKUP: &str = "backup";
static OPT_BACKUP_NO_ARG: &str = "b";
static OPT_FORCE: &str = "force";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_NO_CLOBBER: &str = "no-clobber";
static OPT_STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
static OPT_SUFFIX: &str = "suffix";
static OPT_TARGET_DIRECTORY: &str = "target-directory";
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
static OPT_UPDATE: &str = "update";
static OPT_VERBOSE: &str = "verbose";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!(
        "{0} [OPTION]... [-T] SOURCE DEST
{0} [OPTION]... SOURCE... DIRECTORY
{0} [OPTION]... -t DIRECTORY SOURCE...",
        executable!()
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();

    let matches = uu_app()
        .after_help(&*format!(
            "{}\n{}",
            LONG_HELP,
            backup_control::BACKUP_CONTROL_LONG_HELP
        ))
        .usage(&usage[..])
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let overwrite_mode = determine_overwrite_mode(&matches);
    let backup_mode = backup_control::determine_backup_mode(
        matches.is_present(OPT_BACKUP_NO_ARG) || matches.is_present(OPT_BACKUP),
        matches.value_of(OPT_BACKUP),
    );

    if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup {
        show_usage_error!("options --backup and --no-clobber are mutually exclusive");
        return 1;
    }

    let backup_suffix = backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX));

    let behavior = Behavior {
        overwrite: overwrite_mode,
        backup: backup_mode,
        suffix: backup_suffix,
        update: matches.is_present(OPT_UPDATE),
        target_dir: matches.value_of(OPT_TARGET_DIRECTORY).map(String::from),
        no_target_dir: matches.is_present(OPT_NO_TARGET_DIRECTORY),
        verbose: matches.is_present(OPT_VERBOSE),
    };

    let paths: Vec<PathBuf> = {
        fn strip_slashes(p: &Path) -> &Path {
            p.components().as_path()
        }
        let to_owned = |p: &Path| p.to_owned();
        let paths = files.iter().map(Path::new);

        if matches.is_present(OPT_STRIP_TRAILING_SLASHES) {
            paths.map(strip_slashes).map(to_owned).collect()
        } else {
            paths.map(to_owned).collect()
        }
    };

    exec(&paths[..], behavior)
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
    .arg(
            Arg::with_name(OPT_BACKUP)
            .long(OPT_BACKUP)
            .help("make a backup of each existing destination file")
            .takes_value(true)
            .require_equals(true)
            .min_values(0)
            .possible_values(backup_control::BACKUP_CONTROL_VALUES)
            .value_name("CONTROL")
    )
    .arg(
            Arg::with_name(OPT_BACKUP_NO_ARG)
            .short(OPT_BACKUP_NO_ARG)
            .help("like --backup but does not accept an argument")
    )
    .arg(
            Arg::with_name(OPT_FORCE)
            .short("f")
            .long(OPT_FORCE)
            .help("do not prompt before overwriting")
    )
    .arg(
            Arg::with_name(OPT_INTERACTIVE)
            .short("i")
            .long(OPT_INTERACTIVE)
            .help("prompt before override")
    )
    .arg(
            Arg::with_name(OPT_NO_CLOBBER).short("n")
            .long(OPT_NO_CLOBBER)
            .help("do not overwrite an existing file")
    )
    .arg(
            Arg::with_name(OPT_STRIP_TRAILING_SLASHES)
            .long(OPT_STRIP_TRAILING_SLASHES)
            .help("remove any trailing slashes from each SOURCE argument")
    )
    .arg(
            Arg::with_name(OPT_SUFFIX)
            .short("S")
            .long(OPT_SUFFIX)
            .help("override the usual backup suffix")
            .takes_value(true)
            .value_name("SUFFIX")
    )
    .arg(
        Arg::with_name(OPT_TARGET_DIRECTORY)
        .short("t")
        .long(OPT_TARGET_DIRECTORY)
        .help("move all SOURCE arguments into DIRECTORY")
        .takes_value(true)
        .value_name("DIRECTORY")
        .conflicts_with(OPT_NO_TARGET_DIRECTORY)
    )
    .arg(
            Arg::with_name(OPT_NO_TARGET_DIRECTORY)
            .short("T")
            .long(OPT_NO_TARGET_DIRECTORY).
            help("treat DEST as a normal file")
    )
    .arg(
            Arg::with_name(OPT_UPDATE)
            .short("u")
            .long(OPT_UPDATE)
            .help("move only when the SOURCE file is newer than the destination file or when the destination file is missing")
    )
    .arg(
            Arg::with_name(OPT_VERBOSE)
            .short("v")
            .long(OPT_VERBOSE).help("explain what is being done")
    )
    .arg(
        Arg::with_name(ARG_FILES)
            .multiple(true)
            .takes_value(true)
            .min_values(2)
            .required(true)
        )
}

fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode {
    // This does not exactly match the GNU implementation:
    // The GNU mv defaults to Force, but if more than one of the
    // overwrite options are supplied, only the last takes effect.
    // To default to no-clobber in that situation seems safer:
    //
    if matches.is_present(OPT_NO_CLOBBER) {
        OverwriteMode::NoClobber
    } else if matches.is_present(OPT_INTERACTIVE) {
        OverwriteMode::Interactive
    } else {
        OverwriteMode::Force
    }
}

fn exec(files: &[PathBuf], b: Behavior) -> i32 {
    if let Some(ref name) = b.target_dir {
        return move_files_into_dir(files, &PathBuf::from(name), &b);
    }
    match files.len() {
        /* case 0/1 are not possible thanks to clap */
        2 => {
            let source = &files[0];
            let target = &files[1];
            // Here we use the `symlink_metadata()` method instead of `exists()`,
            // since it handles dangling symlinks correctly. The method gives an
            // `Ok()` results unless the source does not exist, or the user
            // lacks permission to access metadata.
            if source.symlink_metadata().is_err() {
                show_error!(
                    "cannot stat '{}': No such file or directory",
                    source.display()
                );
                return 1;
            }

            if target.is_dir() {
                if b.no_target_dir {
                    if !source.is_dir() {
                        show_error!(
                            "cannot overwrite directory '{}' with non-directory",
                            target.display()
                        );
                        return 1;
                    }

                    return match rename(source, target, &b) {
                        Err(e) => {
                            show_error!(
                                "cannot move '{}' to '{}': {}",
                                source.display(),
                                target.display(),
                                e.to_string()
                            );
                            1
                        }
                        _ => 0,
                    };
                }

                return move_files_into_dir(&[source.clone()], target, &b);
            } else if target.exists() && source.is_dir() {
                show_error!(
                    "cannot overwrite non-directory '{}' with directory '{}'",
                    target.display(),
                    source.display()
                );
                return 1;
            }

            if let Err(e) = rename(source, target, &b) {
                show_error!("{}", e);
                return 1;
            }
        }
        _ => {
            if b.no_target_dir {
                show_error!(
                    "mv: extra operand '{}'\n\
                     Try '{} --help' for more information.",
                    files[2].display(),
                    executable!()
                );
                return 1;
            }
            let target_dir = files.last().unwrap();
            move_files_into_dir(&files[..files.len() - 1], target_dir, &b);
        }
    }
    0
}

fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target '{}' is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.file_name() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!(
                    "cannot stat '{}': No such file or directory",
                    sourcepath.display()
                );

                all_successful = false;
                continue;
            }
        };

        if let Err(e) = rename(sourcepath, &targetpath, b) {
            show_error!(
                "cannot move '{}' to '{}': {}",
                sourcepath.display(),
                targetpath.display(),
                e.to_string()
            );
            all_successful = false;
        }
    }

    if all_successful {
        0
    } else {
        1
    }
}

fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> {
    let mut backup_path = None;

    if to.exists() {
        match b.overwrite {
            OverwriteMode::NoClobber => return Ok(()),
            OverwriteMode::Interactive => {
                println!("{}: overwrite '{}'? ", executable!(), to.display());
                if !read_yes() {
                    return Ok(());
                }
            }
            OverwriteMode::Force => {}
        };

        backup_path = backup_control::get_backup_path(b.backup, to, &b.suffix);
        if let Some(ref backup_path) = backup_path {
            rename_with_fallback(to, backup_path)?;
        }

        if b.update && fs::metadata(from)?.modified()? <= fs::metadata(to)?.modified()? {
            return Ok(());
        }
    }

    // "to" may no longer exist if it was backed up
    if to.exists() && to.is_dir() {
        // normalize behavior between *nix and windows
        if from.is_dir() {
            if is_empty_dir(to) {
                fs::remove_dir(to)?
            } else {
                return Err(io::Error::new(io::ErrorKind::Other, "Directory not empty"));
            }
        }
    }

    rename_with_fallback(from, to)?;

    if b.verbose {
        print!("'{}' -> '{}'", from.display(), to.display());
        match backup_path {
            Some(path) => println!(" (backup: '{}')", path.display()),
            None => println!(),
        }
    }
    Ok(())
}

/// A wrapper around `fs::rename`, so that if it fails, we try falling back on
/// copying and removing.
fn rename_with_fallback(from: &Path, to: &Path) -> io::Result<()> {
    if fs::rename(from, to).is_err() {
        // Get metadata without following symlinks
        let metadata = from.symlink_metadata()?;
        let file_type = metadata.file_type();

        if file_type.is_symlink() {
            rename_symlink_fallback(from, to)?;
        } else if file_type.is_dir() {
            // We remove the destination directory if it exists to match the
            // behavior of `fs::rename`. As far as I can tell, `fs_extra`'s
            // `move_dir` would otherwise behave differently.
            if to.exists() {
                fs::remove_dir_all(to)?;
            }
            let options = DirCopyOptions {
                // From the `fs_extra` documentation:
                // "Recursively copy a directory with a new name or place it
                // inside the destination. (same behaviors like cp -r in Unix)"
                copy_inside: true,
                ..DirCopyOptions::new()
            };
            if let Err(err) = move_dir(from, to, &options) {
                return match err.kind {
                    fs_extra::error::ErrorKind::PermissionDenied => Err(io::Error::new(
                        io::ErrorKind::PermissionDenied,
                        "Permission denied",
                    )),
                    _ => Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", err))),
                };
            }
        } else {
            fs::copy(from, to).and_then(|_| fs::remove_file(from))?;
        }
    }
    Ok(())
}

/// Move the given symlink to the given destination. On Windows, dangling
/// symlinks return an error.
#[inline]
fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
    let path_symlink_points_to = fs::read_link(from)?;
    #[cfg(unix)]
    {
        unix::fs::symlink(&path_symlink_points_to, &to).and_then(|_| fs::remove_file(&from))?;
    }
    #[cfg(windows)]
    {
        if path_symlink_points_to.exists() {
            if path_symlink_points_to.is_dir() {
                windows::fs::symlink_dir(&path_symlink_points_to, &to)?;
            } else {
                windows::fs::symlink_file(&path_symlink_points_to, &to)?;
            }
            fs::remove_file(&from)?;
        } else {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "can't determine symlink type, since it is dangling",
            ));
        }
    }
    #[cfg(not(any(windows, unix)))]
    {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "your operating system does not support symlinks",
        ));
    }
    Ok(())
}

fn read_yes() -> bool {
    let mut s = String::new();
    match stdin().read_line(&mut s) {
        Ok(_) => match s.chars().next() {
            Some(x) => x == 'y' || x == 'Y',
            _ => false,
        },
        _ => false,
    }
}

fn is_empty_dir(path: &Path) -> bool {
    match fs::read_dir(path) {
        Ok(contents) => contents.peekable().peek().is_none(),
        Err(_e) => false,
    }
}
<commit_msg>mv: Adapt for new backup_control utilities<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Orvar Segerström <orvarsegerstrom@gmail.com>
// (c) Sokovikov Evgeniy  <skv-headless@yandex.ru>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

// spell-checker:ignore (ToDO) sourcepath targetpath

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg, ArgMatches};
use std::env;
use std::fs;
use std::io::{self, stdin};
#[cfg(unix)]
use std::os::unix;
#[cfg(windows)]
use std::os::windows;
use std::path::{Path, PathBuf};
use uucore::backup_control::{self, BackupMode};

use fs_extra::dir::{move_dir, CopyOptions as DirCopyOptions};

pub struct Behavior {
    overwrite: OverwriteMode,
    backup: BackupMode,
    suffix: String,
    update: bool,
    target_dir: Option<String>,
    no_target_dir: bool,
    verbose: bool,
}

#[derive(Clone, Eq, PartialEq)]
pub enum OverwriteMode {
    NoClobber,
    Interactive,
    Force,
}

static ABOUT: &str = "Move SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.";
static LONG_HELP: &str = "";

static OPT_BACKUP: &str = "backup";
static OPT_BACKUP_NO_ARG: &str = "b";
static OPT_FORCE: &str = "force";
static OPT_INTERACTIVE: &str = "interactive";
static OPT_NO_CLOBBER: &str = "no-clobber";
static OPT_STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
static OPT_SUFFIX: &str = "suffix";
static OPT_TARGET_DIRECTORY: &str = "target-directory";
static OPT_NO_TARGET_DIRECTORY: &str = "no-target-directory";
static OPT_UPDATE: &str = "update";
static OPT_VERBOSE: &str = "verbose";

static ARG_FILES: &str = "files";

fn get_usage() -> String {
    format!(
        "{0} [OPTION]... [-T] SOURCE DEST
{0} [OPTION]... SOURCE... DIRECTORY
{0} [OPTION]... -t DIRECTORY SOURCE...",
        executable!()
    )
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();

    let matches = uu_app()
        .after_help(&*format!(
            "{}\n{}",
            LONG_HELP,
            backup_control::BACKUP_CONTROL_LONG_HELP
        ))
        .usage(&usage[..])
        .get_matches_from(args);

    let files: Vec<String> = matches
        .values_of(ARG_FILES)
        .map(|v| v.map(ToString::to_string).collect())
        .unwrap_or_default();

    let overwrite_mode = determine_overwrite_mode(&matches);
    let backup_mode = backup_control::determine_backup_mode(
        matches.is_present(OPT_BACKUP_NO_ARG),
        matches.is_present(OPT_BACKUP),
        matches.value_of(OPT_BACKUP),
    );
    let backup_mode = match backup_mode {
        Err(err) => {
            show_usage_error!("{}", err);
            return 1;
        }
        Ok(mode) => mode,
    };

    if overwrite_mode == OverwriteMode::NoClobber && backup_mode != BackupMode::NoBackup {
        show_usage_error!("options --backup and --no-clobber are mutually exclusive");
        return 1;
    }

    let backup_suffix = backup_control::determine_backup_suffix(matches.value_of(OPT_SUFFIX));

    let behavior = Behavior {
        overwrite: overwrite_mode,
        backup: backup_mode,
        suffix: backup_suffix,
        update: matches.is_present(OPT_UPDATE),
        target_dir: matches.value_of(OPT_TARGET_DIRECTORY).map(String::from),
        no_target_dir: matches.is_present(OPT_NO_TARGET_DIRECTORY),
        verbose: matches.is_present(OPT_VERBOSE),
    };

    let paths: Vec<PathBuf> = {
        fn strip_slashes(p: &Path) -> &Path {
            p.components().as_path()
        }
        let to_owned = |p: &Path| p.to_owned();
        let paths = files.iter().map(Path::new);

        if matches.is_present(OPT_STRIP_TRAILING_SLASHES) {
            paths.map(strip_slashes).map(to_owned).collect()
        } else {
            paths.map(to_owned).collect()
        }
    };

    exec(&paths[..], behavior)
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(executable!())
        .version(crate_version!())
        .about(ABOUT)
    .arg(
            Arg::with_name(OPT_BACKUP)
            .long(OPT_BACKUP)
            .help("make a backup of each existing destination file")
            .takes_value(true)
            .require_equals(true)
            .min_values(0)
            .value_name("CONTROL")
    )
    .arg(
            Arg::with_name(OPT_BACKUP_NO_ARG)
            .short(OPT_BACKUP_NO_ARG)
            .help("like --backup but does not accept an argument")
    )
    .arg(
            Arg::with_name(OPT_FORCE)
            .short("f")
            .long(OPT_FORCE)
            .help("do not prompt before overwriting")
    )
    .arg(
            Arg::with_name(OPT_INTERACTIVE)
            .short("i")
            .long(OPT_INTERACTIVE)
            .help("prompt before override")
    )
    .arg(
            Arg::with_name(OPT_NO_CLOBBER).short("n")
            .long(OPT_NO_CLOBBER)
            .help("do not overwrite an existing file")
    )
    .arg(
            Arg::with_name(OPT_STRIP_TRAILING_SLASHES)
            .long(OPT_STRIP_TRAILING_SLASHES)
            .help("remove any trailing slashes from each SOURCE argument")
    )
    .arg(
            Arg::with_name(OPT_SUFFIX)
            .short("S")
            .long(OPT_SUFFIX)
            .help("override the usual backup suffix")
            .takes_value(true)
            .value_name("SUFFIX")
    )
    .arg(
        Arg::with_name(OPT_TARGET_DIRECTORY)
        .short("t")
        .long(OPT_TARGET_DIRECTORY)
        .help("move all SOURCE arguments into DIRECTORY")
        .takes_value(true)
        .value_name("DIRECTORY")
        .conflicts_with(OPT_NO_TARGET_DIRECTORY)
    )
    .arg(
            Arg::with_name(OPT_NO_TARGET_DIRECTORY)
            .short("T")
            .long(OPT_NO_TARGET_DIRECTORY).
            help("treat DEST as a normal file")
    )
    .arg(
            Arg::with_name(OPT_UPDATE)
            .short("u")
            .long(OPT_UPDATE)
            .help("move only when the SOURCE file is newer than the destination file or when the destination file is missing")
    )
    .arg(
            Arg::with_name(OPT_VERBOSE)
            .short("v")
            .long(OPT_VERBOSE).help("explain what is being done")
    )
    .arg(
        Arg::with_name(ARG_FILES)
            .multiple(true)
            .takes_value(true)
            .min_values(2)
            .required(true)
        )
}

fn determine_overwrite_mode(matches: &ArgMatches) -> OverwriteMode {
    // This does not exactly match the GNU implementation:
    // The GNU mv defaults to Force, but if more than one of the
    // overwrite options are supplied, only the last takes effect.
    // To default to no-clobber in that situation seems safer:
    //
    if matches.is_present(OPT_NO_CLOBBER) {
        OverwriteMode::NoClobber
    } else if matches.is_present(OPT_INTERACTIVE) {
        OverwriteMode::Interactive
    } else {
        OverwriteMode::Force
    }
}

fn exec(files: &[PathBuf], b: Behavior) -> i32 {
    if let Some(ref name) = b.target_dir {
        return move_files_into_dir(files, &PathBuf::from(name), &b);
    }
    match files.len() {
        /* case 0/1 are not possible thanks to clap */
        2 => {
            let source = &files[0];
            let target = &files[1];
            // Here we use the `symlink_metadata()` method instead of `exists()`,
            // since it handles dangling symlinks correctly. The method gives an
            // `Ok()` results unless the source does not exist, or the user
            // lacks permission to access metadata.
            if source.symlink_metadata().is_err() {
                show_error!(
                    "cannot stat '{}': No such file or directory",
                    source.display()
                );
                return 1;
            }

            if target.is_dir() {
                if b.no_target_dir {
                    if !source.is_dir() {
                        show_error!(
                            "cannot overwrite directory '{}' with non-directory",
                            target.display()
                        );
                        return 1;
                    }

                    return match rename(source, target, &b) {
                        Err(e) => {
                            show_error!(
                                "cannot move '{}' to '{}': {}",
                                source.display(),
                                target.display(),
                                e.to_string()
                            );
                            1
                        }
                        _ => 0,
                    };
                }

                return move_files_into_dir(&[source.clone()], target, &b);
            } else if target.exists() && source.is_dir() {
                show_error!(
                    "cannot overwrite non-directory '{}' with directory '{}'",
                    target.display(),
                    source.display()
                );
                return 1;
            }

            if let Err(e) = rename(source, target, &b) {
                show_error!("{}", e);
                return 1;
            }
        }
        _ => {
            if b.no_target_dir {
                show_error!(
                    "mv: extra operand '{}'\n\
                     Try '{} --help' for more information.",
                    files[2].display(),
                    executable!()
                );
                return 1;
            }
            let target_dir = files.last().unwrap();
            move_files_into_dir(&files[..files.len() - 1], target_dir, &b);
        }
    }
    0
}

fn move_files_into_dir(files: &[PathBuf], target_dir: &Path, b: &Behavior) -> i32 {
    if !target_dir.is_dir() {
        show_error!("target '{}' is not a directory", target_dir.display());
        return 1;
    }

    let mut all_successful = true;
    for sourcepath in files.iter() {
        let targetpath = match sourcepath.file_name() {
            Some(name) => target_dir.join(name),
            None => {
                show_error!(
                    "cannot stat '{}': No such file or directory",
                    sourcepath.display()
                );

                all_successful = false;
                continue;
            }
        };

        if let Err(e) = rename(sourcepath, &targetpath, b) {
            show_error!(
                "cannot move '{}' to '{}': {}",
                sourcepath.display(),
                targetpath.display(),
                e.to_string()
            );
            all_successful = false;
        }
    }

    if all_successful {
        0
    } else {
        1
    }
}

fn rename(from: &Path, to: &Path, b: &Behavior) -> io::Result<()> {
    let mut backup_path = None;

    if to.exists() {
        match b.overwrite {
            OverwriteMode::NoClobber => return Ok(()),
            OverwriteMode::Interactive => {
                println!("{}: overwrite '{}'? ", executable!(), to.display());
                if !read_yes() {
                    return Ok(());
                }
            }
            OverwriteMode::Force => {}
        };

        backup_path = backup_control::get_backup_path(b.backup, to, &b.suffix);
        if let Some(ref backup_path) = backup_path {
            rename_with_fallback(to, backup_path)?;
        }

        if b.update && fs::metadata(from)?.modified()? <= fs::metadata(to)?.modified()? {
            return Ok(());
        }
    }

    // "to" may no longer exist if it was backed up
    if to.exists() && to.is_dir() {
        // normalize behavior between *nix and windows
        if from.is_dir() {
            if is_empty_dir(to) {
                fs::remove_dir(to)?
            } else {
                return Err(io::Error::new(io::ErrorKind::Other, "Directory not empty"));
            }
        }
    }

    rename_with_fallback(from, to)?;

    if b.verbose {
        print!("'{}' -> '{}'", from.display(), to.display());
        match backup_path {
            Some(path) => println!(" (backup: '{}')", path.display()),
            None => println!(),
        }
    }
    Ok(())
}

/// A wrapper around `fs::rename`, so that if it fails, we try falling back on
/// copying and removing.
fn rename_with_fallback(from: &Path, to: &Path) -> io::Result<()> {
    if fs::rename(from, to).is_err() {
        // Get metadata without following symlinks
        let metadata = from.symlink_metadata()?;
        let file_type = metadata.file_type();

        if file_type.is_symlink() {
            rename_symlink_fallback(from, to)?;
        } else if file_type.is_dir() {
            // We remove the destination directory if it exists to match the
            // behavior of `fs::rename`. As far as I can tell, `fs_extra`'s
            // `move_dir` would otherwise behave differently.
            if to.exists() {
                fs::remove_dir_all(to)?;
            }
            let options = DirCopyOptions {
                // From the `fs_extra` documentation:
                // "Recursively copy a directory with a new name or place it
                // inside the destination. (same behaviors like cp -r in Unix)"
                copy_inside: true,
                ..DirCopyOptions::new()
            };
            if let Err(err) = move_dir(from, to, &options) {
                return match err.kind {
                    fs_extra::error::ErrorKind::PermissionDenied => Err(io::Error::new(
                        io::ErrorKind::PermissionDenied,
                        "Permission denied",
                    )),
                    _ => Err(io::Error::new(io::ErrorKind::Other, format!("{:?}", err))),
                };
            }
        } else {
            fs::copy(from, to).and_then(|_| fs::remove_file(from))?;
        }
    }
    Ok(())
}

/// Move the given symlink to the given destination. On Windows, dangling
/// symlinks return an error.
#[inline]
fn rename_symlink_fallback(from: &Path, to: &Path) -> io::Result<()> {
    let path_symlink_points_to = fs::read_link(from)?;
    #[cfg(unix)]
    {
        unix::fs::symlink(&path_symlink_points_to, &to).and_then(|_| fs::remove_file(&from))?;
    }
    #[cfg(windows)]
    {
        if path_symlink_points_to.exists() {
            if path_symlink_points_to.is_dir() {
                windows::fs::symlink_dir(&path_symlink_points_to, &to)?;
            } else {
                windows::fs::symlink_file(&path_symlink_points_to, &to)?;
            }
            fs::remove_file(&from)?;
        } else {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "can't determine symlink type, since it is dangling",
            ));
        }
    }
    #[cfg(not(any(windows, unix)))]
    {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "your operating system does not support symlinks",
        ));
    }
    Ok(())
}

fn read_yes() -> bool {
    let mut s = String::new();
    match stdin().read_line(&mut s) {
        Ok(_) => match s.chars().next() {
            Some(x) => x == 'y' || x == 'Y',
            _ => false,
        },
        _ => false,
    }
}

fn is_empty_dir(path: &Path) -> bool {
    match fs::read_dir(path) {
        Ok(contents) => contents.peekable().peek().is_none(),
        Err(_e) => false,
    }
}
<|endoftext|>


<commit_before>use {Cursive, Printer, With};
use align::HAlign;
use direction::Direction;
use event::*;
use theme::ColorStyle;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::View;

/// Simple text label with a callback when <Enter> is pressed.
///
/// A button shows its content in a single line and has a fixed size.
///
/// # Examples
///
/// ```
/// # use cursive::views::Button;
/// let quit_button = Button::new("Quit", |s| s.quit());
/// ```
pub struct Button {
    label: String,
    callback: Callback,
    enabled: bool,
}

impl Button {
    /// Creates a new button with the given content and callback.
    pub fn new<F, R, S: Into<String>>(label: S, cb: F) -> Self
    where
        F: 'static + Fn(&mut Cursive) -> R,
    {
        let label = label.into();
        Self::new_raw(format!("<{}>", label), cb)
    }

    /// Creates a new button without angle brackets.
    pub fn new_raw<F, R, S: Into<String>>(label: S, cb: F) -> Self
    where
        F: 'static + Fn(&mut Cursive) -> R,
    {
        Button {
            label: label.into(),
            callback: Callback::from_fn(cb),
            enabled: true,
        }
    }

    /// Sets the function to be called when the button is pressed.
    ///
    /// Replaces the previous callback.
    pub fn set_callback<F>(&mut self, cb: F)
    where
        F: Fn(&mut Cursive) + 'static,
    {
        self.callback = Callback::from_fn(cb);
    }

    /// Disables this view.
    ///
    /// A disabled view cannot be selected.
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Disables this view.
    ///
    /// Chainable variant.
    pub fn disabled(self) -> Self {
        self.with(Self::disable)
    }

    /// Re-enables this view.
    pub fn enable(&mut self) {
        self.enabled = true;
    }

    /// Enable or disable this view.
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    /// Returns `true` if this view is enabled.
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Returns the label for this button.
    ///
    /// Includes brackets.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use cursive::views::Button;
    /// let button = Button::new("Quit", |s| s.quit());
    /// assert_eq!(button.label(), "<Quit>");
    /// ```
    pub fn label(&self) -> &str {
        &self.label
    }

    fn req_size(&self) -> Vec2 {
        Vec2::new(self.label.width(), 1)
    }
}

impl View for Button {
    fn draw(&self, printer: &Printer) {
        if printer.size.x == 0 {
            return;
        }

        let style = if !self.enabled {
            ColorStyle::Secondary
        } else if !printer.focused {
            ColorStyle::Primary
        } else {
            ColorStyle::Highlight
        };

        let offset =
            HAlign::Center.get_offset(self.label.len(), printer.size.x);

        printer.with_color(style, |printer| {
            printer.print((offset, 0), &self.label);
        });
    }

    fn required_size(&mut self, _: Vec2) -> Vec2 {
        // Meh. Fixed size we are.
        self.req_size()
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        // eprintln!("{:?}", event);
        // eprintln!("{:?}", self.req_size());
        match event {
            // 10 is the ascii code for '\n', that is the return key
            Event::Key(Key::Enter) => {
                EventResult::Consumed(Some(self.callback.clone()))
            }
            Event::Mouse {
                event: MouseEvent::Release(MouseButton::Left),
                position,
                offset,
            } if position.fits_in_rect(offset, self.req_size()) =>
            {
                EventResult::Consumed(Some(self.callback.clone()))
            }
            _ => EventResult::Ignored,
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled
    }
}
<commit_msg>Add Button::set_label<commit_after>use {Cursive, Printer, With};
use align::HAlign;
use direction::Direction;
use event::*;
use theme::ColorStyle;
use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::View;

/// Simple text label with a callback when <Enter> is pressed.
///
/// A button shows its content in a single line and has a fixed size.
///
/// # Examples
///
/// ```
/// # use cursive::views::Button;
/// let quit_button = Button::new("Quit", |s| s.quit());
/// ```
pub struct Button {
    label: String,
    callback: Callback,
    enabled: bool,
}

impl Button {
    /// Creates a new button with the given content and callback.
    pub fn new<F, R, S>(label: S, cb: F) -> Self
    where
        F: 'static + Fn(&mut Cursive) -> R,
        S: Into<String>,
    {
        let label = label.into();
        Self::new_raw(format!("<{}>", label), cb)
    }

    /// Creates a new button without angle brackets.
    pub fn new_raw<F, R, S: Into<String>>(label: S, cb: F) -> Self
    where
        F: 'static + Fn(&mut Cursive) -> R,
    {
        Button {
            label: label.into(),
            callback: Callback::from_fn(cb),
            enabled: true,
        }
    }

    /// Sets the function to be called when the button is pressed.
    ///
    /// Replaces the previous callback.
    pub fn set_callback<F>(&mut self, cb: F)
    where
        F: Fn(&mut Cursive) + 'static,
    {
        self.callback = Callback::from_fn(cb);
    }

    /// Disables this view.
    ///
    /// A disabled view cannot be selected.
    pub fn disable(&mut self) {
        self.enabled = false;
    }

    /// Disables this view.
    ///
    /// Chainable variant.
    pub fn disabled(self) -> Self {
        self.with(Self::disable)
    }

    /// Re-enables this view.
    pub fn enable(&mut self) {
        self.enabled = true;
    }

    /// Enable or disable this view.
    pub fn set_enabled(&mut self, enabled: bool) {
        self.enabled = enabled;
    }

    /// Returns `true` if this view is enabled.
    pub fn is_enabled(&self) -> bool {
        self.enabled
    }

    /// Returns the label for this button.
    ///
    /// Includes brackets.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use cursive::views::Button;
    /// let button = Button::new("Quit", |s| s.quit());
    /// assert_eq!(button.label(), "<Quit>");
    /// ```
    pub fn label(&self) -> &str {
        &self.label
    }

    /// Sets the label to the given value.
    ///
    /// This will include brackets.
    pub fn set_label<S>(&mut self, label: S)
    where
        S: Into<String>,
    {
        self.set_label_raw(format!("<{}>", label.into()));
    }

    /// Sets the label exactly to the given value.
    ///
    /// This will not include brackets.
    pub fn set_label_raw<S>(&mut self, label: S)
    where
        S: Into<String>,
    {
        self.label = label.into();
    }

    fn req_size(&self) -> Vec2 {
        Vec2::new(self.label.width(), 1)
    }
}

impl View for Button {
    fn draw(&self, printer: &Printer) {
        if printer.size.x == 0 {
            return;
        }

        let style = if !self.enabled {
            ColorStyle::Secondary
        } else if !printer.focused {
            ColorStyle::Primary
        } else {
            ColorStyle::Highlight
        };

        let offset =
            HAlign::Center.get_offset(self.label.len(), printer.size.x);

        printer.with_color(style, |printer| {
            printer.print((offset, 0), &self.label);
        });
    }

    fn required_size(&mut self, _: Vec2) -> Vec2 {
        // Meh. Fixed size we are.
        self.req_size()
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        // eprintln!("{:?}", event);
        // eprintln!("{:?}", self.req_size());
        match event {
            // 10 is the ascii code for '\n', that is the return key
            Event::Key(Key::Enter) => {
                EventResult::Consumed(Some(self.callback.clone()))
            }
            Event::Mouse {
                event: MouseEvent::Release(MouseButton::Left),
                position,
                offset,
            } if position.fits_in_rect(offset, self.req_size()) =>
            {
                EventResult::Consumed(Some(self.callback.clone()))
            }
            _ => EventResult::Ignored,
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        self.enabled
    }
}
<|endoftext|>


<commit_before>#![feature(old_path)]

extern crate piston;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::cell::RefCell;
use opengl_graphics::{
    GlGraphics,
    OpenGL,
    Texture,
};
use sdl2_window::Sdl2Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        piston::window::WindowSettings {
            title: "Image".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    let image = Path::new("./bin/assets/rust-logo.png");
    let image = Texture::from_path(&image).unwrap();
    let ref mut gl = GlGraphics::new(opengl);
    let window = RefCell::new(window);
    for e in piston::events(&window) {
        use piston::event::RenderEvent;

        if let Some(args) = e.render_args() {
            use graphics::*;
            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                graphics::clear([1.0; 4], gl);
                graphics::image(&image, &c, gl);
            });
        };
    }
}
<commit_msg>Upgraded "image" example to latest piston-graphics<commit_after>#![feature(old_path)]

extern crate piston;
extern crate graphics;
extern crate sdl2_window;
extern crate opengl_graphics;

use std::cell::RefCell;
use opengl_graphics::{
    GlGraphics,
    OpenGL,
    Texture,
};
use sdl2_window::Sdl2Window;

fn main() {
    let opengl = OpenGL::_3_2;
    let window = Sdl2Window::new(
        opengl,
        piston::window::WindowSettings {
            title: "Image".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            samples: 0,
        }
    );

    let image = Path::new("./bin/assets/rust-logo.png");
    let image = Texture::from_path(&image).unwrap();
    let ref mut gl = GlGraphics::new(opengl);
    let window = RefCell::new(window);
    for e in piston::events(&window) {
        use piston::event::RenderEvent;

        if let Some(args) = e.render_args() {
            use graphics::*;
            gl.draw([0, 0, args.width as i32, args.height as i32], |c, gl| {
                graphics::clear([1.0; 4], gl);
                graphics::image(&image, c.transform, gl);
            });
        };
    }
}
<|endoftext|>


<commit_before>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};

#[derive(Debug)]
pub struct Aspect {
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String) -> Aspect {
        Aspect {
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        unimplemented!()
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        unimplemented!()
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        unimplemented!()
    }
}

<commit_msg>Impl StoreIdAccessor::access() for Aspect<commit_after>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

#[derive(Debug)]
pub struct Aspect {
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String) -> Aspect {
        Aspect {
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::StoreIdAccess(accessor) => try!(accessor.access(id)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        unimplemented!()
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        unimplemented!()
    }
}

<|endoftext|>


<commit_before>//! Provides the following services:
//! * Starts processes on startup
//! * Program status annoncements
//! * Program running status queries

#![no_std]
#![feature(alloc_prelude)]
#![deny(unused_must_use)]

#[macro_use]
extern crate alloc;

#[macro_use]
extern crate libd7;

use alloc::collections::VecDeque;
use alloc::prelude::v1::*;
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};

use libd7::{
    attachment::{self, RequestFileOperation, ResponseFileOperation},
    d7abi::fs::FileDescriptor,
    fs, pinecone,
    process::{Process, ProcessId},
    select,
    syscall::SyscallResult,
};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct ServiceName(String);

/// Analogous to systemd service files
#[derive(Debug, Clone, Deserialize, Serialize)]
struct ServiceDefinition {
    /// Name of the service
    name: ServiceName,
    /// A (short) description of the service
    description: Option<String>,
    /// Requires these services to be running before starting
    requires: HashSet<String>,
    /// Absolute path to the executable
    executable: String,
}

#[derive(Debug)]
struct RunningService {
    name: ServiceName,
    process: Process,
    startup_complete: bool,
}

#[derive(Debug)]
struct Services {
    definitions: Vec<ServiceDefinition>,
    start_queue: VecDeque<ServiceName>,
    running: Vec<RunningService>,
}
impl Services {
    pub fn new(path: &str) -> SyscallResult<Self> {
        let s = fs::read(path)?;
        let definitions: Vec<ServiceDefinition> = serde_json::from_slice(&s).unwrap();
        let start_queue = definitions.iter().map(|s| s.name.clone()).collect();

        Ok(Self {
            definitions,
            start_queue,
            running: Vec::new(),
        })
    }

    // All descriptors. Used for `select!`.
    fn fds(&self) -> HashSet<FileDescriptor> {
        self.running.iter().map(|r| r.process.fd).collect()
    }

    fn definition_by_name(&self, name: &ServiceName) -> Option<ServiceDefinition> {
        for def in &self.definitions {
            if def.name == *name {
                return Some(def.clone());
            }
        }
        return None;
    }

    fn get_running(&mut self, name: &ServiceName) -> Option<&RunningService> {
        for r in &self.running {
            if r.name == *name {
                return Some(&r);
            }
        }
        return None;
    }

    /// Check requirements
    fn are_requirements_up(&mut self, def: &ServiceDefinition) -> bool {
        for req in &def.requires {
            if self.get_running(&def.name).is_none() {
                return false;
            }
        }
        return true;
    }

    /// Start a service if it's not already running
    /// The requirements MUST BE met before calling this
    fn start(&mut self, def: ServiceDefinition) {
        if self.get_running(&def.name).is_some() {
            return;
        }

        let process = Process::spawn(&def.executable).unwrap();
        self.running.push(RunningService {
            name: def.name,
            process,
            startup_complete: false,
        });
    }

    /// Returns true if no more if the called should block
    /// TODO: ^ better explanation
    fn step(&mut self) -> bool {
        if let Some(name) = self.start_queue.pop_front() {
            let def = self.definition_by_name(&name).unwrap();
            if self.are_requirements_up(&def) {
                self.start(def);
                return false;
            }
        }

        return true;
    }

    fn on_process_up(&mut self, pid: ProcessId) {
        for r in &mut self.running {
            if r.process.pid() == pid {
                r.startup_complete = true;
            }
        }
        println!("Process with returned fd was not runniong");
    }

    fn on_process_completed(&mut self, completed_fd: FileDescriptor) {
        let index = self
            .running
            .iter()
            .position(|r| r.process.fd == completed_fd)
            .expect("Process with returned fd was not runniong");

        let service = self.running.remove(index);
        todo!("PROC WAIT {:?}", service.name);
        let result = service.process.wait();

        todo!("Process over, result {:?}", result);
    }
}

fn on_request(a: &attachment::Leaf, services: &mut Services) {
    let req = a.next_request().unwrap();
    match &req.operation {
        RequestFileOperation::Write(data) => {
            // Any writes should (for now, at least)
            // be "service has started up" announcements
            assert!(data == &[1]);
            services.on_process_up(req.sender.pid);
            a.reply(req.response(ResponseFileOperation::Write(1)))
                .unwrap();
        }
        RequestFileOperation::Close => {}
        other => panic!("Unsupported operation to a netd ({:?})", other),
    }
}

#[no_mangle]
fn main() -> ! {
    println!("Service daemon starting");

    let mut services = Services::new("/mnt/staticfs/startup_services.json").unwrap();
    let a = attachment::Leaf::new("/srv/service").unwrap();

    loop {
        let should_block = services.step();

        // Only block if there are no processes in startup queue
        if should_block {
            select! {
                any(services.fds()) -> fd => services.on_process_completed(fd),
                one(a.fd) => on_request(&a, &mut services)
            };
        } else {
            select! {
                any(services.fds()) -> fd => services.on_process_completed(fd),
                one(a.fd) => on_request(&a, &mut services),
                would_block => {}
            };
        }
    }
}
<commit_msg>Typofix<commit_after>//! Provides the following services:
//! * Starts processes on startup
//! * Program status annoncements
//! * Program running status queries

#![no_std]
#![feature(alloc_prelude)]
#![deny(unused_must_use)]

#[macro_use]
extern crate alloc;

#[macro_use]
extern crate libd7;

use alloc::collections::VecDeque;
use alloc::prelude::v1::*;
use hashbrown::HashSet;
use serde::{Deserialize, Serialize};

use libd7::{
    attachment::{self, RequestFileOperation, ResponseFileOperation},
    d7abi::fs::FileDescriptor,
    fs, pinecone,
    process::{Process, ProcessId},
    select,
    syscall::SyscallResult,
};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct ServiceName(String);

/// Analogous to systemd service files
#[derive(Debug, Clone, Deserialize, Serialize)]
struct ServiceDefinition {
    /// Name of the service
    name: ServiceName,
    /// A (short) description of the service
    description: Option<String>,
    /// Requires these services to be running before starting
    requires: HashSet<String>,
    /// Absolute path to the executable
    executable: String,
}

#[derive(Debug)]
struct RunningService {
    name: ServiceName,
    process: Process,
    startup_complete: bool,
}

#[derive(Debug)]
struct Services {
    definitions: Vec<ServiceDefinition>,
    start_queue: VecDeque<ServiceName>,
    running: Vec<RunningService>,
}
impl Services {
    pub fn new(path: &str) -> SyscallResult<Self> {
        let s = fs::read(path)?;
        let definitions: Vec<ServiceDefinition> = serde_json::from_slice(&s).unwrap();
        let start_queue = definitions.iter().map(|s| s.name.clone()).collect();

        Ok(Self {
            definitions,
            start_queue,
            running: Vec::new(),
        })
    }

    // All descriptors. Used for `select!`.
    fn fds(&self) -> HashSet<FileDescriptor> {
        self.running.iter().map(|r| r.process.fd).collect()
    }

    fn definition_by_name(&self, name: &ServiceName) -> Option<ServiceDefinition> {
        for def in &self.definitions {
            if def.name == *name {
                return Some(def.clone());
            }
        }
        return None;
    }

    fn get_running(&mut self, name: &ServiceName) -> Option<&RunningService> {
        for r in &self.running {
            if r.name == *name {
                return Some(&r);
            }
        }
        return None;
    }

    /// Check requirements
    fn are_requirements_up(&mut self, def: &ServiceDefinition) -> bool {
        for req in &def.requires {
            if self.get_running(&def.name).is_none() {
                return false;
            }
        }
        return true;
    }

    /// Start a service if it's not already running
    /// The requirements MUST BE met before calling this
    fn start(&mut self, def: ServiceDefinition) {
        if self.get_running(&def.name).is_some() {
            return;
        }

        let process = Process::spawn(&def.executable).unwrap();
        self.running.push(RunningService {
            name: def.name,
            process,
            startup_complete: false,
        });
    }

    /// Returns true if no more if the called should block
    /// TODO: ^ better explanation
    fn step(&mut self) -> bool {
        if let Some(name) = self.start_queue.pop_front() {
            let def = self.definition_by_name(&name).unwrap();
            if self.are_requirements_up(&def) {
                self.start(def);
                return false;
            }
        }

        return true;
    }

    fn on_process_up(&mut self, pid: ProcessId) {
        for r in &mut self.running {
            if r.process.pid() == pid {
                r.startup_complete = true;
            }
        }
        println!("Process with returned fd was not running");
    }

    fn on_process_completed(&mut self, completed_fd: FileDescriptor) {
        let index = self
            .running
            .iter()
            .position(|r| r.process.fd == completed_fd)
            .expect("Process with returned fd was not running");

        let service = self.running.remove(index);
        todo!("PROC WAIT {:?}", service.name);
        let result = service.process.wait();

        todo!("Process over, result {:?}", result);
    }
}

fn on_request(a: &attachment::Leaf, services: &mut Services) {
    let req = a.next_request().unwrap();
    match &req.operation {
        RequestFileOperation::Write(data) => {
            // Any writes should (for now, at least)
            // be "service has started up" announcements
            assert!(data == &[1]);
            services.on_process_up(req.sender.pid);
            a.reply(req.response(ResponseFileOperation::Write(1)))
                .unwrap();
        }
        RequestFileOperation::Close => {}
        other => panic!("Unsupported operation to a netd ({:?})", other),
    }
}

#[no_mangle]
fn main() -> ! {
    println!("Service daemon starting");

    let mut services = Services::new("/mnt/staticfs/startup_services.json").unwrap();
    let a = attachment::Leaf::new("/srv/service").unwrap();

    loop {
        let should_block = services.step();

        // Only block if there are no processes in startup queue
        if should_block {
            select! {
                any(services.fds()) -> fd => services.on_process_completed(fd),
                one(a.fd) => on_request(&a, &mut services)
            };
        } else {
            select! {
                any(services.fds()) -> fd => services.on_process_completed(fd),
                one(a.fd) => on_request(&a, &mut services),
                would_block => {}
            };
        }
    }
}
<|endoftext|>


<commit_before>pub mod client;
mod json;
<commit_msg>matrix: expose events mod<commit_after>pub mod client;
pub mod events;
mod json;
<|endoftext|>


<commit_before>use std::str::Chars;
use std::io::Reader;
use std::str::SendStr;
use span::Span;
use std::str::Owned;

/// A `Token` represents a special symbol within
/// the language. These do not contain any extra metadata
/// with it, that's stored elsewhere.
///
/// Unlike most languages, characters like `+`, `-`, `*`, `/`
/// are **not** considered tokens, but, in fact regular identifiers.
/// This allows us to implement these without the compiler
/// to have any sort of knowledge of them.
#[deriving(PartialEq, Show, Clone)]
pub enum Token {
    Ident(String),
    Str(String),
    Keyword(SendStr),
    Colon,
    Caret,
    LParen,
    RParen,
    Noop,
    Done
}

/// The lexer is the system that has any notion of an input stream. The
/// stream can come from many sources, thus, we use the Reader trait
/// that is implemented for strings, files, tcp, etc...
///
/// The lexer should also not consume the whole stream at once, as
/// that will be a bottleneck for larger input sources.
pub struct Lexer<'a> {
    source: &'a str,
    iter: Chars<'a>,
    token: Token,
    done: bool,
    span: Span
}

impl<'a> Lexer<'a> {
    /// Return a brand new lexer containing an iterator
    /// of chars and a bunch of state to keep track of (such
    /// as the current Span).
    pub fn new(source: &'a str) -> Lexer<'a> {
        Lexer {
            source: source,
            iter: source.chars(),
            token: Noop,
            done: false,
            span: Span::new(0, 0)
        }
    }

    pub fn collect(&mut self) -> Vec<Token> {
        let mut vec = Vec::new();

        self.bump().while_some(|tok| {
            if tok != Done && tok != Noop {
                vec.push(tok);
            }

            if !self.done {
                self.bump()
            } else {
                None
            }
        });

        vec
    }

    /// Parse and fetch the next token in the text stream.
    /// This doesn't return the token, it stores it
    /// in the lexer.
    pub fn bump(&mut self) -> Option<Token> {
        // Fetch the next token so that we can start matching
        // against it. If we don't return anything, we'll set
        // the token to `Done`, resulting in a completed lexer.
        //
        // FIXME: I don't think I'm checking for a `None` case
        //        anywhere else.
        let c = match self.iter.next() {
            Some(c) => c,
            None => {
                self.done  = true;
                self.token = Done;
                return None;
            }
        };

        match c {
            ' ' => { return self.bump() },
            '\n' => { return self.bump() },
            '\t' => { return self.bump() },
            // Parse some simple tokens.
            '(' => { self.token = LParen },
            ')' => { self.token = RParen },
            '^' => { self.token = Caret },
            // Parse a simple keyword. Keywords are somewhat like
            // strings, but, they're more limited in the format, but
            // a lot nicer to use.
            ':' => {
                let mut keyword = String::new();
                self.iter.next().while_some(|a| {
                    if self.is_keyword(a) {
                        keyword.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                self.token = Keyword(Owned(keyword));
            },
            // Parse the beginning of a string until we find the ending
            // quote. Anything can be placed within the double quotes.
            //
            // This doesn't support any escaping yet, so that'll break.
            // To support it, we'll need to keep track of the previous
            // token. Finding a `\` character will result in it
            // being saved until we look at the next character; which
            // we'll look back at the previous character to figure
            // out our current state. Escaping will only be set if
            // it matches a good escape pattern.
            '"' => {
                let mut concat = String::new();
                self.iter.next().while_some(|a| {
                    if a == '"' {
                        None
                    } else {
                        concat.push_char(a);
                        self.iter.next()
                    }
                });

                self.token = Str(concat);
            },
            // An identifier is composed of any
            // characters that contain no whitespace
            // and doesn't start with the designated
            // characters above.
            ch => {
                let mut ident = String::new();

                if self.is_ident(ch) {
                    ident.push_char(ch);
                } else {
                    // Push an `Unknown` token here.
                }

                self.iter.next().while_some(|a| {
                    if self.is_ident(a) {
                        ident.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                if ident.len() > 0 {
                    self.token = Ident(ident);
                }
            }
        }

        Some(self.token.clone())
    }

    /// Determine if a char is allowed within an identifier.
    /// An identifier can be used for:
    ///
    /// * Function names
    /// * Binding names
    /// * Other
    ///
    /// Because many of the built-in forms have identifiers
    /// such as `+`, `-`, `*`, `/`, we can't make these tokens,
    /// nor can we exclude them from the identifier format.
    pub fn is_ident(&self, ch: char) -> bool {
        if ch.is_whitespace() || ch == ':' || ch == '(' || ch == ')' || ch == '"'
            || ch == '@' || ch == '!' || ch == '\n' || ch == '\t' {
            false
        } else {
            true
        }
    }

    /// Verify if the char is within the allowed character set for keywords. This
    /// only checks a single character at a time.
    #[inline]
    pub fn is_keyword(&self, a: char) -> bool {
        if a.is_whitespace() || a == '(' || a == ')' || a == '^' || a == ':' {
            false
        } else {
            true
        }
    }

    /// Expect a given token to come next, otherwise
    /// fail. Failing will result in a formatted
    /// error message that uses the Span and lets
    /// the user know what went wrong.
    pub fn expect(&mut self, tok: &Token) {
        if self.token == *tok {
            self.bump();
        } else {
            fail!("Expected {} but found {}", *tok, self.token);
        }
    }

    /// Optionally eat a given token. If the token is not
    /// found, this will simply return false and
    /// won't fail.
    pub fn eat(&mut self, tok: &Token) -> bool {
        if self.token == *tok {
            self.bump();
            true
        } else {
            false
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use span::Span;
    use std::str::{Slice, Owned};

    #[test]
    fn noop() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.token, Noop);
    }

    #[test]
    fn zeroed_span() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.span, Span::new(0, 0));
    }

    #[test]
    fn bump_empty() {
        let mut lexer = Lexer::new("");
        lexer.bump();
        assert_eq!(lexer.token, Done);
    }

    #[test]
    fn bump_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.token, LParen);
    }

    #[test]
    fn bump_rparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        assert_eq!(lexer.token, RParen);
    }

    #[test]
    fn bump_simple_keyword() {
        let mut lexer = Lexer::new(":foo");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo")));
    }

    #[test]
    fn bump_keyword_dash() {
        let mut lexer = Lexer::new(":foo-");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-")));
    }

    #[test]
    fn bump_keyword_dash_middle() {
        let mut lexer = Lexer::new(":foo-bar");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-bar")));
    }

    #[test]
    fn bump_string() {
        let mut lexer = Lexer::new("\"hah-w091:--:\"");
        lexer.bump();
        assert_eq!(lexer.token, Str("hah-w091:--:".to_string()));
    }

    #[test]
    fn bump_ident() {
        let mut lexer = Lexer::new("foo-bar-&#^");
        lexer.bump();
        assert_eq!(lexer.token, Ident("foo-bar-&#^".to_string()));
    }

    #[test]
    fn should_eat_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.eat(&LParen), true);
    }

    #[test]
    #[should_fail]
    fn should_expect_lparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        lexer.expect(&LParen);
    }

    #[test]
    fn should_expect() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        lexer.expect(&LParen);
    }
}
<commit_msg>Added vectors and i64-bit numbers<commit_after>use std::str::Chars;
use std::io::Reader;
use std::str::SendStr;
use span::Span;
use std::str::Owned;
use std::from_str::from_str;

/// A `Token` represents a special symbol within
/// the language. These do not contain any extra metadata
/// with it, that's stored elsewhere.
///
/// Unlike most languages, characters like `+`, `-`, `*`, `/`
/// are **not** considered tokens, but, in fact regular identifiers.
/// This allows us to implement these without the compiler
/// to have any sort of knowledge of them.
#[deriving(PartialEq, Show, Clone)]
pub enum Token {
    Ident(String),
    Str(String),
    Integer(i64),
    LBracket,
    RBracket,
    Comma,
    Keyword(SendStr),
    Colon,
    Caret,
    LParen,
    RParen,
    Noop,
    Done
}

/// The lexer is the system that has any notion of an input stream. The
/// stream can come from many sources, thus, we use the Reader trait
/// that is implemented for strings, files, tcp, etc...
///
/// The lexer should also not consume the whole stream at once, as
/// that will be a bottleneck for larger input sources.
pub struct Lexer<'a> {
    source: &'a str,
    iter: Chars<'a>,
    token: Token,
    done: bool,
    span: Span
}

impl<'a> Lexer<'a> {
    /// Return a brand new lexer containing an iterator
    /// of chars and a bunch of state to keep track of (such
    /// as the current Span).
    pub fn new(source: &'a str) -> Lexer<'a> {
        Lexer {
            source: source,
            iter: source.chars(),
            token: Noop,
            done: false,
            span: Span::new(0, 0)
        }
    }

    pub fn collect(&mut self) -> Vec<Token> {
        let mut vec = Vec::new();

        self.bump().while_some(|tok| {
            if tok != Done && tok != Noop {
                vec.push(tok);
            }

            if !self.done {
                self.bump()
            } else {
                None
            }
        });

        vec
    }

    /// Parse and fetch the next token in the text stream.
    /// This doesn't return the token, it stores it
    /// in the lexer.
    pub fn bump(&mut self) -> Option<Token> {
        // Fetch the next token so that we can start matching
        // against it. If we don't return anything, we'll set
        // the token to `Done`, resulting in a completed lexer.
        //
        // FIXME: I don't think I'm checking for a `None` case
        //        anywhere else.
        let c = match self.iter.next() {
            Some(c) => c,
            None => {
                self.done  = true;
                self.token = Done;
                return None;
            }
        };

        match c {
            ' ' => { return self.bump() },
            '\n' => { return self.bump() },
            '\t' => { return self.bump() },
            ',' => { self.token = Comma },
            // Parse some simple tokens.
            '(' => { self.token = LParen },
            ')' => { self.token = RParen },
            '^' => { self.token = Caret },
            '[' => { self.token = LBracket },
            ']' => { self.token = RBracket },
            num @ '0'..'9' => {
                let mut combined = String::new();
                combined.push_char(num);

                self.iter.next().while_some(|n| {

                    match n {
                       '0'..'9' => combined.push_char(n),
                        '\n' => { return None; },
                        n if n.is_whitespace() => { return None; },
                        _ => fail!("A number must be isolated from other tokens.")
                    }

                    self.iter.next()
                });

                let num = match from_str::<i64>(combined.as_slice()) {
                    Some(num) => num,
                    None => fail!("Not a number.")
                };

                self.token = Integer(num);
            },
            // Parse a simple keyword. Keywords are somewhat like
            // strings, but, they're more limited in the format, but
            // a lot nicer to use.
            ':' => {
                let mut keyword = String::new();
                self.iter.next().while_some(|a| {
                    if self.is_keyword(a) {
                        keyword.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                self.token = Keyword(Owned(keyword));
            },
            // Parse the beginning of a string until we find the ending
            // quote. Anything can be placed within the double quotes.
            //
            // This doesn't support any escaping yet, so that'll break.
            // To support it, we'll need to keep track of the previous
            // token. Finding a `\` character will result in it
            // being saved until we look at the next character; which
            // we'll look back at the previous character to figure
            // out our current state. Escaping will only be set if
            // it matches a good escape pattern.
            '"' => {
                let mut concat = String::new();
                self.iter.next().while_some(|a| {
                    if a == '"' {
                        None
                    } else {
                        concat.push_char(a);
                        self.iter.next()
                    }
                });

                self.token = Str(concat);
            },
            // An identifier is composed of any
            // characters that contain no whitespace
            // and doesn't start with the designated
            // characters above.
            ch => {
                let mut ident = String::new();

                if self.is_ident(ch) {
                    ident.push_char(ch);
                } else {
                    // Push an `Unknown` token here.
                }

                self.iter.next().while_some(|a| {
                    if self.is_ident(a) {
                        ident.push_char(a);
                        self.iter.next()
                    } else {
                        None
                    }
                });

                if ident.len() > 0 {
                    self.token = Ident(ident);
                }
            }
        }

        Some(self.token.clone())
    }

    /// Determine if a char is allowed within an identifier.
    /// An identifier can be used for:
    ///
    /// * Function names
    /// * Binding names
    /// * Other
    ///
    /// Because many of the built-in forms have identifiers
    /// such as `+`, `-`, `*`, `/`, we can't make these tokens,
    /// nor can we exclude them from the identifier format.
    pub fn is_ident(&self, ch: char) -> bool {
        if ch.is_whitespace() || ch == ':' || ch == '(' || ch == ')' || ch == '"'
            || ch == '@' || ch == '!' || ch == '\n' || ch == '\t' || ch == ',' {
            false
        } else {
            true
        }
    }

    /// Verify if the char is within the allowed character set for keywords. This
    /// only checks a single character at a time.
    #[inline]
    pub fn is_keyword(&self, a: char) -> bool {
        if a.is_whitespace() || a == '(' || a == ')' || a == '^' || a == ':' || a == ',' {
            false
        } else {
            true
        }
    }

    /// Expect a given token to come next, otherwise
    /// fail. Failing will result in a formatted
    /// error message that uses the Span and lets
    /// the user know what went wrong.
    pub fn expect(&mut self, tok: &Token) {
        if self.token == *tok {
            self.bump();
        } else {
            fail!("Expected {} but found {}", *tok, self.token);
        }
    }

    /// Optionally eat a given token. If the token is not
    /// found, this will simply return false and
    /// won't fail.
    pub fn eat(&mut self, tok: &Token) -> bool {
        if self.token == *tok {
            self.bump();
            true
        } else {
            false
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use span::Span;
    use std::str::{Slice, Owned};

    #[test]
    fn noop() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.token, Noop);
    }

    #[test]
    fn zeroed_span() {
        let lexer = Lexer::new("foo");
        assert_eq!(lexer.span, Span::new(0, 0));
    }

    #[test]
    fn bump_empty() {
        let mut lexer = Lexer::new("");
        lexer.bump();
        assert_eq!(lexer.token, Done);
    }

    #[test]
    fn bump_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.token, LParen);
    }

    #[test]
    fn bump_rparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        assert_eq!(lexer.token, RParen);
    }

    #[test]
    fn bump_simple_keyword() {
        let mut lexer = Lexer::new(":foo");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo")));
    }

    #[test]
    fn bump_keyword_dash() {
        let mut lexer = Lexer::new(":foo-");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-")));
    }

    #[test]
    fn bump_keyword_dash_middle() {
        let mut lexer = Lexer::new(":foo-bar");
        lexer.bump();
        assert_eq!(lexer.token, Keyword(Slice("foo-bar")));
    }

    #[test]
    fn bump_string() {
        let mut lexer = Lexer::new("\"hah-w091:--:\"");
        lexer.bump();
        assert_eq!(lexer.token, Str("hah-w091:--:".to_string()));
    }

    #[test]
    fn bump_ident() {
        let mut lexer = Lexer::new("foo-bar-&#^");
        lexer.bump();
        assert_eq!(lexer.token, Ident("foo-bar-&#^".to_string()));
    }

    #[test]
    fn should_eat_lparen() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        assert_eq!(lexer.eat(&LParen), true);
    }

    #[test]
    #[should_fail]
    fn should_expect_lparen() {
        let mut lexer = Lexer::new(")");
        lexer.bump();
        lexer.expect(&LParen);
    }

    #[test]
    fn should_expect() {
        let mut lexer = Lexer::new("(");
        lexer.bump();
        lexer.expect(&LParen);
    }
}
<|endoftext|>


<commit_before>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use gio::{File, FileInputStream, IOErrorEnum};
use gio::cast::AsFile;
use grust::refcount::{Ref,SyncRef};
use grust::mainloop::{LoopRunner,MainLoop};
use grust::object;
use grust::error::Match as ErrorMatch;
use std::error::Error;

fn run_on_mainloop<F>(setup: F) where F: FnOnce(SyncRef<MainLoop>) {
    let runner = LoopRunner::new();
    runner.run_after(setup);
}

#[test]
fn as_file() {
    let mut f = File::new_for_path("/dev/null");
    let mut g = f.as_mut_gio_file();
    let path = g.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn deref() {
    let mut f = File::new_for_path("/dev/null");
    let path = f.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn new_ref() {
    let mut f = File::new_for_path("/dev/null");
    let mut g = Ref::new(&mut *f);
    let path = g.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn clone() {
    let rf = File::new_for_path("/dev/null");
    let mut rg = rf.clone();
    let path = rg.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
#[should_fail]
fn cast_fail() {
    let rf = File::new_for_path("/dev/null");
    let _: &FileInputStream = object::cast(&*rf);
}

#[test]
fn async() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("/dev/null");
        f.read_async(0, None,
            move |obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => {}
                    Err(e) => { println!("Error: {}", e.description()) }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_to_domain() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            move |obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        match IOErrorEnum::from_error(&e) {
                            ErrorMatch::Known(code) => {
                                assert_eq!(code, IOErrorEnum::NotFound);
                            }
                            ErrorMatch::Unknown(code) => {
                                panic!("unknown error code {}", code)
                            }
                            _ => unreachable!()
                        }
                    }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_match_partial_eq() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            move |obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        assert_eq!(IOErrorEnum::from_error(&e),
                                   ErrorMatch::Known(IOErrorEnum::NotFound));
                    }
                }
                mainloop.quit();
            });
    })
}
<commit_msg>Drop the move keyword from test closures, where no crashes result<commit_after>// This file is part of Grust, GObject introspection bindings for Rust
//
// Copyright (C) 2013, 2014  Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

use gio::{File, FileInputStream, IOErrorEnum};
use gio::cast::AsFile;
use grust::refcount::{Ref,SyncRef};
use grust::mainloop::{LoopRunner,MainLoop};
use grust::object;
use grust::error::Match as ErrorMatch;
use std::error::Error;

fn run_on_mainloop<F>(setup: F) where F: FnOnce(SyncRef<MainLoop>) {
    let runner = LoopRunner::new();
    runner.run_after(setup);
}

#[test]
fn as_file() {
    let mut f = File::new_for_path("/dev/null");
    let mut g = f.as_mut_gio_file();
    let path = g.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn deref() {
    let mut f = File::new_for_path("/dev/null");
    let path = f.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn new_ref() {
    let mut f = File::new_for_path("/dev/null");
    let mut g = Ref::new(&mut *f);
    let path = g.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
fn clone() {
    let rf = File::new_for_path("/dev/null");
    let mut rg = rf.clone();
    let path = rg.get_path();
    assert_eq!(path.parse_as_utf8().unwrap(), "/dev/null");
}

#[test]
#[should_fail]
fn cast_fail() {
    let rf = File::new_for_path("/dev/null");
    let _: &FileInputStream = object::cast(&*rf);
}

#[test]
fn async() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("/dev/null");
        f.read_async(0, None,
            move |obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => {}
                    Err(e) => { println!("Error: {}", e.description()) }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_to_domain() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            |obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        match IOErrorEnum::from_error(&e) {
                            ErrorMatch::Known(code) => {
                                assert_eq!(code, IOErrorEnum::NotFound);
                            }
                            ErrorMatch::Unknown(code) => {
                                panic!("unknown error code {}", code)
                            }
                            _ => unreachable!()
                        }
                    }
                }
                mainloop.quit();
            });
    })
}

#[test]
fn error_match_partial_eq() {
    run_on_mainloop(|mut mainloop| {
        let mut f = File::new_for_path("./does-not-exist");
        f.read_async(0, None,
            |obj, res| {
                let f: &mut File = object::cast_mut(obj);
                match f.read_finish(res) {
                    Ok(_)  => { unreachable!() }
                    Err(e) => {
                        assert_eq!(IOErrorEnum::from_error(&e),
                                   ErrorMatch::Known(IOErrorEnum::NotFound));
                    }
                }
                mainloop.quit();
            });
    })
}
<|endoftext|>