<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.

//! Common functions to manage permissions

use crate::error::strip_errno;
use crate::error::UResult;
use crate::error::USimpleError;
pub use crate::features::entries;
use crate::fs::resolve_relative_path;
use crate::show_error;
use clap::App;
use clap::Arg;
use clap::ArgMatches;
use libc::{self, gid_t, uid_t};
use walkdir::WalkDir;

use std::io::Error as IOError;
use std::io::Result as IOResult;

use std::ffi::CString;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;

use std::os::unix::ffi::OsStrExt;
use std::path::Path;

/// The various level of verbosity
#[derive(PartialEq, Clone, Debug)]
pub enum VerbosityLevel {
    Silent,
    Changes,
    Verbose,
    Normal,
}
#[derive(PartialEq, Clone, Debug)]
pub struct Verbosity {
    pub groups_only: bool,
    pub level: VerbosityLevel,
}

/// Actually perform the change of owner on a path
fn chown<P: AsRef<Path>>(path: P, uid: uid_t, gid: gid_t, follow: bool) -> IOResult<()> {
    let path = path.as_ref();
    let s = CString::new(path.as_os_str().as_bytes()).unwrap();
    let ret = unsafe {
        if follow {
            libc::chown(s.as_ptr(), uid, gid)
        } else {
            libc::lchown(s.as_ptr(), uid, gid)
        }
    };
    if ret == 0 {
        Ok(())
    } else {
        Err(IOError::last_os_error())
    }
}

/// Perform the change of owner on a path
/// with the various options
/// and error messages management
pub fn wrap_chown<P: AsRef<Path>>(
    path: P,
    meta: &Metadata,
    dest_uid: Option<u32>,
    dest_gid: Option<u32>,
    follow: bool,
    verbosity: Verbosity,
) -> Result<String, String> {
    let dest_uid = dest_uid.unwrap_or_else(|| meta.uid());
    let dest_gid = dest_gid.unwrap_or_else(|| meta.gid());
    let path = path.as_ref();
    let mut out: String = String::new();

    if let Err(e) = chown(path, dest_uid, dest_gid, follow) {
        match verbosity.level {
            VerbosityLevel::Silent => (),
            level => {
                out = format!(
                    "changing {} of '{}': {}",
                    if verbosity.groups_only {
                        "group"
                    } else {
                        "ownership"
                    },
                    path.display(),
                    e
                );
                if level == VerbosityLevel::Verbose {
                    out = if verbosity.groups_only {
                        format!(
                            "{}\nfailed to change group of '{}' from {} to {}",
                            out,
                            path.display(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    } else {
                        format!(
                            "{}\nfailed to change ownership of '{}' from {}:{} to {}:{}",
                            out,
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    };
                };
            }
        }
        return Err(out);
    } else {
        let changed = dest_uid != meta.uid() || dest_gid != meta.gid();
        if changed {
            match verbosity.level {
                VerbosityLevel::Changes | VerbosityLevel::Verbose => {
                    out = if verbosity.groups_only {
                        format!(
                            "changed group of '{}' from {} to {}",
                            path.display(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    } else {
                        format!(
                            "changed ownership of '{}' from {}:{} to {}:{}",
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    };
                }
                _ => (),
            };
        } else if verbosity.level == VerbosityLevel::Verbose {
            out = if verbosity.groups_only {
                format!(
                    "group of '{}' retained as {}",
                    path.display(),
                    entries::gid2grp(dest_gid).unwrap_or_default()
                )
            } else {
                format!(
                    "ownership of '{}' retained as {}:{}",
                    path.display(),
                    entries::uid2usr(dest_uid).unwrap(),
                    entries::gid2grp(dest_gid).unwrap()
                )
            };
        }
    }
    Ok(out)
}

pub enum IfFrom {
    All,
    User(u32),
    Group(u32),
    UserGroup(u32, u32),
}

#[derive(PartialEq, Eq)]
pub enum TraverseSymlinks {
    None,
    First,
    All,
}

pub struct ChownExecutor {
    pub dest_uid: Option<u32>,
    pub dest_gid: Option<u32>,
    pub traverse_symlinks: TraverseSymlinks,
    pub verbosity: Verbosity,
    pub filter: IfFrom,
    pub files: Vec<String>,
    pub recursive: bool,
    pub preserve_root: bool,
    // Must be true if traverse_symlinks is not None
    pub dereference: bool,
}

impl ChownExecutor {
    pub fn exec(&self) -> UResult<()> {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        if ret != 0 {
            return Err(ret.into());
        }
        Ok(())
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, self.dereference) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if self.dereference {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() {
                    show_error!("it is dangerous to operate recursively on '/'");
                    show_error!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = if self.matched(meta.uid(), meta.gid()) {
            match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                self.dereference,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity.level != VerbosityLevel::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        } else {
            0
        };

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let root = root.as_ref();

        // walkdir always dereferences the root directory, so we have to check it ourselves
        // TODO: replace with `root.is_symlink()` once it is stable
        if self.traverse_symlinks == TraverseSymlinks::None
            && std::fs::symlink_metadata(root)
                .map(|m| m.file_type().is_symlink())
                .unwrap_or(false)
        {
            return 0;
        }

        let mut ret = 0;
        let mut iterator = WalkDir::new(root)
            .follow_links(self.traverse_symlinks == TraverseSymlinks::All)
            .min_depth(1)
            .into_iter();
        // We can't use a for loop because we need to manipulate the iterator inside the loop.
        while let Some(entry) = iterator.next() {
            let entry = match entry {
                Err(e) => {
                    ret = 1;
                    if let Some(path) = e.path() {
                        show_error!(
                            "cannot access '{}': {}",
                            path.display(),
                            if let Some(error) = e.io_error() {
                                strip_errno(error)
                            } else {
                                "Too many levels of symbolic links".into()
                            }
                        )
                    } else {
                        show_error!("{}", e)
                    }
                    continue;
                }
                Ok(entry) => entry,
            };
            let path = entry.path();
            let meta = match self.obtain_meta(path, self.dereference) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    if entry.file_type().is_dir() {
                        // Instruct walkdir to skip this directory to avoid getting another error
                        // when walkdir tries to query the children of this directory.
                        iterator.skip_current_dir();
                    }
                    continue;
                }
            };

            if !self.matched(meta.uid(), meta.gid()) {
                continue;
            }

            ret = match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                self.dereference,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity.level != VerbosityLevel::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        let path = path.as_ref();
        let meta = if follow {
            path.metadata()
        } else {
            path.symlink_metadata()
        };
        match meta {
            Err(e) => {
                match self.verbosity.level {
                    VerbosityLevel::Silent => (),
                    _ => show_error!(
                        "cannot {} '{}': {}",
                        if follow { "dereference" } else { "access" },
                        path.display(),
                        strip_errno(&e)
                    ),
                }
                None
            }
            Ok(meta) => Some(meta),
        }
    }

    #[inline]
    fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
        match self.filter {
            IfFrom::All => true,
            IfFrom::User(u) => u == uid,
            IfFrom::Group(g) => g == gid,
            IfFrom::UserGroup(u, g) => u == uid && g == gid,
        }
    }
}

pub mod options {
    pub mod verbosity {
        pub const CHANGES: &str = "changes";
        pub const QUIET: &str = "quiet";
        pub const SILENT: &str = "silent";
        pub const VERBOSE: &str = "verbose";
    }
    pub mod preserve_root {
        pub const PRESERVE: &str = "preserve-root";
        pub const NO_PRESERVE: &str = "no-preserve-root";
    }
    pub mod dereference {
        pub const DEREFERENCE: &str = "dereference";
        pub const NO_DEREFERENCE: &str = "no-dereference";
    }
    pub const FROM: &str = "from";
    pub const RECURSIVE: &str = "recursive";
    pub mod traverse {
        pub const TRAVERSE: &str = "H";
        pub const NO_TRAVERSE: &str = "P";
        pub const EVERY: &str = "L";
    }
    pub const REFERENCE: &str = "reference";
    pub const ARG_OWNER: &str = "OWNER";
    pub const ARG_GROUP: &str = "GROUP";
    pub const ARG_FILES: &str = "FILE";
}

type GidUidFilterParser<'a> = fn(&ArgMatches<'a>) -> UResult<(Option<u32>, Option<u32>, IfFrom)>;

/// Base implementation for `chgrp` and `chown`.
///
/// An argument called `add_arg_if_not_reference` will be added to `app` if
/// `args` does not contain the `--reference` option.
/// `parse_gid_uid_and_filter` will be called to obtain the target gid and uid, and the filter,
/// from `ArgMatches`.
/// `groups_only` determines whether verbose output will only mention the group.
pub fn chown_base<'a>(
    mut app: App<'a, 'a>,
    args: impl crate::Args,
    add_arg_if_not_reference: &'a str,
    parse_gid_uid_and_filter: GidUidFilterParser<'a>,
    groups_only: bool,
) -> UResult<()> {
    let args: Vec<_> = args.collect();
    let mut reference = false;
    let mut help = false;
    // stop processing options on --
    for arg in args.iter().take_while(|s| *s != "--") {
        if arg.to_string_lossy().starts_with("--reference=") || arg == "--reference" {
            reference = true;
        } else if arg == "--help" {
            // we stop processing once we see --help,
            // as it doesn't matter if we've seen reference or not
            help = true;
            break;
        }
    }

    if help || !reference {
        // add both positional arguments
        // arg_group is only required if
        app = app.arg(
            Arg::with_name(add_arg_if_not_reference)
                .value_name(add_arg_if_not_reference)
                .required(true)
                .takes_value(true)
                .multiple(false),
        )
    }
    app = app.arg(
        Arg::with_name(options::ARG_FILES)
            .value_name(options::ARG_FILES)
            .multiple(true)
            .takes_value(true)
            .required(true)
            .min_values(1),
    );
    let matches = app.get_matches_from(args);

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

    let preserve_root = matches.is_present(options::preserve_root::PRESERVE);

    let mut dereference = if matches.is_present(options::dereference::DEREFERENCE) {
        Some(true)
    } else if matches.is_present(options::dereference::NO_DEREFERENCE) {
        Some(false)
    } else {
        None
    };

    let mut traverse_symlinks = if matches.is_present(options::traverse::TRAVERSE) {
        TraverseSymlinks::First
    } else if matches.is_present(options::traverse::EVERY) {
        TraverseSymlinks::All
    } else {
        TraverseSymlinks::None
    };

    let recursive = matches.is_present(options::RECURSIVE);
    if recursive {
        if traverse_symlinks == TraverseSymlinks::None {
            if dereference == Some(true) {
                return Err(USimpleError::new(1, "-R --dereference requires -H or -L"));
            }
            dereference = Some(false);
        }
    } else {
        traverse_symlinks = TraverseSymlinks::None;
    }

    let verbosity_level = if matches.is_present(options::verbosity::CHANGES) {
        VerbosityLevel::Changes
    } else if matches.is_present(options::verbosity::SILENT)
        || matches.is_present(options::verbosity::QUIET)
    {
        VerbosityLevel::Silent
    } else if matches.is_present(options::verbosity::VERBOSE) {
        VerbosityLevel::Verbose
    } else {
        VerbosityLevel::Normal
    };
    let (dest_gid, dest_uid, filter) = parse_gid_uid_and_filter(&matches)?;

    let executor = ChownExecutor {
        traverse_symlinks,
        dest_gid,
        dest_uid,
        verbosity: Verbosity {
            groups_only,
            level: verbosity_level,
        },
        recursive,
        dereference: dereference.unwrap_or(true),
        preserve_root,
        files,
        filter,
    };
    executor.exec()
}
<commit_msg>uucore/perms: remove erroneous comment<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.

//! Common functions to manage permissions

use crate::error::strip_errno;
use crate::error::UResult;
use crate::error::USimpleError;
pub use crate::features::entries;
use crate::fs::resolve_relative_path;
use crate::show_error;
use clap::App;
use clap::Arg;
use clap::ArgMatches;
use libc::{self, gid_t, uid_t};
use walkdir::WalkDir;

use std::io::Error as IOError;
use std::io::Result as IOResult;

use std::ffi::CString;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;

use std::os::unix::ffi::OsStrExt;
use std::path::Path;

/// The various level of verbosity
#[derive(PartialEq, Clone, Debug)]
pub enum VerbosityLevel {
    Silent,
    Changes,
    Verbose,
    Normal,
}
#[derive(PartialEq, Clone, Debug)]
pub struct Verbosity {
    pub groups_only: bool,
    pub level: VerbosityLevel,
}

/// Actually perform the change of owner on a path
fn chown<P: AsRef<Path>>(path: P, uid: uid_t, gid: gid_t, follow: bool) -> IOResult<()> {
    let path = path.as_ref();
    let s = CString::new(path.as_os_str().as_bytes()).unwrap();
    let ret = unsafe {
        if follow {
            libc::chown(s.as_ptr(), uid, gid)
        } else {
            libc::lchown(s.as_ptr(), uid, gid)
        }
    };
    if ret == 0 {
        Ok(())
    } else {
        Err(IOError::last_os_error())
    }
}

/// Perform the change of owner on a path
/// with the various options
/// and error messages management
pub fn wrap_chown<P: AsRef<Path>>(
    path: P,
    meta: &Metadata,
    dest_uid: Option<u32>,
    dest_gid: Option<u32>,
    follow: bool,
    verbosity: Verbosity,
) -> Result<String, String> {
    let dest_uid = dest_uid.unwrap_or_else(|| meta.uid());
    let dest_gid = dest_gid.unwrap_or_else(|| meta.gid());
    let path = path.as_ref();
    let mut out: String = String::new();

    if let Err(e) = chown(path, dest_uid, dest_gid, follow) {
        match verbosity.level {
            VerbosityLevel::Silent => (),
            level => {
                out = format!(
                    "changing {} of '{}': {}",
                    if verbosity.groups_only {
                        "group"
                    } else {
                        "ownership"
                    },
                    path.display(),
                    e
                );
                if level == VerbosityLevel::Verbose {
                    out = if verbosity.groups_only {
                        format!(
                            "{}\nfailed to change group of '{}' from {} to {}",
                            out,
                            path.display(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    } else {
                        format!(
                            "{}\nfailed to change ownership of '{}' from {}:{} to {}:{}",
                            out,
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    };
                };
            }
        }
        return Err(out);
    } else {
        let changed = dest_uid != meta.uid() || dest_gid != meta.gid();
        if changed {
            match verbosity.level {
                VerbosityLevel::Changes | VerbosityLevel::Verbose => {
                    out = if verbosity.groups_only {
                        format!(
                            "changed group of '{}' from {} to {}",
                            path.display(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    } else {
                        format!(
                            "changed ownership of '{}' from {}:{} to {}:{}",
                            path.display(),
                            entries::uid2usr(meta.uid()).unwrap(),
                            entries::gid2grp(meta.gid()).unwrap(),
                            entries::uid2usr(dest_uid).unwrap(),
                            entries::gid2grp(dest_gid).unwrap()
                        )
                    };
                }
                _ => (),
            };
        } else if verbosity.level == VerbosityLevel::Verbose {
            out = if verbosity.groups_only {
                format!(
                    "group of '{}' retained as {}",
                    path.display(),
                    entries::gid2grp(dest_gid).unwrap_or_default()
                )
            } else {
                format!(
                    "ownership of '{}' retained as {}:{}",
                    path.display(),
                    entries::uid2usr(dest_uid).unwrap(),
                    entries::gid2grp(dest_gid).unwrap()
                )
            };
        }
    }
    Ok(out)
}

pub enum IfFrom {
    All,
    User(u32),
    Group(u32),
    UserGroup(u32, u32),
}

#[derive(PartialEq, Eq)]
pub enum TraverseSymlinks {
    None,
    First,
    All,
}

pub struct ChownExecutor {
    pub dest_uid: Option<u32>,
    pub dest_gid: Option<u32>,
    pub traverse_symlinks: TraverseSymlinks,
    pub verbosity: Verbosity,
    pub filter: IfFrom,
    pub files: Vec<String>,
    pub recursive: bool,
    pub preserve_root: bool,
    pub dereference: bool,
}

impl ChownExecutor {
    pub fn exec(&self) -> UResult<()> {
        let mut ret = 0;
        for f in &self.files {
            ret |= self.traverse(f);
        }
        if ret != 0 {
            return Err(ret.into());
        }
        Ok(())
    }

    fn traverse<P: AsRef<Path>>(&self, root: P) -> i32 {
        let path = root.as_ref();
        let meta = match self.obtain_meta(path, self.dereference) {
            Some(m) => m,
            _ => return 1,
        };

        // Prohibit only if:
        // (--preserve-root and -R present) &&
        // (
        //     (argument is not symlink && resolved to be '/') ||
        //     (argument is symlink && should follow argument && resolved to be '/')
        // )
        if self.recursive && self.preserve_root {
            let may_exist = if self.dereference {
                path.canonicalize().ok()
            } else {
                let real = resolve_relative_path(path);
                if real.is_dir() {
                    Some(real.canonicalize().expect("failed to get real path"))
                } else {
                    Some(real.into_owned())
                }
            };

            if let Some(p) = may_exist {
                if p.parent().is_none() {
                    show_error!("it is dangerous to operate recursively on '/'");
                    show_error!("use --no-preserve-root to override this failsafe");
                    return 1;
                }
            }
        }

        let ret = if self.matched(meta.uid(), meta.gid()) {
            match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                self.dereference,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity.level != VerbosityLevel::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        } else {
            0
        };

        if !self.recursive {
            ret
        } else {
            ret | self.dive_into(&root)
        }
    }

    fn dive_into<P: AsRef<Path>>(&self, root: P) -> i32 {
        let root = root.as_ref();

        // walkdir always dereferences the root directory, so we have to check it ourselves
        // TODO: replace with `root.is_symlink()` once it is stable
        if self.traverse_symlinks == TraverseSymlinks::None
            && std::fs::symlink_metadata(root)
                .map(|m| m.file_type().is_symlink())
                .unwrap_or(false)
        {
            return 0;
        }

        let mut ret = 0;
        let mut iterator = WalkDir::new(root)
            .follow_links(self.traverse_symlinks == TraverseSymlinks::All)
            .min_depth(1)
            .into_iter();
        // We can't use a for loop because we need to manipulate the iterator inside the loop.
        while let Some(entry) = iterator.next() {
            let entry = match entry {
                Err(e) => {
                    ret = 1;
                    if let Some(path) = e.path() {
                        show_error!(
                            "cannot access '{}': {}",
                            path.display(),
                            if let Some(error) = e.io_error() {
                                strip_errno(error)
                            } else {
                                "Too many levels of symbolic links".into()
                            }
                        )
                    } else {
                        show_error!("{}", e)
                    }
                    continue;
                }
                Ok(entry) => entry,
            };
            let path = entry.path();
            let meta = match self.obtain_meta(path, self.dereference) {
                Some(m) => m,
                _ => {
                    ret = 1;
                    if entry.file_type().is_dir() {
                        // Instruct walkdir to skip this directory to avoid getting another error
                        // when walkdir tries to query the children of this directory.
                        iterator.skip_current_dir();
                    }
                    continue;
                }
            };

            if !self.matched(meta.uid(), meta.gid()) {
                continue;
            }

            ret = match wrap_chown(
                path,
                &meta,
                self.dest_uid,
                self.dest_gid,
                self.dereference,
                self.verbosity.clone(),
            ) {
                Ok(n) => {
                    if !n.is_empty() {
                        show_error!("{}", n);
                    }
                    0
                }
                Err(e) => {
                    if self.verbosity.level != VerbosityLevel::Silent {
                        show_error!("{}", e);
                    }
                    1
                }
            }
        }
        ret
    }

    fn obtain_meta<P: AsRef<Path>>(&self, path: P, follow: bool) -> Option<Metadata> {
        let path = path.as_ref();
        let meta = if follow {
            path.metadata()
        } else {
            path.symlink_metadata()
        };
        match meta {
            Err(e) => {
                match self.verbosity.level {
                    VerbosityLevel::Silent => (),
                    _ => show_error!(
                        "cannot {} '{}': {}",
                        if follow { "dereference" } else { "access" },
                        path.display(),
                        strip_errno(&e)
                    ),
                }
                None
            }
            Ok(meta) => Some(meta),
        }
    }

    #[inline]
    fn matched(&self, uid: uid_t, gid: gid_t) -> bool {
        match self.filter {
            IfFrom::All => true,
            IfFrom::User(u) => u == uid,
            IfFrom::Group(g) => g == gid,
            IfFrom::UserGroup(u, g) => u == uid && g == gid,
        }
    }
}

pub mod options {
    pub mod verbosity {
        pub const CHANGES: &str = "changes";
        pub const QUIET: &str = "quiet";
        pub const SILENT: &str = "silent";
        pub const VERBOSE: &str = "verbose";
    }
    pub mod preserve_root {
        pub const PRESERVE: &str = "preserve-root";
        pub const NO_PRESERVE: &str = "no-preserve-root";
    }
    pub mod dereference {
        pub const DEREFERENCE: &str = "dereference";
        pub const NO_DEREFERENCE: &str = "no-dereference";
    }
    pub const FROM: &str = "from";
    pub const RECURSIVE: &str = "recursive";
    pub mod traverse {
        pub const TRAVERSE: &str = "H";
        pub const NO_TRAVERSE: &str = "P";
        pub const EVERY: &str = "L";
    }
    pub const REFERENCE: &str = "reference";
    pub const ARG_OWNER: &str = "OWNER";
    pub const ARG_GROUP: &str = "GROUP";
    pub const ARG_FILES: &str = "FILE";
}

type GidUidFilterParser<'a> = fn(&ArgMatches<'a>) -> UResult<(Option<u32>, Option<u32>, IfFrom)>;

/// Base implementation for `chgrp` and `chown`.
///
/// An argument called `add_arg_if_not_reference` will be added to `app` if
/// `args` does not contain the `--reference` option.
/// `parse_gid_uid_and_filter` will be called to obtain the target gid and uid, and the filter,
/// from `ArgMatches`.
/// `groups_only` determines whether verbose output will only mention the group.
pub fn chown_base<'a>(
    mut app: App<'a, 'a>,
    args: impl crate::Args,
    add_arg_if_not_reference: &'a str,
    parse_gid_uid_and_filter: GidUidFilterParser<'a>,
    groups_only: bool,
) -> UResult<()> {
    let args: Vec<_> = args.collect();
    let mut reference = false;
    let mut help = false;
    // stop processing options on --
    for arg in args.iter().take_while(|s| *s != "--") {
        if arg.to_string_lossy().starts_with("--reference=") || arg == "--reference" {
            reference = true;
        } else if arg == "--help" {
            // we stop processing once we see --help,
            // as it doesn't matter if we've seen reference or not
            help = true;
            break;
        }
    }

    if help || !reference {
        // add both positional arguments
        // arg_group is only required if
        app = app.arg(
            Arg::with_name(add_arg_if_not_reference)
                .value_name(add_arg_if_not_reference)
                .required(true)
                .takes_value(true)
                .multiple(false),
        )
    }
    app = app.arg(
        Arg::with_name(options::ARG_FILES)
            .value_name(options::ARG_FILES)
            .multiple(true)
            .takes_value(true)
            .required(true)
            .min_values(1),
    );
    let matches = app.get_matches_from(args);

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

    let preserve_root = matches.is_present(options::preserve_root::PRESERVE);

    let mut dereference = if matches.is_present(options::dereference::DEREFERENCE) {
        Some(true)
    } else if matches.is_present(options::dereference::NO_DEREFERENCE) {
        Some(false)
    } else {
        None
    };

    let mut traverse_symlinks = if matches.is_present(options::traverse::TRAVERSE) {
        TraverseSymlinks::First
    } else if matches.is_present(options::traverse::EVERY) {
        TraverseSymlinks::All
    } else {
        TraverseSymlinks::None
    };

    let recursive = matches.is_present(options::RECURSIVE);
    if recursive {
        if traverse_symlinks == TraverseSymlinks::None {
            if dereference == Some(true) {
                return Err(USimpleError::new(1, "-R --dereference requires -H or -L"));
            }
            dereference = Some(false);
        }
    } else {
        traverse_symlinks = TraverseSymlinks::None;
    }

    let verbosity_level = if matches.is_present(options::verbosity::CHANGES) {
        VerbosityLevel::Changes
    } else if matches.is_present(options::verbosity::SILENT)
        || matches.is_present(options::verbosity::QUIET)
    {
        VerbosityLevel::Silent
    } else if matches.is_present(options::verbosity::VERBOSE) {
        VerbosityLevel::Verbose
    } else {
        VerbosityLevel::Normal
    };
    let (dest_gid, dest_uid, filter) = parse_gid_uid_and_filter(&matches)?;

    let executor = ChownExecutor {
        traverse_symlinks,
        dest_gid,
        dest_uid,
        verbosity: Verbosity {
            groups_only,
            level: verbosity_level,
        },
        recursive,
        dereference: dereference.unwrap_or(true),
        preserve_root,
        files,
        filter,
    };
    executor.exec()
}
<|endoftext|>


<commit_before>// Lumol, an extensible molecular simulation engine
// Copyright (C) 2015-2016 G. Fraux — BSD license

//! Encoding restrictions in interactions.

/// Possible restrictions on the pair interactions.
///
/// In some force fields, some interactions are restricted to a subset of the
/// possible pairs. For example, we may want to have Lennard-Jones interactions
/// between atoms in different molecules only, or scale the coulombic
/// interactions if the particles are at 1-4 distance.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PairRestriction {
    /// No pair should be excluded
    None,
    /// Only apply the interaction to intra-molecular pairs
    IntraMolecular,
    /// Only apply the interaction to inter-molecular pairs
    InterMolecular,
    /// Only apply the interaction to pairs which are not in 1-2 position
    Exclude12,
    /// Only apply the interaction to pairs which are not in 1-2 or 1-3 position
    Exclude13,
    /// Only apply the interaction to pairs which are not in 1-2, 1-3 or 1-4 position
    Exclude14,
    /// Only apply the interaction to pairs which are not in 1-2 or 1-3 position,
    /// and scale the interaction for pairs in 1-4 position
    Scale14(f64)
}

/// Restriction information attached to a pair of Particles in a System
#[derive(Clone, Copy, Debug)]
pub struct RestrictionInfo {
    /// Is this pair excluded
    pub excluded: bool,
    /// Scaling for this restrictions
    pub scaling: f64
}

impl PairRestriction {
    /// Get the restrictions information at the given [bond
    /// `distance`][distance].
    ///
    /// [distance]: ../sys/struct.System.html#method.bond_distance
    pub fn information(&self, distance: i8) -> RestrictionInfo {
        let are_in_same_molecule = distance >= 0;
        let excluded = match *self {
            PairRestriction::None => false,
            PairRestriction::InterMolecular => are_in_same_molecule,
            PairRestriction::IntraMolecular => !are_in_same_molecule,
            PairRestriction::Exclude12 => distance == 1,
            PairRestriction::Exclude13 | PairRestriction::Scale14(..) => {
                distance == 1 || distance == 2
            },
            PairRestriction::Exclude14 => {
                distance == 1 || distance == 2 || distance == 3
            }
        };

        let scaling = if let PairRestriction::Scale14(scaling) = *self {
            if distance == 3 {scaling} else {1.0}
        } else {
            1.0
        };

        RestrictionInfo {
            excluded: excluded,
            scaling: scaling
        }
    }
}

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

    fn testing_system() -> System {
        // Creating 2 pentane molecule
        //    C   C        C   C
        //   / \ / \      / \ / \
        //  C   C   C    C   C   C
        let mut system = System::new();
        for _ in 0..10 {
            system.add_particle(Particle::new("C"));
        }

        let _ = system.add_bond(0, 1);
        let _ = system.add_bond(1, 2);
        let _ = system.add_bond(2, 3);
        let _ = system.add_bond(3, 4);

        let _ = system.add_bond(5, 6);
        let _ = system.add_bond(6, 7);
        let _ = system.add_bond(7, 8);
        let _ = system.add_bond(8, 9);

        return system;
    }

    #[test]
    fn none() {
        let restriction = PairRestriction::None;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                let info = restriction.information(distance);
                assert_eq!(info.excluded, false);
                assert_eq!(info.scaling, 1.0);
            }
        }
    }

    #[test]
    fn intra() {
        let restriction = PairRestriction::IntraMolecular;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                let info = restriction.information(distance);
                assert_eq!(info.excluded, !system.are_in_same_molecule(i, j));
                assert_eq!(info.scaling, 1.0);
            }
        }
    }

    #[test]
    fn inter() {
        let restriction = PairRestriction::InterMolecular;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                let info = restriction.information(distance);
                assert_eq!(info.excluded, system.are_in_same_molecule(i, j));
                assert_eq!(info.scaling, 1.0);
            }
        }
    }

    #[test]
    fn exclude_12() {
        let restriction = PairRestriction::Exclude12;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                assert_eq!(restriction.information(distance).scaling, 1.0);
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 8)).excluded, true);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(0, 8)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }

    #[test]
    fn exclude_13() {
        let restriction = PairRestriction::Exclude13;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                assert_eq!(restriction.information(distance).scaling, 1.0);
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 6)).excluded, true);

        // Angles
        assert_eq!(restriction.information(system.bond_distance(0, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 9)).excluded, true);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(4, 5)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }

    #[test]
    fn exclude_14() {
        let restriction = PairRestriction::Exclude14;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                assert_eq!(restriction.information(distance).scaling, 1.0);
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 6)).excluded, true);

        // Angles
        assert_eq!(restriction.information(system.bond_distance(0, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 9)).excluded, true);

        // Dihedrals
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 4)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(6, 9)).excluded, true);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(4, 5)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 4)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }

    #[test]
    fn scale_14() {
        let restriction = PairRestriction::Scale14(0.8);
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                if distance == 3 {
                    assert_eq!(restriction.information(distance).scaling, 0.8);
                } else {
                    assert_eq!(restriction.information(distance).scaling, 1.0);
                }
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 6)).excluded, true);

        // Angles
        assert_eq!(restriction.information(system.bond_distance(0, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 9)).excluded, true);

        // Dihedrals are not excluded, just scaled
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(1, 4)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(6, 9)).excluded, false);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(4, 5)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 4)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }
}
<commit_msg>[doc] Documentation and example for PairRestriction<commit_after>// Lumol, an extensible molecular simulation engine
// Copyright (C) 2015-2016 G. Fraux — BSD license

//! Encoding restrictions in interactions.

/// Possible restrictions on the pair interactions.
///
/// In some force fields, interactions are restricted to a subset of the
/// possible pairs. For example, we may want to have Lennard-Jones interactions
/// between atoms in different molecules only, or scale the coulombic
/// interactions if the particles are at 1-4 distance.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PairRestriction {
    /// No pair should be excluded.
    None,
    /// Only apply the interaction to intra-molecular pairs.
    IntraMolecular,
    /// Only apply the interaction to inter-molecular pairs.
    InterMolecular,
    /// Only apply the interaction to pairs which are not in 1-2 position
    /// (separated by one bond).
    Exclude12,
    /// Only apply the interaction to pairs which are not in 1-2 or 1-3 position
    /// (separated by one or two bonds).
    Exclude13,
    /// Only apply the interaction to pairs which are not in 1-2, 1-3 or 1-4
    /// position (separated by one, two or three bonds).
    Exclude14,
    /// Only apply the interaction to pairs which are not in 1-2 or 1-3
    /// position, and scale the interaction for pairs in 1-4 position (separated
    /// by three bonds).
    Scale14(f64)
}

/// Restriction information attached to a pair of `Particles` in a `System`.
#[derive(Clone, Copy, Debug)]
pub struct RestrictionInfo {
    /// Is this pair excluded?
    pub excluded: bool,
    /// Scaling factor for the potential. This value is contained between 0 and
    /// 1.
    pub scaling: f64
}

impl PairRestriction {
    /// Get the restriction at the given [bond `distance`][distance].
    ///
    /// [distance]: ../sys/struct.System.html#method.bond_distance
    ///
    /// # Example
    ///
    /// ```
    /// # use lumol::energy::PairRestriction;
    /// let restriction = PairRestriction::None;
    /// assert_eq!(restriction.information(3).excluded, false);
    /// assert_eq!(restriction.information(2).scaling, 1.0);
    ///
    /// let restriction = PairRestriction::Exclude13;
    /// assert_eq!(restriction.information(2).excluded, true);
    /// assert_eq!(restriction.information(3).excluded, false);
    ///
    /// let restriction = PairRestriction::Scale14(0.5);
    /// assert_eq!(restriction.information(2).excluded, true);
    /// assert_eq!(restriction.information(3).excluded, false);
    /// assert_eq!(restriction.information(3).scaling, 0.5);
    /// assert_eq!(restriction.information(4).scaling, 1.0);
    /// ```
    pub fn information(&self, distance: i8) -> RestrictionInfo {
        let are_in_same_molecule = distance >= 0;
        let excluded = match *self {
            PairRestriction::None => false,
            PairRestriction::InterMolecular => are_in_same_molecule,
            PairRestriction::IntraMolecular => !are_in_same_molecule,
            PairRestriction::Exclude12 => distance == 1,
            PairRestriction::Exclude13 | PairRestriction::Scale14(..) => {
                distance == 1 || distance == 2
            },
            PairRestriction::Exclude14 => {
                distance == 1 || distance == 2 || distance == 3
            }
        };

        let scaling = if let PairRestriction::Scale14(scaling) = *self {
            if distance == 3 {scaling} else {1.0}
        } else {
            1.0
        };

        RestrictionInfo {
            excluded: excluded,
            scaling: scaling
        }
    }
}

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

    fn testing_system() -> System {
        // Creating 2 pentane molecule
        //    C   C        C   C
        //   / \ / \      / \ / \
        //  C   C   C    C   C   C
        let mut system = System::new();
        for _ in 0..10 {
            system.add_particle(Particle::new("C"));
        }

        let _ = system.add_bond(0, 1);
        let _ = system.add_bond(1, 2);
        let _ = system.add_bond(2, 3);
        let _ = system.add_bond(3, 4);

        let _ = system.add_bond(5, 6);
        let _ = system.add_bond(6, 7);
        let _ = system.add_bond(7, 8);
        let _ = system.add_bond(8, 9);

        return system;
    }

    #[test]
    fn none() {
        let restriction = PairRestriction::None;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                let info = restriction.information(distance);
                assert_eq!(info.excluded, false);
                assert_eq!(info.scaling, 1.0);
            }
        }
    }

    #[test]
    fn intra() {
        let restriction = PairRestriction::IntraMolecular;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                let info = restriction.information(distance);
                assert_eq!(info.excluded, !system.are_in_same_molecule(i, j));
                assert_eq!(info.scaling, 1.0);
            }
        }
    }

    #[test]
    fn inter() {
        let restriction = PairRestriction::InterMolecular;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                let info = restriction.information(distance);
                assert_eq!(info.excluded, system.are_in_same_molecule(i, j));
                assert_eq!(info.scaling, 1.0);
            }
        }
    }

    #[test]
    fn exclude_12() {
        let restriction = PairRestriction::Exclude12;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                assert_eq!(restriction.information(distance).scaling, 1.0);
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 8)).excluded, true);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(0, 8)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }

    #[test]
    fn exclude_13() {
        let restriction = PairRestriction::Exclude13;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                assert_eq!(restriction.information(distance).scaling, 1.0);
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 6)).excluded, true);

        // Angles
        assert_eq!(restriction.information(system.bond_distance(0, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 9)).excluded, true);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(4, 5)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }

    #[test]
    fn exclude_14() {
        let restriction = PairRestriction::Exclude14;
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                assert_eq!(restriction.information(distance).scaling, 1.0);
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 6)).excluded, true);

        // Angles
        assert_eq!(restriction.information(system.bond_distance(0, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 9)).excluded, true);

        // Dihedrals
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 4)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(6, 9)).excluded, true);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(4, 5)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 4)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }

    #[test]
    fn scale_14() {
        let restriction = PairRestriction::Scale14(0.8);
        let system = testing_system();
        for i in 0..10 {
            for j in 0..10 {
                let distance = system.bond_distance(i, j);
                if distance == 3 {
                    assert_eq!(restriction.information(distance).scaling, 0.8);
                } else {
                    assert_eq!(restriction.information(distance).scaling, 1.0);
                }
            }
        }

        // Bonds
        assert_eq!(restriction.information(system.bond_distance(0, 1)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 6)).excluded, true);

        // Angles
        assert_eq!(restriction.information(system.bond_distance(0, 2)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(1, 3)).excluded, true);
        assert_eq!(restriction.information(system.bond_distance(7, 9)).excluded, true);

        // Dihedrals are not excluded, just scaled
        assert_eq!(restriction.information(system.bond_distance(0, 3)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(1, 4)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(6, 9)).excluded, false);

        // Not excluded
        assert_eq!(restriction.information(system.bond_distance(4, 5)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(0, 4)).excluded, false);
        assert_eq!(restriction.information(system.bond_distance(8, 2)).excluded, false);
    }
}
<|endoftext|>


<commit_before>#![warn(clippy::missing_docs_in_private_items)]
#![allow(dead_code)]
#![feature(associated_type_defaults)]

//! Some garbage docs for the crate here
#![doc = "More garbage"]

struct Foo {
    a: isize,
    b: isize,
}

pub struct PubFoo {
    pub a: isize,
    b: isize,
}

#[allow(clippy::missing_docs_in_private_items)]
pub struct PubFoo2 {
    pub a: isize,
    pub c: isize,
}

/// dox
pub trait A {
    /// dox
    fn foo(&self);
    /// dox
    fn foo_with_impl(&self) {}
}

#[allow(clippy::missing_docs_in_private_items)]
trait B {
    fn foo(&self);
    fn foo_with_impl(&self) {}
}

pub trait C {
    fn foo(&self);
    fn foo_with_impl(&self) {}
}

#[allow(clippy::missing_docs_in_private_items)]
pub trait D {
    fn dummy(&self) {}
}

/// dox
pub trait E {
    type AssociatedType;
    type AssociatedTypeDef = Self;

    /// dox
    type DocumentedType;
    /// dox
    type DocumentedTypeDef = Self;
    /// dox
    fn dummy(&self) {}
}

impl Foo {
    pub fn foo() {}
    fn bar() {}
}

impl PubFoo {
    pub fn foo() {}
    /// dox
    pub fn foo1() {}
    fn foo2() {}
    #[allow(clippy::missing_docs_in_private_items)]
    pub fn foo3() {}
}

#[allow(clippy::missing_docs_in_private_items)]
trait F {
    fn a();
    fn b(&self);
}

// should need to redefine documentation for implementations of traits
impl F for Foo {
    fn a() {}
    fn b(&self) {}
}

fn main() {}
<commit_msg>Auto merge of #5231 - JohnTitor:associated-type-defaults, r=phansch<commit_after>#![warn(clippy::missing_docs_in_private_items)]
#![allow(dead_code)]
#![feature(associated_type_defaults)]

//! Some garbage docs for the crate here
#![doc = "More garbage"]

struct Foo {
    a: isize,
    b: isize,
}

pub struct PubFoo {
    pub a: isize,
    b: isize,
}

#[allow(clippy::missing_docs_in_private_items)]
pub struct PubFoo2 {
    pub a: isize,
    pub c: isize,
}

/// dox
pub trait A {
    /// dox
    fn foo(&self);
    /// dox
    fn foo_with_impl(&self) {}
}

#[allow(clippy::missing_docs_in_private_items)]
trait B {
    fn foo(&self);
    fn foo_with_impl(&self) {}
}

pub trait C {
    fn foo(&self);
    fn foo_with_impl(&self) {}
}

#[allow(clippy::missing_docs_in_private_items)]
pub trait D {
    fn dummy(&self) {}
}

/// dox
pub trait E: Sized {
    type AssociatedType;
    type AssociatedTypeDef = Self;

    /// dox
    type DocumentedType;
    /// dox
    type DocumentedTypeDef = Self;
    /// dox
    fn dummy(&self) {}
}

impl Foo {
    pub fn foo() {}
    fn bar() {}
}

impl PubFoo {
    pub fn foo() {}
    /// dox
    pub fn foo1() {}
    fn foo2() {}
    #[allow(clippy::missing_docs_in_private_items)]
    pub fn foo3() {}
}

#[allow(clippy::missing_docs_in_private_items)]
trait F {
    fn a();
    fn b(&self);
}

// should need to redefine documentation for implementations of traits
impl F for Foo {
    fn a() {}
    fn b(&self) {}
}

fn main() {}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use tikv::pd::PdClient;
use tikv::util::HandyRwLock;
use super::server::new_server_cluster;
use super::util;

#[test]
fn test_pd_heartbeat() {
    let mut cluster = new_server_cluster(0, 1);
    cluster.cfg.store_cfg.pd_heartbeat_tick_interval = 50;

    cluster.bootstrap_region().unwrap();
    cluster.start();

    util::sleep_ms(100);

    let pd_client = cluster.pd_client.clone();
    let store = pd_client.rl().get_store(0, 1).unwrap();
    assert!(store.get_address().len() > 0);

    // force update a wrong store meta.
    pd_client.wl().put_store(0, util::new_store(1, "".to_owned())).unwrap();

    util::sleep_ms(100);
    let store1 = pd_client.rl().get_store(0, 1).unwrap();
    assert_eq!(store1.get_address(), store.get_address());
}
<commit_msg>raftstore: increase the wait time. (#555)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

use tikv::pd::PdClient;
use tikv::util::HandyRwLock;
use super::server::new_server_cluster;
use super::util;

#[test]
fn test_pd_heartbeat() {
    let mut cluster = new_server_cluster(0, 1);
    cluster.cfg.store_cfg.pd_heartbeat_tick_interval = 50;

    cluster.bootstrap_region().unwrap();
    cluster.start();

    util::sleep_ms(100);

    let pd_client = cluster.pd_client.clone();
    let store = pd_client.rl().get_store(0, 1).unwrap();
    assert!(store.get_address().len() > 0);

    // force update a wrong store meta.
    pd_client.wl().put_store(0, util::new_store(1, "".to_owned())).unwrap();

    util::sleep_ms(500);
    let store1 = pd_client.rl().get_store(0, 1).unwrap();
    assert_eq!(store1.get_address(), store.get_address());
}
<|endoftext|>


<commit_before>//! See [`HtmlWithLimit`].

use std::fmt::Write;
use std::ops::ControlFlow;

use crate::html::escape::Escape;

/// A buffer that allows generating HTML with a length limit.
///
/// This buffer ensures that:
///
/// * all tags are closed,
/// * only the most recently opened tag is closed,
/// * no tags are left empty (e.g., `<em></em>`) due to the length limit being reached,
/// * all text is escaped.
#[derive(Debug)]
pub(super) struct HtmlWithLimit {
    buf: String,
    len: usize,
    limit: usize,
    /// A list of tags that have been requested to be opened via [`Self::open_tag()`]
    /// but have not actually been pushed to `buf` yet. This ensures that tags are not
    /// left empty (e.g., `<em></em>`) due to the length limit being reached.
    queued_tags: Vec<&'static str>,
    /// A list of all tags that have been opened but not yet closed.
    unclosed_tags: Vec<&'static str>,
}

impl HtmlWithLimit {
    /// Create a new buffer, with a limit of `length_limit`.
    pub(super) fn new(length_limit: usize) -> Self {
        Self {
            buf: String::new(),
            len: 0,
            limit: length_limit,
            unclosed_tags: Vec::new(),
            queued_tags: Vec::new(),
        }
    }

    /// Finish using the buffer and get the written output.
    /// This function will close all unclosed tags for you.
    pub(super) fn finish(mut self) -> String {
        self.close_all_tags();
        self.buf
    }

    /// Write some plain text to the buffer, escaping as needed.
    ///
    /// This function skips writing the text if the length limit was reached
    /// and returns [`ControlFlow::Break`].
    pub(super) fn push(&mut self, text: &str) -> ControlFlow<(), ()> {
        if self.len + text.len() > self.limit {
            return ControlFlow::BREAK;
        }

        self.flush_queue();
        write!(self.buf, "{}", Escape(text)).unwrap();
        self.len += text.len();

        ControlFlow::CONTINUE
    }

    /// Open an HTML tag.
    pub(super) fn open_tag(&mut self, tag_name: &'static str) {
        self.queued_tags.push(tag_name);
    }

    /// Close the most recently opened HTML tag.
    pub(super) fn close_tag(&mut self) {
        let tag_name = self.unclosed_tags.pop().unwrap();
        self.buf.push_str("</");
        self.buf.push_str(tag_name);
        self.buf.push('>');
    }

    /// Write all queued tags and add them to the `unclosed_tags` list.
    fn flush_queue(&mut self) {
        for tag_name in self.queued_tags.drain(..) {
            self.buf.push('<');
            self.buf.push_str(tag_name);
            self.buf.push('>');

            self.unclosed_tags.push(tag_name);
        }
    }

    /// Close all unclosed tags.
    fn close_all_tags(&mut self) {
        while !self.unclosed_tags.is_empty() {
            self.close_tag();
        }
    }
}
<commit_msg>Clarify wording in docs<commit_after>//! See [`HtmlWithLimit`].

use std::fmt::Write;
use std::ops::ControlFlow;

use crate::html::escape::Escape;

/// A buffer that allows generating HTML with a length limit.
///
/// This buffer ensures that:
///
/// * all tags are closed,
/// * tags are closed in the reverse order of when they were opened (i.e., the correct HTML order),
/// * no tags are left empty (e.g., `<em></em>`) due to the length limit being reached,
/// * all text is escaped.
#[derive(Debug)]
pub(super) struct HtmlWithLimit {
    buf: String,
    len: usize,
    limit: usize,
    /// A list of tags that have been requested to be opened via [`Self::open_tag()`]
    /// but have not actually been pushed to `buf` yet. This ensures that tags are not
    /// left empty (e.g., `<em></em>`) due to the length limit being reached.
    queued_tags: Vec<&'static str>,
    /// A list of all tags that have been opened but not yet closed.
    unclosed_tags: Vec<&'static str>,
}

impl HtmlWithLimit {
    /// Create a new buffer, with a limit of `length_limit`.
    pub(super) fn new(length_limit: usize) -> Self {
        Self {
            buf: String::new(),
            len: 0,
            limit: length_limit,
            unclosed_tags: Vec::new(),
            queued_tags: Vec::new(),
        }
    }

    /// Finish using the buffer and get the written output.
    /// This function will close all unclosed tags for you.
    pub(super) fn finish(mut self) -> String {
        self.close_all_tags();
        self.buf
    }

    /// Write some plain text to the buffer, escaping as needed.
    ///
    /// This function skips writing the text if the length limit was reached
    /// and returns [`ControlFlow::Break`].
    pub(super) fn push(&mut self, text: &str) -> ControlFlow<(), ()> {
        if self.len + text.len() > self.limit {
            return ControlFlow::BREAK;
        }

        self.flush_queue();
        write!(self.buf, "{}", Escape(text)).unwrap();
        self.len += text.len();

        ControlFlow::CONTINUE
    }

    /// Open an HTML tag.
    pub(super) fn open_tag(&mut self, tag_name: &'static str) {
        self.queued_tags.push(tag_name);
    }

    /// Close the most recently opened HTML tag.
    pub(super) fn close_tag(&mut self) {
        let tag_name = self.unclosed_tags.pop().unwrap();
        self.buf.push_str("</");
        self.buf.push_str(tag_name);
        self.buf.push('>');
    }

    /// Write all queued tags and add them to the `unclosed_tags` list.
    fn flush_queue(&mut self) {
        for tag_name in self.queued_tags.drain(..) {
            self.buf.push('<');
            self.buf.push_str(tag_name);
            self.buf.push('>');

            self.unclosed_tags.push(tag_name);
        }
    }

    /// Close all unclosed tags.
    fn close_all_tags(&mut self) {
        while !self.unclosed_tags.is_empty() {
            self.close_tag();
        }
    }
}
<|endoftext|>


<commit_before>use std;
import std::io;
import std::io::{writer_util, reader_util};
import std::map;
import map::hashmap;
import result;

export rowreader, rowaccess, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inquotedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mutable offset : uint,
    mutable buffers : [@[char]],
    mutable state : state,
    mutable trailing_nl : bool,
    mutable terminating : bool
};

type row = {
    fields : [ fieldtype ]
};

type bufferdescr = {
    escaped: bool,
    buffers: [@[char]],
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow() -> result::t<row, str>;
}

iface rowaccess {
    fn len() -> uint;
    fn getchars(uint) -> [char];
    fn getstr(uint) -> str;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        readlen: 1024u,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false),
        mutable trailing_nl : false,
        mutable terminating: false
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false),
        mutable trailing_nl : false,
        mutable terminating: false
    }
}

impl of rowaccess for row {
    fn len() -> uint {
        vec::len(self.fields)
    }
    fn getchars(field: uint) -> [char] {
        fn unescape(escaped: [char]) -> [char] {
            let r : [char] = [];
            vec::reserve(r, vec::len(escaped));
            let in_q = false;
            for c in escaped {
                if in_q {
                    assert(c == '"');
                    in_q = false;
                } else {
                    in_q = c == '"';
                    r += [c];
                }
            }
            ret r;
        }
        alt self.fields[field] {
            emptyfield() { ret []; }
            bufferfield(desc) {
                let buf = [];
                {
                    let i = 0u;
                    while i < vec::len(desc.buffers) {
                        let from = if (i == 0u)
                            { desc.start } else { 0u };
                        let to = if (i == vec::len(desc.buffers) - 1u)
                            { desc.end } else { vec::len(*desc.buffers[i]) };
                        buf += vec::slice(*desc.buffers[i], from, to);
                        i = i + 1u;
                    }
                }
                if field == self.len() - 1u {
                    // there may be a trailing \r on the last field; we should strip it
                    // if so. bodgy here but seems the most efficient place to deal with this
                    if vec::len(buf) > 0u {
                        if buf[vec::len(buf)-1u] == '\r' {
                            buf = vec::slice(buf, 0u, vec::len(buf)-1u);
                        }
                    }
                }
                if desc.escaped {
                    buf = unescape(buf);
                }
                ret buf;
            }
        };
    }
    fn getstr(field: uint) -> str {
        ret str::from_chars(self.getchars(field));
    }
    fn getall() -> [str] {
        let a = [];
        self.map() { |s|
            a += [s];
        }
        ret a;
    }
    fn map(f: fn(s: str)) {
        let i = 0u;
        let len = self.len();
        while i < len {
            f(self.getstr(i));
            i += 1u;
        }
    }
}

impl of rowiter for rowreader {
    fn readrow() -> result::t<row, str> {
        fn statestr(state: state) -> str {
            alt state {
                fieldstart(after_delim) {
                    #fmt("fieldstart : after_delim %b", after_delim)
                }
                infield(b,o) {
                    #fmt("field : %u %u", b, o)
                }
                inquotedfield(b, o) {
                    #fmt("inquotedfield : %u %u", b, o)
                }
                inquote(b, o) {
                    #fmt("inquote : %u %u", b, o)
                }
            }
        }
        fn row_from_buf(self: rowreader, &fields: [fieldtype]) -> bool {
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let eb = vec::len(self.buffers) - 1u;
                let sb = sb, so = so, eo = eo;
                //#debug("sb %u so %u eb %u eo %u", sb, so, eb, eo);
                //log(debug, vec::map(self.buffers) { |t| str::from_chars(*t) } );
                //log(debug, vec::map(self.buffers) { |t| vec::len(*t) });
                if escaped {
                    so += 1u;
                    if so > vec::len(*self.buffers[sb]) {
                        sb += 1u;
                        so = vec::len(*self.buffers[sb]) - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = vec::len(*self.buffers[eb]) - 1u;
                    }
                }
                //#debug("sb %u so %u eb %u eo %u", sb, so, eb, eo);
                bufferfield({ escaped: escaped, buffers: vec::slice(self.buffers, sb, eb+1u), start: so, end: eo })
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf: @[char] = self.buffers[cbuffer];
            while self.offset < vec::len(*buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                #debug("got '%c' | %s", c, statestr(self.state));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        #debug("fieldstart : after_delim %b", after_delim);
                        if c == self.quote {
                            self.state = inquotedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [emptyfield];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [emptyfield];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        #debug("field : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                        }
                    }
                    inquotedfield(b, o) {
                        #debug("inquotedfield : %u %u", b, o);
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                    }
                    inquote(b, o) {
                        #debug("inquote : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                            ret true;
                        } else if c == self.quote {
                            self.state = inquotedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                #debug("now %s", statestr(self.state));
            }
            ret false;
        }

        self.state = fieldstart(false);
        let do_read = vec::len(self.buffers) == 0u;
        let fields = [];

        while !self.terminating {
            if do_read {
                let data: @[char] = @self.f.read_chars(self.readlen);
                if vec::len(*data) == 0u {
                    if !self.trailing_nl {
                        self.terminating = true;
                        data = @['\n'];
                    } else {
                        ret result::err("EOF");
                    }
                } else {
                    self.trailing_nl = data[vec::len(*data) - 1u] == '\n';
                }
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, fields) {
                let r: row = { fields: fields };
                fields = [];
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                ret result::ok(r);
            }
            do_read = true;
        }
        ret result::err("unreachable");
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    while true {
        let res = r.readrow();
        if result::failure(res) {
            break;
        }
        let m : map::hashmap<str, str> = map::new_str_hash();
        let col = 0u;
        let row = result::get(res);
        if row.len() != vec::len(cols) {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        result::get(res).map() { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            hashmap_iter_cols(r, result::get(res).getall(), f);
        }
        result::err(_) { }
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            let cols : [str] = vec::map(result::get(res).getall(), hmap);
            if !hver(cols) {
                ret;
            }
            hashmap_iter_cols(r, cols, f);
        }
        result::err(_) { }
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(testdata);
            let r = mk(f);
            let i = 0u;
            while true {
                let res = r.readrow();
                if result::failure(res) {
                    break;
                }
                let row = result::get(res);
                let expect = expected[i];

                assert(row.len() == vec::len(expect));
                let j = 0u;
                while j < row.len() {
                    assert(row.getstr(j) == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(testdata: str) {
            // test default reader params
            chk() { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test continuations over read buffers
            let j = 1u;
            while j < str::len(testdata) {
                chk() { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
            ret;
        };
        // so we can test trailing newline case, testdata
        // must not end in \n - leave off the last newline
        runchecks(testdata);
        runchecks(testdata+"\n");
        runchecks(str::replace(testdata, "\n", "\r\n"));
    }

    #[test]
    fn test_simple() {
        rowmatch("a,b,c,d\n1,2,3,4",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn test_trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn test_leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn test_quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_with_comma() {
        rowmatch("\"1,2\"",
                 [["1,2"]])
    }

    #[test]
    fn test_quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn test_blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<commit_msg>s/while true/loop<commit_after>use std;
import std::io;
import std::io::{writer_util, reader_util};
import std::map;
import map::hashmap;
import result;

export rowreader, rowaccess, rowiter,
       new_reader, new_reader_readlen,
       hashmap_iter, hashmap_iter_full;

enum state {
    fieldstart(bool),
    infield(uint, uint),
    inquotedfield(uint, uint),
    inquote(uint, uint)
}

type rowreader = {
    readlen: uint,
    delim: char,
    quote: char,
    f : io::reader,
    mutable offset : uint,
    mutable buffers : [@[char]],
    mutable state : state,
    mutable trailing_nl : bool,
    mutable terminating : bool
};

type row = {
    fields : [ fieldtype ]
};

type bufferdescr = {
    escaped: bool,
    buffers: [@[char]],
    start: uint,
    end: uint
};

enum fieldtype {
    emptyfield(),
    bufferfield(bufferdescr)
}

iface rowiter {
    fn readrow() -> result::t<row, str>;
}

iface rowaccess {
    fn len() -> uint;
    fn getchars(uint) -> [char];
    fn getstr(uint) -> str;
}

fn new_reader(+f: io::reader, +delim: char, +quote: char) -> rowreader {
    {
        readlen: 1024u,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false),
        mutable trailing_nl : false,
        mutable terminating: false
    }
}

fn new_reader_readlen(+f: io::reader, +delim: char, +quote: char, rl: uint) -> rowreader {
    {
        readlen: rl,
        delim: delim,
        quote: quote,
        f: f,
        mutable offset : 0u,
        mutable buffers : [],
        mutable state : fieldstart(false),
        mutable trailing_nl : false,
        mutable terminating: false
    }
}

impl of rowaccess for row {
    fn len() -> uint {
        vec::len(self.fields)
    }
    fn getchars(field: uint) -> [char] {
        fn unescape(escaped: [char]) -> [char] {
            let r : [char] = [];
            vec::reserve(r, vec::len(escaped));
            let in_q = false;
            for c in escaped {
                if in_q {
                    assert(c == '"');
                    in_q = false;
                } else {
                    in_q = c == '"';
                    r += [c];
                }
            }
            ret r;
        }
        alt self.fields[field] {
            emptyfield() { ret []; }
            bufferfield(desc) {
                let buf = [];
                {
                    let i = 0u;
                    while i < vec::len(desc.buffers) {
                        let from = if (i == 0u)
                            { desc.start } else { 0u };
                        let to = if (i == vec::len(desc.buffers) - 1u)
                            { desc.end } else { vec::len(*desc.buffers[i]) };
                        buf += vec::slice(*desc.buffers[i], from, to);
                        i = i + 1u;
                    }
                }
                if field == self.len() - 1u {
                    // there may be a trailing \r on the last field; we should strip it
                    // if so. bodgy here but seems the most efficient place to deal with this
                    if vec::len(buf) > 0u {
                        if buf[vec::len(buf)-1u] == '\r' {
                            buf = vec::slice(buf, 0u, vec::len(buf)-1u);
                        }
                    }
                }
                if desc.escaped {
                    buf = unescape(buf);
                }
                ret buf;
            }
        };
    }
    fn getstr(field: uint) -> str {
        ret str::from_chars(self.getchars(field));
    }
    fn getall() -> [str] {
        let a = [];
        self.map() { |s|
            a += [s];
        }
        ret a;
    }
    fn map(f: fn(s: str)) {
        let i = 0u;
        let len = self.len();
        while i < len {
            f(self.getstr(i));
            i += 1u;
        }
    }
}

impl of rowiter for rowreader {
    fn readrow() -> result::t<row, str> {
        fn statestr(state: state) -> str {
            alt state {
                fieldstart(after_delim) {
                    #fmt("fieldstart : after_delim %b", after_delim)
                }
                infield(b,o) {
                    #fmt("field : %u %u", b, o)
                }
                inquotedfield(b, o) {
                    #fmt("inquotedfield : %u %u", b, o)
                }
                inquote(b, o) {
                    #fmt("inquote : %u %u", b, o)
                }
            }
        }
        fn row_from_buf(self: rowreader, &fields: [fieldtype]) -> bool {
            fn new_bufferfield(self: rowreader, escaped: bool, sb: uint, so: uint, eo: uint) -> fieldtype {
                let eb = vec::len(self.buffers) - 1u;
                let sb = sb, so = so, eo = eo;
                //#debug("sb %u so %u eb %u eo %u", sb, so, eb, eo);
                //log(debug, vec::map(self.buffers) { |t| str::from_chars(*t) } );
                //log(debug, vec::map(self.buffers) { |t| vec::len(*t) });
                if escaped {
                    so += 1u;
                    if so > vec::len(*self.buffers[sb]) {
                        sb += 1u;
                        so = vec::len(*self.buffers[sb]) - 1u;
                    }
                    if eo > 0u {
                        eo -= 1u;
                    } else {
                        eb -= 1u;
                        eo = vec::len(*self.buffers[eb]) - 1u;
                    }
                }
                //#debug("sb %u so %u eb %u eo %u", sb, so, eb, eo);
                bufferfield({ escaped: escaped, buffers: vec::slice(self.buffers, sb, eb+1u), start: so, end: eo })
            }
            let cbuffer = vec::len(self.buffers) - 1u;
            let buf: @[char] = self.buffers[cbuffer];
            while self.offset < vec::len(*buf) {
                let coffset = self.offset;
                let c : char = buf[coffset];
                #debug("got '%c' | %s", c, statestr(self.state));
                self.offset += 1u;
                alt self.state {
                    fieldstart(after_delim) {
                        #debug("fieldstart : after_delim %b", after_delim);
                        if c == self.quote {
                            self.state = inquotedfield(cbuffer, coffset);
                        } else if c == '\n' {
                            if after_delim {
                                fields += [emptyfield];
                            }
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [emptyfield];
                        } else {
                            self.state = infield(cbuffer, coffset);
                        }
                    }
                    infield(b,o) {
                        #debug("field : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                            ret true;
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, false, b, o, coffset)];
                        }
                    }
                    inquotedfield(b, o) {
                        #debug("inquotedfield : %u %u", b, o);
                        if c == self.quote {
                            self.state = inquote(b, o);
                        }
                    }
                    inquote(b, o) {
                        #debug("inquote : %u %u", b, o);
                        if c == '\n' {
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                            ret true;
                        } else if c == self.quote {
                            self.state = inquotedfield(b, o);
                        } else if c == self.delim {
                            self.state = fieldstart(true);
                            fields += [new_bufferfield(self, true, b, o, coffset)];
                        }
                        // swallow odd chars, eg. space between field and "
                    }
                }
                #debug("now %s", statestr(self.state));
            }
            ret false;
        }

        self.state = fieldstart(false);
        let do_read = vec::len(self.buffers) == 0u;
        let fields = [];

        while !self.terminating {
            if do_read {
                let data: @[char] = @self.f.read_chars(self.readlen);
                if vec::len(*data) == 0u {
                    if !self.trailing_nl {
                        self.terminating = true;
                        data = @['\n'];
                    } else {
                        ret result::err("EOF");
                    }
                } else {
                    self.trailing_nl = data[vec::len(*data) - 1u] == '\n';
                }
                self.buffers += [data];
                self.offset = 0u;
            }

            if row_from_buf(self, fields) {
                let r: row = { fields: fields };
                fields = [];
                if vec::len(self.buffers) > 1u {
                    self.buffers = vec::slice(self.buffers, vec::len(self.buffers) - 1u, vec::len(self.buffers));
                }
                ret result::ok(r);
            }
            do_read = true;
        }
        ret result::err("unreachable");
    }
}

fn hashmap_iter_cols(r: rowreader, cols: [str], f: fn(map::hashmap<str, str>)) {
    loop {
        let res = r.readrow();
        if result::failure(res) {
            break;
        }
        let m : map::hashmap<str, str> = map::new_str_hash();
        let col = 0u;
        let row = result::get(res);
        if row.len() != vec::len(cols) {
            cont; // FIXME: how to flag that we dropped a crazy row?
        }
        result::get(res).map() { |s|
            m.insert(cols[col], s);
            col += 1u;
        };
        f(m);
    }
}

// reads the first row as a header, to derive keys for a hashmap
// emitted for each subsequent row
fn hashmap_iter(r: rowreader, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            hashmap_iter_cols(r, result::get(res).getall(), f);
        }
        result::err(_) { }
    }
}

// as hashmap_iter, but first apply 'hc' to each header; allows
// cleaning up headers; also allows verification that heads are
// satisfactory
fn hashmap_iter_full(r: rowreader, hmap: fn(&&h: str) -> str, hver: fn(cols: [str]) -> bool, f: fn(map::hashmap<str, str>)) {
    let res = r.readrow();
    alt res {
        result::ok(row) {
            let cols : [str] = vec::map(result::get(res).getall(), hmap);
            if !hver(cols) {
                ret;
            }
            hashmap_iter_cols(r, cols, f);
        }
        result::err(_) { }
    }
}

#[cfg(test)]
mod test {
    fn rowmatch(testdata: str, expected: [[str]]) {
        let chk = fn@(mk: fn(io::reader) -> rowreader) {
            let f = io::str_reader(testdata);
            let r = mk(f);
            let i = 0u;
            loop {
                let res = r.readrow();
                if result::failure(res) {
                    break;
                }
                let row = result::get(res);
                let expect = expected[i];

                assert(row.len() == vec::len(expect));
                let j = 0u;
                while j < row.len() {
                    assert(row.getstr(j) == expect[j]);
                    j += 1u;
                }
                i += 1u;
            }
            assert(i == vec::len(expected));
        };
        let runchecks = fn@(testdata: str) {
            // test default reader params
            chk() { |inp|
                new_reader_readlen(inp, ',', '"', 2u)
            };
            // test continuations over read buffers
            let j = 1u;
            while j < str::len(testdata) {
                chk() { |inp|
                    new_reader_readlen(inp, ',', '"', j)
                };
                j += 1u;
            }
            ret;
        };
        // so we can test trailing newline case, testdata
        // must not end in \n - leave off the last newline
        runchecks(testdata);
        runchecks(testdata+"\n");
        runchecks(str::replace(testdata, "\n", "\r\n"));
    }

    #[test]
    fn test_simple() {
        rowmatch("a,b,c,d\n1,2,3,4",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4"]]);
    }

    #[test]
    fn test_trailing_comma() {
        rowmatch("a,b,c,d\n1,2,3,4,",
                 [["a", "b", "c", "d"], ["1", "2", "3", "4", ""]]);
    }

    #[test]
    fn test_leading_comma() {
        rowmatch("a,b,c,d\n,1,2,3,4",
                 [["a", "b", "c", "d"], ["", "1", "2", "3", "4"]]);
    }

    #[test]
    fn test_quote_simple() {
        rowmatch("\"Hello\",\"There\"\na,b,\"c\",d",
                 [["Hello", "There"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_nested() {
        rowmatch("\"Hello\",\"There is a \"\"fly\"\" in my soup\"\na,b,\"c\",d",
                 [["Hello", "There is a \"fly\" in my soup"], ["a", "b", "c", "d"]]);
    }

    #[test]
    fn test_quote_with_comma() {
        rowmatch("\"1,2\"",
                 [["1,2"]])
    }

    #[test]
    fn test_quote_with_other_comma() {
        rowmatch("1,2,3,\"a,b,c\"",
                 [["1", "2", "3", "a,b,c"]])
    }

    #[test]
    fn test_blank_line() {
        rowmatch("\n\n", [[], []]);
    }
}

<|endoftext|>


<commit_before>/* Copyright (c) 2015 The Robigalia Project Developers
 * Licensed under the Apache License, Version 2.0
 * <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT
 * license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
 * at your option. All files in the project carrying such
 * notice may not be copied, modified, or distributed except
 * according to those terms.
 */
#![no_std]
#![feature(asm, no_std)]
#![allow(bad_style, unused_parens, unused_assignments)]

#[cfg(all(target_arch = "x86", target_pointer_width = "32"))]
include!("arch/x86.rs");

#[cfg(all(target_arch = "arm", target_pointer_width = "32"))]
include!("arch/arm.rs");

#[cfg(all(target_arch = "x86", target_pointer_width = "32"))]
include!(concat!(env!("OUT_DIR"), "/x86_invocation.rs"));

#[cfg(all(target_arch = "arm", target_pointer_width = "32"))]
include!(concat!(env!("OUT_DIR"), "/arm_invocation.rs"));

#[cfg(all(target_arch = "x86", target_pointer_width = "32"))]
//include!(concat!(env!("OUT_DIR"), "/x86_syscall_stub.rs"));

#[cfg(all(target_arch = "arm", target_pointer_width = "32"))]
include!(concat!(env!("OUT_DIR"), "/arm_syscall_stub.rs"));

include!(concat!(env!("OUT_DIR"), "/types.rs"));
include!(concat!(env!("OUT_DIR"), "/syscalls.rs"));
<commit_msg>Add html_root_url for rustdoc<commit_after>/* Copyright (c) 2015 The Robigalia Project Developers
 * Licensed under the Apache License, Version 2.0
 * <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT
 * license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
 * at your option. All files in the project carrying such
 * notice may not be copied, modified, or distributed except
 * according to those terms.
 */
#![no_std]
#![feature(asm, no_std)]
#![allow(bad_style, unused_parens, unused_assignments)]
#![doc(html_root_url = "https://doc.robigalia.org/")]

#[cfg(all(target_arch = "x86", target_pointer_width = "32"))]
include!("arch/x86.rs");

#[cfg(all(target_arch = "arm", target_pointer_width = "32"))]
include!("arch/arm.rs");

#[cfg(all(target_arch = "x86", target_pointer_width = "32"))]
include!(concat!(env!("OUT_DIR"), "/x86_invocation.rs"));

#[cfg(all(target_arch = "arm", target_pointer_width = "32"))]
include!(concat!(env!("OUT_DIR"), "/arm_invocation.rs"));

#[cfg(all(target_arch = "x86", target_pointer_width = "32"))]
//include!(concat!(env!("OUT_DIR"), "/x86_syscall_stub.rs"));

#[cfg(all(target_arch = "arm", target_pointer_width = "32"))]
include!(concat!(env!("OUT_DIR"), "/arm_syscall_stub.rs"));

include!(concat!(env!("OUT_DIR"), "/types.rs"));
include!(concat!(env!("OUT_DIR"), "/syscalls.rs"));
<|endoftext|>