<commit_before>use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
use crate::core::profiles::Profiles;
use crate::core::{InternedString, PackageIdSpec, Workspace};
use crate::ops;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::paths;
use crate::util::Config;
use std::fs;
use std::path::Path;

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    /// A list of packages to clean. If empty, everything is cleaned.
    pub spec: Vec<String>,
    /// The target arch triple to clean, or None for the host arch
    pub targets: Vec<String>,
    /// Whether to clean the release directory
    pub profile_specified: bool,
    /// Whether to clean the directory of a certain build profile
    pub requested_profile: InternedString,
    /// Whether to just clean the doc directory
    pub doc: bool,
}

/// Cleans the package's build artifacts.
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
    let mut target_dir = ws.target_dir();
    let config = ws.config();

    // If the doc option is set, we just want to delete the doc directory.
    if opts.doc {
        target_dir = target_dir.join("doc");
        return rm_rf(&target_dir.into_path_unlocked(), config);
    }

    let profiles = Profiles::new(ws.profiles(), config, opts.requested_profile, ws.features())?;

    if opts.profile_specified {
        // After parsing profiles we know the dir-name of the profile, if a profile
        // was passed from the command line. If so, delete only the directory of
        // that profile.
        let dir_name = profiles.get_dir_name();
        target_dir = target_dir.join(dir_name);
    }

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        return rm_rf(&target_dir.into_path_unlocked(), config);
    }

    // Clean specific packages.
    let requested_kinds = CompileKind::from_requested_targets(config, &opts.targets)?;
    let target_data = RustcTargetData::new(ws, &requested_kinds)?;
    let (pkg_set, resolve) = ops::resolve_ws(ws)?;
    let prof_dir_name = profiles.get_dir_name();
    let host_layout = Layout::new(ws, None, &prof_dir_name)?;
    // Convert requested kinds to a Vec of layouts.
    let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds
        .into_iter()
        .filter_map(|kind| match kind {
            CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) {
                Ok(layout) => Some(Ok((kind, layout))),
                Err(e) => Some(Err(e)),
            },
            CompileKind::Host => None,
        })
        .collect::<CargoResult<_>>()?;
    // A Vec of layouts. This is a little convoluted because there can only be
    // one host_layout.
    let layouts = if opts.targets.is_empty() {
        vec![(CompileKind::Host, &host_layout)]
    } else {
        target_layouts
            .iter()
            .map(|(kind, layout)| (*kind, layout))
            .collect()
    };
    // Create a Vec that also includes the host for things that need to clean both.
    let layouts_with_host: Vec<(CompileKind, &Layout)> =
        std::iter::once((CompileKind::Host, &host_layout))
            .chain(layouts.iter().map(|(k, l)| (*k, *l)))
            .collect();

    // Cleaning individual rustdoc crates is currently not supported.
    // For example, the search index would need to be rebuilt to fully
    // remove it (otherwise you're left with lots of broken links).
    // Doc tests produce no output.

    // Get Packages for the specified specs.
    let mut pkg_ids = Vec::new();
    for spec_str in opts.spec.iter() {
        // Translate the spec to a Package.
        let spec = PackageIdSpec::parse(spec_str)?;
        if spec.version().is_some() {
            config.shell().warn(&format!(
                "version qualifier in `-p {}` is ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        if spec.url().is_some() {
            config.shell().warn(&format!(
                "url qualifier in `-p {}` ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        let matches: Vec<_> = resolve.iter().filter(|id| spec.matches(*id)).collect();
        if matches.is_empty() {
            anyhow::bail!("package ID specification `{}` matched no packages", spec);
        }
        pkg_ids.extend(matches);
    }
    let packages = pkg_set.get_many(pkg_ids)?;

    for pkg in packages {
        let pkg_dir = format!("{}-*", pkg.name());

        // Clean fingerprints.
        for (_, layout) in &layouts_with_host {
            rm_rf_glob(&layout.fingerprint().join(&pkg_dir), config)?;
        }

        for target in pkg.targets() {
            if target.is_custom_build() {
                // Get both the build_script_build and the output directory.
                for (_, layout) in &layouts_with_host {
                    rm_rf_glob(&layout.build().join(&pkg_dir), config)?;
                }
                continue;
            }
            let crate_name = target.crate_name();
            for &mode in &[
                CompileMode::Build,
                CompileMode::Test,
                CompileMode::Check { test: false },
            ] {
                for (compile_kind, layout) in &layouts {
                    let triple = target_data.short_name(compile_kind);

                    let (file_types, _unsupported) = target_data
                        .info(*compile_kind)
                        .rustc_outputs(mode, target.kind(), triple)?;
                    let (dir, uplift_dir) = if target.is_example() {
                        (layout.examples(), layout.examples())
                    } else {
                        (layout.deps(), layout.dest())
                    };
                    for file_type in file_types {
                        // Some files include a hash in the filename, some don't.
                        let hashed_name = file_type.output_filename(target, Some("*"));
                        let unhashed_name = file_type.output_filename(target, None);
                        rm_rf_glob(&dir.join(&hashed_name), config)?;
                        rm_rf(&dir.join(&unhashed_name), config)?;
                        // Remove dep-info file generated by rustc. It is not tracked in
                        // file_types. It does not have a prefix.
                        let hashed_dep_info = dir.join(format!("{}-*.d", crate_name));
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf_glob(&hashed_dep_info, config)?;
                        rm_rf(&unhashed_dep_info, config)?;

                        // Remove the uplifted copy.
                        let uplifted_path = uplift_dir.join(file_type.uplift_filename(target));
                        rm_rf(&uplifted_path, config)?;
                        // Dep-info generated by Cargo itself.
                        let dep_info = uplifted_path.with_extension("d");
                        rm_rf(&dep_info, config)?;
                    }
                    // TODO: what to do about build_script_build?
                    let incremental = layout.incremental().join(format!("{}-*", crate_name));
                    rm_rf_glob(&incremental, config)?;
                    rm_rf(&uplift_dir.join("CACHEDIR.TAG"), config)?;
                }
            }
        }
    }

    Ok(())
}

fn rm_rf_glob(pattern: &Path, config: &Config) -> CargoResult<()> {
    // TODO: Display utf8 warning to user?  Or switch to globset?
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    for path in glob::glob(pattern)? {
        rm_rf(&path?, config)?;
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
    let m = fs::symlink_metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_dir_all(path)
            .chain_err(|| anyhow::format_err!("could not remove build directory"))?;
    } else if m.is_ok() {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_file(path)
            .chain_err(|| anyhow::format_err!("failed to remove build artifact"))?;
    }
    Ok(())
}
<commit_msg>Try to resolve a build-time conflict with master<commit_after>use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
use crate::core::profiles::Profiles;
use crate::core::{InternedString, PackageIdSpec, Workspace};
use crate::ops;
use crate::util::errors::{CargoResult, CargoResultExt};
use crate::util::paths;
use crate::util::Config;
use std::fs;
use std::path::Path;

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    /// A list of packages to clean. If empty, everything is cleaned.
    pub spec: Vec<String>,
    /// The target arch triple to clean, or None for the host arch
    pub targets: Vec<String>,
    /// Whether to clean the release directory
    pub profile_specified: bool,
    /// Whether to clean the directory of a certain build profile
    pub requested_profile: InternedString,
    /// Whether to just clean the doc directory
    pub doc: bool,
}

/// Cleans the package's build artifacts.
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
    let mut target_dir = ws.target_dir();
    let config = ws.config();

    // If the doc option is set, we just want to delete the doc directory.
    if opts.doc {
        target_dir = target_dir.join("doc");
        return rm_rf(&target_dir.into_path_unlocked(), config);
    }

    let profiles = Profiles::new(ws.profiles(), config, opts.requested_profile, ws.features())?;

    if opts.profile_specified {
        // After parsing profiles we know the dir-name of the profile, if a profile
        // was passed from the command line. If so, delete only the directory of
        // that profile.
        let dir_name = profiles.get_dir_name();
        target_dir = target_dir.join(dir_name);
    }

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        return rm_rf(&target_dir.into_path_unlocked(), config);
    }

    // Clean specific packages.
    let requested_kinds = CompileKind::from_requested_targets(config, &opts.targets)?;
    let target_data = RustcTargetData::new(ws, &requested_kinds)?;
    let (pkg_set, resolve) = ops::resolve_ws(ws)?;
    let prof_dir_name = profiles.get_dir_name();
    let host_layout = Layout::new(ws, None, &prof_dir_name)?;
    // Convert requested kinds to a Vec of layouts.
    let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds
        .into_iter()
        .filter_map(|kind| match kind {
            CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) {
                Ok(layout) => Some(Ok((kind, layout))),
                Err(e) => Some(Err(e)),
            },
            CompileKind::Host => None,
        })
        .collect::<CargoResult<_>>()?;
    // A Vec of layouts. This is a little convoluted because there can only be
    // one host_layout.
    let layouts = if opts.targets.is_empty() {
        vec![(CompileKind::Host, &host_layout)]
    } else {
        target_layouts
            .iter()
            .map(|(kind, layout)| (*kind, layout))
            .collect()
    };
    // Create a Vec that also includes the host for things that need to clean both.
    let layouts_with_host: Vec<(CompileKind, &Layout)> =
        std::iter::once((CompileKind::Host, &host_layout))
            .chain(layouts.iter().map(|(k, l)| (*k, *l)))
            .collect();

    // Cleaning individual rustdoc crates is currently not supported.
    // For example, the search index would need to be rebuilt to fully
    // remove it (otherwise you're left with lots of broken links).
    // Doc tests produce no output.

    // Get Packages for the specified specs.
    let mut pkg_ids = Vec::new();
    for spec_str in opts.spec.iter() {
        // Translate the spec to a Package.
        let spec = PackageIdSpec::parse(spec_str)?;
        if spec.version().is_some() {
            config.shell().warn(&format!(
                "version qualifier in `-p {}` is ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        if spec.url().is_some() {
            config.shell().warn(&format!(
                "url qualifier in `-p {}` ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        let matches: Vec<_> = resolve.iter().filter(|id| spec.matches(*id)).collect();
        if matches.is_empty() {
            anyhow::bail!("package ID specification `{}` matched no packages", spec);
        }
        pkg_ids.extend(matches);
    }
    let packages = pkg_set.get_many(pkg_ids)?;

    for pkg in packages {
        let pkg_dir = format!("{}-*", pkg.name());

        // Clean fingerprints.
        for (_, layout) in &layouts_with_host {
            rm_rf_glob(&layout.fingerprint().join(&pkg_dir), config)?;
        }

        for target in pkg.targets() {
            if target.is_custom_build() {
                // Get both the build_script_build and the output directory.
                for (_, layout) in &layouts_with_host {
                    rm_rf_glob(&layout.build().join(&pkg_dir), config)?;
                }
                continue;
            }
            let crate_name = target.crate_name();
            for &mode in &[
                CompileMode::Build,
                CompileMode::Test,
                CompileMode::Check { test: false },
            ] {
                for (compile_kind, layout) in &layouts {
                    let triple = target_data.short_name(compile_kind);

                    let (file_types, _unsupported) = target_data
                        .info(*compile_kind)
                        .rustc_outputs(mode, target.kind(), triple)?;
                    let (dir, uplift_dir) = if target.is_example() {
                        (layout.examples(), layout.examples())
                    } else {
                        (layout.deps(), layout.dest())
                    };
                    for file_type in file_types {
                        // Some files include a hash in the filename, some don't.
                        let hashed_name = file_type.output_filename(target, Some("*"));
                        let unhashed_name = file_type.output_filename(target, None);
                        rm_rf_glob(&dir.join(&hashed_name), config)?;
                        rm_rf(&dir.join(&unhashed_name), config)?;
                        // Remove dep-info file generated by rustc. It is not tracked in
                        // file_types. It does not have a prefix.
                        let hashed_dep_info = dir.join(format!("{}-*.d", crate_name));
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf_glob(&hashed_dep_info, config)?;
                        rm_rf(&unhashed_dep_info, config)?;

                        // Remove the uplifted copy.
                        let uplifted_path = uplift_dir.join(file_type.uplift_filename(target));
                        rm_rf(&uplifted_path, config)?;
                        // Dep-info generated by Cargo itself.
                        let dep_info = uplifted_path.with_extension("d");
                        rm_rf(&dep_info, config)?;
                    }
                    // TODO: what to do about build_script_build?
                    let incremental = layout.incremental().join(format!("{}-*", crate_name));
                    rm_rf_glob(&incremental, config)?;
                    if let Some(uplift_dir) = uplift_dir {
                        rm_rf(&uplift_dir.join("CACHEDIR.TAG"), config)?;
                    }
                }
            }
        }
    }

    Ok(())
}

fn rm_rf_glob(pattern: &Path, config: &Config) -> CargoResult<()> {
    // TODO: Display utf8 warning to user?  Or switch to globset?
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    for path in glob::glob(pattern)? {
        rm_rf(&path?, config)?;
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
    let m = fs::symlink_metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_dir_all(path)
            .chain_err(|| anyhow::format_err!("could not remove build directory"))?;
    } else if m.is_ok() {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_file(path)
            .chain_err(|| anyhow::format_err!("failed to remove build artifact"))?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::option::Option;

type OptInt32 = Option<i32>;
type OptInt64 = Option<i64>;
type OptString = Option<String>;

pub enum CellType {
    Int32 (OptInt32),
    Int64 (OptInt64),
    Text  (OptString)
}

trait Metainf {
    fn name(&self) -> String;
}

impl Metainf for CellType {
    fn name(&self) -> String {
        return match *self {
            CellType::Int32(_) => "INT32".to_string(),
            CellType::Int64(_) => "INT64".to_string(),
            CellType::Text(_) => "TEXT".to_string()
        }
    }
}

pub struct CellFormat {
    name: String,
    type_: CellType
}

trait Metaformat<T> {
    fn construct(format_: CellFormat) -> T;
}

pub struct Cell {
    value: CellType
}

impl Metaformat<Cell> for Cell {
    fn construct(format_: CellFormat) -> Cell {
        return match format_.type_ {
            CellType::Int32(_) => Cell { value: CellType::Int32(None) },
            CellType::Int64(_) => Cell { value: CellType::Int64(None) },
            CellType::Text(_) => Cell { value: CellType::Text(None) }
        }
    }
}

pub struct Row {
    cells: Vec<CellType>
}

pub struct Table {
    rows: Vec<Row>
}

trait ISet {
    fn size(&self) -> usize;
    fn add(&mut self, row: Row);
    fn remove(&mut self, index: usize); // FIXME
}
<commit_msg>Try to implement container common<commit_after>use std::option::Option;

type OptInt32 = Option<i32>;
type OptInt64 = Option<i64>;
type OptString = Option<String>;

pub enum CellType {
    Int32 (OptInt32),
    Int64 (OptInt64),
    Text  (OptString)
}

trait Metainf {
    fn name(&self) -> String;
}

impl Metainf for CellType {
    fn name(&self) -> String {
        return match *self {
            CellType::Int32(_) => "INT32".to_string(),
            CellType::Int64(_) => "INT64".to_string(),
            CellType::Text(_) => "TEXT".to_string()
        }
    }
}

pub struct CellFormat {
    name: String,
    type_: CellType
}

trait Metaformat<T> {
    fn construct(format_: CellFormat) -> T;
}

pub struct Cell {
    value: CellType
}

impl Metaformat<Cell> for Cell {
    fn construct(format_: CellFormat) -> Cell {
        return match format_.type_ {
            CellType::Int32(_) => Cell { value: CellType::Int32(None) },
            CellType::Int64(_) => Cell { value: CellType::Int64(None) },
            CellType::Text(_) => Cell { value: CellType::Text(None) }
        }
    }
}

pub struct RowFormat {
    cols: Vec<CellFormat>
}

trait Container<T> {
    fn size(&self) -> usize;
    fn push(&mut self, item: T);
    //fn remove
}

pub struct Row {
    cells: Vec<Cell>
}

impl Container<Cell> for Row {
    fn size(&self) -> usize {
        return self.cells.len();
    }

    fn push(&mut self, item: Cell) {
        self.cells.push(item);
    }
}

pub struct Table {
    name: String,
    rows: Vec<Row>
}

impl Container<Row> for Table {
    fn size(&self) -> usize {
        return self.rows.len();
    }

    fn push(&mut self, item: Row) {
        self.rows.push(item);
    }
}
<|endoftext|>


<commit_before>pub use self::bvh::*;
pub use self::optimization::*;
mod bvh;
mod optimization;
<commit_msg>Add documentation<commit_after>//! This module defines a [`BVH`].
//!
//! [`BVH`]: struct.BVH.html
//!

pub use self::bvh::*;
pub use self::optimization::*;
mod bvh;
mod optimization;
<|endoftext|>


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

// This file is part of the uutils coreutils package.
//
// (c) Jordi Boggiano <j.boggiano@seld.be>
// (c) Evgeniy Klyuchikov <evgeniy.klyuchikov@gmail.com>
// (c) Joshua S. Miller <jsmiller@uchicago.edu>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate quick_error;
#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate uucore;

// last synced with: cat (GNU coreutils) 8.13
use quick_error::ResultExt;
use std::fs::{metadata, File};
use std::io::{self, stdout, stdin, stderr, Write, Read, BufWriter};
use uucore::fs::is_stdin_interactive;

/// Unix domain socket support
#[cfg(unix)] use std::net::Shutdown;
#[cfg(unix)] use std::os::unix::fs::FileTypeExt;
#[cfg(unix)] use unix_socket::UnixStream;

static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Concatenate FILE(s), or standard input, to standard output
 With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &'static str = "";

#[derive(PartialEq)]
enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

quick_error! {
    #[derive(Debug)]
    enum CatError {
        /// Wrapper for io::Error with path context
        Input(err: io::Error, path: String) {
            display("cat: {0}: {1}", path, err)
            context(path: &'a str, err: io::Error) -> (err, path.to_owned())
            cause(err)
        }

        /// Wrapper for io::Error with no context
        Output(err: io::Error) {
            display("cat: {0}", err) from()
            cause(err)
        }

        /// Uknown Filetype  classification
        UnknownFiletype(path: String) {
            display("cat: {0}: unknown filetype", path)
        }

        /// At least one error was encountered in reading or writing
        EncounteredErrors(count: usize) {
            display("cat: encountered {0} errors", count)
        }

        /// Denotes an error caused by trying to `cat` a directory
        IsDirectory(path: String) {
            display("cat: {0}: Is a directory", path)
        }
    }
}

struct OutputOptions {
    /// Line numbering mode
    number: NumberingMode,

    /// Suppress repeated empty output lines
    squeeze_blank: bool,

    /// display TAB characters as `tab`
    show_tabs: bool,

    /// If `show_tabs == true`, this string will be printed in the
    /// place of tabs
    tab: String,

    /// Can be set to show characters other than '\n' a the end of
    /// each line, e.g. $
    end_of_line: String,

    /// use ^ and M- notation, except for LF (\\n) and TAB (\\t)
    show_nonprint: bool,
}

/// Represents an open file handle, stream, or other device
struct InputHandle {
    reader: Box<Read>,
    is_interactive: bool,
}

/// Concrete enum of recognized file types.
///
/// *Note*: `cat`-ing a directory should result in an
/// CatError::IsDirectory
enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)] BlockDevice,
    #[cfg(unix)] CharacterDevice,
    #[cfg(unix)] Fifo,
    #[cfg(unix)] Socket,
 }

type CatResult<T> = Result<T, CatError>;

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
        .optflag("A", "show-all", "equivalent to -vET")
        .optflag("b",
                 "number-nonblank",
                 "number nonempty output lines, overrides -n")
        .optflag("e", "", "equivalent to -vE")
        .optflag("E", "show-ends", "display $ at end of each line")
        .optflag("n", "number", "number all output lines")
        .optflag("s", "squeeze-blank", "suppress repeated empty output lines")
        .optflag("t", "", "equivalent to -vT")
        .optflag("T", "show-tabs", "display TAB characters as ^I")
        .optflag("v",
                 "show-nonprinting",
                 "use ^ and M- notation, except for LF (\\n) and TAB (\\t)")
        .parse(args);

    let number_mode = if matches.opt_present("b") {
        NumberingMode::NumberNonEmpty
    } else if matches.opt_present("n") {
        NumberingMode::NumberAll
    } else {
        NumberingMode::NumberNone
    };

    let show_nonprint =
        matches.opts_present(&["A".to_owned(), "e".to_owned(), "t".to_owned(), "v".to_owned()]);
    let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]);
    let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
    let squeeze_blank = matches.opt_present("s");
    let mut files = matches.free;
    if files.is_empty() {
        files.push("-".to_owned());
    }

    let can_write_fast = !(show_tabs
                          || show_nonprint
                          || show_ends
                          || squeeze_blank
                          || number_mode != NumberingMode::NumberNone);

    let success = if can_write_fast {
        write_fast(files).is_ok()

    } else {
        let tab = match show_tabs {
            true => "^I",
            false => "\t",
        }.to_owned();

        let end_of_line = match show_ends {
            true => "$\n",
            false => "\n",
        }.to_owned();

        let options = OutputOptions {
            end_of_line: end_of_line,
            number: number_mode,
            show_nonprint: show_nonprint,
            show_tabs: show_tabs,
            squeeze_blank: squeeze_blank,
            tab: tab,
        };

        write_lines(files, &options).is_ok()
    };

    match success {
        true => 0,
        false => 1,
    }
}

/// Classifies the `InputType` of file at `path` if possible
///
/// # Arguments
///
/// * `path` - Path on a file system to classify metadata
fn get_input_type(path: &str) -> CatResult<InputType> {
    if path == "-" {
      return Ok(InputType::StdIn)
    }

    match metadata(path).context(path)?.file_type() {
        #[cfg(unix)] ft if ft.is_block_device() => Ok(InputType::BlockDevice),
        #[cfg(unix)] ft if ft.is_char_device()  => Ok(InputType::CharacterDevice),
        #[cfg(unix)] ft if ft.is_fifo()         => Ok(InputType::Fifo),
        #[cfg(unix)] ft if ft.is_socket()       => Ok(InputType::Socket),
        ft if ft.is_dir()                       => Ok(InputType::Directory),
        ft if ft.is_file()                      => Ok(InputType::File),
        ft if ft.is_symlink()                   => Ok(InputType::SymLink),
        _                                       => Err(CatError::UnknownFiletype(path.to_owned()))
    }
}

/// Returns an InputHandle from which a Reader can be accessed or an
/// error
///
/// # Arguments
///
/// * `path` - `InputHandler` will wrap a reader from this file path
fn open(path: &str) -> CatResult<InputHandle> {
    if path == "-" {
        let stdin = stdin();
        return Ok(InputHandle {
            reader: Box::new(stdin) as Box<Read>,
            is_interactive: is_stdin_interactive(),
        });
    }

    match get_input_type(path)? {
        InputType::Directory => Err(CatError::IsDirectory(path.to_owned())),
        #[cfg(unix)] InputType::Socket => {
            let socket = UnixStream::connect(path).context(path)?;
            socket.shutdown(Shutdown::Write).context(path)?;
            Ok(InputHandle {
                reader: Box::new(socket) as Box<Read>,
                is_interactive: false,
            })
        },
        _ => {
            let file = File::open(path).context(path)?;
            Ok(InputHandle {
                reader: Box::new(file) as Box<Read>,
                is_interactive: false,
            })
        },
    }
}

/// Writes files to stdout with no configuration.  This allows a
/// simple memory copy. Returns `Ok(())` if no errors were
/// encountered, or an error with the number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_fast(files: Vec<String>) -> CatResult<()> {
    let mut writer = stdout();
    let mut in_buf = [0; 1024 * 64];
    let mut error_count = 0;

    for file in files {
        match open(&file[..]) {
            Ok(mut handle) => {
                while let Ok(n) = handle.reader.read(&mut in_buf) {
                    if n == 0 {
                        break;
                    }
                    writer.write_all(&in_buf[..n]).context(&file[..])?;
                }
            },
            Err(error) => {
                writeln!(&mut stderr(), "{}", error)?;
                error_count += 1;
            },
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// Writes files to stdout with `options` as configuration.  Returns
/// `Ok(())` if no errors were encountered, or an error with the
/// number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_lines(files: Vec<String>, options: &OutputOptions) -> CatResult<()> {
    let mut line_counter: usize = 1;
    let mut error_count = 0;
    let mut at_line_start = true;

    for file in files {
        let written = write_file_lines(&file[..], options, line_counter, &mut at_line_start);
        line_counter += match written {
            Ok(lines) => lines,
            Err(error) => {
                writeln!(&mut stderr(), "{}", error).context(&file[..])?;
                error_count += 1;
                0
            }
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// Outputs file and returns result with the number of lines to stdout
/// from `file`.  If line numbering is enabled, then start output
/// numbering at `line_number`.
fn write_file_lines(file: &str,
                    options: &OutputOptions,
                    line_number: usize,
                    at_line_start: &mut bool) -> CatResult<usize> {
    let mut handle = open(&file[..])?;
    let mut in_buf = [0; 1024 * 31];
    let mut writer = BufWriter::with_capacity(1024 * 64, stdout());
    let mut one_blank_kept = false;

    let mut lines = 0;

    while let Ok(n) = handle.reader.read(&mut in_buf) {
        if n == 0 {
            break;
        }
        let in_buf = &in_buf[..n];
        let mut pos = 0;
        while pos < n {
            // skip empty line_number enumerating them if needed
            if in_buf[pos] == '\n' as u8 {
                if !*at_line_start || ! options.squeeze_blank || !one_blank_kept {
                    one_blank_kept = true;
                    if *at_line_start && options.number == NumberingMode::NumberAll {
                        write!(&mut writer, "{0:6}\t", line_number + lines)?;
                        lines += 1;
                    }
                    writer.write_all(options.end_of_line.as_bytes())?;
                    if handle.is_interactive {
                        writer.flush().context(&file[..])?;
                    }
                }
                *at_line_start = true;
                pos += 1;
                continue;
            }
            one_blank_kept = false;
            if *at_line_start && options.number != NumberingMode::NumberNone {
                write!(&mut writer, "{0:6}\t", line_number + lines)?;
                lines += 1;
            }

            // print to end of line or end of buffer
            let offset = if options.show_nonprint {
                write_nonprint_to_end(&in_buf[pos..], &mut writer, options.tab.as_bytes())
            } else if options.show_tabs {
                write_tab_to_end(&in_buf[pos..], &mut writer)
            } else {
                write_to_end(&in_buf[pos..], &mut writer)
            };
            // end of buffer?
            if offset == 0 {
                *at_line_start = false;
                break;
            }
            // print suitable end of line
            writer.write_all(options.end_of_line.as_bytes())?;
            if handle.is_interactive {
                writer.flush()?;
            }
            *at_line_start = true;
            pos += offset;
        }
    }

    Ok(lines)
}

// write***_to_end methods
// Write all symbols till end of line or end of buffer is reached
// Return the (number of written symbols + 1) or 0 if the end of buffer is reached
fn write_to_end<W: Write>(in_buf: &[u8], writer: &mut W) -> usize {
    match in_buf.iter().position(|c| *c == '\n' as u8) {
        Some(p) => {
            writer.write_all(&in_buf[..p]).unwrap();
            p + 1
        }
        None => {
            writer.write_all(in_buf).unwrap();
            0
        }
    }
}

fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize {
    loop {
        match in_buf.iter().position(|c| *c == '\n' as u8 || *c == '\t' as u8) {
            Some(p) => {
                writer.write_all(&in_buf[..p]).unwrap();
                if in_buf[p] == '\n' as u8 {
                    return p + 1;
                } else {
                    writer.write_all("^I".as_bytes()).unwrap();
                    in_buf = &in_buf[p + 1..];
                }
            }
            None => {
                writer.write_all(in_buf).unwrap();
                return 0;
            }
        };
    }
}

fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) -> usize {
    let mut count = 0;

    for byte in in_buf.iter().map(|c| *c) {
        if byte == '\n' as u8 {
            break;
        }
        match byte {
            9 => writer.write_all(tab),
            0...8 | 10...31 => writer.write_all(&['^' as u8, byte + 64]),
            32...126 => writer.write_all(&[byte]),
            127 => writer.write_all(&['^' as u8, byte - 64]),
            128...159 => writer.write_all(&['M' as u8, '-' as u8, '^' as u8, byte - 64]),
            160...254 => writer.write_all(&['M' as u8, '-' as u8, byte - 128]),
            _ => writer.write_all(&['M' as u8, '-' as u8, '^' as u8, 63]),
        }.unwrap();
        count += 1;
    }
    if count != in_buf.len() { count + 1 } else { 0 }
}
<commit_msg>cat: Collect output state into a struct<commit_after>#![crate_name = "uu_cat"]

// This file is part of the uutils coreutils package.
//
// (c) Jordi Boggiano <j.boggiano@seld.be>
// (c) Evgeniy Klyuchikov <evgeniy.klyuchikov@gmail.com>
// (c) Joshua S. Miller <jsmiller@uchicago.edu>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

#[macro_use]
extern crate quick_error;
#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate uucore;

// last synced with: cat (GNU coreutils) 8.13
use quick_error::ResultExt;
use std::fs::{metadata, File};
use std::io::{self, stdout, stdin, stderr, Write, Read, BufWriter};
use uucore::fs::is_stdin_interactive;

/// Unix domain socket support
#[cfg(unix)] use std::net::Shutdown;
#[cfg(unix)] use std::os::unix::fs::FileTypeExt;
#[cfg(unix)] use unix_socket::UnixStream;

static SYNTAX: &'static str = "[OPTION]... [FILE]...";
static SUMMARY: &'static str = "Concatenate FILE(s), or standard input, to standard output
 With no FILE, or when FILE is -, read standard input.";
static LONG_HELP: &'static str = "";

#[derive(PartialEq)]
enum NumberingMode {
    NumberNone,
    NumberNonEmpty,
    NumberAll,
}

quick_error! {
    #[derive(Debug)]
    enum CatError {
        /// Wrapper for io::Error with path context
        Input(err: io::Error, path: String) {
            display("cat: {0}: {1}", path, err)
            context(path: &'a str, err: io::Error) -> (err, path.to_owned())
            cause(err)
        }

        /// Wrapper for io::Error with no context
        Output(err: io::Error) {
            display("cat: {0}", err) from()
            cause(err)
        }

        /// Uknown Filetype  classification
        UnknownFiletype(path: String) {
            display("cat: {0}: unknown filetype", path)
        }

        /// At least one error was encountered in reading or writing
        EncounteredErrors(count: usize) {
            display("cat: encountered {0} errors", count)
        }

        /// Denotes an error caused by trying to `cat` a directory
        IsDirectory(path: String) {
            display("cat: {0}: Is a directory", path)
        }
    }
}

struct OutputOptions {
    /// Line numbering mode
    number: NumberingMode,

    /// Suppress repeated empty output lines
    squeeze_blank: bool,

    /// display TAB characters as `tab`
    show_tabs: bool,

    /// If `show_tabs == true`, this string will be printed in the
    /// place of tabs
    tab: String,

    /// Can be set to show characters other than '\n' a the end of
    /// each line, e.g. $
    end_of_line: String,

    /// use ^ and M- notation, except for LF (\\n) and TAB (\\t)
    show_nonprint: bool,
}

/// Represents an open file handle, stream, or other device
struct InputHandle {
    reader: Box<Read>,
    is_interactive: bool,
}

/// Concrete enum of recognized file types.
///
/// *Note*: `cat`-ing a directory should result in an
/// CatError::IsDirectory
enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)] BlockDevice,
    #[cfg(unix)] CharacterDevice,
    #[cfg(unix)] Fifo,
    #[cfg(unix)] Socket,
 }

type CatResult<T> = Result<T, CatError>;

pub fn uumain(args: Vec<String>) -> i32 {
    let matches = new_coreopts!(SYNTAX, SUMMARY, LONG_HELP)
        .optflag("A", "show-all", "equivalent to -vET")
        .optflag("b",
                 "number-nonblank",
                 "number nonempty output lines, overrides -n")
        .optflag("e", "", "equivalent to -vE")
        .optflag("E", "show-ends", "display $ at end of each line")
        .optflag("n", "number", "number all output lines")
        .optflag("s", "squeeze-blank", "suppress repeated empty output lines")
        .optflag("t", "", "equivalent to -vT")
        .optflag("T", "show-tabs", "display TAB characters as ^I")
        .optflag("v",
                 "show-nonprinting",
                 "use ^ and M- notation, except for LF (\\n) and TAB (\\t)")
        .parse(args);

    let number_mode = if matches.opt_present("b") {
        NumberingMode::NumberNonEmpty
    } else if matches.opt_present("n") {
        NumberingMode::NumberAll
    } else {
        NumberingMode::NumberNone
    };

    let show_nonprint =
        matches.opts_present(&["A".to_owned(), "e".to_owned(), "t".to_owned(), "v".to_owned()]);
    let show_ends = matches.opts_present(&["E".to_owned(), "A".to_owned(), "e".to_owned()]);
    let show_tabs = matches.opts_present(&["A".to_owned(), "T".to_owned(), "t".to_owned()]);
    let squeeze_blank = matches.opt_present("s");
    let mut files = matches.free;
    if files.is_empty() {
        files.push("-".to_owned());
    }

    let can_write_fast = !(show_tabs
                          || show_nonprint
                          || show_ends
                          || squeeze_blank
                          || number_mode != NumberingMode::NumberNone);

    let success = if can_write_fast {
        write_fast(files).is_ok()

    } else {
        let tab = match show_tabs {
            true => "^I",
            false => "\t",
        }.to_owned();

        let end_of_line = match show_ends {
            true => "$\n",
            false => "\n",
        }.to_owned();

        let options = OutputOptions {
            end_of_line: end_of_line,
            number: number_mode,
            show_nonprint: show_nonprint,
            show_tabs: show_tabs,
            squeeze_blank: squeeze_blank,
            tab: tab,
        };

        write_lines(files, &options).is_ok()
    };

    match success {
        true => 0,
        false => 1,
    }
}

/// Classifies the `InputType` of file at `path` if possible
///
/// # Arguments
///
/// * `path` - Path on a file system to classify metadata
fn get_input_type(path: &str) -> CatResult<InputType> {
    if path == "-" {
      return Ok(InputType::StdIn)
    }

    match metadata(path).context(path)?.file_type() {
        #[cfg(unix)] ft if ft.is_block_device() => Ok(InputType::BlockDevice),
        #[cfg(unix)] ft if ft.is_char_device()  => Ok(InputType::CharacterDevice),
        #[cfg(unix)] ft if ft.is_fifo()         => Ok(InputType::Fifo),
        #[cfg(unix)] ft if ft.is_socket()       => Ok(InputType::Socket),
        ft if ft.is_dir()                       => Ok(InputType::Directory),
        ft if ft.is_file()                      => Ok(InputType::File),
        ft if ft.is_symlink()                   => Ok(InputType::SymLink),
        _                                       => Err(CatError::UnknownFiletype(path.to_owned()))
    }
}

/// Returns an InputHandle from which a Reader can be accessed or an
/// error
///
/// # Arguments
///
/// * `path` - `InputHandler` will wrap a reader from this file path
fn open(path: &str) -> CatResult<InputHandle> {
    if path == "-" {
        let stdin = stdin();
        return Ok(InputHandle {
            reader: Box::new(stdin) as Box<Read>,
            is_interactive: is_stdin_interactive(),
        });
    }

    match get_input_type(path)? {
        InputType::Directory => Err(CatError::IsDirectory(path.to_owned())),
        #[cfg(unix)] InputType::Socket => {
            let socket = UnixStream::connect(path).context(path)?;
            socket.shutdown(Shutdown::Write).context(path)?;
            Ok(InputHandle {
                reader: Box::new(socket) as Box<Read>,
                is_interactive: false,
            })
        },
        _ => {
            let file = File::open(path).context(path)?;
            Ok(InputHandle {
                reader: Box::new(file) as Box<Read>,
                is_interactive: false,
            })
        },
    }
}

/// Writes files to stdout with no configuration.  This allows a
/// simple memory copy. Returns `Ok(())` if no errors were
/// encountered, or an error with the number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_fast(files: Vec<String>) -> CatResult<()> {
    let mut writer = stdout();
    let mut in_buf = [0; 1024 * 64];
    let mut error_count = 0;

    for file in files {
        match open(&file[..]) {
            Ok(mut handle) => {
                while let Ok(n) = handle.reader.read(&mut in_buf) {
                    if n == 0 {
                        break;
                    }
                    writer.write_all(&in_buf[..n]).context(&file[..])?;
                }
            },
            Err(error) => {
                writeln!(&mut stderr(), "{}", error)?;
                error_count += 1;
            },
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// State that persists between output of each file
struct OutputState {
    /// The current line number
    line_number: usize,

    /// Whether the output cursor is at the beginning of a new line
    at_line_start: bool,
}

/// Writes files to stdout with `options` as configuration.  Returns
/// `Ok(())` if no errors were encountered, or an error with the
/// number of errors encountered.
///
/// # Arguments
///
/// * `files` - There is no short circuit when encountiner an error
/// reading a file in this vector
fn write_lines(files: Vec<String>, options: &OutputOptions) -> CatResult<()> {
    let mut error_count = 0;
    let mut state = OutputState {
        line_number: 1,
        at_line_start: true,
    };

    for file in files {
        if let Err(error) = write_file_lines(&file, options, &mut state) {
            writeln!(&mut stderr(), "{}", error).context(&file[..])?;
            error_count += 1;
        }
    }

    match error_count {
        0 => Ok(()),
        _ => Err(CatError::EncounteredErrors(error_count)),
    }
}

/// Outputs file contents to stdout in a linewise fashion,
/// propagating any errors that might occur.
fn write_file_lines(file: &str,
                    options: &OutputOptions,
                    state: &mut OutputState) -> CatResult<()> {
    let mut handle = open(file)?;
    let mut in_buf = [0; 1024 * 31];
    let mut writer = BufWriter::with_capacity(1024 * 64, stdout());
    let mut one_blank_kept = false;

    while let Ok(n) = handle.reader.read(&mut in_buf) {
        if n == 0 {
            break;
        }
        let in_buf = &in_buf[..n];
        let mut pos = 0;
        while pos < n {
            // skip empty line_number enumerating them if needed
            if in_buf[pos] == '\n' as u8 {
                if !state.at_line_start || !options.squeeze_blank || !one_blank_kept {
                    one_blank_kept = true;
                    if state.at_line_start && options.number == NumberingMode::NumberAll {
                        write!(&mut writer, "{0:6}\t", state.line_number)?;
                        state.line_number += 1;
                    }
                    writer.write_all(options.end_of_line.as_bytes())?;
                    if handle.is_interactive {
                        writer.flush().context(&file[..])?;
                    }
                }
                state.at_line_start = true;
                pos += 1;
                continue;
            }
            one_blank_kept = false;
            if state.at_line_start && options.number != NumberingMode::NumberNone {
                write!(&mut writer, "{0:6}\t", state.line_number)?;
                state.line_number += 1;
            }

            // print to end of line or end of buffer
            let offset = if options.show_nonprint {
                write_nonprint_to_end(&in_buf[pos..], &mut writer, options.tab.as_bytes())
            } else if options.show_tabs {
                write_tab_to_end(&in_buf[pos..], &mut writer)
            } else {
                write_to_end(&in_buf[pos..], &mut writer)
            };
            // end of buffer?
            if offset == 0 {
                state.at_line_start = false;
                break;
            }
            // print suitable end of line
            writer.write_all(options.end_of_line.as_bytes())?;
            if handle.is_interactive {
                writer.flush()?;
            }
            state.at_line_start = true;
            pos += offset;
        }
    }

    Ok(())
}

// write***_to_end methods
// Write all symbols till end of line or end of buffer is reached
// Return the (number of written symbols + 1) or 0 if the end of buffer is reached
fn write_to_end<W: Write>(in_buf: &[u8], writer: &mut W) -> usize {
    match in_buf.iter().position(|c| *c == '\n' as u8) {
        Some(p) => {
            writer.write_all(&in_buf[..p]).unwrap();
            p + 1
        }
        None => {
            writer.write_all(in_buf).unwrap();
            0
        }
    }
}

fn write_tab_to_end<W: Write>(mut in_buf: &[u8], writer: &mut W) -> usize {
    loop {
        match in_buf.iter().position(|c| *c == '\n' as u8 || *c == '\t' as u8) {
            Some(p) => {
                writer.write_all(&in_buf[..p]).unwrap();
                if in_buf[p] == '\n' as u8 {
                    return p + 1;
                } else {
                    writer.write_all("^I".as_bytes()).unwrap();
                    in_buf = &in_buf[p + 1..];
                }
            }
            None => {
                writer.write_all(in_buf).unwrap();
                return 0;
            }
        };
    }
}

fn write_nonprint_to_end<W: Write>(in_buf: &[u8], writer: &mut W, tab: &[u8]) -> usize {
    let mut count = 0;

    for byte in in_buf.iter().map(|c| *c) {
        if byte == '\n' as u8 {
            break;
        }
        match byte {
            9 => writer.write_all(tab),
            0...8 | 10...31 => writer.write_all(&['^' as u8, byte + 64]),
            32...126 => writer.write_all(&[byte]),
            127 => writer.write_all(&['^' as u8, byte - 64]),
            128...159 => writer.write_all(&['M' as u8, '-' as u8, '^' as u8, byte - 64]),
            160...254 => writer.write_all(&['M' as u8, '-' as u8, byte - 128]),
            _ => writer.write_all(&['M' as u8, '-' as u8, '^' as u8, 63]),
        }.unwrap();
        count += 1;
    }
    if count != in_buf.len() { count + 1 } else { 0 }
}
<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use std::result::Result as StdResult;
use std::sync::{Arc, Weak, Mutex};
use std::sync::atomic::{Ordering, AtomicUsize};
use std::thread;

use bincode;
use futures::prelude::*;
use futures::task::AtomicTask;
use qp_trie::{Entry, Trie};

use errors::*;
use marshal::ObjectHash;

const OK_NOT_READY: usize = 0;
const OK_READY: usize = 1;
const ERR: usize = 2;

#[derive(Debug)]
struct CatalogLockInner {
    task: AtomicTask,
    locked: AtomicUsize,
}

#[derive(Debug)]
#[must_use = "CatalogLock must be .release()'d on success!"]
pub struct CatalogLock {
    inner: Weak<CatalogLockInner>,
    hash: ObjectHash,
    catalog: Catalog,
}

impl CatalogLock {
    fn new(catalog: &Catalog, hash: ObjectHash) -> (CatalogLock, CatalogFuture) {
        let inner = Arc::new(CatalogLockInner {
            task: AtomicTask::new(),
            locked: AtomicUsize::new(OK_NOT_READY),
        });

        let lock = CatalogLock {
            inner: Arc::downgrade(&inner),
            hash,
            catalog: catalog.clone(),
        };
        let future = CatalogFuture { inner };

        (lock, future)
    }

    pub fn release(self) {}
}

impl Drop for CatalogLock {
    fn drop(&mut self) {
        if thread::panicking() {
            let inner = Weak::upgrade(&self.inner).unwrap();
            inner.locked.store(ERR, Ordering::SeqCst);
        } else {
            let inner = Weak::upgrade(&self.inner).unwrap();
            let previous_state = inner.locked.compare_and_swap(
                OK_NOT_READY,
                OK_READY,
                Ordering::SeqCst,
            );
            assert_eq!(previous_state, OK_NOT_READY);
            self.catalog.inner.lock().unwrap().objects.insert(
                self.hash,
                CatalogEntry::Finished,
            );
            inner.task.notify();
        }
    }
}

#[derive(Debug, Clone)]
pub struct CatalogFuture {
    inner: Arc<CatalogLockInner>,
}

impl Future for CatalogFuture {
    type Item = ();
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.inner.locked.load(Ordering::SeqCst) {
            OK_NOT_READY => {
                self.inner.task.register();
                Ok(Async::NotReady)
            }
            OK_READY => Ok(Async::Ready(())),
            ERR => bail!(ErrorKind::CatalogPoisoned),
            _ => unreachable!("Invalid CatalogLock state!"),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CatalogEntry {
    /// An unlocked catalog entry indicates that something has been pushed to a remote or local
    /// blob store successfully and may be pulled from it.
    ///
    /// FIXME: [a bincode bug](https://github.com/TyOverby/bincode/issues/184) means this variant
    /// cannot be named `Unlocked` or else through some asymmetrical oddities `bincode`'s
    /// serializer/deserializer will attempt to deserialize the `Unlocked` variant and cause
    /// everything to blow up.
    Finished,

    /// A "locked" catalog entry has a reference to the future in it, so that a task can wait until
    /// a catalog entry is done filling.
    ///
    /// Note: If serialized, serde will error; however, a `Locked` catalog entry should never be
    /// serialized as catalog locks hold references to the catalog and also replaced `Locked`
    /// entries with `Finished` when dropped.
    #[serde(skip_serializing)]
    #[serde(skip_deserializing)]
    Locked(CatalogFuture),
}

impl Future for CatalogEntry {
    type Item = ();
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match *self {
            CatalogEntry::Locked(ref mut future) => future.poll(),
            CatalogEntry::Finished => Ok(Async::Ready(())),
        }
    }
}

#[derive(Debug, Clone)]
pub struct CatalogTrie {
    objects: Trie<ObjectHash, CatalogEntry>,
}

impl CatalogTrie {
    pub fn new() -> Self {
        Self { objects: Trie::new() }
    }

    pub fn insert(&mut self, hash: ObjectHash) {
        self.objects.insert(hash, CatalogEntry::Finished);
    }
}

#[derive(Debug)]
struct CatalogInner {
    catalog_path: PathBuf,
    objects: Trie<ObjectHash, CatalogEntry>,
}

#[derive(Debug, Clone)]
pub struct Catalog {
    inner: Arc<Mutex<CatalogInner>>,
}

impl Catalog {
    pub fn new(catalog_trie: CatalogTrie, catalog_path: PathBuf) -> Result<Catalog> {
        Ok(Catalog {
            inner: Arc::new(Mutex::new(CatalogInner {
                catalog_path,
                objects: catalog_trie.objects,
            })),
        })
    }

    pub fn load(catalog_path: PathBuf) -> Result<Catalog> {
        let objects = if catalog_path.is_file() {
            bincode::deserialize_from(
                &mut OpenOptions::new()
                    .read(true)
                    .open(&catalog_path)
                    .chain_err(|| ErrorKind::CatalogOpen(catalog_path.clone()))?,
                bincode::Infinite,
            ).chain_err(|| ErrorKind::CatalogDeserialize(catalog_path.clone()))?
        } else {
            Trie::new()
        };

        Ok(Catalog {
            inner: Arc::new(Mutex::new(CatalogInner {
                catalog_path,
                objects,
            })),
        })
    }

    pub fn try_lock(&self, hash: ObjectHash) -> StdResult<CatalogLock, CatalogEntry> {
        let mut inner_lock = self.inner.lock().unwrap();

        match inner_lock.objects.entry(hash) {
            Entry::Vacant(vacant) => {
                let (lock, future) = CatalogLock::new(self, hash);
                vacant.insert(CatalogEntry::Locked(future));
                Ok(lock)
            }
            Entry::Occupied(occupied) => Err(occupied.get().clone()),
        }
    }

    pub fn get(&self, hash: ObjectHash) -> Option<CatalogEntry> {
        self.inner.lock().unwrap().objects.get(&hash).map(|entry| {
            entry.clone()
        })
    }
}

impl Drop for CatalogInner {
    fn drop(&mut self) {
        let mut file = File::create(&self.catalog_path).unwrap();
        bincode::serialize_into(&mut file, &self.objects, bincode::Infinite).unwrap();
    }
}
<commit_msg>Add trie-searching ability to Catalog, for easier reference to hashes on the command line<commit_after>use std::borrow::Borrow;
use std::fs::{File, OpenOptions};
use std::path::PathBuf;
use std::result::Result as StdResult;
use std::sync::{Arc, Weak, Mutex};
use std::sync::atomic::{Ordering, AtomicUsize};
use std::thread;

use bincode;
use futures::prelude::*;
use futures::task::AtomicTask;
use qp_trie::{Entry, Trie};

use errors::*;
use marshal::ObjectHash;

const OK_NOT_READY: usize = 0;
const OK_READY: usize = 1;
const ERR: usize = 2;

#[derive(Debug)]
struct CatalogLockInner {
    task: AtomicTask,
    locked: AtomicUsize,
}

#[derive(Debug)]
#[must_use = "CatalogLock must be .release()'d on success!"]
pub struct CatalogLock {
    inner: Weak<CatalogLockInner>,
    hash: ObjectHash,
    catalog: Catalog,
}

impl CatalogLock {
    fn new(catalog: &Catalog, hash: ObjectHash) -> (CatalogLock, CatalogFuture) {
        let inner = Arc::new(CatalogLockInner {
            task: AtomicTask::new(),
            locked: AtomicUsize::new(OK_NOT_READY),
        });

        let lock = CatalogLock {
            inner: Arc::downgrade(&inner),
            hash,
            catalog: catalog.clone(),
        };
        let future = CatalogFuture { inner };

        (lock, future)
    }

    pub fn release(self) {}
}

impl Drop for CatalogLock {
    fn drop(&mut self) {
        if thread::panicking() {
            let inner = Weak::upgrade(&self.inner).unwrap();
            inner.locked.store(ERR, Ordering::SeqCst);
        } else {
            let inner = Weak::upgrade(&self.inner).unwrap();
            let previous_state = inner.locked.compare_and_swap(
                OK_NOT_READY,
                OK_READY,
                Ordering::SeqCst,
            );
            assert_eq!(previous_state, OK_NOT_READY);
            self.catalog.inner.lock().unwrap().objects.insert(
                self.hash,
                CatalogEntry::Finished,
            );
            inner.task.notify();
        }
    }
}

#[derive(Debug, Clone)]
pub struct CatalogFuture {
    inner: Arc<CatalogLockInner>,
}

impl Future for CatalogFuture {
    type Item = ();
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match self.inner.locked.load(Ordering::SeqCst) {
            OK_NOT_READY => {
                self.inner.task.register();
                Ok(Async::NotReady)
            }
            OK_READY => Ok(Async::Ready(())),
            ERR => bail!(ErrorKind::CatalogPoisoned),
            _ => unreachable!("Invalid CatalogLock state!"),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CatalogEntry {
    /// An unlocked catalog entry indicates that something has been pushed to a remote or local
    /// blob store successfully and may be pulled from it.
    ///
    /// FIXME: [a bincode bug](https://github.com/TyOverby/bincode/issues/184) means this variant
    /// cannot be named `Unlocked` or else through some asymmetrical oddities `bincode`'s
    /// serializer/deserializer will attempt to deserialize the `Unlocked` variant and cause
    /// everything to blow up.
    Finished,

    /// A "locked" catalog entry has a reference to the future in it, so that a task can wait until
    /// a catalog entry is done filling.
    ///
    /// Note: If serialized, serde will error; however, a `Locked` catalog entry should never be
    /// serialized as catalog locks hold references to the catalog and also replaced `Locked`
    /// entries with `Finished` when dropped.
    #[serde(skip_serializing)]
    #[serde(skip_deserializing)]
    Locked(CatalogFuture),
}

impl Future for CatalogEntry {
    type Item = ();
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        match *self {
            CatalogEntry::Locked(ref mut future) => future.poll(),
            CatalogEntry::Finished => Ok(Async::Ready(())),
        }
    }
}

#[derive(Debug, Clone)]
pub struct CatalogTrie {
    objects: Trie<ObjectHash, CatalogEntry>,
}

impl CatalogTrie {
    pub fn new() -> Self {
        Self { objects: Trie::new() }
    }

    pub fn insert(&mut self, hash: ObjectHash) {
        self.objects.insert(hash, CatalogEntry::Finished);
    }
}

#[derive(Debug)]
struct CatalogInner {
    catalog_path: PathBuf,
    objects: Trie<ObjectHash, CatalogEntry>,
}

#[derive(Debug, Clone)]
pub struct Catalog {
    // We must *never* attempt to wait for a catalog entry future while inner is locked - this will
    // cause a deadlock!
    inner: Arc<Mutex<CatalogInner>>,
}

impl Catalog {
    pub fn new(catalog_trie: CatalogTrie, catalog_path: PathBuf) -> Result<Catalog> {
        Ok(Catalog {
            inner: Arc::new(Mutex::new(CatalogInner {
                catalog_path,
                objects: catalog_trie.objects,
            })),
        })
    }

    pub fn load(catalog_path: PathBuf) -> Result<Catalog> {
        let objects = if catalog_path.is_file() {
            bincode::deserialize_from(
                &mut OpenOptions::new()
                    .read(true)
                    .open(&catalog_path)
                    .chain_err(|| ErrorKind::CatalogOpen(catalog_path.clone()))?,
                bincode::Infinite,
            ).chain_err(|| ErrorKind::CatalogDeserialize(catalog_path.clone()))?
        } else {
            Trie::new()
        };

        Ok(Catalog {
            inner: Arc::new(Mutex::new(CatalogInner {
                catalog_path,
                objects,
            })),
        })
    }

    pub fn try_lock(&self, hash: ObjectHash) -> StdResult<CatalogLock, CatalogEntry> {
        let mut inner_lock = self.inner.lock().unwrap();

        match inner_lock.objects.entry(hash) {
            Entry::Vacant(vacant) => {
                let (lock, future) = CatalogLock::new(self, hash);
                vacant.insert(CatalogEntry::Locked(future));
                Ok(lock)
            }
            Entry::Occupied(occupied) => Err(occupied.get().clone()),
        }
    }

    pub fn get(&self, hash: ObjectHash) -> Option<CatalogEntry> {
        self.inner.lock().unwrap().objects.get(&hash).map(|entry| {
            entry.clone()
        })
    }

    pub fn search<K: Borrow<[u8]>>(&self, bytes: K) -> Vec<ObjectHash> {
        self.inner
            .lock()
            .unwrap()
            .objects
            .iter_prefix(bytes.borrow())
            .map(|(&hash, _)| hash)
            .collect()
    }
}

impl Drop for CatalogInner {
    fn drop(&mut self) {
        let mut file = File::create(&self.catalog_path).unwrap();
        bincode::serialize_into(&mut file, &self.objects, bincode::Infinite).unwrap();
    }
}
<|endoftext|>


<commit_before>// Feature: Format String Completion.
//
// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
//
// The following postfix snippets are available:
//
// - `format` -> `format!(...)`
// - `panic` -> `panic!(...)`
// - `println` -> `println!(...)`
// - `log`:
//   + `logd` -> `log::debug!(...)`
//   + `logt` -> `log::trace!(...)`
//   + `logi` -> `log::info!(...)`
//   + `logw` -> `log::warn!(...)`
//   + `loge` -> `log::error!(...)`
//
// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]

use ide_db::helpers::SnippetCap;
use syntax::ast::{self, AstToken};

use crate::{completions::postfix::postfix_snippet, context::CompletionContext, Completions};

/// Mapping ("postfix completion item" => "macro to use")
static KINDS: &[(&str, &str)] = &[
    ("format", "format!"),
    ("panic", "panic!"),
    ("println", "println!"),
    ("eprintln", "eprintln!"),
    ("logd", "log::debug!"),
    ("logt", "log::trace!"),
    ("logi", "log::info!"),
    ("logw", "log::warn!"),
    ("loge", "log::error!"),
];

pub(crate) fn add_format_like_completions(
    acc: &mut Completions,
    ctx: &CompletionContext,
    dot_receiver: &ast::Expr,
    cap: SnippetCap,
    receiver_text: &ast::String,
) {
    let input = match string_literal_contents(receiver_text) {
        // It's not a string literal, do not parse input.
        Some(input) => input,
        None => return,
    };

    let mut parser = FormatStrParser::new(input);

    if parser.parse().is_ok() {
        for (label, macro_name) in KINDS {
            let snippet = parser.into_suggestion(macro_name);

            postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc);
        }
    }
}

/// Checks whether provided item is a string literal.
fn string_literal_contents(item: &ast::String) -> Option<String> {
    let item = item.text();
    if item.len() >= 2 && item.starts_with('\"') && item.ends_with('\"') {
        return Some(item[1..item.len() - 1].to_owned());
    }

    None
}

/// Parser for a format-like string. It is more allowing in terms of string contents,
/// as we expect variable placeholders to be filled with expressions.
#[derive(Debug)]
pub(crate) struct FormatStrParser {
    input: String,
    output: String,
    extracted_expressions: Vec<String>,
    state: State,
    parsed: bool,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum State {
    NotExpr,
    MaybeExpr,
    Expr,
    MaybeIncorrect,
    FormatOpts,
}

impl FormatStrParser {
    pub(crate) fn new(input: String) -> Self {
        Self {
            input: input,
            output: String::new(),
            extracted_expressions: Vec::new(),
            state: State::NotExpr,
            parsed: false,
        }
    }

    pub(crate) fn parse(&mut self) -> Result<(), ()> {
        let mut current_expr = String::new();

        let mut placeholder_id = 1;

        // Count of open braces inside of an expression.
        // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g.
        // "{MyStruct { val_a: 0, val_b: 1 }}".
        let mut inexpr_open_count = 0;

        let mut chars = self.input.chars().peekable();
        while let Some(chr) = chars.next() {
            match (self.state, chr) {
                (State::NotExpr, '{') => {
                    self.output.push(chr);
                    self.state = State::MaybeExpr;
                }
                (State::NotExpr, '}') => {
                    self.output.push(chr);
                    self.state = State::MaybeIncorrect;
                }
                (State::NotExpr, _) => {
                    self.output.push(chr);
                }
                (State::MaybeIncorrect, '}') => {
                    // It's okay, we met "}}".
                    self.output.push(chr);
                    self.state = State::NotExpr;
                }
                (State::MaybeIncorrect, _) => {
                    // Error in the string.
                    return Err(());
                }
                (State::MaybeExpr, '{') => {
                    self.output.push(chr);
                    self.state = State::NotExpr;
                }
                (State::MaybeExpr, '}') => {
                    // This is an empty sequence '{}'. Replace it with placeholder.
                    self.output.push(chr);
                    self.extracted_expressions.push(format!("${}", placeholder_id));
                    placeholder_id += 1;
                    self.state = State::NotExpr;
                }
                (State::MaybeExpr, _) => {
                    current_expr.push(chr);
                    self.state = State::Expr;
                }
                (State::Expr, '}') => {
                    if inexpr_open_count == 0 {
                        self.output.push(chr);
                        self.extracted_expressions.push(current_expr.trim().into());
                        current_expr = String::new();
                        self.state = State::NotExpr;
                    } else {
                        // We're closing one brace met before inside of the expression.
                        current_expr.push(chr);
                        inexpr_open_count -= 1;
                    }
                }
                (State::Expr, ':') if chars.peek().copied() == Some(':') => {
                    // path seperator
                    current_expr.push_str("::");
                    chars.next();
                }
                (State::Expr, ':') => {
                    if inexpr_open_count == 0 {
                        // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
                        self.output.push(chr);
                        self.extracted_expressions.push(current_expr.trim().into());
                        current_expr = String::new();
                        self.state = State::FormatOpts;
                    } else {
                        // We're inside of braced expression, assume that it's a struct field name/value delimeter.
                        current_expr.push(chr);
                    }
                }
                (State::Expr, '{') => {
                    current_expr.push(chr);
                    inexpr_open_count += 1;
                }
                (State::Expr, _) => {
                    current_expr.push(chr);
                }
                (State::FormatOpts, '}') => {
                    self.output.push(chr);
                    self.state = State::NotExpr;
                }
                (State::FormatOpts, _) => {
                    self.output.push(chr);
                }
            }
        }

        if self.state != State::NotExpr {
            return Err(());
        }

        self.parsed = true;
        Ok(())
    }

    pub(crate) fn into_suggestion(&self, macro_name: &str) -> String {
        assert!(self.parsed, "Attempt to get a suggestion from not parsed expression");

        let expressions_as_string = self.extracted_expressions.join(", ");
        format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use expect_test::{expect, Expect};

    fn check(input: &str, expect: &Expect) {
        let mut parser = FormatStrParser::new((*input).to_owned());
        let outcome_repr = if parser.parse().is_ok() {
            // Parsing should be OK, expected repr is "string; expr_1, expr_2".
            if parser.extracted_expressions.is_empty() {
                parser.output
            } else {
                format!("{}; {}", parser.output, parser.extracted_expressions.join(", "))
            }
        } else {
            // Parsing should fail, expected repr is "-".
            "-".to_owned()
        };

        expect.assert_eq(&outcome_repr);
    }

    #[test]
    fn format_str_parser() {
        let test_vector = &[
            ("no expressions", expect![["no expressions"]]),
            ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
            ("{expr:?}", expect![["{:?}; expr"]]),
            ("{malformed", expect![["-"]]),
            ("malformed}", expect![["-"]]),
            ("{{correct", expect![["{{correct"]]),
            ("correct}}", expect![["correct}}"]]),
            ("{correct}}}", expect![["{}}}; correct"]]),
            ("{correct}}}}}", expect![["{}}}}}; correct"]]),
            ("{incorrect}}", expect![["-"]]),
            ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
            ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
            (
                "{SomeStruct { val_a: 0, val_b: 1 }}",
                expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
            ),
            ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
            (
                "{SomeStruct { val_a: 0, val_b: 1 }:?}",
                expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
            ),
            ("{     2 + 2        }", expect![["{}; 2 + 2"]]),
            ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]),
            ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]),
            ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]),
        ];

        for (input, output) in test_vector {
            check(input, output)
        }
    }

    #[test]
    fn test_into_suggestion() {
        let test_vector = &[
            ("println!", "{}", r#"println!("{}", $1)"#),
            ("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
            (
                "log::info!",
                "{} {expr} {} {2 + 2}",
                r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#,
            ),
            ("format!", "{expr:?}", r#"format!("{:?}", expr)"#),
        ];

        for (kind, input, output) in test_vector {
            let mut parser = FormatStrParser::new((*input).to_owned());
            parser.parse().expect("Parsing must succeed");

            assert_eq!(&parser.into_suggestion(*kind), output);
        }
    }
}
<commit_msg>Fix feature description inconsistency<commit_after>// Feature: Format String Completion
//
// `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`.
//
// The following postfix snippets are available:
//
// - `format` -> `format!(...)`
// - `panic` -> `panic!(...)`
// - `println` -> `println!(...)`
// - `log`:
//   + `logd` -> `log::debug!(...)`
//   + `logt` -> `log::trace!(...)`
//   + `logi` -> `log::info!(...)`
//   + `logw` -> `log::warn!(...)`
//   + `loge` -> `log::error!(...)`
//
// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]

use ide_db::helpers::SnippetCap;
use syntax::ast::{self, AstToken};

use crate::{completions::postfix::postfix_snippet, context::CompletionContext, Completions};

/// Mapping ("postfix completion item" => "macro to use")
static KINDS: &[(&str, &str)] = &[
    ("format", "format!"),
    ("panic", "panic!"),
    ("println", "println!"),
    ("eprintln", "eprintln!"),
    ("logd", "log::debug!"),
    ("logt", "log::trace!"),
    ("logi", "log::info!"),
    ("logw", "log::warn!"),
    ("loge", "log::error!"),
];

pub(crate) fn add_format_like_completions(
    acc: &mut Completions,
    ctx: &CompletionContext,
    dot_receiver: &ast::Expr,
    cap: SnippetCap,
    receiver_text: &ast::String,
) {
    let input = match string_literal_contents(receiver_text) {
        // It's not a string literal, do not parse input.
        Some(input) => input,
        None => return,
    };

    let mut parser = FormatStrParser::new(input);

    if parser.parse().is_ok() {
        for (label, macro_name) in KINDS {
            let snippet = parser.into_suggestion(macro_name);

            postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc);
        }
    }
}

/// Checks whether provided item is a string literal.
fn string_literal_contents(item: &ast::String) -> Option<String> {
    let item = item.text();
    if item.len() >= 2 && item.starts_with('\"') && item.ends_with('\"') {
        return Some(item[1..item.len() - 1].to_owned());
    }

    None
}

/// Parser for a format-like string. It is more allowing in terms of string contents,
/// as we expect variable placeholders to be filled with expressions.
#[derive(Debug)]
pub(crate) struct FormatStrParser {
    input: String,
    output: String,
    extracted_expressions: Vec<String>,
    state: State,
    parsed: bool,
}

#[derive(Debug, Clone, Copy, PartialEq)]
enum State {
    NotExpr,
    MaybeExpr,
    Expr,
    MaybeIncorrect,
    FormatOpts,
}

impl FormatStrParser {
    pub(crate) fn new(input: String) -> Self {
        Self {
            input: input,
            output: String::new(),
            extracted_expressions: Vec::new(),
            state: State::NotExpr,
            parsed: false,
        }
    }

    pub(crate) fn parse(&mut self) -> Result<(), ()> {
        let mut current_expr = String::new();

        let mut placeholder_id = 1;

        // Count of open braces inside of an expression.
        // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g.
        // "{MyStruct { val_a: 0, val_b: 1 }}".
        let mut inexpr_open_count = 0;

        let mut chars = self.input.chars().peekable();
        while let Some(chr) = chars.next() {
            match (self.state, chr) {
                (State::NotExpr, '{') => {
                    self.output.push(chr);
                    self.state = State::MaybeExpr;
                }
                (State::NotExpr, '}') => {
                    self.output.push(chr);
                    self.state = State::MaybeIncorrect;
                }
                (State::NotExpr, _) => {
                    self.output.push(chr);
                }
                (State::MaybeIncorrect, '}') => {
                    // It's okay, we met "}}".
                    self.output.push(chr);
                    self.state = State::NotExpr;
                }
                (State::MaybeIncorrect, _) => {
                    // Error in the string.
                    return Err(());
                }
                (State::MaybeExpr, '{') => {
                    self.output.push(chr);
                    self.state = State::NotExpr;
                }
                (State::MaybeExpr, '}') => {
                    // This is an empty sequence '{}'. Replace it with placeholder.
                    self.output.push(chr);
                    self.extracted_expressions.push(format!("${}", placeholder_id));
                    placeholder_id += 1;
                    self.state = State::NotExpr;
                }
                (State::MaybeExpr, _) => {
                    current_expr.push(chr);
                    self.state = State::Expr;
                }
                (State::Expr, '}') => {
                    if inexpr_open_count == 0 {
                        self.output.push(chr);
                        self.extracted_expressions.push(current_expr.trim().into());
                        current_expr = String::new();
                        self.state = State::NotExpr;
                    } else {
                        // We're closing one brace met before inside of the expression.
                        current_expr.push(chr);
                        inexpr_open_count -= 1;
                    }
                }
                (State::Expr, ':') if chars.peek().copied() == Some(':') => {
                    // path seperator
                    current_expr.push_str("::");
                    chars.next();
                }
                (State::Expr, ':') => {
                    if inexpr_open_count == 0 {
                        // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
                        self.output.push(chr);
                        self.extracted_expressions.push(current_expr.trim().into());
                        current_expr = String::new();
                        self.state = State::FormatOpts;
                    } else {
                        // We're inside of braced expression, assume that it's a struct field name/value delimeter.
                        current_expr.push(chr);
                    }
                }
                (State::Expr, '{') => {
                    current_expr.push(chr);
                    inexpr_open_count += 1;
                }
                (State::Expr, _) => {
                    current_expr.push(chr);
                }
                (State::FormatOpts, '}') => {
                    self.output.push(chr);
                    self.state = State::NotExpr;
                }
                (State::FormatOpts, _) => {
                    self.output.push(chr);
                }
            }
        }

        if self.state != State::NotExpr {
            return Err(());
        }

        self.parsed = true;
        Ok(())
    }

    pub(crate) fn into_suggestion(&self, macro_name: &str) -> String {
        assert!(self.parsed, "Attempt to get a suggestion from not parsed expression");

        let expressions_as_string = self.extracted_expressions.join(", ");
        format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use expect_test::{expect, Expect};

    fn check(input: &str, expect: &Expect) {
        let mut parser = FormatStrParser::new((*input).to_owned());
        let outcome_repr = if parser.parse().is_ok() {
            // Parsing should be OK, expected repr is "string; expr_1, expr_2".
            if parser.extracted_expressions.is_empty() {
                parser.output
            } else {
                format!("{}; {}", parser.output, parser.extracted_expressions.join(", "))
            }
        } else {
            // Parsing should fail, expected repr is "-".
            "-".to_owned()
        };

        expect.assert_eq(&outcome_repr);
    }

    #[test]
    fn format_str_parser() {
        let test_vector = &[
            ("no expressions", expect![["no expressions"]]),
            ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
            ("{expr:?}", expect![["{:?}; expr"]]),
            ("{malformed", expect![["-"]]),
            ("malformed}", expect![["-"]]),
            ("{{correct", expect![["{{correct"]]),
            ("correct}}", expect![["correct}}"]]),
            ("{correct}}}", expect![["{}}}; correct"]]),
            ("{correct}}}}}", expect![["{}}}}}; correct"]]),
            ("{incorrect}}", expect![["-"]]),
            ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
            ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
            (
                "{SomeStruct { val_a: 0, val_b: 1 }}",
                expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
            ),
            ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
            (
                "{SomeStruct { val_a: 0, val_b: 1 }:?}",
                expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
            ),
            ("{     2 + 2        }", expect![["{}; 2 + 2"]]),
            ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]),
            ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]),
            ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]),
        ];

        for (input, output) in test_vector {
            check(input, output)
        }
    }

    #[test]
    fn test_into_suggestion() {
        let test_vector = &[
            ("println!", "{}", r#"println!("{}", $1)"#),
            ("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
            (
                "log::info!",
                "{} {expr} {} {2 + 2}",
                r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#,
            ),
            ("format!", "{expr:?}", r#"format!("{:?}", expr)"#),
        ];

        for (kind, input, output) in test_vector {
            let mut parser = FormatStrParser::new((*input).to_owned());
            parser.parse().expect("Parsing must succeed");

            assert_eq!(&parser.into_suggestion(*kind), output);
        }
    }
}
<|endoftext|>


<commit_before>//! Solve linear problem using LU decomposition

use lapack::c;

use error::*;
use layout::MatrixLayout;
use types::*;

use super::{Pivot, Transpose, into_result};

/// Wraps `*getrf`, `*getri`, and `*getrs`
pub trait Solve_: Sized {
    /// Computes the LU factorization of a general `m x n` matrix `a` using
    /// partial pivoting with row interchanges.
    ///
    /// If the result matches `Err(LinalgError::Lapack(LapackError {
    /// return_code )) if return_code > 0`, then `U[(return_code,
    /// return_code)]` is exactly zero. The factorization has been completed,
    /// but the factor `U` is exactly singular, and division by zero will occur
    /// if it is used to solve a system of equations.
    unsafe fn lu(MatrixLayout, a: &mut [Self]) -> Result<Pivot>;
    unsafe fn inv(MatrixLayout, a: &mut [Self], &Pivot) -> Result<()>;
    unsafe fn solve(MatrixLayout, Transpose, a: &[Self], &Pivot, b: &mut [Self]) -> Result<()>;
}

macro_rules! impl_solve {
    ($scalar:ty, $getrf:path, $getri:path, $getrs:path) => {

impl Solve_ for $scalar {
    unsafe fn lu(l: MatrixLayout, a: &mut [Self]) -> Result<Pivot> {
        let (row, col) = l.size();
        let k = ::std::cmp::min(row, col);
        let mut ipiv = vec![0; k as usize];
        let info = $getrf(l.lapacke_layout(), row, col, a, l.lda(), &mut ipiv);
        into_result(info, ipiv)
    }

    unsafe fn inv(l: MatrixLayout, a: &mut [Self], ipiv: &Pivot) -> Result<()> {
        let (n, _) = l.size();
        let info = $getri(l.lapacke_layout(), n, a, l.lda(), ipiv);
        into_result(info, ())
    }

    unsafe fn solve(l: MatrixLayout, t: Transpose, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()> {
        let (n, _) = l.size();
        let nrhs = 1;
        let ldb = 1;
        let info = $getrs(l.lapacke_layout(), t as u8, n, nrhs, a, l.lda(), ipiv, b, ldb);
        into_result(info, ())
    }
}

}} // impl_solve!

impl_solve!(f64, c::dgetrf, c::dgetri, c::dgetrs);
impl_solve!(f32, c::sgetrf, c::sgetri, c::sgetrs);
impl_solve!(c64, c::zgetrf, c::zgetri, c::zgetrs);
impl_solve!(c32, c::cgetrf, c::cgetri, c::cgetrs);
<commit_msg>Fix indices in docs for Solve_::lu()<commit_after>//! Solve linear problem using LU decomposition

use lapack::c;

use error::*;
use layout::MatrixLayout;
use types::*;

use super::{Pivot, Transpose, into_result};

/// Wraps `*getrf`, `*getri`, and `*getrs`
pub trait Solve_: Sized {
    /// Computes the LU factorization of a general `m x n` matrix `a` using
    /// partial pivoting with row interchanges.
    ///
    /// If the result matches `Err(LinalgError::Lapack(LapackError {
    /// return_code )) if return_code > 0`, then `U[(return_code-1,
    /// return_code-1)]` is exactly zero. The factorization has been completed,
    /// but the factor `U` is exactly singular, and division by zero will occur
    /// if it is used to solve a system of equations.
    unsafe fn lu(MatrixLayout, a: &mut [Self]) -> Result<Pivot>;
    unsafe fn inv(MatrixLayout, a: &mut [Self], &Pivot) -> Result<()>;
    unsafe fn solve(MatrixLayout, Transpose, a: &[Self], &Pivot, b: &mut [Self]) -> Result<()>;
}

macro_rules! impl_solve {
    ($scalar:ty, $getrf:path, $getri:path, $getrs:path) => {

impl Solve_ for $scalar {
    unsafe fn lu(l: MatrixLayout, a: &mut [Self]) -> Result<Pivot> {
        let (row, col) = l.size();
        let k = ::std::cmp::min(row, col);
        let mut ipiv = vec![0; k as usize];
        let info = $getrf(l.lapacke_layout(), row, col, a, l.lda(), &mut ipiv);
        into_result(info, ipiv)
    }

    unsafe fn inv(l: MatrixLayout, a: &mut [Self], ipiv: &Pivot) -> Result<()> {
        let (n, _) = l.size();
        let info = $getri(l.lapacke_layout(), n, a, l.lda(), ipiv);
        into_result(info, ())
    }

    unsafe fn solve(l: MatrixLayout, t: Transpose, a: &[Self], ipiv: &Pivot, b: &mut [Self]) -> Result<()> {
        let (n, _) = l.size();
        let nrhs = 1;
        let ldb = 1;
        let info = $getrs(l.lapacke_layout(), t as u8, n, nrhs, a, l.lda(), ipiv, b, ldb);
        into_result(info, ())
    }
}

}} // impl_solve!

impl_solve!(f64, c::dgetrf, c::dgetri, c::dgetrs);
impl_solve!(f32, c::sgetrf, c::sgetri, c::sgetrs);
impl_solve!(c64, c::zgetrf, c::zgetri, c::zgetrs);
impl_solve!(c32, c::cgetrf, c::cgetri, c::cgetrs);
<|endoftext|>