<commit_before>use std::collections::HashMap;
use std::fmt;
use std::io::fs::PathExtensions;
use std::io::{fs, USER_RWX, File};
use std::str;
use std::sync::Mutex;

use core::{Package, Target, PackageId, PackageSet};
use util::{CargoResult, CargoError, human};
use util::{internal, ChainError, Require};

use super::job::Work;
use super::{fingerprint, process, Kind, Context, Platform};
use util::Freshness;

/// Contains the parsed output of a custom build script.
#[deriving(Clone)]
pub struct BuildOutput {
    /// Paths to pass to rustc with the `-L` flag
    pub library_paths: Vec<Path>,
    /// Names and link kinds of libraries, suitable for the `-l` flag
    pub library_links: Vec<String>,
    /// Metadata to pass to the immediate dependencies
    pub metadata: Vec<(String, String)>,
}

pub struct BuildState {
    pub outputs: Mutex<HashMap<(PackageId, Kind), BuildOutput>>,
}

/// Prepares a `Work` that executes the target as a custom build script.
///
/// The `req` given is the requirement which this run of the build script will
/// prepare work for. If the requirement is specified as both the target and the
/// host platforms it is assumed that the two are equal and the build script is
/// only run once (not twice).
pub fn prepare(pkg: &Package, target: &Target, req: Platform,
               cx: &mut Context) -> CargoResult<(Work, Work, Freshness)> {
    let kind = match req { Platform::Plugin => Kind::Host, _ => Kind::Target, };
    let (script_output, build_output) = {
        (cx.layout(pkg, Kind::Host).build(pkg),
         cx.layout(pkg, Kind::Target).build_out(pkg))
    };

    // Building the command to execute
    let to_exec = try!(cx.target_filenames(target))[0].clone();
    let to_exec = script_output.join(to_exec);

    // Start preparing the process to execute, starting out with some
    // environment variables.
    let profile = target.get_profile();
    let mut p = try!(super::process(to_exec, pkg, target, cx))
                     .env("OUT_DIR", Some(&build_output))
                     .env("CARGO_MANIFEST_DIR", Some(pkg.get_manifest_path()
                                                        .dir_path()
                                                        .display().to_string()))
                     .env("NUM_JOBS", Some(cx.config.jobs().to_string()))
                     .env("TARGET", Some(match kind {
                         Kind::Host => cx.config.rustc_host(),
                         Kind::Target => cx.target_triple(),
                     }))
                     .env("DEBUG", Some(profile.get_debug().to_string()))
                     .env("OPT_LEVEL", Some(profile.get_opt_level().to_string()))
                     .env("PROFILE", Some(profile.get_env()));

    // Be sure to pass along all enabled features for this package, this is the
    // last piece of statically known information that we have.
    match cx.resolve.features(pkg.get_package_id()) {
        Some(features) => {
            for feat in features.iter() {
                p = p.env(format!("CARGO_FEATURE_{}",
                                  super::envify(feat.as_slice())).as_slice(),
                          Some("1"));
            }
        }
        None => {}
    }

    // Gather the set of native dependencies that this package has along with
    // some other variables to close over.
    //
    // This information will be used at build-time later on to figure out which
    // sorts of variables need to be discovered at that time.
    let lib_deps = {
        let non_build_target = pkg.get_targets().iter().find(|t| {
            !t.get_profile().is_custom_build()
        }).unwrap();
        cx.dep_targets(pkg, non_build_target).iter().filter_map(|&(pkg, _)| {
            pkg.get_manifest().get_links().map(|links| {
                (links.to_string(), pkg.get_package_id().clone())
            })
        }).collect::<Vec<_>>()
    };
    let pkg_name = pkg.to_string();
    let build_state = cx.build_state.clone();
    let id = pkg.get_package_id().clone();
    let all = (id.clone(), pkg_name.clone(), build_state.clone(),
               build_output.clone());

    try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Target).build(pkg), USER_RWX));
    try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Host).build(pkg), USER_RWX));

    // Prepare the unit of "dirty work" which will actually run the custom build
    // command.
    //
    // Note that this has to do some extra work just before running the command
    // to determine extra environment variables and such.
    let work = proc(desc_tx: Sender<String>) {
        // Make sure that OUT_DIR exists.
        //
        // If we have an old build directory, then just move it into place,
        // otherwise create it!
        if !build_output.exists() {
            try!(fs::mkdir(&build_output, USER_RWX).chain_error(|| {
                internal("failed to create script output directory for \
                          build command")
            }));
        }

        // For all our native lib dependencies, pick up their metadata to pass
        // along to this custom build command.
        let mut p = p;
        {
            let build_state = build_state.outputs.lock();
            for &(ref name, ref id) in lib_deps.iter() {
                let data = &build_state[(id.clone(), kind)].metadata;
                for &(ref key, ref value) in data.iter() {
                    p = p.env(format!("DEP_{}_{}",
                                      super::envify(name.as_slice()),
                                      super::envify(key.as_slice())).as_slice(),
                              Some(value.as_slice()));
                }
            }
        }

        // And now finally, run the build command itself!
        desc_tx.send_opt(p.to_string()).ok();
        let output = try!(p.exec_with_output().map_err(|mut e| {
            e.msg = format!("Failed to run custom build command for `{}`\n{}",
                            pkg_name, e.msg);
            e.concrete().mark_human()
        }));

        // After the build command has finished running, we need to be sure to
        // remember all of its output so we can later discover precisely what it
        // was, even if we don't run the build command again (due to freshness).
        //
        // This is also the location where we provide feedback into the build
        // state informing what variables were discovered via our script as
        // well.
        let output = try!(str::from_utf8(output.output.as_slice()).require(|| {
            human("build script output was not valid utf-8")
        }));
        let parsed_output = try!(BuildOutput::parse(output, pkg_name.as_slice()));
        build_state.insert(id, req, parsed_output);

        try!(File::create(&build_output.dir_path().join("output"))
                  .write_str(output).map_err(|e| {
            human(format!("failed to write output of custom build command: {}",
                          e))
        }));

        Ok(())
    };

    // Now that we've prepared our work-to-do, we need to prepare the fresh work
    // itself to run when we actually end up just discarding what we calculated
    // above.
    //
    // Note that the freshness calculation here is the build_cmd freshness, not
    // target specific freshness. This is because we don't actually know what
    // the inputs are to this command!
    //
    // Also note that a fresh build command needs to
    let (freshness, dirty, fresh) =
            try!(fingerprint::prepare_build_cmd(cx, pkg, Some(target)));
    let dirty = proc(tx: Sender<String>) { try!(work(tx.clone())); dirty(tx) };
    let fresh = proc(tx) {
        let (id, pkg_name, build_state, build_output) = all;
        let new_loc = build_output.dir_path().join("output");
        let mut f = try!(File::open(&new_loc).map_err(|e| {
            human(format!("failed to read cached build command output: {}", e))
        }));
        let contents = try!(f.read_to_string());
        let output = try!(BuildOutput::parse(contents.as_slice(),
                                             pkg_name.as_slice()));
        build_state.insert(id, req, output);

        fresh(tx)
    };

    Ok((dirty, fresh, freshness))
}

impl BuildState {
    pub fn new(config: super::BuildConfig,
               packages: &PackageSet) -> BuildState {
        let mut sources = HashMap::new();
        for package in packages.iter() {
            match package.get_manifest().get_links() {
                Some(links) => {
                    sources.insert(links.to_string(),
                                   package.get_package_id().clone());
                }
                None => {}
            }
        }
        let mut outputs = HashMap::new();
        let i1 = config.host.overrides.into_iter().map(|p| (p, Kind::Host));
        let i2 = config.target.overrides.into_iter().map(|p| (p, Kind::Target));
        for ((name, output), kind) in i1.chain(i2) {
            match sources.get(&name) {
                Some(id) => { outputs.insert((id.clone(), kind), output); }

                // If no package is using the library named `name`, then this is
                // just an override that we ignore.
                None => {}
            }
        }
        BuildState { outputs: Mutex::new(outputs) }
    }

    fn insert(&self, id: PackageId, req: Platform,
              output: BuildOutput) {
        let mut outputs = self.outputs.lock();
        match req {
            Platform::Target => { outputs.insert((id, Kind::Target), output); }
            Platform::Plugin => { outputs.insert((id, Kind::Host), output); }

            // If this build output was for both the host and target platforms,
            // we need to insert it at both places.
            Platform::PluginAndTarget => {
                outputs.insert((id.clone(), Kind::Host), output.clone());
                outputs.insert((id, Kind::Target), output);
            }
        }
    }
}

impl BuildOutput {
    // Parses the output of a script.
    // The `pkg_name` is used for error messages.
    pub fn parse(input: &str, pkg_name: &str) -> CargoResult<BuildOutput> {
        let mut library_paths = Vec::new();
        let mut library_links = Vec::new();
        let mut metadata = Vec::new();
        let whence = format!("build script of `{}`", pkg_name);

        for line in input.lines() {
            let mut iter = line.splitn(1, |c: char| c == ':');
            if iter.next() != Some("cargo") {
                // skip this line since it doesn't start with "cargo:"
                continue;
            }
            let data = match iter.next() {
                Some(val) => val,
                None => continue
            };

            // getting the `key=value` part of the line
            let mut iter = data.splitn(1, |c: char| c == '=');
            let key = iter.next();
            let value = iter.next();
            let (key, value) = match (key, value) {
                (Some(a), Some(b)) => (a, b.trim_right()),
                // line started with `cargo:` but didn't match `key=value`
                _ => return Err(human(format!("Wrong output in {}: `{}`",
                                              whence, line)))
            };

            if key == "rustc-flags" {
                let whence = whence.as_slice();
                let (libs, links) = try!(
                    BuildOutput::parse_rustc_flags(value, whence)
                );
                library_links.extend(links.into_iter());
                library_paths.extend(libs.into_iter());
            } else {
                metadata.push((key.to_string(), value.to_string()))
            }
        }

        Ok(BuildOutput {
            library_paths: library_paths,
            library_links: library_links,
            metadata: metadata,
        })
    }

    pub fn parse_rustc_flags(value: &str, whence: &str)
                             -> CargoResult<(Vec<Path>, Vec<String>)> {
        // TODO: some arguments (like paths) may contain spaces
        let value = value.trim();
        let mut flags_iter = value.words();
        let (mut library_links, mut library_paths) = (Vec::new(), Vec::new());
        loop {
            let flag = match flags_iter.next() {
                Some(f) => f,
                None => break
            };
            if flag != "-l" && flag != "-L" {
                return Err(human(format!("Only `-l` and `-L` flags are allowed \
                                         in {}: `{}`",
                                         whence, value)))
            }
            let value = match flags_iter.next() {
                Some(v) => v,
                None => return Err(human(format!("Flag in rustc-flags has no value\
                                                  in {}: `{}`",
                                                  whence, value)))
            };
            match flag {
                "-l" => library_links.push(value.to_string()),
                "-L" => library_paths.push(Path::new(value)),

                // was already checked above
                _ => return Err(human("only -l and -L flags are allowed"))
            };
        }
        Ok((library_paths, library_links))
    }
}

impl fmt::Show for BuildOutput {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "BuildOutput {{ paths: [..], libs: {}, metadata: {} }}",
               self.library_links, self.metadata)
    }
}
<commit_msg>auto merge of #916 : kballard/cargo/patch-2, r=alexcrichton<commit_after>use std::collections::HashMap;
use std::fmt;
use std::io::fs::PathExtensions;
use std::io::{fs, USER_RWX, File};
use std::str;
use std::sync::Mutex;

use core::{Package, Target, PackageId, PackageSet};
use util::{CargoResult, CargoError, human};
use util::{internal, ChainError, Require};

use super::job::Work;
use super::{fingerprint, process, Kind, Context, Platform};
use util::Freshness;

/// Contains the parsed output of a custom build script.
#[deriving(Clone)]
pub struct BuildOutput {
    /// Paths to pass to rustc with the `-L` flag
    pub library_paths: Vec<Path>,
    /// Names and link kinds of libraries, suitable for the `-l` flag
    pub library_links: Vec<String>,
    /// Metadata to pass to the immediate dependencies
    pub metadata: Vec<(String, String)>,
}

pub struct BuildState {
    pub outputs: Mutex<HashMap<(PackageId, Kind), BuildOutput>>,
}

/// Prepares a `Work` that executes the target as a custom build script.
///
/// The `req` given is the requirement which this run of the build script will
/// prepare work for. If the requirement is specified as both the target and the
/// host platforms it is assumed that the two are equal and the build script is
/// only run once (not twice).
pub fn prepare(pkg: &Package, target: &Target, req: Platform,
               cx: &mut Context) -> CargoResult<(Work, Work, Freshness)> {
    let kind = match req { Platform::Plugin => Kind::Host, _ => Kind::Target, };
    let (script_output, build_output) = {
        (cx.layout(pkg, Kind::Host).build(pkg),
         cx.layout(pkg, Kind::Target).build_out(pkg))
    };

    // Building the command to execute
    let to_exec = try!(cx.target_filenames(target))[0].clone();
    let to_exec = script_output.join(to_exec);

    // Start preparing the process to execute, starting out with some
    // environment variables.
    let profile = target.get_profile();
    let mut p = try!(super::process(to_exec, pkg, target, cx))
                     .env("OUT_DIR", Some(&build_output))
                     .env("CARGO_MANIFEST_DIR", Some(pkg.get_manifest_path()
                                                        .dir_path()
                                                        .display().to_string()))
                     .env("NUM_JOBS", Some(cx.config.jobs().to_string()))
                     .env("TARGET", Some(match kind {
                         Kind::Host => cx.config.rustc_host(),
                         Kind::Target => cx.target_triple(),
                     }))
                     .env("DEBUG", Some(profile.get_debug().to_string()))
                     .env("OPT_LEVEL", Some(profile.get_opt_level().to_string()))
                     .env("PROFILE", Some(profile.get_env()));

    // Be sure to pass along all enabled features for this package, this is the
    // last piece of statically known information that we have.
    match cx.resolve.features(pkg.get_package_id()) {
        Some(features) => {
            for feat in features.iter() {
                p = p.env(format!("CARGO_FEATURE_{}",
                                  super::envify(feat.as_slice())).as_slice(),
                          Some("1"));
            }
        }
        None => {}
    }

    // Gather the set of native dependencies that this package has along with
    // some other variables to close over.
    //
    // This information will be used at build-time later on to figure out which
    // sorts of variables need to be discovered at that time.
    let lib_deps = {
        let non_build_target = pkg.get_targets().iter().find(|t| {
            !t.get_profile().is_custom_build()
        }).unwrap();
        cx.dep_targets(pkg, non_build_target).iter().filter_map(|&(pkg, _)| {
            pkg.get_manifest().get_links().map(|links| {
                (links.to_string(), pkg.get_package_id().clone())
            })
        }).collect::<Vec<_>>()
    };
    let pkg_name = pkg.to_string();
    let build_state = cx.build_state.clone();
    let id = pkg.get_package_id().clone();
    let all = (id.clone(), pkg_name.clone(), build_state.clone(),
               build_output.clone());

    try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Target).build(pkg), USER_RWX));
    try!(fs::mkdir_recursive(&cx.layout(pkg, Kind::Host).build(pkg), USER_RWX));

    // Prepare the unit of "dirty work" which will actually run the custom build
    // command.
    //
    // Note that this has to do some extra work just before running the command
    // to determine extra environment variables and such.
    let work = proc(desc_tx: Sender<String>) {
        // Make sure that OUT_DIR exists.
        //
        // If we have an old build directory, then just move it into place,
        // otherwise create it!
        if !build_output.exists() {
            try!(fs::mkdir(&build_output, USER_RWX).chain_error(|| {
                internal("failed to create script output directory for \
                          build command")
            }));
        }

        // For all our native lib dependencies, pick up their metadata to pass
        // along to this custom build command.
        let mut p = p;
        {
            let build_state = build_state.outputs.lock();
            for &(ref name, ref id) in lib_deps.iter() {
                let data = &build_state[(id.clone(), kind)].metadata;
                for &(ref key, ref value) in data.iter() {
                    p = p.env(format!("DEP_{}_{}",
                                      super::envify(name.as_slice()),
                                      super::envify(key.as_slice())).as_slice(),
                              Some(value.as_slice()));
                }
            }
        }

        // And now finally, run the build command itself!
        desc_tx.send_opt(p.to_string()).ok();
        let output = try!(p.exec_with_output().map_err(|mut e| {
            e.msg = format!("Failed to run custom build command for `{}`\n{}",
                            pkg_name, e.msg);
            e.concrete().mark_human()
        }));

        // After the build command has finished running, we need to be sure to
        // remember all of its output so we can later discover precisely what it
        // was, even if we don't run the build command again (due to freshness).
        //
        // This is also the location where we provide feedback into the build
        // state informing what variables were discovered via our script as
        // well.
        let output = try!(str::from_utf8(output.output.as_slice()).require(|| {
            human("build script output was not valid utf-8")
        }));
        let parsed_output = try!(BuildOutput::parse(output, pkg_name.as_slice()));
        build_state.insert(id, req, parsed_output);

        try!(File::create(&build_output.dir_path().join("output"))
                  .write_str(output).map_err(|e| {
            human(format!("failed to write output of custom build command: {}",
                          e))
        }));

        Ok(())
    };

    // Now that we've prepared our work-to-do, we need to prepare the fresh work
    // itself to run when we actually end up just discarding what we calculated
    // above.
    //
    // Note that the freshness calculation here is the build_cmd freshness, not
    // target specific freshness. This is because we don't actually know what
    // the inputs are to this command!
    //
    // Also note that a fresh build command needs to
    let (freshness, dirty, fresh) =
            try!(fingerprint::prepare_build_cmd(cx, pkg, Some(target)));
    let dirty = proc(tx: Sender<String>) { try!(work(tx.clone())); dirty(tx) };
    let fresh = proc(tx) {
        let (id, pkg_name, build_state, build_output) = all;
        let new_loc = build_output.dir_path().join("output");
        let mut f = try!(File::open(&new_loc).map_err(|e| {
            human(format!("failed to read cached build command output: {}", e))
        }));
        let contents = try!(f.read_to_string());
        let output = try!(BuildOutput::parse(contents.as_slice(),
                                             pkg_name.as_slice()));
        build_state.insert(id, req, output);

        fresh(tx)
    };

    Ok((dirty, fresh, freshness))
}

impl BuildState {
    pub fn new(config: super::BuildConfig,
               packages: &PackageSet) -> BuildState {
        let mut sources = HashMap::new();
        for package in packages.iter() {
            match package.get_manifest().get_links() {
                Some(links) => {
                    sources.insert(links.to_string(),
                                   package.get_package_id().clone());
                }
                None => {}
            }
        }
        let mut outputs = HashMap::new();
        let i1 = config.host.overrides.into_iter().map(|p| (p, Kind::Host));
        let i2 = config.target.overrides.into_iter().map(|p| (p, Kind::Target));
        for ((name, output), kind) in i1.chain(i2) {
            match sources.get(&name) {
                Some(id) => { outputs.insert((id.clone(), kind), output); }

                // If no package is using the library named `name`, then this is
                // just an override that we ignore.
                None => {}
            }
        }
        BuildState { outputs: Mutex::new(outputs) }
    }

    fn insert(&self, id: PackageId, req: Platform,
              output: BuildOutput) {
        let mut outputs = self.outputs.lock();
        match req {
            Platform::Target => { outputs.insert((id, Kind::Target), output); }
            Platform::Plugin => { outputs.insert((id, Kind::Host), output); }

            // If this build output was for both the host and target platforms,
            // we need to insert it at both places.
            Platform::PluginAndTarget => {
                outputs.insert((id.clone(), Kind::Host), output.clone());
                outputs.insert((id, Kind::Target), output);
            }
        }
    }
}

impl BuildOutput {
    // Parses the output of a script.
    // The `pkg_name` is used for error messages.
    pub fn parse(input: &str, pkg_name: &str) -> CargoResult<BuildOutput> {
        let mut library_paths = Vec::new();
        let mut library_links = Vec::new();
        let mut metadata = Vec::new();
        let whence = format!("build script of `{}`", pkg_name);

        for line in input.lines() {
            let mut iter = line.splitn(1, |c: char| c == ':');
            if iter.next() != Some("cargo") {
                // skip this line since it doesn't start with "cargo:"
                continue;
            }
            let data = match iter.next() {
                Some(val) => val,
                None => continue
            };

            // getting the `key=value` part of the line
            let mut iter = data.splitn(1, |c: char| c == '=');
            let key = iter.next();
            let value = iter.next();
            let (key, value) = match (key, value) {
                (Some(a), Some(b)) => (a, b.trim_right()),
                // line started with `cargo:` but didn't match `key=value`
                _ => return Err(human(format!("Wrong output in {}: `{}`",
                                              whence, line)))
            };

            if key == "rustc-flags" {
                let whence = whence.as_slice();
                let (libs, links) = try!(
                    BuildOutput::parse_rustc_flags(value, whence)
                );
                library_links.extend(links.into_iter());
                library_paths.extend(libs.into_iter());
            } else {
                metadata.push((key.to_string(), value.to_string()))
            }
        }

        Ok(BuildOutput {
            library_paths: library_paths,
            library_links: library_links,
            metadata: metadata,
        })
    }

    pub fn parse_rustc_flags(value: &str, whence: &str)
                             -> CargoResult<(Vec<Path>, Vec<String>)> {
        // TODO: some arguments (like paths) may contain spaces
        let value = value.trim();
        let mut flags_iter = value.words();
        let (mut library_links, mut library_paths) = (Vec::new(), Vec::new());
        loop {
            let flag = match flags_iter.next() {
                Some(f) => f,
                None => break
            };
            if flag != "-l" && flag != "-L" {
                return Err(human(format!("Only `-l` and `-L` flags are allowed \
                                         in {}: `{}`",
                                         whence, value)))
            }
            let value = match flags_iter.next() {
                Some(v) => v,
                None => return Err(human(format!("Flag in rustc-flags has no value \
                                                  in {}: `{}`",
                                                  whence, value)))
            };
            match flag {
                "-l" => library_links.push(value.to_string()),
                "-L" => library_paths.push(Path::new(value)),

                // was already checked above
                _ => return Err(human("only -l and -L flags are allowed"))
            };
        }
        Ok((library_paths, library_links))
    }
}

impl fmt::Show for BuildOutput {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "BuildOutput {{ paths: [..], libs: {}, metadata: {} }}",
               self.library_links, self.metadata)
    }
}
<|endoftext|>


<commit_before>use super::{Cell, Mesh, Point};

pub struct Samples;

impl Samples {
    /// Returns two quadrilaterals along the horizontal
    ///
    /// ```text
    /// 1.0  3-----------2-----------5
    ///      |           |           |
    ///      |    [0]    |    [1]    |  [*] indicates id
    ///      |    (1)    |    (2)    |  (*) indicates attribute_id
    ///      |           |           |
    /// 0.0  0-----------1-----------4
    ///     0.0         1.0         2.0
    /// ```
    #[rustfmt::skip]
    pub fn two_quads_horizontal() -> Mesh {
        Mesh {
            space_ndim: 2,
            points: vec![
                Point { id: 0, coords: vec![0.0, 0.0] },
                Point { id: 1, coords: vec![1.0, 0.0] },
                Point { id: 2, coords: vec![1.0, 1.0] },
                Point { id: 3, coords: vec![0.0, 1.0] },
                Point { id: 4, coords: vec![2.0, 0.0] },
                Point { id: 5, coords: vec![2.0, 1.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 2, points: vec![0, 1, 2, 3] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 2, points: vec![1, 4, 5, 2] },
            ],
        }
    }

    /// Returns two cubes along the vertical
    ///
    /// ```text
    ///           8-------------11
    ///          /.             /|
    ///         / .            / |
    ///        /  .           /  |
    ///       /   .          /   |       id = 1
    /// 2.0  9-------------10    |       attribute_id = 2
    ///      |    .         |    |
    ///      |    4---------|----7
    ///      |   /.         |   /|
    ///      |  / .         |  / |
    ///      | /  .         | /  |
    ///      |/   .         |/   |
    /// 1.0  5--------------6    |       id = 0
    ///      |    .         |    |       attribute_id = 1
    ///      |    0---------|----3  0.0
    ///      |   /          |   /
    ///      |  /           |  /
    ///      | /            | /
    ///      |/             |/
    /// 0.0  1--------------2   1.0
    ///     0.0            1.0
    /// ```
    #[rustfmt::skip]
    pub fn two_cubes_vertical() -> Mesh {
        Mesh {
            space_ndim: 3,
            points: vec![
                Point { id:  0, coords: vec![0.0, 0.0, 0.0] },
                Point { id:  1, coords: vec![1.0, 0.0, 0.0] },
                Point { id:  2, coords: vec![1.0, 1.0, 0.0] },
                Point { id:  3, coords: vec![0.0, 1.0, 0.0] },
                Point { id:  4, coords: vec![0.0, 0.0, 1.0] },
                Point { id:  5, coords: vec![1.0, 0.0, 1.0] },
                Point { id:  6, coords: vec![1.0, 1.0, 1.0] },
                Point { id:  7, coords: vec![0.0, 1.0, 1.0] },
                Point { id:  8, coords: vec![0.0, 0.0, 2.0] },
                Point { id:  9, coords: vec![1.0, 0.0, 2.0] },
                Point { id: 10, coords: vec![1.0, 1.0, 2.0] },
                Point { id: 11, coords: vec![0.0, 1.0, 2.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 3, points: vec![0,1,2,3, 4,5,6,7] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 3, points: vec![4,5,6,7, 8,9,10,11] },
            ],
        }
    }

    /// Returns a mesh with mixed shapes in 2D
    ///
    /// ```text
    /// 1.0              4-----------3
    ///                  |           |
    ///                  |    [1]    |   [*] indicates id
    ///                  |    (2)    |   (*) indicates attribute_id
    ///                  |           |
    /// 0.0  0-----------1-----------2
    ///           [0]
    ///           (1)
    ///
    ///     0.0         1.0         2.0
    /// ```
    #[rustfmt::skip]
    pub fn mixed_shapes_2d() -> Mesh {
        Mesh {
            space_ndim: 2,
            points: vec![
                Point { id: 0, coords: vec![0.0, 0.0] },
                Point { id: 1, coords: vec![1.0, 0.0] },
                Point { id: 2, coords: vec![2.0, 0.0] },
                Point { id: 3, coords: vec![2.0, 1.0] },
                Point { id: 4, coords: vec![1.0, 1.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 1, points: vec![0, 1] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 2, points: vec![1, 2, 3, 4] },
            ],
        }
    }

    /// Returns a mesh with mixed shapes in 3D
    ///
    /// ```text
    ///                       4------------7-----------10
    ///                      /.           /|            |
    ///                     / .          / |            |
    ///                    /  .         /  |            |
    ///                   /   .        /   |            |
    ///                  5------------6    |            |
    ///                  |    .       |`.  |            |
    ///                  |    0-------|--`.3------------9
    ///                  |   /        |   /`.          /
    ///                  |  /         |  /   `.       /
    ///                  | /          | /      `.    /
    ///                  |/           |/         `. /
    ///  12-----11-------1------------2------------8
    /// ```
    #[rustfmt::skip]
    pub fn mixed_shapes_3d() -> Mesh {
        Mesh {
            space_ndim: 3,
            points: vec![
                Point { id: 0, coords: vec![0.0, 0.0, 0.0] },
                Point { id: 1, coords: vec![1.0, 0.0, 0.0] },
                Point { id: 2, coords: vec![1.0, 1.0, 0.0] },
                Point { id: 3, coords: vec![0.0, 1.0, 0.0] },
                Point { id: 4, coords: vec![0.0, 0.0, 1.0] },
                Point { id: 5, coords: vec![1.0, 0.0, 1.0] },
                Point { id: 6, coords: vec![1.0, 1.0, 1.0] },
                Point { id: 7, coords: vec![0.0, 1.0, 1.0] },
                Point { id: 8, coords: vec![1.0, 2.0, 0.0] },
                Point { id: 9, coords: vec![0.0, 2.0, 0.0] },
                Point { id:10, coords: vec![0.0, 2.0, 1.0] },
                Point { id:11, coords: vec![1.0,-0.5, 0.0] },
                Point { id:12, coords: vec![1.0,-1.0, 0.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 3, points: vec![0,1,2,3,4,5,6,7] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 3, points: vec![2,8,3,6] },
                Cell { id: 2, attribute_id: 2, geo_ndim: 2, points: vec![3,9,10,7] },
                Cell { id: 3, attribute_id: 2, geo_ndim: 2, points: vec![8,9,3] },
                Cell { id: 4, attribute_id: 3, geo_ndim: 1, points: vec![1,11,12] },
            ],
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn samples_work() {
        let mesh = Samples::two_quads_horizontal();
        assert_eq!(mesh.space_ndim, 2);
        assert_eq!(mesh.points.len(), 6);
        assert_eq!(mesh.cells.len(), 2);

        let mesh = Samples::two_cubes_vertical();
        assert_eq!(mesh.space_ndim, 3);
        assert_eq!(mesh.points.len(), 12);
        assert_eq!(mesh.cells.len(), 2);

        let mesh = Samples::mixed_shapes_2d();
        assert_eq!(mesh.space_ndim, 2);
        assert_eq!(mesh.points.len(), 5);
        assert_eq!(mesh.cells.len(), 2);

        let mesh = Samples::mixed_shapes_3d();
        assert_eq!(mesh.space_ndim, 3);
        assert_eq!(mesh.points.len(), 13);
        assert_eq!(mesh.cells.len(), 5);
    }
}
<commit_msg>Fix doc<commit_after>use super::{Cell, Mesh, Point};

/// Holds samples of meshes
pub struct Samples;

impl Samples {
    /// Returns two quadrilaterals along the horizontal
    ///
    /// ```text
    /// 1.0  3-----------2-----------5
    ///      |           |           |
    ///      |    [0]    |    [1]    |  [*] indicates id
    ///      |    (1)    |    (2)    |  (*) indicates attribute_id
    ///      |           |           |
    /// 0.0  0-----------1-----------4
    ///     0.0         1.0         2.0
    /// ```
    #[rustfmt::skip]
    pub fn two_quads_horizontal() -> Mesh {
        Mesh {
            space_ndim: 2,
            points: vec![
                Point { id: 0, coords: vec![0.0, 0.0] },
                Point { id: 1, coords: vec![1.0, 0.0] },
                Point { id: 2, coords: vec![1.0, 1.0] },
                Point { id: 3, coords: vec![0.0, 1.0] },
                Point { id: 4, coords: vec![2.0, 0.0] },
                Point { id: 5, coords: vec![2.0, 1.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 2, points: vec![0, 1, 2, 3] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 2, points: vec![1, 4, 5, 2] },
            ],
        }
    }

    /// Returns two cubes along the vertical
    ///
    /// ```text
    ///           8-------------11
    ///          /.             /|
    ///         / .            / |
    ///        /  .           /  |
    ///       /   .          /   |       id = 1
    /// 2.0  9-------------10    |       attribute_id = 2
    ///      |    .         |    |
    ///      |    4---------|----7
    ///      |   /.         |   /|
    ///      |  / .         |  / |
    ///      | /  .         | /  |
    ///      |/   .         |/   |
    /// 1.0  5--------------6    |       id = 0
    ///      |    .         |    |       attribute_id = 1
    ///      |    0---------|----3  0.0
    ///      |   /          |   /
    ///      |  /           |  /
    ///      | /            | /
    ///      |/             |/
    /// 0.0  1--------------2   1.0
    ///     0.0            1.0
    /// ```
    #[rustfmt::skip]
    pub fn two_cubes_vertical() -> Mesh {
        Mesh {
            space_ndim: 3,
            points: vec![
                Point { id:  0, coords: vec![0.0, 0.0, 0.0] },
                Point { id:  1, coords: vec![1.0, 0.0, 0.0] },
                Point { id:  2, coords: vec![1.0, 1.0, 0.0] },
                Point { id:  3, coords: vec![0.0, 1.0, 0.0] },
                Point { id:  4, coords: vec![0.0, 0.0, 1.0] },
                Point { id:  5, coords: vec![1.0, 0.0, 1.0] },
                Point { id:  6, coords: vec![1.0, 1.0, 1.0] },
                Point { id:  7, coords: vec![0.0, 1.0, 1.0] },
                Point { id:  8, coords: vec![0.0, 0.0, 2.0] },
                Point { id:  9, coords: vec![1.0, 0.0, 2.0] },
                Point { id: 10, coords: vec![1.0, 1.0, 2.0] },
                Point { id: 11, coords: vec![0.0, 1.0, 2.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 3, points: vec![0,1,2,3, 4,5,6,7] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 3, points: vec![4,5,6,7, 8,9,10,11] },
            ],
        }
    }

    /// Returns a mesh with mixed shapes in 2D
    ///
    /// ```text
    /// 1.0              4-----------3
    ///                  |           |
    ///                  |    [1]    |   [*] indicates id
    ///                  |    (2)    |   (*) indicates attribute_id
    ///                  |           |
    /// 0.0  0-----------1-----------2
    ///           [0]
    ///           (1)
    ///
    ///     0.0         1.0         2.0
    /// ```
    #[rustfmt::skip]
    pub fn mixed_shapes_2d() -> Mesh {
        Mesh {
            space_ndim: 2,
            points: vec![
                Point { id: 0, coords: vec![0.0, 0.0] },
                Point { id: 1, coords: vec![1.0, 0.0] },
                Point { id: 2, coords: vec![2.0, 0.0] },
                Point { id: 3, coords: vec![2.0, 1.0] },
                Point { id: 4, coords: vec![1.0, 1.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 1, points: vec![0, 1] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 2, points: vec![1, 2, 3, 4] },
            ],
        }
    }

    /// Returns a mesh with mixed shapes in 3D
    ///
    /// ```text
    ///                       4------------7-----------10
    ///                      /.           /|            |
    ///                     / .          / |            |
    ///                    /  .         /  |            |
    ///                   /   .        /   |            |
    ///                  5------------6    |            |
    ///                  |    .       |`.  |            |
    ///                  |    0-------|--`.3------------9
    ///                  |   /        |   /`.          /
    ///                  |  /         |  /   `.       /
    ///                  | /          | /      `.    /
    ///                  |/           |/         `. /
    ///  12-----11-------1------------2------------8
    /// ```
    #[rustfmt::skip]
    pub fn mixed_shapes_3d() -> Mesh {
        Mesh {
            space_ndim: 3,
            points: vec![
                Point { id: 0, coords: vec![0.0, 0.0, 0.0] },
                Point { id: 1, coords: vec![1.0, 0.0, 0.0] },
                Point { id: 2, coords: vec![1.0, 1.0, 0.0] },
                Point { id: 3, coords: vec![0.0, 1.0, 0.0] },
                Point { id: 4, coords: vec![0.0, 0.0, 1.0] },
                Point { id: 5, coords: vec![1.0, 0.0, 1.0] },
                Point { id: 6, coords: vec![1.0, 1.0, 1.0] },
                Point { id: 7, coords: vec![0.0, 1.0, 1.0] },
                Point { id: 8, coords: vec![1.0, 2.0, 0.0] },
                Point { id: 9, coords: vec![0.0, 2.0, 0.0] },
                Point { id:10, coords: vec![0.0, 2.0, 1.0] },
                Point { id:11, coords: vec![1.0,-0.5, 0.0] },
                Point { id:12, coords: vec![1.0,-1.0, 0.0] },
            ],
            cells: vec![
                Cell { id: 0, attribute_id: 1, geo_ndim: 3, points: vec![0,1,2,3,4,5,6,7] },
                Cell { id: 1, attribute_id: 2, geo_ndim: 3, points: vec![2,8,3,6] },
                Cell { id: 2, attribute_id: 2, geo_ndim: 2, points: vec![3,9,10,7] },
                Cell { id: 3, attribute_id: 2, geo_ndim: 2, points: vec![8,9,3] },
                Cell { id: 4, attribute_id: 3, geo_ndim: 1, points: vec![1,11,12] },
            ],
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn samples_work() {
        let mesh = Samples::two_quads_horizontal();
        assert_eq!(mesh.space_ndim, 2);
        assert_eq!(mesh.points.len(), 6);
        assert_eq!(mesh.cells.len(), 2);

        let mesh = Samples::two_cubes_vertical();
        assert_eq!(mesh.space_ndim, 3);
        assert_eq!(mesh.points.len(), 12);
        assert_eq!(mesh.cells.len(), 2);

        let mesh = Samples::mixed_shapes_2d();
        assert_eq!(mesh.space_ndim, 2);
        assert_eq!(mesh.points.len(), 5);
        assert_eq!(mesh.cells.len(), 2);

        let mesh = Samples::mixed_shapes_3d();
        assert_eq!(mesh.space_ndim, 3);
        assert_eq!(mesh.points.len(), 13);
        assert_eq!(mesh.cells.len(), 5);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

use std::borrow::Cow;
use std::iter::repeat;

use syntax::codemap::{BytePos, Pos, Span};

use codemap::LineRangeUtils;
use comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
use config::WriteMode;
use shape::{Indent, Shape};
use utils::{count_newlines, mk_sp};
use visitor::FmtVisitor;

impl<'a> FmtVisitor<'a> {
    fn output_at_start(&self) -> bool {
        self.buffer.len == 0
    }

    // TODO these format_missing methods are ugly. Refactor and add unit tests
    // for the central whitespace stripping loop.
    pub fn format_missing(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.buffer.push_str(last_snippet)
        })
    }

    pub fn format_missing_with_indent(&mut self, end: BytePos) {
        let config = self.config;
        self.format_missing_inner(end, |this, last_snippet, snippet| {
            this.buffer.push_str(last_snippet.trim_right());
            if last_snippet == snippet && !this.output_at_start() {
                // No new lines in the snippet.
                this.buffer.push_str("\n");
            }
            let indent = this.block_indent.to_string(config);
            this.buffer.push_str(&indent);
        })
    }

    pub fn format_missing_no_indent(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.buffer.push_str(last_snippet.trim_right());
        })
    }

    fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(
        &mut self,
        end: BytePos,
        process_last_snippet: F,
    ) {
        let start = self.last_pos;

        if start == end {
            // Do nothing if this is the beginning of the file.
            if !self.output_at_start() {
                process_last_snippet(self, "", "");
            }
            return;
        }

        assert!(
            start < end,
            "Request to format inverted span: {:?} to {:?}",
            self.codemap.lookup_char_pos(start),
            self.codemap.lookup_char_pos(end)
        );

        self.last_pos = end;
        let span = mk_sp(start, end);
        let snippet = self.snippet(span);
        if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) {
            // Keep vertical spaces within range.
            self.push_vertical_spaces(count_newlines(&snippet));
            process_last_snippet(self, "", &snippet);
        } else {
            self.write_snippet(span, &process_last_snippet);
        }
    }

    fn push_vertical_spaces(&mut self, mut newline_count: usize) {
        let newline_upper_bound = self.config.blank_lines_upper_bound() + 1;
        let newline_lower_bound = self.config.blank_lines_lower_bound() + 1;
        if newline_count > newline_upper_bound {
            newline_count = newline_upper_bound;
        } else if newline_count < newline_lower_bound {
            newline_count = newline_lower_bound;
        }
        let blank_lines: String = repeat('\n').take(newline_count).collect();
        self.buffer.push_str(&blank_lines);
    }

    fn write_snippet<F>(&mut self, span: Span, process_last_snippet: F)
    where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Get a snippet from the file start to the span's hi without allocating.
        // We need it to determine what precedes the current comment. If the comment
        // follows code on the same line, we won't touch it.
        let big_span_lo = self.codemap.lookup_char_pos(span.lo()).file.start_pos;
        let local_begin = self.codemap.lookup_byte_offset(big_span_lo);
        let local_end = self.codemap.lookup_byte_offset(span.hi());
        let start_index = local_begin.pos.to_usize();
        let end_index = local_end.pos.to_usize();
        let big_snippet = &local_begin.fm.src.as_ref().unwrap()[start_index..end_index];

        let big_diff = (span.lo() - big_span_lo).to_usize();
        let snippet = self.snippet(span);

        debug!("write_snippet `{}`", snippet);

        self.write_snippet_inner(big_snippet, big_diff, &snippet, span, process_last_snippet);
    }

    fn process_comment(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        big_snippet: &str,
        offset: usize,
        big_diff: usize,
        subslice: &str,
        file_name: &str,
    ) -> bool {
        let last_char = big_snippet[..(offset + big_diff)]
            .chars()
            .rev()
            .skip_while(|rev_c| [' ', '\t'].contains(rev_c))
            .next();

        let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c));

        let subslice_num_lines = count_newlines(subslice);
        let skip_this_range = !self.config.file_lines().intersects_range(
            file_name,
            status.cur_line,
            status.cur_line + subslice_num_lines,
        );

        if status.rewrite_next_comment && skip_this_range {
            status.rewrite_next_comment = false;
        }

        if status.rewrite_next_comment {
            if fix_indent {
                if let Some('{') = last_char {
                    self.buffer.push_str("\n");
                }
                self.buffer
                    .push_str(&self.block_indent.to_string(self.config));
            } else {
                self.buffer.push_str(" ");
            }

            let comment_width = ::std::cmp::min(
                self.config.comment_width(),
                self.config.max_width() - self.block_indent.width(),
            );
            let comment_indent = Indent::from_width(self.config, self.buffer.cur_offset());
            let comment_shape = Shape::legacy(comment_width, comment_indent);
            let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
                .unwrap_or_else(|| String::from(subslice));
            self.buffer.push_str(&comment_str);

            status.last_wspace = None;
            status.line_start = offset + subslice.len();

            if let Some('/') = subslice.chars().nth(1) {
                // check that there are no contained block comments
                if !subslice
                    .split('\n')
                    .map(|s| s.trim_left())
                    .any(|s| s.len() >= 2 && &s[0..2] == "/*")
                {
                    // Add a newline after line comments
                    self.buffer.push_str("\n");
                }
            } else if status.line_start <= snippet.len() {
                // For other comments add a newline if there isn't one at the end already
                match snippet[status.line_start..].chars().next() {
                    Some('\n') | Some('\r') => (),
                    _ => self.buffer.push_str("\n"),
                }
            }

            status.cur_line += subslice_num_lines;
            true
        } else {
            status.rewrite_next_comment = false;
            false
        }
    }

    fn write_snippet_inner<F>(
        &mut self,
        big_snippet: &str,
        big_diff: usize,
        old_snippet: &str,
        span: Span,
        process_last_snippet: F,
    ) where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Trim whitespace from the right hand side of each line.
        // Annoyingly, the library functions for splitting by lines etc. are not
        // quite right, so we must do it ourselves.
        let char_pos = self.codemap.lookup_char_pos(span.lo());
        let file_name = &char_pos.file.name;
        let mut status = SnippetStatus::new(char_pos.line);

        fn replace_chars<'a>(string: &'a str) -> Cow<'a, str> {
            if string.contains(char::is_whitespace) {
                Cow::from(
                    string
                        .chars()
                        .map(|ch| if ch.is_whitespace() { ch } else { 'X' })
                        .collect::<String>(),
                )
            } else {
                Cow::from(string)
            }
        }

        let snippet = &*match self.config.write_mode() {
            WriteMode::Coverage => replace_chars(old_snippet),
            _ => Cow::from(old_snippet),
        };

        for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
            debug!("{:?}: {:?}", kind, subslice);

            if let CodeCharKind::Comment = kind {
                if self.process_comment(
                    &mut status,
                    snippet,
                    big_snippet,
                    offset,
                    big_diff,
                    subslice,
                    file_name,
                ) {
                    continue;
                }
            }

            let newline_count = count_newlines(&subslice);
            if subslice.trim().is_empty() && newline_count > 0
                && self.config.file_lines().intersects_range(
                    file_name,
                    status.cur_line,
                    status.cur_line + newline_count,
                ) {
                self.push_vertical_spaces(newline_count);
                status.cur_line += newline_count;
                status.rewrite_next_comment = true;
                status.line_start = offset + newline_count;
            } else {
                for (mut i, c) in subslice.char_indices() {
                    i += offset;

                    if c == '\n' {
                        if !self.config
                            .file_lines()
                            .contains_line(file_name, status.cur_line)
                        {
                            status.last_wspace = None;
                        }

                        if let Some(lw) = status.last_wspace {
                            self.buffer.push_str(&snippet[status.line_start..lw]);
                            self.buffer.push_str("\n");
                        } else {
                            self.buffer.push_str(&snippet[status.line_start..i + 1]);
                        }

                        status.cur_line += 1;
                        status.line_start = i + 1;
                        status.last_wspace = None;
                        status.rewrite_next_comment = true;
                    } else if c.is_whitespace() {
                        if status.last_wspace.is_none() {
                            status.last_wspace = Some(i);
                        }
                    } else if c == ';' {
                        if status.last_wspace.is_some() {
                            status.line_start = i;
                        }

                        status.rewrite_next_comment = true;
                        status.last_wspace = None;
                    } else {
                        status.rewrite_next_comment = true;
                        status.last_wspace = None;
                    }
                }

                let remaining = snippet[status.line_start..subslice.len() + offset].trim();
                if !remaining.is_empty() {
                    self.buffer.push_str(remaining);
                    status.line_start = subslice.len() + offset;
                    status.rewrite_next_comment = true;
                }
            }
        }

        process_last_snippet(self, &snippet[status.line_start..], snippet);
    }
}

struct SnippetStatus {
    line_start: usize,
    last_wspace: Option<usize>,
    rewrite_next_comment: bool,
    cur_line: usize,
}

impl SnippetStatus {
    fn new(cur_line: usize) -> Self {
        SnippetStatus {
            line_start: 0,
            last_wspace: None,
            rewrite_next_comment: true,
            cur_line,
        }
    }
}
<commit_msg>Take into account a trailing newline in buffer<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

use std::borrow::Cow;
use std::iter::repeat;

use syntax::codemap::{BytePos, Pos, Span};

use codemap::LineRangeUtils;
use comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
use config::WriteMode;
use shape::{Indent, Shape};
use utils::{count_newlines, mk_sp};
use visitor::FmtVisitor;

impl<'a> FmtVisitor<'a> {
    fn output_at_start(&self) -> bool {
        self.buffer.len == 0
    }

    // TODO these format_missing methods are ugly. Refactor and add unit tests
    // for the central whitespace stripping loop.
    pub fn format_missing(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.buffer.push_str(last_snippet)
        })
    }

    pub fn format_missing_with_indent(&mut self, end: BytePos) {
        let config = self.config;
        self.format_missing_inner(end, |this, last_snippet, snippet| {
            this.buffer.push_str(last_snippet.trim_right());
            if last_snippet == snippet && !this.output_at_start() {
                // No new lines in the snippet.
                this.buffer.push_str("\n");
            }
            let indent = this.block_indent.to_string(config);
            this.buffer.push_str(&indent);
        })
    }

    pub fn format_missing_no_indent(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.buffer.push_str(last_snippet.trim_right());
        })
    }

    fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(
        &mut self,
        end: BytePos,
        process_last_snippet: F,
    ) {
        let start = self.last_pos;

        if start == end {
            // Do nothing if this is the beginning of the file.
            if !self.output_at_start() {
                process_last_snippet(self, "", "");
            }
            return;
        }

        assert!(
            start < end,
            "Request to format inverted span: {:?} to {:?}",
            self.codemap.lookup_char_pos(start),
            self.codemap.lookup_char_pos(end)
        );

        self.last_pos = end;
        let span = mk_sp(start, end);
        let snippet = self.snippet(span);
        if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) {
            // Keep vertical spaces within range.
            self.push_vertical_spaces(count_newlines(&snippet));
            process_last_snippet(self, "", &snippet);
        } else {
            self.write_snippet(span, &process_last_snippet);
        }
    }

    fn push_vertical_spaces(&mut self, mut newline_count: usize) {
        // The buffer already has a trailing newline.
        if self.buffer.cur_offset() == 0 {
            newline_count = newline_count.checked_sub(1).unwrap_or(0);
        }
        let newline_upper_bound = self.config.blank_lines_upper_bound() + 1;
        let newline_lower_bound = self.config.blank_lines_lower_bound() + 1;
        if newline_count > newline_upper_bound {
            newline_count = newline_upper_bound;
        } else if newline_count < newline_lower_bound {
            newline_count = newline_lower_bound;
        }
        let blank_lines: String = repeat('\n').take(newline_count).collect();
        self.buffer.push_str(&blank_lines);
    }

    fn write_snippet<F>(&mut self, span: Span, process_last_snippet: F)
    where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Get a snippet from the file start to the span's hi without allocating.
        // We need it to determine what precedes the current comment. If the comment
        // follows code on the same line, we won't touch it.
        let big_span_lo = self.codemap.lookup_char_pos(span.lo()).file.start_pos;
        let local_begin = self.codemap.lookup_byte_offset(big_span_lo);
        let local_end = self.codemap.lookup_byte_offset(span.hi());
        let start_index = local_begin.pos.to_usize();
        let end_index = local_end.pos.to_usize();
        let big_snippet = &local_begin.fm.src.as_ref().unwrap()[start_index..end_index];

        let big_diff = (span.lo() - big_span_lo).to_usize();
        let snippet = self.snippet(span);

        debug!("write_snippet `{}`", snippet);

        self.write_snippet_inner(big_snippet, big_diff, &snippet, span, process_last_snippet);
    }

    fn process_comment(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        big_snippet: &str,
        offset: usize,
        big_diff: usize,
        subslice: &str,
        file_name: &str,
    ) -> bool {
        let last_char = big_snippet[..(offset + big_diff)]
            .chars()
            .rev()
            .skip_while(|rev_c| [' ', '\t'].contains(rev_c))
            .next();

        let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c));

        let subslice_num_lines = count_newlines(subslice);
        let skip_this_range = !self.config.file_lines().intersects_range(
            file_name,
            status.cur_line,
            status.cur_line + subslice_num_lines,
        );

        if status.rewrite_next_comment && skip_this_range {
            status.rewrite_next_comment = false;
        }

        if status.rewrite_next_comment {
            if fix_indent {
                if let Some('{') = last_char {
                    self.buffer.push_str("\n");
                }
                self.buffer
                    .push_str(&self.block_indent.to_string(self.config));
            } else {
                self.buffer.push_str(" ");
            }

            let comment_width = ::std::cmp::min(
                self.config.comment_width(),
                self.config.max_width() - self.block_indent.width(),
            );
            let comment_indent = Indent::from_width(self.config, self.buffer.cur_offset());
            let comment_shape = Shape::legacy(comment_width, comment_indent);
            let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
                .unwrap_or_else(|| String::from(subslice));
            self.buffer.push_str(&comment_str);

            status.last_wspace = None;
            status.line_start = offset + subslice.len();

            if let Some('/') = subslice.chars().nth(1) {
                // check that there are no contained block comments
                if !subslice
                    .split('\n')
                    .map(|s| s.trim_left())
                    .any(|s| s.len() >= 2 && &s[0..2] == "/*")
                {
                    // Add a newline after line comments
                    self.buffer.push_str("\n");
                }
            } else if status.line_start <= snippet.len() {
                // For other comments add a newline if there isn't one at the end already
                match snippet[status.line_start..].chars().next() {
                    Some('\n') | Some('\r') => (),
                    _ => self.buffer.push_str("\n"),
                }
            }

            status.cur_line += subslice_num_lines;
            true
        } else {
            status.rewrite_next_comment = false;
            false
        }
    }

    fn write_snippet_inner<F>(
        &mut self,
        big_snippet: &str,
        big_diff: usize,
        old_snippet: &str,
        span: Span,
        process_last_snippet: F,
    ) where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Trim whitespace from the right hand side of each line.
        // Annoyingly, the library functions for splitting by lines etc. are not
        // quite right, so we must do it ourselves.
        let char_pos = self.codemap.lookup_char_pos(span.lo());
        let file_name = &char_pos.file.name;
        let mut status = SnippetStatus::new(char_pos.line);

        fn replace_chars<'a>(string: &'a str) -> Cow<'a, str> {
            if string.contains(char::is_whitespace) {
                Cow::from(
                    string
                        .chars()
                        .map(|ch| if ch.is_whitespace() { ch } else { 'X' })
                        .collect::<String>(),
                )
            } else {
                Cow::from(string)
            }
        }

        let snippet = &*match self.config.write_mode() {
            WriteMode::Coverage => replace_chars(old_snippet),
            _ => Cow::from(old_snippet),
        };

        for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
            debug!("{:?}: {:?}", kind, subslice);

            if let CodeCharKind::Comment = kind {
                if self.process_comment(
                    &mut status,
                    snippet,
                    big_snippet,
                    offset,
                    big_diff,
                    subslice,
                    file_name,
                ) {
                    continue;
                }
            }

            let newline_count = count_newlines(&subslice);
            if subslice.trim().is_empty() && newline_count > 0
                && self.config.file_lines().intersects_range(
                    file_name,
                    status.cur_line,
                    status.cur_line + newline_count,
                ) {
                self.push_vertical_spaces(newline_count);
                status.cur_line += newline_count;
                status.rewrite_next_comment = true;
                status.line_start = offset + newline_count;
            } else {
                for (mut i, c) in subslice.char_indices() {
                    i += offset;

                    if c == '\n' {
                        if !self.config
                            .file_lines()
                            .contains_line(file_name, status.cur_line)
                        {
                            status.last_wspace = None;
                        }

                        if let Some(lw) = status.last_wspace {
                            self.buffer.push_str(&snippet[status.line_start..lw]);
                            self.buffer.push_str("\n");
                        } else {
                            self.buffer.push_str(&snippet[status.line_start..i + 1]);
                        }

                        status.cur_line += 1;
                        status.line_start = i + 1;
                        status.last_wspace = None;
                        status.rewrite_next_comment = true;
                    } else if c.is_whitespace() {
                        if status.last_wspace.is_none() {
                            status.last_wspace = Some(i);
                        }
                    } else if c == ';' {
                        if status.last_wspace.is_some() {
                            status.line_start = i;
                        }

                        status.rewrite_next_comment = true;
                        status.last_wspace = None;
                    } else {
                        status.rewrite_next_comment = true;
                        status.last_wspace = None;
                    }
                }

                let remaining = snippet[status.line_start..subslice.len() + offset].trim();
                if !remaining.is_empty() {
                    self.buffer.push_str(remaining);
                    status.line_start = subslice.len() + offset;
                    status.rewrite_next_comment = true;
                }
            }
        }

        process_last_snippet(self, &snippet[status.line_start..], snippet);
    }
}

struct SnippetStatus {
    line_start: usize,
    last_wspace: Option<usize>,
    rewrite_next_comment: bool,
    cur_line: usize,
}

impl SnippetStatus {
    fn new(cur_line: usize) -> Self {
        SnippetStatus {
            line_start: 0,
            last_wspace: None,
            rewrite_next_comment: true,
            cur_line,
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::env;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use log::debug;

use users::UsersCache;

use crate::style::Colours;
use crate::output::cell::TextCell;
use crate::output::render::TimeRender;
use crate::output::time::TimeFormat;
use crate::fs::{File, fields as f};
use crate::fs::feature::git::GitCache;

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub columns: Columns,
}

// I had to make other types derive Debug,
// and Mutex<UsersCache> is not that!
impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Table({:#?})", self.columns)
    }
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,

    // Defaults to true:
    pub permissions: bool,
    pub filesize: bool,
    pub user: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        if self.permissions {
            columns.push(Column::Permissions);
        }

        if self.links {
            columns.push(Column::HardLinks);
        }

        if self.filesize {
            columns.push(Column::FileSize);
        }

        if self.blocks {
            columns.push(Column::Blocks);
        }

        if self.user {
            columns.push(Column::User);
        }

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.changed {
            columns.push(Column::Timestamp(TimeType::Changed));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize      => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {
    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s changed time (`st_ctime`)
    Changed,

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s creation time (`btime` or `birthtime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(self) -> &'static str {
        match self {
            TimeType::Modified  => "Date Modified",
            TimeType::Changed   => "Date Changed",
            TimeType::Accessed  => "Date Accessed",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub modified: bool,
    pub changed:  bool,
    pub accessed: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { modified: true, changed: false, accessed: false, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    let tz = env::var("TZ");
    if tz.is_err() {
        return TimeZone::from_file("/etc/localtime");
    } else {
        return TimeZone::from_file(format!("/usr/share/zoneinfo/{}", tz.unwrap()));
    }
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, git: Option<&'a GitCache>, colours: &'a Colours) -> Table<'a> {
        let columns = options.columns.collect(git.is_some());
        let widths = TableWidths::zero(columns.len());

        Table {
            colours, widths, columns, git,
            env:         &options.env,
            time_format: &options.time_format,
            size_format:  options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use crate::output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(self.colours),
            Column::FileSize       => file.size().render(self.colours, self.size_format, &self.env.numeric),
            Column::HardLinks      => file.links().render(self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(self.colours.inode),
            Column::Blocks         => file.blocks().render(self.colours),
            Column::User           => file.user().render(self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(self.colours, &*self.env.lock_users()),
            Column::GitStatus      => self.git_status(file).render(self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Changed)   => file.changed_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
        }
    }

    fn git_status(&self, file: &File) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);
        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<commit_msg>deconstruct the TZ environment variable instead of unwrapping<commit_after>use std::cmp::max;
use std::env;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use log::debug;

use users::UsersCache;

use crate::style::Colours;
use crate::output::cell::TextCell;
use crate::output::render::TimeRender;
use crate::output::time::TimeFormat;
use crate::fs::{File, fields as f};
use crate::fs::feature::git::GitCache;

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub columns: Columns,
}

// I had to make other types derive Debug,
// and Mutex<UsersCache> is not that!
impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Table({:#?})", self.columns)
    }
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,

    // Defaults to true:
    pub permissions: bool,
    pub filesize: bool,
    pub user: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        if self.permissions {
            columns.push(Column::Permissions);
        }

        if self.links {
            columns.push(Column::HardLinks);
        }

        if self.filesize {
            columns.push(Column::FileSize);
        }

        if self.blocks {
            columns.push(Column::Blocks);
        }

        if self.user {
            columns.push(Column::User);
        }

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.changed {
            columns.push(Column::Timestamp(TimeType::Changed));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize      => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {
    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s changed time (`st_ctime`)
    Changed,

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s creation time (`btime` or `birthtime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(self) -> &'static str {
        match self {
            TimeType::Modified  => "Date Modified",
            TimeType::Changed   => "Date Changed",
            TimeType::Accessed  => "Date Accessed",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub modified: bool,
    pub changed:  bool,
    pub accessed: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { modified: true, changed: false, accessed: false, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    if let Ok(file) = env::var("TZ") {
        TimeZone::from_file(format!("/usr/share/zoneinfo/{}", file))
    } else {
        TimeZone::from_file("/etc/localtime")
    }
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, git: Option<&'a GitCache>, colours: &'a Colours) -> Table<'a> {
        let columns = options.columns.collect(git.is_some());
        let widths = TableWidths::zero(columns.len());

        Table {
            colours, widths, columns, git,
            env:         &options.env,
            time_format: &options.time_format,
            size_format:  options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use crate::output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(self.colours),
            Column::FileSize       => file.size().render(self.colours, self.size_format, &self.env.numeric),
            Column::HardLinks      => file.links().render(self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(self.colours.inode),
            Column::Blocks         => file.blocks().render(self.colours),
            Column::User           => file.user().render(self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(self.colours, &*self.env.lock_users()),
            Column::GitStatus      => self.git_status(file).render(self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Changed)   => file.changed_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
        }
    }

    fn git_status(&self, file: &File) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);
        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use parser::parse;
use normalizer::normalize;
use parser::span::parse_spans;
use parser::Block;
use parser::Block::{Header, Paragraph, Blockquote, Hr};
use parser::Span;
use parser::Span::{Break, Text, Emphasis, Strong, Code, Link, Image};

static ATX_HEADER      : Regex = regex!(r"^(?P<level>#{1,6})\s(?P<text>.*)");
static SETEXT_HEADER_1 : Regex = regex!(r"(?P<text>.+)\n===+");
static SETEXT_HEADER_2 : Regex = regex!(r"(?P<text>.+)\n---+");
static HORIZONTAL_RULE : Regex = regex!(r"\n---+");
static BLOCKQUOTE      : Regex = regex!(r"(?m)^> ?");

pub fn parse_block (text : &str) -> Option<Block>{
    if text.is_empty(){
        return None;
    }else if BLOCKQUOTE.is_match(text){
        // remove top-level >s
        let caps = BLOCKQUOTE.replace_all(text, "");
        return Some(
            Blockquote(parse(caps.as_slice()))
        );
    }else if ATX_HEADER.is_match(text){
        let caps = ATX_HEADER.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                caps.name("level").len()
                )
            );
    }else if SETEXT_HEADER_1.is_match(text){
        let caps = SETEXT_HEADER_1.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                1
                )
            );
    }else if SETEXT_HEADER_2.is_match(text){
        let caps = SETEXT_HEADER_2.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                2
                )
            );
    }else if HORIZONTAL_RULE.is_match(text){
        return Some(Hr);
    }
    return Some(Paragraph(parse_spans(text)));
}

#[test]
fn parse_atx_header_test() {
    assert_eq!(
        parse_block("### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 3)
    );

    assert_eq!(
        parse_block("# Test").unwrap(),
        Header(vec![Text("Test".to_string())], 1)
    );

    assert_eq!(
        parse_block("###### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 6)
    );

    assert_eq!(
        parse_block("####### Test").unwrap(),
        Paragraph(vec![Text("####### Test".to_string())])
    );
}

#[test]
fn parse_blockquote_test() {
    assert_eq!(
        parse_block("> One Paragraph\n>\n> ## H2 \n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]), Header(vec![Text("H2 ".to_string())], 2)])
    );

    assert_eq!(
        parse_block("> One Paragraph\n>\n> > Another blockquote\n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]),
                   Blockquote(vec![Paragraph(vec![Text("Another blockquote".to_string())])])])
    );

    assert_eq!(
        parse_block("> > One Paragraph\n> >\n> > Another blockquote\n>\n").unwrap(),
        Blockquote(vec![Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]),
                   Paragraph(vec![Text("Another blockquote".to_string())])])])
    );

    assert_eq!(
        parse_block("> One Paragraph, just > text \n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph, just > text ".to_string())])])
    );

    assert_eq!(
        parse_block("> One Paragraph,\n just\n > text ").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph,\n just\n > text ".to_string())])])
    );

    assert_eq!(
        parse_block("> One Paragraph\n>\n> just > text \n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]),Paragraph(vec![Text("just > text ".to_string())])])
    );
}

<commit_msg>Implement hr parsing<commit_after>use regex::Regex;
use parser::parse;
use normalizer::normalize;
use parser::span::parse_spans;
use parser::Block;
use parser::Block::{Header, Paragraph, Blockquote, Hr};
use parser::Span;
use parser::Span::{Break, Text, Emphasis, Strong, Code, Link, Image};

static ATX_HEADER      : Regex = regex!(r"^(?P<level>#{1,6})\s(?P<text>.*)");
static SETEXT_HEADER_1 : Regex = regex!(r"(?P<text>.+)\n===+");
static SETEXT_HEADER_2 : Regex = regex!(r"(?P<text>.+)\n---+");
static HORIZONTAL_RULE : Regex = regex!(r"(===+)|(---+)");
static BLOCKQUOTE      : Regex = regex!(r"(?m)^> ?");

pub fn parse_block (text : &str) -> Option<Block>{
    if text.is_empty(){
        return None;
    }else if BLOCKQUOTE.is_match(text){
        // remove top-level >s
        let caps = BLOCKQUOTE.replace_all(text, "");
        return Some(
            Blockquote(parse(caps.as_slice()))
        );
    }else if ATX_HEADER.is_match(text){
        let caps = ATX_HEADER.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                caps.name("level").len()
                )
            );
    }else if SETEXT_HEADER_1.is_match(text){
        let caps = SETEXT_HEADER_1.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                1
                )
            );
    }else if SETEXT_HEADER_2.is_match(text){
        let caps = SETEXT_HEADER_2.captures(text).unwrap();
        return Some(
            Header(
                parse_spans(caps.name("text")),
                2
                )
            );
    }else if HORIZONTAL_RULE.is_match(text){
        return Some(Hr);
    }
    return Some(Paragraph(parse_spans(text)));
}

#[test]
fn parse_atx_header_test() {
    assert_eq!(
        parse_block("### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 3)
    );

    assert_eq!(
        parse_block("# Test").unwrap(),
        Header(vec![Text("Test".to_string())], 1)
    );

    assert_eq!(
        parse_block("###### Test").unwrap(),
        Header(vec![Text("Test".to_string())], 6)
    );

    assert_eq!(
        parse_block("####### Test").unwrap(),
        Paragraph(vec![Text("####### Test".to_string())])
    );
}

#[test]
fn parse_blockquote_test() {
    assert_eq!(
        parse_block("> One Paragraph\n>\n> ## H2 \n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]), Header(vec![Text("H2 ".to_string())], 2)])
    );

    assert_eq!(
        parse_block("> One Paragraph\n>\n> > Another blockquote\n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]),
                   Blockquote(vec![Paragraph(vec![Text("Another blockquote".to_string())])])])
    );

    assert_eq!(
        parse_block("> > One Paragraph\n> >\n> > Another blockquote\n>\n").unwrap(),
        Blockquote(vec![Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]),
                   Paragraph(vec![Text("Another blockquote".to_string())])])])
    );

    assert_eq!(
        parse_block("> One Paragraph, just > text \n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph, just > text ".to_string())])])
    );

    assert_eq!(
        parse_block("> One Paragraph,\n just\n > text ").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph,\n just\n > text ".to_string())])])
    );

    assert_eq!(
        parse_block("> One Paragraph\n>\n> just > text \n>\n").unwrap(),
        Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_string())]),Paragraph(vec![Text("just > text ".to_string())])])
    );
}

#[test]
fn parse_horizontal_rule_test() {
    assert_eq!(
        parse_block("--------").unwrap(),
        Hr
    );
    assert_eq!(
        parse_block("========").unwrap(),
        Hr
    );
}

<|endoftext|>


<commit_before>use chrono::prelude::*;

struct Ship {
    pub name String,
    pub location String
}

struct ShippingEvent {
    pub ocurred: DateTime<Utc>,
    pub recorded: DateTime<Utc>
}

struct ArrivalEvent: ShippingEvent {
    pub ship Ship,
    pub port String

}

struct DepartureEvent: ShippingEvent {
    pub ship Ship,
    pub port String
}

struct TrackingService {
}

impl TrackingService {
    pub fn recordArrival (ship: Ship, port: Port) {
    }
    pub fn recordDeparture (ship :Ship, port: Port) {
    }
}

fn main () {

}

<commit_msg>Example to implement event processor pattern<commit_after>use chrono::prelude::*;

struct Ship {
    pub name String,
    pub location String
}

impl Ship {
    pub fn new (name: String, location: String) {
        return Ship {name, location};
    }
    pub fn handleDeparture (event: DepartureEvent) {
        println!("go");

    }
}

struct ShippingEvent {
    pub ocurred: DateTime<Utc>,
    pub recorded: DateTime<Utc>
}

impl ShippingEvent {
    pub fn new (ocurred: DateTime<Utc>, recorded: DateTime<Utc>) {
        return ShippingEvent {ocurred, recorded};
    }
}

struct DepartureEvent {
    pub ShippingEvent shippingEvent,
    pub ship Ship,
    pub port String
}

impl DepartureEvent {
    pub fn new (ocurred: DateTime<Utc>, recorded: DateTime<Utc>,
                ship: Ship, port: String) {
        return DepartureEvent {ShippingEvent::new(ocurred, recorded), ship, port};
    }
    pub fn process () {
        self.ship.handleDeparture();
    }
}

struct EventProcessor {
}

impl TrackingService {
    pub fn process (departureEvent: DepartureEvent) {
        departureEvent.process();
    }
}

fn main () {

}

<|endoftext|>