<commit_before>/// Solve least square problem `|b - Ax|`
use approx::AbsDiffEq;
use ndarray::*;
use ndarray_linalg::*;

/// A is square. `x = A^{-1} b`, `|b - Ax| = 0`
fn test_exact<T: Scalar + Lapack>(a: Array2<T>) {
    let b: Array1<T> = random(3);
    let result = a.least_squares(&b).unwrap();
    // unpack result
    let x = result.solution;
    let residual_l2_square = result.residual_sum_of_squares.unwrap()[()];

    // must be full-rank
    assert_eq!(result.rank, 3);

    // |b - Ax| == 0
    assert!(residual_l2_square < T::real(1.0e-4));

    // b == Ax
    let ax = a.dot(&x);
    assert_close_l2!(&b, &ax, T::real(1.0e-4));
}

macro_rules! impl_exact {
    ($scalar:ty) => {
        paste::item! {
            #[test]
            fn [<least_squares_ $scalar _exact>]() {
                let a: Array2<$scalar> = random((3, 3));
                test_exact(a)
            }

            #[test]
            fn [<least_squares_ $scalar _exact_t>]() {
                let a: Array2<$scalar> = random((3, 3).f());
                test_exact(a)
            }
        }
    };
}

impl_exact!(f32);
impl_exact!(f64);
impl_exact!(c32);
impl_exact!(c64);

/// #column < #row case.
/// Linear problem is overdetermined, `|b - Ax| > 0`.
fn test_overdetermined<T: Scalar + Lapack>(a: Array2<T>)
where
    T::Real: AbsDiffEq<Epsilon = T::Real>,
{
    let b: Array1<T> = random(4);
    let result = a.least_squares(&b).unwrap();
    // unpack result
    let x = result.solution;
    let residual_l2_square = result.residual_sum_of_squares.unwrap()[()];

    // Must be full-rank
    assert_eq!(result.rank, 3);

    // eval `residual = b - Ax`
    let residual = &b - &a.dot(&x);
    assert!(residual_l2_square.abs_diff_eq(&residual.norm_l2().powi(2), T::real(1.0e-4)));

    // `|residual| < |b|`
    assert!(residual.norm_l2() < b.norm_l2());
}

macro_rules! impl_overdetermined {
    ($scalar:ty) => {
        paste::item! {
            #[test]
            fn [<least_squares_ $scalar _overdetermined>]() {
                let a: Array2<$scalar> = random((4, 3));
                test_overdetermined(a)
            }

            #[test]
            fn [<least_squares_ $scalar _overdetermined_t>]() {
                let a: Array2<$scalar> = random((4, 3).f());
                test_overdetermined(a)
            }
        }
    };
}

impl_overdetermined!(f32);
impl_overdetermined!(f64);
impl_overdetermined!(c32);
impl_overdetermined!(c64);

/// #column > #row case.
/// Linear problem is underdetermined, `|b - Ax| = 0` and `x` is not unique
fn test_underdetermined<T: Scalar + Lapack>(a: Array2<T>) {
    let b: Array1<T> = random(3);
    let result = a.least_squares(&b).unwrap();
    assert_eq!(result.rank, 3);
    assert!(result.residual_sum_of_squares.is_none());

    // b == Ax
    let x = result.solution;
    let ax = a.dot(&x);
    assert_close_l2!(&b, &ax, T::real(1.0e-4));
}

macro_rules! impl_underdetermined {
    ($scalar:ty) => {
        paste::item! {
            #[test]
            fn [<least_squares_ $scalar _underdetermined>]() {
                let a: Array2<$scalar> = random((3, 4));
                test_underdetermined(a)
            }

            #[test]
            fn [<least_squares_ $scalar _underdetermined_t>]() {
                let a: Array2<$scalar> = random((3, 4).f());
                test_underdetermined(a)
            }
        }
    };
}

impl_underdetermined!(f32);
impl_underdetermined!(f64);
impl_underdetermined!(c32);
impl_underdetermined!(c64);
<commit_msg>Use sup norm for testing least_squares<commit_after>/// Solve least square problem `|b - Ax|`
use approx::AbsDiffEq;
use ndarray::*;
use ndarray_linalg::*;

/// A is square. `x = A^{-1} b`, `|b - Ax| = 0`
fn test_exact<T: Scalar + Lapack>(a: Array2<T>) {
    let b: Array1<T> = random(3);
    let result = a.least_squares(&b).unwrap();
    // unpack result
    let x = result.solution;
    let residual_l2_square = result.residual_sum_of_squares.unwrap()[()];

    // must be full-rank
    assert_eq!(result.rank, 3);

    // |b - Ax| == 0
    assert!(residual_l2_square < T::real(1.0e-4));

    // b == Ax
    let ax = a.dot(&x);
    assert_close_max!(&b, &ax, T::real(1.0e-4));
}

macro_rules! impl_exact {
    ($scalar:ty) => {
        paste::item! {
            #[test]
            fn [<least_squares_ $scalar _exact>]() {
                let a: Array2<$scalar> = random((3, 3));
                test_exact(a)
            }

            #[test]
            fn [<least_squares_ $scalar _exact_t>]() {
                let a: Array2<$scalar> = random((3, 3).f());
                test_exact(a)
            }
        }
    };
}

impl_exact!(f32);
impl_exact!(f64);
impl_exact!(c32);
impl_exact!(c64);

/// #column < #row case.
/// Linear problem is overdetermined, `|b - Ax| > 0`.
fn test_overdetermined<T: Scalar + Lapack>(a: Array2<T>)
where
    T::Real: AbsDiffEq<Epsilon = T::Real>,
{
    let b: Array1<T> = random(4);
    let result = a.least_squares(&b).unwrap();
    // unpack result
    let x = result.solution;
    let residual_l2_square = result.residual_sum_of_squares.unwrap()[()];

    // Must be full-rank
    assert_eq!(result.rank, 3);

    // eval `residual = b - Ax`
    let residual = &b - &a.dot(&x);
    assert!(residual_l2_square.abs_diff_eq(&residual.norm_l2().powi(2), T::real(1.0e-4)));

    // `|residual| < |b|`
    assert!(residual.norm_l2() < b.norm_l2());
}

macro_rules! impl_overdetermined {
    ($scalar:ty) => {
        paste::item! {
            #[test]
            fn [<least_squares_ $scalar _overdetermined>]() {
                let a: Array2<$scalar> = random((4, 3));
                test_overdetermined(a)
            }

            #[test]
            fn [<least_squares_ $scalar _overdetermined_t>]() {
                let a: Array2<$scalar> = random((4, 3).f());
                test_overdetermined(a)
            }
        }
    };
}

impl_overdetermined!(f32);
impl_overdetermined!(f64);
impl_overdetermined!(c32);
impl_overdetermined!(c64);

/// #column > #row case.
/// Linear problem is underdetermined, `|b - Ax| = 0` and `x` is not unique
fn test_underdetermined<T: Scalar + Lapack>(a: Array2<T>) {
    let b: Array1<T> = random(3);
    let result = a.least_squares(&b).unwrap();
    assert_eq!(result.rank, 3);
    assert!(result.residual_sum_of_squares.is_none());

    // b == Ax
    let x = result.solution;
    let ax = a.dot(&x);
    assert_close_max!(&b, &ax, T::real(1.0e-4));
}

macro_rules! impl_underdetermined {
    ($scalar:ty) => {
        paste::item! {
            #[test]
            fn [<least_squares_ $scalar _underdetermined>]() {
                let a: Array2<$scalar> = random((3, 4));
                test_underdetermined(a)
            }

            #[test]
            fn [<least_squares_ $scalar _underdetermined_t>]() {
                let a: Array2<$scalar> = random((3, 4).f());
                test_underdetermined(a)
            }
        }
    };
}

impl_underdetermined!(f32);
impl_underdetermined!(f64);
impl_underdetermined!(c32);
impl_underdetermined!(c64);
<|endoftext|>


<commit_before>use anyhow::{bail, Result};
use core::ops::Deref;
use diesel::prelude::*;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::path::{self, Path, PathBuf};

use crate::db::{mount_points, DB};

#[derive(Clone, Debug, Deserialize, Insertable, PartialEq, Eq, Queryable, Serialize)]
#[diesel(table_name = mount_points)]
pub struct MountDir {
	pub source: String,
	pub name: String,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct Mount {
	pub source: PathBuf,
	pub name: String,
}

impl From<MountDir> for Mount {
	fn from(m: MountDir) -> Self {
		let separator_regex = Regex::new(r"\\|/").unwrap();
		let mut correct_separator = String::new();
		correct_separator.push(path::MAIN_SEPARATOR);
		let path_string = separator_regex.replace_all(&m.source, correct_separator.as_str());
		let source = PathBuf::from(path_string.deref());
		Self {
			name: m.name,
			source,
		}
	}
}

#[allow(clippy::upper_case_acronyms)]
pub struct VFS {
	mounts: Vec<Mount>,
}

impl VFS {
	pub fn new(mounts: Vec<Mount>) -> VFS {
		VFS { mounts }
	}

	pub fn real_to_virtual<P: AsRef<Path>>(&self, real_path: P) -> Result<PathBuf> {
		for mount in &self.mounts {
			if let Ok(p) = real_path.as_ref().strip_prefix(&mount.source) {
				let mount_path = Path::new(&mount.name);
				return if p.components().count() == 0 {
					Ok(mount_path.to_path_buf())
				} else {
					Ok(mount_path.join(p))
				};
			}
		}
		bail!("Real path has no match in VFS")
	}

	pub fn virtual_to_real<P: AsRef<Path>>(&self, virtual_path: P) -> Result<PathBuf> {
		for mount in &self.mounts {
			let mount_path = Path::new(&mount.name);
			if let Ok(p) = virtual_path.as_ref().strip_prefix(mount_path) {
				return if p.components().count() == 0 {
					Ok(mount.source.clone())
				} else {
					Ok(mount.source.join(p))
				};
			}
		}
		bail!("Virtual path has no match in VFS")
	}

	pub fn mounts(&self) -> &Vec<Mount> {
		&self.mounts
	}
}

#[derive(Clone)]
pub struct Manager {
	db: DB,
}

impl Manager {
	pub fn new(db: DB) -> Self {
		Self { db }
	}

	pub fn get_vfs(&self) -> Result<VFS> {
		let mount_dirs = self.mount_dirs()?;
		let mounts = mount_dirs.into_iter().map(|p| p.into()).collect();
		Ok(VFS::new(mounts))
	}

	pub fn mount_dirs(&self) -> Result<Vec<MountDir>> {
		use self::mount_points::dsl::*;
		let mut connection = self.db.connect()?;
		let mount_dirs: Vec<MountDir> = mount_points
			.select((source, name))
			.get_results(&mut connection)?;
		Ok(mount_dirs)
	}

	pub fn set_mount_dirs(&self, mount_dirs: &[MountDir]) -> Result<()> {
		use self::mount_points::dsl::*;
		let mut connection = self.db.connect()?;
		diesel::delete(mount_points).execute(&mut connection)?;
		diesel::insert_into(mount_points)
			.values(mount_dirs)
			.execute(&mut *connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
		Ok(())
	}
}

#[cfg(test)]
mod test {

	use super::*;

	#[test]
	fn converts_virtual_to_real() {
		let vfs = VFS::new(vec![Mount {
			name: "root".to_owned(),
			source: Path::new("test_dir").to_owned(),
		}]);
		let real_path: PathBuf = ["test_dir", "somewhere", "something.png"].iter().collect();
		let virtual_path: PathBuf = ["root", "somewhere", "something.png"].iter().collect();
		let converted_path = vfs.virtual_to_real(virtual_path.as_path()).unwrap();
		assert_eq!(converted_path, real_path);
	}

	#[test]
	fn converts_virtual_to_real_top_level() {
		let vfs = VFS::new(vec![Mount {
			name: "root".to_owned(),
			source: Path::new("test_dir").to_owned(),
		}]);
		let real_path = Path::new("test_dir");
		let converted_path = vfs.virtual_to_real(Path::new("root")).unwrap();
		assert_eq!(converted_path, real_path);
	}

	#[test]
	fn converts_real_to_virtual() {
		let vfs = VFS::new(vec![Mount {
			name: "root".to_owned(),
			source: Path::new("test_dir").to_owned(),
		}]);
		let virtual_path: PathBuf = ["root", "somewhere", "something.png"].iter().collect();
		let real_path: PathBuf = ["test_dir", "somewhere", "something.png"].iter().collect();
		let converted_path = vfs.real_to_virtual(real_path.as_path()).unwrap();
		assert_eq!(converted_path, virtual_path);
	}

	#[test]
	fn cleans_path_string() {
		let mut correct_path = path::PathBuf::new();
		if cfg!(target_os = "windows") {
			correct_path.push("C:\\");
		} else {
			correct_path.push("/usr");
		}
		correct_path.push("some");
		correct_path.push("path");

		let tests = if cfg!(target_os = "windows") {
			vec![
				r#"C:/some/path"#,
				r#"C:\some\path"#,
				r#"C:\some\path\"#,
				r#"C:\some\path\\\\"#,
				r#"C:\some/path//"#,
			]
		} else {
			vec![
				r#"/usr/some/path"#,
				r#"/usr\some\path"#,
				r#"/usr\some\path\"#,
				r#"/usr\some\path\\\\"#,
				r#"/usr\some/path//"#,
			]
		};

		for test in tests {
			let mount_dir = MountDir {
				source: test.to_owned(),
				name: "name".to_owned(),
			};
			let mount: Mount = mount_dir.into();
			assert_eq!(mount.source, correct_path);
		}
	}
}
<commit_msg>Fixed a bug where all music sources would be deleted when trying to add sources with duplicate names<commit_after>use anyhow::{bail, Result};
use core::ops::Deref;
use diesel::prelude::*;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::path::{self, Path, PathBuf};

use crate::db::{mount_points, DB};

#[derive(Clone, Debug, Deserialize, Insertable, PartialEq, Eq, Queryable, Serialize)]
#[diesel(table_name = mount_points)]
pub struct MountDir {
	pub source: String,
	pub name: String,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
pub struct Mount {
	pub source: PathBuf,
	pub name: String,
}

impl From<MountDir> for Mount {
	fn from(m: MountDir) -> Self {
		let separator_regex = Regex::new(r"\\|/").unwrap();
		let mut correct_separator = String::new();
		correct_separator.push(path::MAIN_SEPARATOR);
		let path_string = separator_regex.replace_all(&m.source, correct_separator.as_str());
		let source = PathBuf::from(path_string.deref());
		Self {
			name: m.name,
			source,
		}
	}
}

#[allow(clippy::upper_case_acronyms)]
pub struct VFS {
	mounts: Vec<Mount>,
}

impl VFS {
	pub fn new(mounts: Vec<Mount>) -> VFS {
		VFS { mounts }
	}

	pub fn real_to_virtual<P: AsRef<Path>>(&self, real_path: P) -> Result<PathBuf> {
		for mount in &self.mounts {
			if let Ok(p) = real_path.as_ref().strip_prefix(&mount.source) {
				let mount_path = Path::new(&mount.name);
				return if p.components().count() == 0 {
					Ok(mount_path.to_path_buf())
				} else {
					Ok(mount_path.join(p))
				};
			}
		}
		bail!("Real path has no match in VFS")
	}

	pub fn virtual_to_real<P: AsRef<Path>>(&self, virtual_path: P) -> Result<PathBuf> {
		for mount in &self.mounts {
			let mount_path = Path::new(&mount.name);
			if let Ok(p) = virtual_path.as_ref().strip_prefix(mount_path) {
				return if p.components().count() == 0 {
					Ok(mount.source.clone())
				} else {
					Ok(mount.source.join(p))
				};
			}
		}
		bail!("Virtual path has no match in VFS")
	}

	pub fn mounts(&self) -> &Vec<Mount> {
		&self.mounts
	}
}

#[derive(Clone)]
pub struct Manager {
	db: DB,
}

impl Manager {
	pub fn new(db: DB) -> Self {
		Self { db }
	}

	pub fn get_vfs(&self) -> Result<VFS> {
		let mount_dirs = self.mount_dirs()?;
		let mounts = mount_dirs.into_iter().map(|p| p.into()).collect();
		Ok(VFS::new(mounts))
	}

	pub fn mount_dirs(&self) -> Result<Vec<MountDir>> {
		use self::mount_points::dsl::*;
		let mut connection = self.db.connect()?;
		let mount_dirs: Vec<MountDir> = mount_points
			.select((source, name))
			.get_results(&mut connection)?;
		Ok(mount_dirs)
	}

	pub fn set_mount_dirs(&self, mount_dirs: &[MountDir]) -> Result<()> {
		let mut connection = self.db.connect()?;
		connection
			.transaction::<_, diesel::result::Error, _>(|connection| {
				use self::mount_points::dsl::*;
				diesel::delete(mount_points).execute(&mut *connection)?;
				diesel::insert_into(mount_points)
					.values(mount_dirs)
					.execute(&mut *connection)?; // TODO https://github.com/diesel-rs/diesel/issues/1822
				Ok(())
			})
			.map_err(anyhow::Error::new)?;
		Ok(())
	}
}

#[cfg(test)]
mod test {

	use super::*;

	#[test]
	fn converts_virtual_to_real() {
		let vfs = VFS::new(vec![Mount {
			name: "root".to_owned(),
			source: Path::new("test_dir").to_owned(),
		}]);
		let real_path: PathBuf = ["test_dir", "somewhere", "something.png"].iter().collect();
		let virtual_path: PathBuf = ["root", "somewhere", "something.png"].iter().collect();
		let converted_path = vfs.virtual_to_real(virtual_path.as_path()).unwrap();
		assert_eq!(converted_path, real_path);
	}

	#[test]
	fn converts_virtual_to_real_top_level() {
		let vfs = VFS::new(vec![Mount {
			name: "root".to_owned(),
			source: Path::new("test_dir").to_owned(),
		}]);
		let real_path = Path::new("test_dir");
		let converted_path = vfs.virtual_to_real(Path::new("root")).unwrap();
		assert_eq!(converted_path, real_path);
	}

	#[test]
	fn converts_real_to_virtual() {
		let vfs = VFS::new(vec![Mount {
			name: "root".to_owned(),
			source: Path::new("test_dir").to_owned(),
		}]);
		let virtual_path: PathBuf = ["root", "somewhere", "something.png"].iter().collect();
		let real_path: PathBuf = ["test_dir", "somewhere", "something.png"].iter().collect();
		let converted_path = vfs.real_to_virtual(real_path.as_path()).unwrap();
		assert_eq!(converted_path, virtual_path);
	}

	#[test]
	fn cleans_path_string() {
		let mut correct_path = path::PathBuf::new();
		if cfg!(target_os = "windows") {
			correct_path.push("C:\\");
		} else {
			correct_path.push("/usr");
		}
		correct_path.push("some");
		correct_path.push("path");

		let tests = if cfg!(target_os = "windows") {
			vec![
				r#"C:/some/path"#,
				r#"C:\some\path"#,
				r#"C:\some\path\"#,
				r#"C:\some\path\\\\"#,
				r#"C:\some/path//"#,
			]
		} else {
			vec![
				r#"/usr/some/path"#,
				r#"/usr\some\path"#,
				r#"/usr\some\path\"#,
				r#"/usr\some\path\\\\"#,
				r#"/usr\some/path//"#,
			]
		};

		for test in tests {
			let mount_dir = MountDir {
				source: test.to_owned(),
				name: "name".to_owned(),
			};
			let mount: Mount = mount_dir.into();
			assert_eq!(mount.source, correct_path);
		}
	}
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs::{self, File};
use std::io::{self, BufReader, Read, Stderr, StdinLock, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -v -E -T

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -v -E

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -v -T

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::new()
        };
        for arg in env::args().skip(1) {
            if arg.starts_with('-') && &arg != "-" {
                match arg.as_str() {
                    "-h" | "--help" => {
                        stdout.write(MAN_PAGE.as_bytes()).try(stderr);
                        stdout.flush().try(stderr);
                        exit(0);
                    }
                    "-A" | "--show-all" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                        cat.show_tabs = true;
                    },
                    "-b" | "--number-nonblank" => {
                        cat.number_nonblank = true;
                        cat.number = false;
                    },
                    "-e" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                    },
                    "-E" | "--show-ends" => cat.show_ends = true,
                    "-n" | "--number" => {
                        cat.number = true;
                        cat.number_nonblank = false;
                    },
                    "-s" | "--squeeze-blank" => cat.squeeze_blank = true,
                    "-t" => {
                        cat.show_nonprinting = true;
                        cat.show_tabs = true;
                    },
                    "-T" => cat.show_tabs = true,
                    "-v" | "--show-nonprinting" => {
                        cat.show_nonprinting = true;
                    },
                    _ => {
                        stderr.write(b"invalid option -- '").try(stderr);
                        stderr.write(arg.as_bytes()).try(stderr);
                        stderr.write(b"'\nTry 'cat --help' for more information.\n").try(stderr);
                        stderr.flush().try(stderr);
                        exit(1);
                    }
                }
            } else {
                cat.paths.push(arg);
            }
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let stdin = &mut stdin.lock();

        if self.paths.is_empty() {
            io::copy(stdin, stdout).try(stderr);
        } else {
            let flags_enabled = self.number || self.number_nonblank || self.show_ends || self.show_tabs ||
                                self.squeeze_blank || self.show_nonprinting;
            let line_count = &mut 0usize;
            for path in &self.paths {
                if flags_enabled && path == "-" {
                    self.cat_stdin(line_count, stdin, stdout, stderr);
                } else if flags_enabled {
                    let file = match fs::File::open(&path) {
                        Ok(file) => file,
                        Err(message) => {
                            stderr.write(&path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);;
                            continue
                        }
                    };

                    self.cat_file(&file, line_count, stdout, stderr);
                } else if path == "-" {
                    io::copy(stdin, stdout).try(stderr);
                } else {
                    let file = &mut match fs::File::open(&path) {
                        Ok(file) => file,
                        Err(message) => {
                            stderr.write(&path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);;
                            continue
                        }
                    };

                    io::copy(file, stdout).try(stderr);
                }
            }
        }
        self.exit_status.get()
    }

    /// Cats a file based on the flag arguments given to the program.
    fn cat_file(&self, file: &File, line_count: &mut usize, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in BufReader::new(file).bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }

    /// Cats stdin based on the flag arguments given to the program.
    fn cat_stdin(&self, line_count: &mut usize, stdin: &mut StdinLock, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in stdin.bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }
}

/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<commit_msg>Accept Flags With No Arguments<commit_after>#![deny(warnings)]

extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs::{self, File};
use std::io::{self, BufReader, Read, Stderr, StdinLock, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -v -E -T

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -v -E

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -v -T

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::new()
        };
        for arg in env::args().skip(1) {
            if arg.starts_with('-') && &arg != "-" {
                match arg.as_str() {
                    "-h" | "--help" => {
                        stdout.write(MAN_PAGE.as_bytes()).try(stderr);
                        stdout.flush().try(stderr);
                        exit(0);
                    }
                    "-A" | "--show-all" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                        cat.show_tabs = true;
                    },
                    "-b" | "--number-nonblank" => {
                        cat.number_nonblank = true;
                        cat.number = false;
                    },
                    "-e" => {
                        cat.show_nonprinting = true;
                        cat.show_ends = true;
                    },
                    "-E" | "--show-ends" => cat.show_ends = true,
                    "-n" | "--number" => {
                        cat.number = true;
                        cat.number_nonblank = false;
                    },
                    "-s" | "--squeeze-blank" => cat.squeeze_blank = true,
                    "-t" => {
                        cat.show_nonprinting = true;
                        cat.show_tabs = true;
                    },
                    "-T" => cat.show_tabs = true,
                    "-v" | "--show-nonprinting" => {
                        cat.show_nonprinting = true;
                    },
                    _ => {
                        stderr.write(b"invalid option -- '").try(stderr);
                        stderr.write(arg.as_bytes()).try(stderr);
                        stderr.write(b"'\nTry 'cat --help' for more information.\n").try(stderr);
                        stderr.flush().try(stderr);
                        exit(1);
                    }
                }
            } else {
                cat.paths.push(arg);
            }
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let stdin = &mut stdin.lock();
        let line_count = &mut 0usize;
        let flags_enabled = self.number || self.number_nonblank || self.show_ends || self.show_tabs ||
                            self.squeeze_blank || self.show_nonprinting;

        if self.paths.is_empty() && flags_enabled {
            self.cat_stdin(line_count, stdin, stdout, stderr);
        } else if self.paths.is_empty() {
            io::copy(stdin, stdout).try(stderr);
        } else {
            for path in &self.paths {
                if flags_enabled && path == "-" {
                    self.cat_stdin(line_count, stdin, stdout, stderr);
                } else if flags_enabled {
                    let file = match fs::File::open(&path) {
                        Ok(file) => file,
                        Err(message) => {
                            stderr.write(&path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);;
                            continue
                        }
                    };

                    self.cat_file(&file, line_count, stdout, stderr);
                } else if path == "-" {
                    io::copy(stdin, stdout).try(stderr);
                } else {
                    let file = &mut match fs::File::open(&path) {
                        Ok(file) => file,
                        Err(message) => {
                            stderr.write(&path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);;
                            continue
                        }
                    };

                    io::copy(file, stdout).try(stderr);
                }
            }
        }
        self.exit_status.get()
    }

    /// Cats a file based on the flag arguments given to the program.
    fn cat_file(&self, file: &File, line_count: &mut usize, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in BufReader::new(file).bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }

    /// Cats stdin based on the flag arguments given to the program.
    fn cat_stdin(&self, line_count: &mut usize, stdin: &mut StdinLock, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in stdin.bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }
}

/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<|endoftext|>


<commit_before>use collections::slice::{mod, AsSlice, SlicePrelude};
use collections::vec::{mod, Vec};
use core::clone::Clone;
use core::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering};
use core::fmt;
use core::mem;
use core::iter::{mod, order, Extend, AdditiveIterator, Iterator};
use core::option::{mod, Some, None, Option};
use core::result::{Ok, Err};

use iobuf::Iobuf;

/// A span over potentially many Iobufs. This is useful as a "string" type where
/// the contents of the string can come from multiple IObufs, and you want to
/// avoid copying the buffer contents unnecessarily.
///
/// As an optimization, pushing an Iobuf that points to data immediately after
/// the range represented by the last Iobuf pushed will result in just expanding
/// the held Iobuf's range. This prevents allocating lots of unnecessary
/// intermediate buffers, while still maintaining the illusion of "pushing lots
/// of buffers" while incrementally parsing.
///
/// A `BufSpan` is internally represented as either an `Iobuf` or a `Vec<Iobuf>`,
/// depending on how many different buffers were used.
pub enum BufSpan<Buf> {
  /// A span over 0 bytes.
  Empty,
  /// A single span over one range.
  One (Buf),
  /// A span over several backing Iobufs.
  Many(Vec<Buf>),
}

impl<Buf: Iobuf> Clone for BufSpan<Buf> {
  #[inline]
  fn clone(&self) -> BufSpan<Buf> {
    match *self {
      Empty       => Empty,
      One(ref b)  => One ((*b).clone()),
      Many(ref v) => Many((*v).clone()),
    }
  }
}

impl<Buf: Iobuf> fmt::Show for BufSpan<Buf> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let mut first_time = true;

    for b in self.iter() {
      if !first_time {
        try!(write!(f, "\n"));
      }

      try!(b.fmt(f));

      first_time = false;
    }

    Ok(())
  }
}

impl<Buf: Iobuf> Extend<Buf> for BufSpan<Buf> {
  fn extend<T: Iterator<Buf>>(&mut self, mut iterator: T) {
    for x in iterator {
      self.push(x);
    }
  }
}

impl<Buf: Iobuf> BufSpan<Buf> {
  /// Creates a new, empty `Bufspan`.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf};
  ///
  /// let s: BufSpan<ROIobuf<'static>> = BufSpan::new();
  /// assert!(s.is_empty());
  /// ```
  #[inline]
  pub fn new() -> BufSpan<Buf> {
    Empty
  }

  /// Creates a new `BufSpan` from an Iobuf.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf};
  ///
  /// let s = BufSpan::from_buf(ROIobuf::from_slice(b"hello"));
  /// assert_eq!(s.iter().count(), 1);
  /// assert_eq!(s.count_bytes(), 5);
  /// ```
  #[inline]
  pub fn from_buf(b: Buf) -> BufSpan<Buf> {
    if b.is_empty() { Empty } else { One(b) }
  }

  /// Returns `true` iff the span is over an empty range.
  ///
  /// ```
  /// use iobuf::{BufSpan, Iobuf, ROIobuf};
  ///
  /// let mut s = BufSpan::new();
  ///
  /// assert!(s.is_empty());
  ///
  /// s.push(ROIobuf::from_str(""));
  /// assert!(s.is_empty());
  ///
  /// s.push(ROIobuf::from_str("hello, world!"));
  /// assert!(!s.is_empty());
  /// ```
  #[inline]
  pub fn is_empty(&self) -> bool {
    match *self {
      Empty => true,
      _     => false,
    }
  }

  /// The fast path during pushing -- either fills in the first buffer, or
  /// extends an existing one.
  ///
  /// Returns `None` if the fast path was taken and nothing more needs to be
  /// done. Returns `Some` if we need to do a slow push.
  fn try_to_extend(&mut self, b: Buf) -> Option<Buf> {
    let len = b.len();
    if len == 0 { return None; }

    match *self {
      Empty => {},
      One(ref mut b0) => {
        unsafe {
          if b0.is_extended_by(&b) {
            b0.unsafe_extend(len);
            return None;
          } else {
            return Some(b); // Upgrade the `One` into a `Many` in the slow path.
          }
        }
      }
      Many(ref mut v) => {
        // I wish this wouldn't unwind, and just abort... :(
        let last = v.last_mut().unwrap();
        unsafe {
          if last.is_extended_by(&b) {
            last.unsafe_extend(len);
            return None;
          } else {
            return Some(b);
          }
        }
      }
    }

    // Handle this case in the fast path, instead of leaving it for slow_push
    // to clean up.
    *self = One(b);
    None
  }

  /// Appends a buffer to a `BufSpan`. If the buffer is an extension of the
  /// previously pushed buffer, the range will be extended. Otherwise, the new
  /// non-extension buffer will be added to the end of a vector.
  ///
  /// ```
  /// use iobuf::{BufSpan, Iobuf, ROIobuf};
  ///
  /// let mut s = BufSpan::new();
  ///
  /// s.push(ROIobuf::from_str("he"));
  /// s.push(ROIobuf::from_str("llo"));
  ///
  /// assert_eq!(s.count_bytes() as uint, "hello".len());
  /// assert_eq!(s.iter().count(), 2);
  ///
  /// let mut b0 = ROIobuf::from_str(" world");
  /// let mut b1 = b0.clone();
  ///
  /// b0.resize(2).unwrap();
  /// b1.advance(2).unwrap();
  ///
  /// s.push(b0);
  /// s.push(b1);
  ///
  /// // b0 and b1 are immediately after each other, and from the same buffer,
  /// // so get merged into one Iobuf.
  /// assert_eq!(s.count_bytes() as uint, "hello world".len());
  /// assert_eq!(s.iter().count(), 3);
  /// ```
  pub fn push(&mut self, b: Buf) {
    match self.try_to_extend(b) {
      None    => {},
      Some(b) => self.slow_push(b),
    }
  }

  /// The slow path during a push. This is only taken if a `BufSpan` must span
  /// multiple backing buffers.
  #[cold]
  fn slow_push(&mut self, b: Buf) {
    let this = mem::replace(self, Empty);
    *self =
      match this {
        Empty   => One(b),
        One(b0) => {
          let mut v = Vec::with_capacity(2);
          v.push(b0);
          v.push(b);
          Many(v)
        },
        Many(mut bs) => { bs.push(b); Many(bs) }
      };
  }

  /// Returns an iterator over references to the buffers inside the `BufSpan`.
  #[inline]
  pub fn iter<'a>(&'a self) -> SpanIter<'a, Buf> {
    match *self {
      Empty       => Opt(None.into_iter()),
      One (ref b) => Opt(Some(b).into_iter()),
      Many(ref v) => Lot(v.as_slice().iter()),
    }
  }

  /// Returns a moving iterator over the buffers inside the `BufSpan`.
  #[inline]
  pub fn into_iter(self) -> SpanMoveIter<Buf> {
    match self {
      Empty   => MoveOpt(None.into_iter()),
      One (b) => MoveOpt(Some(b).into_iter()),
      Many(v) => MoveLot(v.into_iter()),
    }
  }

  /// Returns an iterator over the bytes in the `BufSpan`.
  #[inline]
  pub fn iter_bytes<'a>(&'a self) -> ByteIter<'a, Buf> {
    self.iter()
        .flat_map(|buf| unsafe { buf.as_window_slice().iter() })
        .map(|&b| b)
  }

  /// Returns `true` iff the bytes in this `BufSpan` are the same as the bytes
  /// in the other `BufSpan`.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf, RWIobuf};
  ///
  /// let a = BufSpan::from_buf(ROIobuf::from_str("hello"));
  /// let b = BufSpan::from_buf(RWIobuf::from_string("hello".into_string()));
  ///
  /// assert!(a.byte_equal(&b));
  ///
  /// let mut c = BufSpan::from_buf(ROIobuf::from_str("hel"));
  /// c.push(ROIobuf::from_str("lo"));
  ///
  /// assert!(a.byte_equal(&c)); assert!(c.byte_equal(&a));
  ///
  /// let d = BufSpan::from_buf(ROIobuf::from_str("helo"));
  /// assert!(!a.byte_equal(&d));
  /// ```
  #[inline]
  pub fn byte_equal<Buf2: Iobuf>(&self, other: &BufSpan<Buf2>) -> bool {
    self.count_bytes() == other.count_bytes()
    && self.iter_bytes().zip(other.iter_bytes()).all(|(a, b)| a == b)
  }

  /// A more efficient version of byte_equal, specialized to work exclusively on
  /// slices.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf};
  ///
  /// let a = BufSpan::from_buf(ROIobuf::from_str("hello"));
  ///
  /// assert!(a.byte_equal_slice(b"hello"));
  /// assert!(!a.byte_equal_slice(b"helo"));
  /// ```
  #[inline]
  pub fn byte_equal_slice(&self, other: &[u8]) -> bool {
    self.count_bytes() as uint == other.len()
    && self.iter_bytes().zip(other.iter()).all(|(a, &b)| a == b)
  }

  /// Counts the number of bytes this `BufSpan` is over. This is
  /// `O(self.iter().len())`.
  #[inline]
  pub fn count_bytes(&self) -> u32 {
    // `self.iter().map(|b| b.len()).sum()` would be shorter, but I like to
    // specialize for the much more common case of empty or singular `BufSpan`s.
    match *self {
      Empty       => 0,
      One (ref b) => b.len(),
      Many(ref v) => v.iter().map(|b| b.len()).sum(),
    }
  }

  /// Extends this span to include the range denoted by another span.
  #[inline]
  pub fn append(&mut self, other: BufSpan<Buf>) {
    if self.is_empty() {
      *self = other;
    } else {
      self.extend(other.into_iter())
    }
  }
}

impl<Buf: Iobuf> PartialEq for BufSpan<Buf> {
    fn eq(&self, other: &BufSpan<Buf>) -> bool {
        self.byte_equal(other)
    }
}

impl<Buf: Iobuf> Eq for BufSpan<Buf> {}

impl<Buf: Iobuf> PartialOrd for BufSpan<Buf> {
    fn partial_cmp(&self, other: &BufSpan<Buf>) -> Option<Ordering> {
        order::partial_cmp(self.iter_bytes(), other.iter_bytes())
    }
}

impl<Buf: Iobuf> Ord for BufSpan<Buf> {
    fn cmp(&self, other: &BufSpan<Buf>) -> Ordering {
        order::cmp(self.iter_bytes(), other.iter_bytes())
    }
}

/// An iterator over the bytes in a `BufSpan`.
pub type ByteIter<'a, Buf> =
  iter::Map<'static, &'a u8, u8,
    iter::FlatMap<'static, &'a Buf,
      SpanIter<'a, Buf>,
      slice::Items<'a, u8>>>;

/// An iterator over references to buffers inside a `BufSpan`.
pub enum SpanIter<'a, Buf: 'a> {
  /// An optional item to iterate over.
  Opt(option::Item<&'a Buf>),
  /// A lot of items to iterate over.
  Lot(slice::Items<'a, Buf>),
}

impl<'a, Buf: Iobuf> Iterator<&'a Buf> for SpanIter<'a, Buf> {
  #[inline(always)]
  fn next(&mut self) -> Option<&'a Buf> {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match *self {
      Opt(ref mut iter) => iter.next(),
      Lot(ref mut iter) => iter.next(),
    }
  }

  #[inline(always)]
  fn size_hint(&self) -> (uint, Option<uint>) {
    match *self {
      Opt(ref iter) => iter.size_hint(),
      Lot(ref iter) => iter.size_hint(),
    }
  }
}

/// A moving iterator over buffers inside a `BufSpan`.
pub enum SpanMoveIter<Buf> {
  /// An optional item to iterate over.
  MoveOpt(option::Item<Buf>),
  /// A lot of items to iterate over.
  MoveLot(vec::MoveItems<Buf>),
}

impl<Buf: Iobuf> Iterator<Buf> for SpanMoveIter<Buf> {
  #[inline(always)]
  fn next(&mut self) -> Option<Buf> {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match *self {
      MoveOpt(ref mut iter) => iter.next(),
      MoveLot(ref mut iter) => iter.next(),
    }
  }

  #[inline(always)]
  fn size_hint(&self) -> (uint, Option<uint>) {
    match *self {
      MoveOpt(ref iter) => iter.size_hint(),
      MoveLot(ref iter) => iter.size_hint(),
    }
  }
}
<commit_msg>allow spans to be reversed<commit_after>use collections::slice::{mod, AsSlice, SlicePrelude};
use collections::vec::{mod, Vec};
use core::clone::Clone;
use core::cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering};
use core::fmt;
use core::mem;
use core::iter::{mod, order, Extend, AdditiveIterator, Iterator};
use core::iter::{DoubleEndedIterator, ExactSize, RandomAccessIterator};
use core::option::{mod, Some, None, Option};
use core::result::{Ok, Err};

use iobuf::Iobuf;

/// A span over potentially many Iobufs. This is useful as a "string" type where
/// the contents of the string can come from multiple IObufs, and you want to
/// avoid copying the buffer contents unnecessarily.
///
/// As an optimization, pushing an Iobuf that points to data immediately after
/// the range represented by the last Iobuf pushed will result in just expanding
/// the held Iobuf's range. This prevents allocating lots of unnecessary
/// intermediate buffers, while still maintaining the illusion of "pushing lots
/// of buffers" while incrementally parsing.
///
/// A `BufSpan` is internally represented as either an `Iobuf` or a `Vec<Iobuf>`,
/// depending on how many different buffers were used.
pub enum BufSpan<Buf> {
  /// A span over 0 bytes.
  Empty,
  /// A single span over one range.
  One (Buf),
  /// A span over several backing Iobufs.
  Many(Vec<Buf>),
}

impl<Buf: Iobuf> Clone for BufSpan<Buf> {
  #[inline]
  fn clone(&self) -> BufSpan<Buf> {
    match *self {
      Empty       => Empty,
      One(ref b)  => One ((*b).clone()),
      Many(ref v) => Many((*v).clone()),
    }
  }
}

impl<Buf: Iobuf> fmt::Show for BufSpan<Buf> {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    let mut first_time = true;

    for b in self.iter() {
      if !first_time {
        try!(write!(f, "\n"));
      }

      try!(b.fmt(f));

      first_time = false;
    }

    Ok(())
  }
}

impl<Buf: Iobuf> Extend<Buf> for BufSpan<Buf> {
  fn extend<T: Iterator<Buf>>(&mut self, mut iterator: T) {
    for x in iterator {
      self.push(x);
    }
  }
}

impl<Buf: Iobuf> BufSpan<Buf> {
  /// Creates a new, empty `Bufspan`.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf};
  ///
  /// let s: BufSpan<ROIobuf<'static>> = BufSpan::new();
  /// assert!(s.is_empty());
  /// ```
  #[inline]
  pub fn new() -> BufSpan<Buf> {
    Empty
  }

  /// Creates a new `BufSpan` from an Iobuf.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf};
  ///
  /// let s = BufSpan::from_buf(ROIobuf::from_slice(b"hello"));
  /// assert_eq!(s.iter().count(), 1);
  /// assert_eq!(s.count_bytes(), 5);
  /// ```
  #[inline]
  pub fn from_buf(b: Buf) -> BufSpan<Buf> {
    if b.is_empty() { Empty } else { One(b) }
  }

  /// Returns `true` iff the span is over an empty range.
  ///
  /// ```
  /// use iobuf::{BufSpan, Iobuf, ROIobuf};
  ///
  /// let mut s = BufSpan::new();
  ///
  /// assert!(s.is_empty());
  ///
  /// s.push(ROIobuf::from_str(""));
  /// assert!(s.is_empty());
  ///
  /// s.push(ROIobuf::from_str("hello, world!"));
  /// assert!(!s.is_empty());
  /// ```
  #[inline]
  pub fn is_empty(&self) -> bool {
    match *self {
      Empty => true,
      _     => false,
    }
  }

  /// The fast path during pushing -- either fills in the first buffer, or
  /// extends an existing one.
  ///
  /// Returns `None` if the fast path was taken and nothing more needs to be
  /// done. Returns `Some` if we need to do a slow push.
  fn try_to_extend(&mut self, b: Buf) -> Option<Buf> {
    let len = b.len();
    if len == 0 { return None; }

    match *self {
      Empty => {},
      One(ref mut b0) => {
        unsafe {
          if b0.is_extended_by(&b) {
            b0.unsafe_extend(len);
            return None;
          } else {
            return Some(b); // Upgrade the `One` into a `Many` in the slow path.
          }
        }
      }
      Many(ref mut v) => {
        // I wish this wouldn't unwind, and just abort... :(
        let last = v.last_mut().unwrap();
        unsafe {
          if last.is_extended_by(&b) {
            last.unsafe_extend(len);
            return None;
          } else {
            return Some(b);
          }
        }
      }
    }

    // Handle this case in the fast path, instead of leaving it for slow_push
    // to clean up.
    *self = One(b);
    None
  }

  /// Appends a buffer to a `BufSpan`. If the buffer is an extension of the
  /// previously pushed buffer, the range will be extended. Otherwise, the new
  /// non-extension buffer will be added to the end of a vector.
  ///
  /// ```
  /// use iobuf::{BufSpan, Iobuf, ROIobuf};
  ///
  /// let mut s = BufSpan::new();
  ///
  /// s.push(ROIobuf::from_str("he"));
  /// s.push(ROIobuf::from_str("llo"));
  ///
  /// assert_eq!(s.count_bytes() as uint, "hello".len());
  /// assert_eq!(s.iter().count(), 2);
  ///
  /// let mut b0 = ROIobuf::from_str(" world");
  /// let mut b1 = b0.clone();
  ///
  /// b0.resize(2).unwrap();
  /// b1.advance(2).unwrap();
  ///
  /// s.push(b0);
  /// s.push(b1);
  ///
  /// // b0 and b1 are immediately after each other, and from the same buffer,
  /// // so get merged into one Iobuf.
  /// assert_eq!(s.count_bytes() as uint, "hello world".len());
  /// assert_eq!(s.iter().count(), 3);
  /// ```
  pub fn push(&mut self, b: Buf) {
    match self.try_to_extend(b) {
      None    => {},
      Some(b) => self.slow_push(b),
    }
  }

  /// The slow path during a push. This is only taken if a `BufSpan` must span
  /// multiple backing buffers.
  #[cold]
  fn slow_push(&mut self, b: Buf) {
    let this = mem::replace(self, Empty);
    *self =
      match this {
        Empty   => One(b),
        One(b0) => {
          let mut v = Vec::with_capacity(2);
          v.push(b0);
          v.push(b);
          Many(v)
        },
        Many(mut bs) => { bs.push(b); Many(bs) }
      };
  }

  /// Returns an iterator over references to the buffers inside the `BufSpan`.
  #[inline]
  pub fn iter<'a>(&'a self) -> SpanIter<'a, Buf> {
    match *self {
      Empty       => Opt(None.into_iter()),
      One (ref b) => Opt(Some(b).into_iter()),
      Many(ref v) => Lot(v.as_slice().iter()),
    }
  }

  /// Returns a moving iterator over the buffers inside the `BufSpan`.
  #[inline]
  pub fn into_iter(self) -> SpanMoveIter<Buf> {
    match self {
      Empty   => MoveOpt(None.into_iter()),
      One (b) => MoveOpt(Some(b).into_iter()),
      Many(v) => MoveLot(v.into_iter()),
    }
  }

  /// Returns an iterator over the bytes in the `BufSpan`.
  #[inline]
  pub fn iter_bytes<'a>(&'a self) -> ByteIter<'a, Buf> {
    self.iter()
        .flat_map(|buf| unsafe { buf.as_window_slice().iter() })
        .map(|&b| b)
  }

  /// Returns `true` iff the bytes in this `BufSpan` are the same as the bytes
  /// in the other `BufSpan`.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf, RWIobuf};
  ///
  /// let a = BufSpan::from_buf(ROIobuf::from_str("hello"));
  /// let b = BufSpan::from_buf(RWIobuf::from_string("hello".into_string()));
  ///
  /// assert!(a.byte_equal(&b));
  ///
  /// let mut c = BufSpan::from_buf(ROIobuf::from_str("hel"));
  /// c.push(ROIobuf::from_str("lo"));
  ///
  /// assert!(a.byte_equal(&c)); assert!(c.byte_equal(&a));
  ///
  /// let d = BufSpan::from_buf(ROIobuf::from_str("helo"));
  /// assert!(!a.byte_equal(&d));
  /// ```
  #[inline]
  pub fn byte_equal<Buf2: Iobuf>(&self, other: &BufSpan<Buf2>) -> bool {
    self.count_bytes() == other.count_bytes()
    && self.iter_bytes().zip(other.iter_bytes()).all(|(a, b)| a == b)
  }

  /// A more efficient version of byte_equal, specialized to work exclusively on
  /// slices.
  ///
  /// ```
  /// use iobuf::{BufSpan, ROIobuf};
  ///
  /// let a = BufSpan::from_buf(ROIobuf::from_str("hello"));
  ///
  /// assert!(a.byte_equal_slice(b"hello"));
  /// assert!(!a.byte_equal_slice(b"helo"));
  /// ```
  #[inline]
  pub fn byte_equal_slice(&self, other: &[u8]) -> bool {
    self.count_bytes() as uint == other.len()
    && self.iter_bytes().zip(other.iter()).all(|(a, &b)| a == b)
  }

  /// Counts the number of bytes this `BufSpan` is over. This is
  /// `O(self.iter().len())`.
  #[inline]
  pub fn count_bytes(&self) -> u32 {
    // `self.iter().map(|b| b.len()).sum()` would be shorter, but I like to
    // specialize for the much more common case of empty or singular `BufSpan`s.
    match *self {
      Empty       => 0,
      One (ref b) => b.len(),
      Many(ref v) => v.iter().map(|b| b.len()).sum(),
    }
  }

  /// Extends this span to include the range denoted by another span.
  #[inline]
  pub fn append(&mut self, other: BufSpan<Buf>) {
    if self.is_empty() {
      *self = other;
    } else {
      self.extend(other.into_iter())
    }
  }
}

impl<Buf: Iobuf> PartialEq for BufSpan<Buf> {
    fn eq(&self, other: &BufSpan<Buf>) -> bool {
        self.byte_equal(other)
    }
}

impl<Buf: Iobuf> Eq for BufSpan<Buf> {}

impl<Buf: Iobuf> PartialOrd for BufSpan<Buf> {
    fn partial_cmp(&self, other: &BufSpan<Buf>) -> Option<Ordering> {
        order::partial_cmp(self.iter_bytes(), other.iter_bytes())
    }
}

impl<Buf: Iobuf> Ord for BufSpan<Buf> {
    fn cmp(&self, other: &BufSpan<Buf>) -> Ordering {
        order::cmp(self.iter_bytes(), other.iter_bytes())
    }
}

/// An iterator over the bytes in a `BufSpan`.
pub type ByteIter<'a, Buf> =
  iter::Map<'static, &'a u8, u8,
    iter::FlatMap<'static, &'a Buf,
      SpanIter<'a, Buf>,
      slice::Items<'a, u8>>>;

/// An iterator over references to buffers inside a `BufSpan`.
pub enum SpanIter<'a, Buf: 'a> {
  /// An optional item to iterate over.
  Opt(option::Item<&'a Buf>),
  /// A lot of items to iterate over.
  Lot(slice::Items<'a, Buf>),
}

impl<'a, Buf: Iobuf> Clone for SpanIter<'a, Buf> {
  #[inline(always)]
  fn clone(&self) -> SpanIter<'a, Buf> {
    match *self {
      Opt(ref iter) => Opt((*iter).clone()),
      Lot(ref iter) => Lot((*iter).clone()),
    }
  }
}

impl<'a, Buf: Iobuf> Iterator<&'a Buf> for SpanIter<'a, Buf> {
  #[inline(always)]
  fn next(&mut self) -> Option<&'a Buf> {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match *self {
      Opt(ref mut iter) => iter.next(),
      Lot(ref mut iter) => iter.next(),
    }
  }

  #[inline(always)]
  fn size_hint(&self) -> (uint, Option<uint>) {
    match *self {
      Opt(ref iter) => iter.size_hint(),
      Lot(ref iter) => iter.size_hint(),
    }
  }
}

impl<'a, Buf: Iobuf> DoubleEndedIterator<&'a Buf> for SpanIter<'a, Buf> {
  #[inline(always)]
  fn next_back(&mut self) -> Option<&'a Buf> {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match *self {
      Opt(ref mut iter) => iter.next_back(),
      Lot(ref mut iter) => iter.next_back(),
    }
  }
}

impl<'a, Buf: Iobuf> ExactSize<&'a Buf> for SpanIter<'a, Buf> {}

impl<'a, Buf: Iobuf> RandomAccessIterator<&'a Buf> for SpanIter<'a, Buf> {
  #[inline(always)]
  fn indexable(&self) -> uint {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match *self {
      Opt(   _    ) => { let (exact, _) = self.size_hint(); exact },
      Lot(ref iter) => iter.indexable(),
    }
  }

  #[inline(always)]
  fn idx(&mut self, index: uint) -> Option<&'a Buf> {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match (*self, index) {
      (Lot(ref mut iter), index) => iter.idx(index),
      (Opt(ref mut iter), 0    ) => iter.next(),
      (Opt(     _      ), index) => panic!("Option index out of range: {}", index),
    }
  }
}

/// A moving iterator over buffers inside a `BufSpan`.
pub enum SpanMoveIter<Buf> {
  /// An optional item to iterate over.
  MoveOpt(option::Item<Buf>),
  /// A lot of items to iterate over.
  MoveLot(vec::MoveItems<Buf>),
}

impl<Buf: Iobuf> Iterator<Buf> for SpanMoveIter<Buf> {
  #[inline(always)]
  fn next(&mut self) -> Option<Buf> {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match *self {
      MoveOpt(ref mut iter) => iter.next(),
      MoveLot(ref mut iter) => iter.next(),
    }
  }

  #[inline(always)]
  fn size_hint(&self) -> (uint, Option<uint>) {
    match *self {
      MoveOpt(ref iter) => iter.size_hint(),
      MoveLot(ref iter) => iter.size_hint(),
    }
  }
}

impl<Buf: Iobuf> DoubleEndedIterator<Buf> for SpanMoveIter<Buf> {
  #[inline(always)]
  fn next_back(&mut self) -> Option<Buf> {
    // I'm couting on this match getting lifted out of the loop with
    // loop-invariant code motion.
    match *self {
      MoveOpt(ref mut iter) => iter.next_back(),
      MoveLot(ref mut iter) => iter.next_back(),
    }
  }
}

impl<Buf: Iobuf> ExactSize<Buf> for SpanMoveIter<Buf> {}
<|endoftext|>


<commit_before>use std::{fs, io, path, string::ToString};

use syntax::print::pprust;
use syntax::{ast, source_map, visit};

use tree::{Tree, Visibility};

pub struct Config {
    pub include_orphans: bool,
    pub ignored_files: Vec<path::PathBuf>,
}

pub struct Builder<'a> {
    tree: Tree,
    path: Vec<String>,
    config: Config,
    source_map: &'a source_map::SourceMap,
}

impl<'a> Builder<'a> {
    pub fn new(config: Config, crate_name: String, source_map: &'a source_map::SourceMap) -> Self {
        let tree = Tree::new_crate(crate_name);
        Builder {
            tree,
            path: vec![],
            config,
            source_map,
        }
    }

    pub fn tree(&self) -> &Tree {
        &self.tree
    }

    fn find_orphan_candidates(
        path: &[String],
        ignored_paths: &[path::PathBuf],
    ) -> Result<Vec<String>, io::Error> {
        let mut dir_path = "./src/".to_string();
        for name in path.iter() {
            dir_path.push_str(name);
            dir_path.push_str("/");
        }
        fn is_mod(entry: &fs::DirEntry) -> bool {
            let path = entry.path();
            if path.is_file() {
                if path.extension().map_or(false, |ext| ext == "rs") {
                    return file_name(entry)
                        .map_or(false, |s| (s != "mod") && (s != "lib") && (s != "main"));
                }
            } else if path.join("mod.rs").exists() {
                return true;
            }
            false
        }
        fn file_name(entry: &fs::DirEntry) -> Option<String> {
            entry
                .path()
                .file_stem()
                .and_then(|s| s.to_str())
                .map(ToString::to_string)
        }
        Ok(try!(fs::read_dir(&dir_path))
            .filter_map(|e| e.ok())
            .filter(is_mod)
            .filter(|e| !ignored_paths.contains(&e.path()))
            .filter_map(|e| file_name(&e))
            .collect::<Vec<_>>())
    }

    fn sanitize_condition(condition: String) -> String {
        let words: Vec<&str> = condition.split_whitespace().collect();
        words.join(" ")
    }
}

impl<'a> visit::Visitor<'a> for Builder<'a> {
    fn visit_item(&mut self, item: &'a ast::Item) {
        let condition = item
            .attrs
            .iter()
            .find(|attr| attr.check_name("cfg"))
            .map(|attr| {
                self.source_map
                    .span_to_snippet(attr.span)
                    .map(Builder::sanitize_condition)
                    .unwrap_or_else(|_| "".to_string())
            });

        match item.node {
            ast::ItemKind::Mod(_) => {
                let name = item.ident.to_string();
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };
                    tree.insert(Tree::new_module(name.clone(), visibility, condition));
                }
                self.path.push(name);
                visit::walk_item(self, item);
                let _ = self.path.pop();
            }
            ast::ItemKind::Use(ref use_tree) => {
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };

                    add_use_tree(tree, visibility, "", use_tree);
                }

                visit::walk_item(self, item);
            }
            _ => {
                visit::walk_item(self, item);
            }
        }
    }

    fn visit_mod(
        &mut self,
        m: &'a ast::Mod,
        _s: source_map::Span,
        _attrs: &[ast::Attribute],
        _n: ast::NodeId,
    ) {
        visit::walk_mod(self, m);
        if !self.config.include_orphans {
            return;
        }
        let path = &self.path;
        if let Ok(candidates) = Builder::find_orphan_candidates(path, &self.config.ignored_files) {
            let tree = self.tree.subtree_at_path(path).unwrap();
            let names: Vec<_> = tree.subtree_names();
            for candidate in candidates {
                if !names.contains(&candidate) {
                    tree.insert(Tree::new_orphan(candidate.clone()));
                }
            }
        }
    }

    fn visit_mac(&mut self, mac: &ast::Mac) {
        visit::walk_mac(self, mac);
    }
}

fn add_use_tree(tree: &mut Tree, visibility: Visibility, prefix: &str, use_tree: &ast::UseTree) {
    let prefix = if prefix.is_empty() {
        String::new()
    } else {
        prefix.to_string()
    };

    match use_tree.kind {
        ast::UseTreeKind::Simple(_, ..) => {
            let path = pprust::path_to_string(&use_tree.prefix);
            if path != "self" {
                tree.insert_use((visibility, prefix + &path));
            } else {
                let mut prefix = prefix;
                let new_len = prefix.len() - 2;
                prefix.truncate(new_len);
                tree.insert_use((visibility, prefix));
            }
        }
        ast::UseTreeKind::Glob => {
            let mut use_path = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            use_path += "*";
            tree.insert_use((visibility, use_path));
        }
        ast::UseTreeKind::Nested(ref items) => {
            let mut prefix = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            for (sub_tree, _) in items {
                add_use_tree(tree, visibility, &prefix, sub_tree);
            }
        }
    }
}
<commit_msg>Remove 2nd unnecessary closure (clippy)<commit_after>use std::{ffi::OsStr, fs, io, path, string::ToString};

use syntax::print::pprust;
use syntax::{ast, source_map, visit};

use tree::{Tree, Visibility};

pub struct Config {
    pub include_orphans: bool,
    pub ignored_files: Vec<path::PathBuf>,
}

pub struct Builder<'a> {
    tree: Tree,
    path: Vec<String>,
    config: Config,
    source_map: &'a source_map::SourceMap,
}

impl<'a> Builder<'a> {
    pub fn new(config: Config, crate_name: String, source_map: &'a source_map::SourceMap) -> Self {
        let tree = Tree::new_crate(crate_name);
        Builder {
            tree,
            path: vec![],
            config,
            source_map,
        }
    }

    pub fn tree(&self) -> &Tree {
        &self.tree
    }

    fn find_orphan_candidates(
        path: &[String],
        ignored_paths: &[path::PathBuf],
    ) -> Result<Vec<String>, io::Error> {
        let mut dir_path = "./src/".to_string();
        for name in path.iter() {
            dir_path.push_str(name);
            dir_path.push_str("/");
        }
        fn is_mod(entry: &fs::DirEntry) -> bool {
            let path = entry.path();
            if path.is_file() {
                if path.extension().map_or(false, |ext| ext == "rs") {
                    return file_name(entry)
                        .map_or(false, |s| (s != "mod") && (s != "lib") && (s != "main"));
                }
            } else if path.join("mod.rs").exists() {
                return true;
            }
            false
        }
        fn file_name(entry: &fs::DirEntry) -> Option<String> {
            entry
                .path()
                .file_stem()
                .and_then(OsStr::to_str)
                .map(ToString::to_string)
        }
        Ok(try!(fs::read_dir(&dir_path))
            .filter_map(|e| e.ok())
            .filter(is_mod)
            .filter(|e| !ignored_paths.contains(&e.path()))
            .filter_map(|e| file_name(&e))
            .collect::<Vec<_>>())
    }

    fn sanitize_condition(condition: String) -> String {
        let words: Vec<&str> = condition.split_whitespace().collect();
        words.join(" ")
    }
}

impl<'a> visit::Visitor<'a> for Builder<'a> {
    fn visit_item(&mut self, item: &'a ast::Item) {
        let condition = item
            .attrs
            .iter()
            .find(|attr| attr.check_name("cfg"))
            .map(|attr| {
                self.source_map
                    .span_to_snippet(attr.span)
                    .map(Builder::sanitize_condition)
                    .unwrap_or_else(|_| "".to_string())
            });

        match item.node {
            ast::ItemKind::Mod(_) => {
                let name = item.ident.to_string();
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };
                    tree.insert(Tree::new_module(name.clone(), visibility, condition));
                }
                self.path.push(name);
                visit::walk_item(self, item);
                let _ = self.path.pop();
            }
            ast::ItemKind::Use(ref use_tree) => {
                {
                    let tree = self.tree.subtree_at_path(&self.path).unwrap();
                    let visibility = if let ast::VisibilityKind::Public = item.vis.node {
                        Visibility::Public
                    } else {
                        Visibility::Private
                    };

                    add_use_tree(tree, visibility, "", use_tree);
                }

                visit::walk_item(self, item);
            }
            _ => {
                visit::walk_item(self, item);
            }
        }
    }

    fn visit_mod(
        &mut self,
        m: &'a ast::Mod,
        _s: source_map::Span,
        _attrs: &[ast::Attribute],
        _n: ast::NodeId,
    ) {
        visit::walk_mod(self, m);
        if !self.config.include_orphans {
            return;
        }
        let path = &self.path;
        if let Ok(candidates) = Builder::find_orphan_candidates(path, &self.config.ignored_files) {
            let tree = self.tree.subtree_at_path(path).unwrap();
            let names: Vec<_> = tree.subtree_names();
            for candidate in candidates {
                if !names.contains(&candidate) {
                    tree.insert(Tree::new_orphan(candidate.clone()));
                }
            }
        }
    }

    fn visit_mac(&mut self, mac: &ast::Mac) {
        visit::walk_mac(self, mac);
    }
}

fn add_use_tree(tree: &mut Tree, visibility: Visibility, prefix: &str, use_tree: &ast::UseTree) {
    let prefix = if prefix.is_empty() {
        String::new()
    } else {
        prefix.to_string()
    };

    match use_tree.kind {
        ast::UseTreeKind::Simple(_, ..) => {
            let path = pprust::path_to_string(&use_tree.prefix);
            if path != "self" {
                tree.insert_use((visibility, prefix + &path));
            } else {
                let mut prefix = prefix;
                let new_len = prefix.len() - 2;
                prefix.truncate(new_len);
                tree.insert_use((visibility, prefix));
            }
        }
        ast::UseTreeKind::Glob => {
            let mut use_path = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            use_path += "*";
            tree.insert_use((visibility, use_path));
        }
        ast::UseTreeKind::Nested(ref items) => {
            let mut prefix = if use_tree.prefix.segments.is_empty() {
                prefix
            } else {
                prefix + &pprust::path_to_string(&use_tree.prefix) + "::"
            };
            for (sub_tree, _) in items {
                add_use_tree(tree, visibility, &prefix, sub_tree);
            }
        }
    }
}
<|endoftext|>