<commit_before>#[macro_use]
extern crate error_chain;

use std::path::{Path,PathBuf};
use std::process::{Command,ExitStatus};
use std::io;
use std::fs::{self, DirEntry};
use std::borrow::ToOwned;

error_chain!{
    foreign_links {
        Io(::std::io::Error);
    }
    errors{
        MissingSourceDir{
            description("Couldn't locate source directory")
            display("Couldn't locate your source directory")
        }
        ForkFailure(command: &'static str){
            description("Failed to fork command")
            display("Failed to fork command {}",command)
        }
        ExitFailure(command: &'static str, status: ExitStatus){
            description("Command exited with failure status")
            display("Command {} exited with status: {}",command,status)
        }
        DistNotDir(dist_path: PathBuf){
            description("The dist path did not point to a directory")
            display("The path {:?} must not point to an existing non-directory file",dist_path)
        }
        NoSources(source_path: PathBuf){
            description("The located source path contains no source files")
            display("The located source path ({:?}) contained no `.java` files",source_path)
        }
    }
}

fn visit_dirs<P: AsRef<Path>,F: FnMut(&DirEntry)>(dir: P, cb: &mut F) -> io::Result<()> {
    let dir = dir.as_ref();
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                try!(visit_dirs(&path, cb));
            } else {
                cb(&entry);
            }
        }
    }
    Ok(())
}

fn find_in_dirs<P: AsRef<Path>,F:FnMut(&DirEntry) -> Option<T>,T>(dir: P, cb: &mut F) -> io::Result<Option<T>> {
    let dir = dir.as_ref();
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                match try!(find_in_dirs(&path, cb)){
                    Some(t) => return Ok(Some(t)),
                    None => continue
                }
            } else {
                match cb(&entry){
                    Some(t) => return Ok(Some(t)),
                    None => continue,
                }
            }
        }
    }
    Ok(None)
}

fn dir_exists<P: AsRef<Path>>(path: P) -> bool{
    let path = path.as_ref();
    path.exists() && path.is_dir()
}

fn source_root() -> Result<PathBuf>{
    const SEARCH_PATHS: &'static [&'static str] = &[
        "./src/main/java/",
        "./src/java/",
        "./src/"
    ];

    SEARCH_PATHS.iter().map(|&s| Path::new(s).to_path_buf()).find(|p| dir_exists(p)).ok_or(ErrorKind::MissingSourceDir.into())
}

fn libs_root() -> Option<PathBuf>{
    const SEARCH_PATHS: &'static [&'static str] = &[
        "./libs/",
        "./deps/"
    ];

    SEARCH_PATHS.iter().map(|&s| Path::new(s).to_path_buf()).find(|p| dir_exists(p))
}

fn find_files_by_extension<P: AsRef<Path>>(root_path: P,extension: &str, strip_root_prefix: bool) -> Result<Vec<PathBuf>>{
    let mut found = vec![];
    visit_dirs(root_path.as_ref(),&mut |entry: &DirEntry|{
        let path = entry.path();
        if path.extension().is_some() && path.extension().unwrap() == extension{
            let path = if strip_root_prefix {
                path.strip_prefix(&root_path).expect("Found something outside of $root_path in $root_path ???").to_path_buf()
            }else{
                path
            };
            found.push(path);
        }
    })?;
    Ok(found)
}

fn source_files<P: AsRef<Path>>(source_root: P) -> Result<Vec<PathBuf>>{
    find_files_by_extension(source_root,"java",false)
}

fn class_files() -> Result<Vec<PathBuf>>{
    find_files_by_extension("dist","class",true)
}

fn libs() -> Result<Vec<PathBuf>>{
    if let Some(dir) = libs_root(){
        find_files_by_extension(dir,"jar",false)
    }else{
        Ok(vec![])
    }
}

pub struct Kohi{
    dist_path: PathBuf,
    libs: Vec<PathBuf>,
    source_path: PathBuf,
    source_files: Vec<PathBuf>
}

impl Kohi{
    pub fn new() -> Result<Self>{
        let source_root = source_root()?;
        let source_files = source_files(&source_root)?;
        if source_files.len() == 0 {
            bail!(ErrorKind::NoSources(source_root));
        }
        Ok(Kohi{
            dist_path: Path::new("./dist/").to_path_buf(),
            libs: libs()?,
            source_path: source_root,
            source_files: source_files,
        })
    }

    pub fn compile<I: Into<Option<String>>>(self,target_version: I) -> Result<Self>{
        let classpath = std::env::join_paths(&self.libs).unwrap();

        if !self.dist_path.exists(){
            std::fs::create_dir_all(&self.dist_path)?;
        }else{
            if !self.dist_path.is_dir(){
                bail!(ErrorKind::DistNotDir(self.dist_path));
            }
        }

        let mut cmd = Command::new("javac");

        if let Some(target_version) = target_version.into(){
            cmd.arg("-version").arg(target_version);
        }

        let status = cmd
            .arg("-d")
            .arg(&self.dist_path)
            .arg("-cp")
            .arg(classpath)
            .arg("-sourcepath")
            .arg(&self.source_path)
            .args(self.source_files.as_slice())
            .status()
            .chain_err(|| ErrorKind::ForkFailure("javac"))?;

        if status.success(){
            Ok(self)
        }else{
            bail!(ErrorKind::ExitFailure("javac",status))
        }
    }

    pub fn run(self, run_class: String) -> Result<Self>{
        //blah.class -> blah
        let run_class = run_class.trim_right_matches(".class");

        let classpath = std::env::join_paths(std::iter::once(&self.dist_path).chain(&self.libs)).unwrap();

        let main = self.find_class(run_class).expect("Couldn't locate the class to run");

        let status = Command::new("java")
                          .arg("-cp")
                          .arg(classpath)
                          .arg(main)
                          .status()
                          .chain_err(|| ErrorKind::ForkFailure("java"))?;

        if status.success(){
            Ok(self)
        }else{
            bail!(ErrorKind::ExitFailure("java",status))
        }
    }

    pub fn package(self, jar_name: &str, entry_point: Option<&str>) -> Result<Self>{
        let mut cmd = Command::new("jar");
        if let Some(entry_point) = entry_point{
            cmd.arg("-cfe")
                .arg(jar_name)
                .arg(entry_point);
        }else{
            cmd.arg("-cf")
                .arg(jar_name);
        }

        let status = cmd
            .arg("-C")
            .arg("dist")
            .args(class_files()?.as_slice())
            .status()
            .chain_err(|| ErrorKind::ForkFailure("jar"))?;

        if status.success(){
            Ok(self)
        }else{
            bail!(ErrorKind::ExitFailure("jar",status))
        }
    }

    fn find_class(&self,class: &str) -> Option<String>{

        let class = class.to_lowercase();
        let mut class_name: Vec<_> = class.split(".").map(ToOwned::to_owned).collect();

        //kinda hacky, but this means we don't have to continuously strip off the dist prefix in the loop
        class_name.insert(0,"dist".to_owned());

        //also a hack to avoid removing the ".class" from the last component constantly
        let len = class_name.len();
        class_name[len-1].push_str(".class");

        let path = find_in_dirs(self.dist_path.as_path(),&mut |entry|{
            let path = entry.path();
            {
                let components = path.components().filter_map(|comp|{
                    use std::path::Component;
                    match comp{
                        Component::Normal(str) => Some(str),
                        _ => None
                    }
                });

                for (a,b) in components.zip(class_name.iter()){
                    if let Some(a) = a.to_str().map(str::to_lowercase){
                        if &a == b{
                            continue
                        }else{
                            return None
                        }
                    }else{
                        return None
                    }
                }
            }
            Some(path)
        }).expect("IO Error while finding class file");

        if let Some(path) = path{
            let mut path = path.strip_prefix(self.dist_path.as_path()).expect("Found something outside of ./dist/ in ./dist/ ???").to_path_buf();
            path.set_extension("");

            let path: Vec<_> = path.iter().flat_map(|s| s.to_str()).map(|s| s.to_owned()).collect();
            let path = path.join(".");

            let path_as_class = path.trim_left_matches(".");
            Some(path_as_class.to_owned())
        }else{
            None
        }
    }
}<commit_msg>fixed incorrect flag being passed<commit_after>#[macro_use]
extern crate error_chain;

use std::path::{Path,PathBuf};
use std::process::{Command,ExitStatus};
use std::io;
use std::fs::{self, DirEntry};
use std::borrow::ToOwned;

error_chain!{
    foreign_links {
        Io(::std::io::Error);
    }
    errors{
        MissingSourceDir{
            description("Couldn't locate source directory")
            display("Couldn't locate your source directory")
        }
        ForkFailure(command: &'static str){
            description("Failed to fork command")
            display("Failed to fork command {}",command)
        }
        ExitFailure(command: &'static str, status: ExitStatus){
            description("Command exited with failure status")
            display("Command {} exited with status: {}",command,status)
        }
        DistNotDir(dist_path: PathBuf){
            description("The dist path did not point to a directory")
            display("The path {:?} must not point to an existing non-directory file",dist_path)
        }
        NoSources(source_path: PathBuf){
            description("The located source path contains no source files")
            display("The located source path ({:?}) contained no `.java` files",source_path)
        }
    }
}

fn visit_dirs<P: AsRef<Path>,F: FnMut(&DirEntry)>(dir: P, cb: &mut F) -> io::Result<()> {
    let dir = dir.as_ref();
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                try!(visit_dirs(&path, cb));
            } else {
                cb(&entry);
            }
        }
    }
    Ok(())
}

fn find_in_dirs<P: AsRef<Path>,F:FnMut(&DirEntry) -> Option<T>,T>(dir: P, cb: &mut F) -> io::Result<Option<T>> {
    let dir = dir.as_ref();
    if dir.is_dir() {
        for entry in try!(fs::read_dir(dir)) {
            let entry = try!(entry);
            let path = entry.path();
            if path.is_dir() {
                match try!(find_in_dirs(&path, cb)){
                    Some(t) => return Ok(Some(t)),
                    None => continue
                }
            } else {
                match cb(&entry){
                    Some(t) => return Ok(Some(t)),
                    None => continue,
                }
            }
        }
    }
    Ok(None)
}

fn dir_exists<P: AsRef<Path>>(path: P) -> bool{
    let path = path.as_ref();
    path.exists() && path.is_dir()
}

fn source_root() -> Result<PathBuf>{
    const SEARCH_PATHS: &'static [&'static str] = &[
        "./src/main/java/",
        "./src/java/",
        "./src/"
    ];

    SEARCH_PATHS.iter().map(|&s| Path::new(s).to_path_buf()).find(|p| dir_exists(p)).ok_or(ErrorKind::MissingSourceDir.into())
}

fn libs_root() -> Option<PathBuf>{
    const SEARCH_PATHS: &'static [&'static str] = &[
        "./libs/",
        "./deps/"
    ];

    SEARCH_PATHS.iter().map(|&s| Path::new(s).to_path_buf()).find(|p| dir_exists(p))
}

fn find_files_by_extension<P: AsRef<Path>>(root_path: P,extension: &str, strip_root_prefix: bool) -> Result<Vec<PathBuf>>{
    let mut found = vec![];
    visit_dirs(root_path.as_ref(),&mut |entry: &DirEntry|{
        let path = entry.path();
        if path.extension().is_some() && path.extension().unwrap() == extension{
            let path = if strip_root_prefix {
                path.strip_prefix(&root_path).expect("Found something outside of $root_path in $root_path ???").to_path_buf()
            }else{
                path
            };
            found.push(path);
        }
    })?;
    Ok(found)
}

fn source_files<P: AsRef<Path>>(source_root: P) -> Result<Vec<PathBuf>>{
    find_files_by_extension(source_root,"java",false)
}

fn class_files() -> Result<Vec<PathBuf>>{
    find_files_by_extension("dist","class",true)
}

fn libs() -> Result<Vec<PathBuf>>{
    if let Some(dir) = libs_root(){
        find_files_by_extension(dir,"jar",false)
    }else{
        Ok(vec![])
    }
}

pub struct Kohi{
    dist_path: PathBuf,
    libs: Vec<PathBuf>,
    source_path: PathBuf,
    source_files: Vec<PathBuf>
}

impl Kohi{
    pub fn new() -> Result<Self>{
        let source_root = source_root()?;
        let source_files = source_files(&source_root)?;
        if source_files.len() == 0 {
            bail!(ErrorKind::NoSources(source_root));
        }
        Ok(Kohi{
            dist_path: Path::new("./dist/").to_path_buf(),
            libs: libs()?,
            source_path: source_root,
            source_files: source_files,
        })
    }

    pub fn compile<I: Into<Option<String>>>(self,target_version: I) -> Result<Self>{
        let classpath = std::env::join_paths(&self.libs).unwrap();

        if !self.dist_path.exists(){
            std::fs::create_dir_all(&self.dist_path)?;
        }else{
            if !self.dist_path.is_dir(){
                bail!(ErrorKind::DistNotDir(self.dist_path));
            }
        }

        let mut cmd = Command::new("javac");

        if let Some(target_version) = target_version.into(){
            cmd.arg("-target").arg(target_version);
        }

        let status = cmd
            .arg("-d")
            .arg(&self.dist_path)
            .arg("-cp")
            .arg(classpath)
            .arg("-sourcepath")
            .arg(&self.source_path)
            .args(self.source_files.as_slice())
            .status()
            .chain_err(|| ErrorKind::ForkFailure("javac"))?;

        if status.success(){
            Ok(self)
        }else{
            bail!(ErrorKind::ExitFailure("javac",status))
        }
    }

    pub fn run(self, run_class: String) -> Result<Self>{
        //blah.class -> blah
        let run_class = run_class.trim_right_matches(".class");

        let classpath = std::env::join_paths(std::iter::once(&self.dist_path).chain(&self.libs)).unwrap();

        let main = self.find_class(run_class).expect("Couldn't locate the class to run");

        let status = Command::new("java")
                          .arg("-cp")
                          .arg(classpath)
                          .arg(main)
                          .status()
                          .chain_err(|| ErrorKind::ForkFailure("java"))?;

        if status.success(){
            Ok(self)
        }else{
            bail!(ErrorKind::ExitFailure("java",status))
        }
    }

    pub fn package(self, jar_name: &str, entry_point: Option<&str>) -> Result<Self>{
        let mut cmd = Command::new("jar");
        if let Some(entry_point) = entry_point{
            cmd.arg("-cfe")
                .arg(jar_name)
                .arg(entry_point);
        }else{
            cmd.arg("-cf")
                .arg(jar_name);
        }

        let status = cmd
            .arg("-C")
            .arg("dist")
            .args(class_files()?.as_slice())
            .status()
            .chain_err(|| ErrorKind::ForkFailure("jar"))?;

        if status.success(){
            Ok(self)
        }else{
            bail!(ErrorKind::ExitFailure("jar",status))
        }
    }

    fn find_class(&self,class: &str) -> Option<String>{

        let class = class.to_lowercase();
        let mut class_name: Vec<_> = class.split(".").map(ToOwned::to_owned).collect();

        //kinda hacky, but this means we don't have to continuously strip off the dist prefix in the loop
        class_name.insert(0,"dist".to_owned());

        //also a hack to avoid removing the ".class" from the last component constantly
        let len = class_name.len();
        class_name[len-1].push_str(".class");

        let path = find_in_dirs(self.dist_path.as_path(),&mut |entry|{
            let path = entry.path();
            {
                let components = path.components().filter_map(|comp|{
                    use std::path::Component;
                    match comp{
                        Component::Normal(str) => Some(str),
                        _ => None
                    }
                });

                for (a,b) in components.zip(class_name.iter()){
                    if let Some(a) = a.to_str().map(str::to_lowercase){
                        if &a == b{
                            continue
                        }else{
                            return None
                        }
                    }else{
                        return None
                    }
                }
            }
            Some(path)
        }).expect("IO Error while finding class file");

        if let Some(path) = path{
            let mut path = path.strip_prefix(self.dist_path.as_path()).expect("Found something outside of ./dist/ in ./dist/ ???").to_path_buf();
            path.set_extension("");

            let path: Vec<_> = path.iter().flat_map(|s| s.to_str()).map(|s| s.to_owned()).collect();
            let path = path.join(".");

            let path_as_class = path.trim_left_matches(".");
            Some(path_as_class.to_owned())
        }else{
            None
        }
    }
}<|endoftext|>


<commit_before>// Copyright ⓒ 2015-2016 Kevin B. Knapp and [`clap-rs` contributors](https://github.com/clap-rs/clap/graphs/contributors).
// Licensed under the MIT license
// (see LICENSE or <http://opensource.org/licenses/MIT>) All files in the project carrying such
// notice may not be copied, modified, or distributed except according to those terms.

#![doc(html_logo_url = "https://clap.rs/images/media/clap.png")]
#![doc(html_root_url = "https://docs.rs/clap/3.0.0-beta.5")]
#![doc = include_str!("../README.md")]
//! <https://github.com/clap-rs/clap>
#![crate_type = "lib"]
#![deny(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    unused_allocation,
    trivial_numeric_casts
)]
#![forbid(unsafe_code)]
// TODO: https://github.com/rust-lang/rust-clippy/issues/7290
#![allow(clippy::single_component_path_imports)]

#[cfg(not(feature = "std"))]
compile_error!("`std` feature is currently required to build `clap`");

pub use crate::{
    build::{
        App, AppFlags, AppSettings, Arg, ArgFlags, ArgGroup, ArgSettings, PossibleValue, ValueHint,
    },
    parse::errors::{Error, ErrorKind, Result},
    parse::{ArgMatches, Indices, OsValues, Values},
    util::color::ColorChoice,
};

pub use crate::derive::{ArgEnum, Args, FromArgMatches, IntoApp, Parser, Subcommand};

#[cfg(feature = "yaml")]
#[doc(hidden)]
pub use yaml_rust::YamlLoader;

#[cfg(feature = "derive")]
#[doc(hidden)]
pub use clap_derive::{self, *};

#[cfg(any(feature = "derive", feature = "cargo"))]
#[doc(hidden)]
pub use lazy_static;

#[macro_use]
#[allow(missing_docs)]
mod macros;

mod derive;

#[cfg(feature = "regex")]
pub use crate::build::arg::RegexRef;

mod build;
mod mkeymap;
mod output;
mod parse;
mod util;

const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a bug \
                                  report at https://github.com/clap-rs/clap/issues";
const INVALID_UTF8: &str = "unexpected invalid UTF-8 code point";

/// Deprecated, see [`App`]
#[derive(Debug, Copy, Clone)]
pub struct SubCommand {}

impl SubCommand {
    /// Deprecated, see [`App::new`]
    #[deprecated(since = "3.0.0", note = "Replaced with `App::new`)")]
    pub fn with_name<'help>(name: &str) -> App<'help> {
        App::new(name)
    }

    /// Deprecated, see [`App::from`]
    #[cfg(feature = "yaml")]
    #[deprecated(since = "3.0.0", note = "Replaced with `App::from`)")]
    pub fn from_yaml(yaml: &yaml_rust::Yaml) -> App {
        App::from(yaml)
    }
}
<commit_msg>chore: Silence bad clippy lint<commit_after>// Copyright ⓒ 2015-2016 Kevin B. Knapp and [`clap-rs` contributors](https://github.com/clap-rs/clap/graphs/contributors).
// Licensed under the MIT license
// (see LICENSE or <http://opensource.org/licenses/MIT>) All files in the project carrying such
// notice may not be copied, modified, or distributed except according to those terms.

#![doc(html_logo_url = "https://clap.rs/images/media/clap.png")]
#![doc(html_root_url = "https://docs.rs/clap/3.0.0-beta.5")]
#![doc = include_str!("../README.md")]
//! <https://github.com/clap-rs/clap>
#![crate_type = "lib"]
#![deny(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    unused_allocation,
    trivial_numeric_casts
)]
#![forbid(unsafe_code)]
// TODO: https://github.com/rust-lang/rust-clippy/issues/7290
#![allow(clippy::single_component_path_imports)]
#![allow(clippy::branches_sharing_code)]

#[cfg(not(feature = "std"))]
compile_error!("`std` feature is currently required to build `clap`");

pub use crate::{
    build::{
        App, AppFlags, AppSettings, Arg, ArgFlags, ArgGroup, ArgSettings, PossibleValue, ValueHint,
    },
    parse::errors::{Error, ErrorKind, Result},
    parse::{ArgMatches, Indices, OsValues, Values},
    util::color::ColorChoice,
};

pub use crate::derive::{ArgEnum, Args, FromArgMatches, IntoApp, Parser, Subcommand};

#[cfg(feature = "yaml")]
#[doc(hidden)]
pub use yaml_rust::YamlLoader;

#[cfg(feature = "derive")]
#[doc(hidden)]
pub use clap_derive::{self, *};

#[cfg(any(feature = "derive", feature = "cargo"))]
#[doc(hidden)]
pub use lazy_static;

#[macro_use]
#[allow(missing_docs)]
mod macros;

mod derive;

#[cfg(feature = "regex")]
pub use crate::build::arg::RegexRef;

mod build;
mod mkeymap;
mod output;
mod parse;
mod util;

const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a bug \
                                  report at https://github.com/clap-rs/clap/issues";
const INVALID_UTF8: &str = "unexpected invalid UTF-8 code point";

/// Deprecated, see [`App`]
#[derive(Debug, Copy, Clone)]
pub struct SubCommand {}

impl SubCommand {
    /// Deprecated, see [`App::new`]
    #[deprecated(since = "3.0.0", note = "Replaced with `App::new`)")]
    pub fn with_name<'help>(name: &str) -> App<'help> {
        App::new(name)
    }

    /// Deprecated, see [`App::from`]
    #[cfg(feature = "yaml")]
    #[deprecated(since = "3.0.0", note = "Replaced with `App::from`)")]
    pub fn from_yaml(yaml: &yaml_rust::Yaml) -> App {
        App::from(yaml)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Runtime Library
//!
//! [Cap'n Proto](https://capnproto.org) is an extremely efficient protocol for
//! sharing data and capabilities.
//!
//! The Rust implementation is split into three separate crates.
//!
//! Code generation is handled by [capnpc-rust](https://github.com/dwrensha/capnpc-rust).
//!
//! The present crate is the runtime library required by that generated code. It is hosted on Github
//! [here](https://github.com/dwrensha/capnproto-rust).
//!
//! [capnp-rpc-rust](https://github.com/dwrensha/capnp-rpc-rust) is an implementation of a
//! distributed object-capability layer.

#![allow(raw_pointer_derive)]

extern crate byteorder;

#[cfg(any(feature="quickcheck", test))]
extern crate quickcheck;

#[cfg(feature = "rpc")]
extern crate gj;

pub mod any_pointer;
pub mod capability;
pub mod data;
pub mod data_list;
pub mod enum_list;
pub mod list_list;
pub mod message;
pub mod primitive_list;
pub mod private;
pub mod serialize;
pub mod serialize_packed;
pub mod struct_list;
pub mod text;
pub mod text_list;
pub mod traits;

mod util;

/// Eight bytes of memory with opaque interior.
///
/// This type is used to ensure that the data of a message is properly aligned.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Word(u64);

impl Word {
    /// Does this, but faster:
    /// `::std::iter::repeat(Word(0)).take(length).collect()`
    pub fn allocate_zeroed_vec(length: usize) -> Vec<Word> {
        let mut result : Vec<Word> = Vec::with_capacity(length);
        unsafe {
            result.set_len(length);
            let p : *mut u8 = result.as_mut_ptr() as *mut u8;
            ::std::ptr::write_bytes(p, 0u8, length * ::std::mem::size_of::<Word>());
        }
        result
    }

    pub fn bytes_to_words<'a>(bytes: &'a [u8]) -> &'a [Word] {
        unsafe {
            ::std::slice::from_raw_parts(bytes.as_ptr() as *const Word, bytes.len() / 8)
        }
    }

    pub fn bytes_to_words_mut<'a>(bytes: &'a mut [u8]) -> &'a mut [Word] {
        unsafe {
            ::std::slice::from_raw_parts_mut(bytes.as_ptr() as *mut Word, bytes.len() / 8)
        }
    }

    pub fn words_to_bytes<'a>(words: &'a [Word]) -> &'a [u8] {
        unsafe {
            ::std::slice::from_raw_parts(words.as_ptr() as *const u8, words.len() * 8)
        }
    }

    pub fn words_to_bytes_mut<'a>(words: &'a mut [Word]) -> &'a mut [u8] {
        unsafe {
            ::std::slice::from_raw_parts_mut(words.as_mut_ptr() as *mut u8, words.len() * 8)
        }
    }

    #[cfg(test)]
    pub fn from(n: u64) -> Word {
        Word(n)
    }
}

#[cfg(any(feature="quickcheck", test))]
impl quickcheck::Arbitrary for Word {
    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Word {
        Word(quickcheck::Arbitrary::arbitrary(g))
    }
}

/// Size of a message. Every generated struct has a method `.total_size()` that returns this.
#[derive(Clone, Copy, PartialEq)]
pub struct MessageSize {
    pub word_count: u64,

    /// Size of the capability table.
    pub cap_count: u32
}

impl MessageSize {
    pub fn plus_eq(&mut self, other : MessageSize) {
        self.word_count += other.word_count;
        self.cap_count += other.cap_count;
    }
}

/// An enum value or union discriminant that was not found among those defined in a schema.
#[derive(PartialEq, Clone, Copy, Debug)]
pub struct NotInSchema(pub u16);

impl ::std::fmt::Display for NotInSchema {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
        write!(fmt, "Enum value or union discriminant {} was not present in the schema.", self.0)
    }
}

impl ::std::error::Error for NotInSchema {
    fn description<'a>(&'a self) -> &'a str {
        "Enum value or union disriminant was not present in schema."
    }
}

/// Because messages are lazily validated, the return type of any method that reads a pointer field
/// must be wrapped in a Result.
pub type Result<T> = ::std::result::Result<T, Error>;

/// Describes an arbitrary error that prevented an operation from completing.
#[derive(Debug, Clone)]
pub struct Error {
    /// The type of the error. The purpose of this enum is not to describe the error itself, but
    /// rather to describe how the client might want to respond to the error.
    pub kind: ErrorKind,

    /// Human-readable failure description.
    pub description: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
    // Something went wrong. This is the usual error kind. It includes decoding errors and I/O
    // errors.
    Failed,

    // The call failed because of a temporary lack of resources. This could be space resources
    // (out of memory, out of disk space) or time resources (request queue overflow, operation
    // timed out).
    //
    // The operation might work if tried again, but it should NOT be repeated immediately as this
    // may simply exacerbate the problem.
    Overloaded,

    // The call required communication over a connection that has been lost. The callee will need
    // to re-establish connections and try again.
    Disconnected,

    // The requested method is not implemented. The caller may wish to revert to a fallback
    // approach based on other methods.
    Unimplemented,
}

impl Error {
    pub fn failed(description: String) -> Error {
        Error { description: description, kind: ErrorKind::Failed }
    }
    pub fn unimplemented(description: String) -> Error {
        Error { description: description, kind: ErrorKind::Unimplemented }
    }
}

impl ::std::convert::From<::std::io::Error> for Error {
    fn from(err: ::std::io::Error) -> Error {
        use std::io;
        let kind = match err.kind() {
            io::ErrorKind::TimedOut => ErrorKind::Overloaded,
            io::ErrorKind::BrokenPipe |
            io::ErrorKind::ConnectionRefused |
            io::ErrorKind::ConnectionReset |
            io::ErrorKind::ConnectionAborted |
            io::ErrorKind::NotConnected  => ErrorKind::Disconnected,
            _ => ErrorKind::Failed,
        };
        Error { description: format!("{}", err), kind: kind }
    }
}

impl ::std::convert::From<NotInSchema> for Error {
    fn from(e: NotInSchema) -> Error {
        Error::failed(format!("Enum value or union discriminant {} was not present in schema.", e.0))
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
        write!(fmt, "{:?}: {}", self.kind, self.description)
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        &self.description
    }
    fn cause(&self) -> Option<&::std::error::Error> {
        None
    }
}

#[cfg(feature = "rpc")]
impl ::gj::FulfillerDropped for Error {
    fn fulfiller_dropped() -> Error {
        Error::failed("Promise fulfiller was dropped.".to_string())
    }
}

/// Helper struct that allows `MessageBuilder::get_segments_for_output()` to avoid heap allocations
/// in the single-segment case.
pub enum OutputSegments<'a> {
    #[doc(hidden)]
    SingleSegment([&'a [Word]; 1]),

    #[doc(hidden)]
    MultiSegment(Vec<&'a [Word]>),
}

impl <'a> ::std::ops::Deref for OutputSegments<'a> {
    type Target = [&'a [Word]];
    fn deref<'b>(&'b self) -> &'b [&'a [Word]] {
        match self {
            &OutputSegments::SingleSegment(ref s) => {
                s
            }
            &OutputSegments::MultiSegment(ref v) => {
                &*v
            }
        }
    }
}
<commit_msg>Make ErrorKind documentation rustdoc-friendly.<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Runtime Library
//!
//! [Cap'n Proto](https://capnproto.org) is an extremely efficient protocol for
//! sharing data and capabilities.
//!
//! The Rust implementation is split into three separate crates.
//!
//! Code generation is handled by [capnpc-rust](https://github.com/dwrensha/capnpc-rust).
//!
//! The present crate is the runtime library required by that generated code. It is hosted on Github
//! [here](https://github.com/dwrensha/capnproto-rust).
//!
//! [capnp-rpc-rust](https://github.com/dwrensha/capnp-rpc-rust) is an implementation of a
//! distributed object-capability layer.

#![allow(raw_pointer_derive)]

extern crate byteorder;

#[cfg(any(feature="quickcheck", test))]
extern crate quickcheck;

#[cfg(feature = "rpc")]
extern crate gj;

pub mod any_pointer;
pub mod capability;
pub mod data;
pub mod data_list;
pub mod enum_list;
pub mod list_list;
pub mod message;
pub mod primitive_list;
pub mod private;
pub mod serialize;
pub mod serialize_packed;
pub mod struct_list;
pub mod text;
pub mod text_list;
pub mod traits;

mod util;

/// Eight bytes of memory with opaque interior.
///
/// This type is used to ensure that the data of a message is properly aligned.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Word(u64);

impl Word {
    /// Does this, but faster:
    /// `::std::iter::repeat(Word(0)).take(length).collect()`
    pub fn allocate_zeroed_vec(length: usize) -> Vec<Word> {
        let mut result : Vec<Word> = Vec::with_capacity(length);
        unsafe {
            result.set_len(length);
            let p : *mut u8 = result.as_mut_ptr() as *mut u8;
            ::std::ptr::write_bytes(p, 0u8, length * ::std::mem::size_of::<Word>());
        }
        result
    }

    pub fn bytes_to_words<'a>(bytes: &'a [u8]) -> &'a [Word] {
        unsafe {
            ::std::slice::from_raw_parts(bytes.as_ptr() as *const Word, bytes.len() / 8)
        }
    }

    pub fn bytes_to_words_mut<'a>(bytes: &'a mut [u8]) -> &'a mut [Word] {
        unsafe {
            ::std::slice::from_raw_parts_mut(bytes.as_ptr() as *mut Word, bytes.len() / 8)
        }
    }

    pub fn words_to_bytes<'a>(words: &'a [Word]) -> &'a [u8] {
        unsafe {
            ::std::slice::from_raw_parts(words.as_ptr() as *const u8, words.len() * 8)
        }
    }

    pub fn words_to_bytes_mut<'a>(words: &'a mut [Word]) -> &'a mut [u8] {
        unsafe {
            ::std::slice::from_raw_parts_mut(words.as_mut_ptr() as *mut u8, words.len() * 8)
        }
    }

    #[cfg(test)]
    pub fn from(n: u64) -> Word {
        Word(n)
    }
}

#[cfg(any(feature="quickcheck", test))]
impl quickcheck::Arbitrary for Word {
    fn arbitrary<G: quickcheck::Gen>(g: &mut G) -> Word {
        Word(quickcheck::Arbitrary::arbitrary(g))
    }
}

/// Size of a message. Every generated struct has a method `.total_size()` that returns this.
#[derive(Clone, Copy, PartialEq)]
pub struct MessageSize {
    pub word_count: u64,

    /// Size of the capability table.
    pub cap_count: u32
}

impl MessageSize {
    pub fn plus_eq(&mut self, other : MessageSize) {
        self.word_count += other.word_count;
        self.cap_count += other.cap_count;
    }
}

/// An enum value or union discriminant that was not found among those defined in a schema.
#[derive(PartialEq, Clone, Copy, Debug)]
pub struct NotInSchema(pub u16);

impl ::std::fmt::Display for NotInSchema {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
        write!(fmt, "Enum value or union discriminant {} was not present in the schema.", self.0)
    }
}

impl ::std::error::Error for NotInSchema {
    fn description<'a>(&'a self) -> &'a str {
        "Enum value or union disriminant was not present in schema."
    }
}

/// Because messages are lazily validated, the return type of any method that reads a pointer field
/// must be wrapped in a Result.
pub type Result<T> = ::std::result::Result<T, Error>;

/// Describes an arbitrary error that prevented an operation from completing.
#[derive(Debug, Clone)]
pub struct Error {
    /// The type of the error. The purpose of this enum is not to describe the error itself, but
    /// rather to describe how the client might want to respond to the error.
    pub kind: ErrorKind,

    /// Human-readable failure description.
    pub description: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ErrorKind {
    /// Something went wrong. This is the usual error kind. It includes decoding errors.
    Failed,

    /// The call failed because of a temporary lack of resources. This could be space resources
    /// (out of memory, out of disk space) or time resources (request queue overflow, operation
    /// timed out).
    //
    /// The operation might work if tried again, but it should NOT be repeated immediately as this
    /// may simply exacerbate the problem.
    Overloaded,

    /// The call required communication over a connection that has been lost. The callee will need
    /// to re-establish connections and try again.
    Disconnected,

    /// The requested method is not implemented. The caller may wish to revert to a fallback
    /// approach based on other methods.
    Unimplemented,
}

impl Error {
    pub fn failed(description: String) -> Error {
        Error { description: description, kind: ErrorKind::Failed }
    }
    pub fn unimplemented(description: String) -> Error {
        Error { description: description, kind: ErrorKind::Unimplemented }
    }
}

impl ::std::convert::From<::std::io::Error> for Error {
    fn from(err: ::std::io::Error) -> Error {
        use std::io;
        let kind = match err.kind() {
            io::ErrorKind::TimedOut => ErrorKind::Overloaded,
            io::ErrorKind::BrokenPipe |
            io::ErrorKind::ConnectionRefused |
            io::ErrorKind::ConnectionReset |
            io::ErrorKind::ConnectionAborted |
            io::ErrorKind::NotConnected  => ErrorKind::Disconnected,
            _ => ErrorKind::Failed,
        };
        Error { description: format!("{}", err), kind: kind }
    }
}

impl ::std::convert::From<NotInSchema> for Error {
    fn from(e: NotInSchema) -> Error {
        Error::failed(format!("Enum value or union discriminant {} was not present in schema.", e.0))
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
        write!(fmt, "{:?}: {}", self.kind, self.description)
    }
}

impl ::std::error::Error for Error {
    fn description(&self) -> &str {
        &self.description
    }
    fn cause(&self) -> Option<&::std::error::Error> {
        None
    }
}

#[cfg(feature = "rpc")]
impl ::gj::FulfillerDropped for Error {
    fn fulfiller_dropped() -> Error {
        Error::failed("Promise fulfiller was dropped.".to_string())
    }
}

/// Helper struct that allows `MessageBuilder::get_segments_for_output()` to avoid heap allocations
/// in the single-segment case.
pub enum OutputSegments<'a> {
    #[doc(hidden)]
    SingleSegment([&'a [Word]; 1]),

    #[doc(hidden)]
    MultiSegment(Vec<&'a [Word]>),
}

impl <'a> ::std::ops::Deref for OutputSegments<'a> {
    type Target = [&'a [Word]];
    fn deref<'b>(&'b self) -> &'b [&'a [Word]] {
        match self {
            &OutputSegments::SingleSegment(ref s) => {
                s
            }
            &OutputSegments::MultiSegment(ref v) => {
                &*v
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Straight-forward functions and types for basic data parallel
//! operations.
//!
//! This library provides a few building blocks for operating on data
//! in parallel, particularly iterators. At the moment, it is not
//! designed to be robust or eke out every last drop of performance,
//! but rather explore some ways in which Rust's type system allows
//! for some fairly fancy things to be written with a guarantee of
//! safety, all without a garbage collector.
//!
//! The only dependency is `std` and the basic functionality has no
//! `unsafe` code at all (the thread pool does use unsafe). Other than
//! the pool, parallelism is built directly from the functionality
//! provided by `std::thread` and `std::sync` and leverages their
//! correctness to automatically ensure the correctness of this
//! library (at the memory safety level).
//!
//! The core design is to simply allow for operations that could occur
//! on a single thread to execute on many, it is not intending to
//! serve as a hard boundary between threads; in particular, if
//! something (a `panic!`) would take down the main thread when run
//! sequentially, it will also take down the main thread (eventually)
//! when run using the functions in this library.
//!
//! On the point of performance and robustness, the top level
//! functions do no thread pooling and so everything essentially
//! spawns a new thread for each element, which is definitely
//! suboptimal for many reasons. Fortunately, not all is lost, the
//! functionality is designed to be as generic as possible, so the
//! iterator functions work with many many iterators, e.g. instead of
//! executing a thread on every element of a vector individually, a
//! user can divide that vector into disjoint sections and spread
//! those across much fewer threads (e.g. [the `chunks`
//! method](http://doc.rust-lang.org/nightly/std/slice/trait.SliceExt.html#tymethod.chunks)).
//!
//! Further, the thread pooling that does exist has a lot of
//! synchronisation overhead, and so is actually rarely a performance
//! improvement (although it is a robustness improvement over the
//! top-level functions, since it limits the number of threads that
//! will be spawned).
//!
//! Either way, **this is not recommended for general use**.
//!
//! # Usage
//!
//! This is [available on
//! crates.io](https://crates.io/crates/simple_parallel). Add this to
//! your Cargo.toml:
//!
//! ```toml
//! [dependencies]
//! simple_parallel = "0.1"
//! ```
//!
//! The latest development version can be obtained [on
//! GitHub](https://github.com/huonw/simple_parallel).
//!
//! # Examples
//!
//! Initialise an array, in parallel.
//!
//! ```rust
//! let mut data = [0; 10];
//! // fill the array, with one thread for each element:
//! simple_parallel::for_(data.iter_mut().enumerate(), |(i, elem)| {
//!     *elem = i as i32;
//! });
//!
//! // now adjust that data, with a threadpool:
//! let mut pool = simple_parallel::Pool::new(4);
//! pool.for_(data.iter_mut(), |elem| *elem *= 2);
//! ```
//!
//! Transform each element of an ordered map in a fancy way, in
//! parallel, with `map` (`map` ensures the output order matches the
//! input order, unlike `unordered_map`),
//!
//! ```rust
//! use std::collections::BTreeMap;
//!
//! let mut map = BTreeMap::new();
//! map.insert('a', 1);
//! map.insert('x', 55);
//!
//! let f = |(&c, &elem): (&char, _)| {
//!     let mut x = elem  * c as i32;
//!     // ... something complicated and expensive ...
//!     return x as f64
//! };
//!
//! // (`IntoIterator` is used, so "direct" iteration like this is fine.)
//! let par_iter = simple_parallel::map(&map, &f);
//!
//! // the computation is executing on several threads in the
//! // background, so that elements are hopefully ready as soon as
//! // possible.
//!
//! for value in par_iter {
//!     println!("I computed {}", value);
//! }
//! ```
//!
//! Sum an arbitrarily long slice, in parallel, by summing subsections and adding
//! everything to a shared mutex, stored on the stack of the main
//! thread. (A parallel fold is currently missing, hence the mutex.)
//!
//! ```rust
//! use std::sync::Mutex;
//!
//! // limit the spew of thread spawning to something sensible
//! const NUM_CHUNKS: usize = 8;
//!
//! fn sum(x: &[f64]) -> f64 {
//!     // (round up)
//!     let elements_per_chunk = (x.len() + NUM_CHUNKS - 1) / NUM_CHUNKS;
//!
//!     let total = Mutex::new(0.0);
//!     simple_parallel::for_(x.chunks(elements_per_chunk), |chunk| {
//!         // sum up this little subsection
//!         let subsum = chunk.iter().fold(0.0, |a, b| a + *b);
//!         *total.lock().unwrap() += subsum;
//!     });
//!
//!     let answer = *total.lock().unwrap();
//!     answer
//! }
//! ```
//!
//! Alternatively, one could use a thread pool, and assign an absolute
//! number of elements to each subsection and let the pool manage
//! distributing the work among threads, instead of being forced to
//! computing the length of the subsections to limit the number of
//! threads spawned.
//!
//! ```rust
//! use std::sync::Mutex;
//!
//! // limit the spew of thread spawning to something sensible
//! const ELEMS_PER_JOB: usize = 1_000;
//!
//! fn pooled_sum(pool: &mut simple_parallel::Pool, x: &[f64]) -> f64 {
//!     let total = Mutex::new(0.0);
//!     pool.for_(x.chunks(ELEMS_PER_JOB), |chunk| {
//!         // sum up this little subsection
//!         let subsum = chunk.iter().fold(0.0, |a, b| a + *b);
//!         *total.lock().unwrap() += subsum;
//!     });
//!
//!     let answer = *total.lock().unwrap();
//!     answer
//! }
//! ```
//!
//! A sketch of a very simple recursive parallel merge-sort, using
//! `both` to handle the recursion. (A working implementation may
//! really need some temporary buffers to mangle the data, but the key
//! point is `both` naturally running things in parallel.)
//!
//! ```rust
//! /// Merges the two sorted runs `left` and `right`.
//! /// That is, after `merge(left, right)`,
//! ///
//! ///    left[0] <= left[1] <= ... <= left[last] <= right[0] <= ...
//! fn merge<T: Ord>(left: &mut [T], right: &mut [T]) {
//!     // magic (but non-parallel, so boring)
//! }
//!
//! fn parallel_merge_sort<T: Ord + Send>(x: &mut [T]) {
//!    // base case
//!    if x.len() <= 1 { return }
//!
//!    // get two disjoint halves of the `x`,
//!    let half = x.len() / 2;
//!    let (left, right) = x.split_at_mut(half);
//!    // and sort them recursively, in parallel
//!    simple_parallel::both(&mut *left, &mut *right, |v| parallel_merge_sort(v));
//!
//!    // now combine the two sorted halves
//!    merge(left, right)
//! }
//! ```
//!
//! The [`examples`
//! folder](https://github.com/huonw/simple_parallel/tree/master/examples)
//! contains more intricate example(s), such as a parallel fast
//! Fourier transform implementation (it really works, and the
//! parallelism does buy something... when tuned).
#![feature(core)]

use std::thread;
use std::iter::IntoIterator;

mod maps;

pub mod pool;

pub mod one_to_one {
    pub use maps::{unordered_map, UnorderedParMap, map, ParMap};
}

pub use one_to_one::{map, unordered_map};

pub use pool::Pool;

/// Execute `f` on each element of `iter`, in their own `scoped`
/// thread.
///
/// If `f` panics, so does `for_`. If this occurs, the number of
/// elements of `iter` that have had `f` called on them is
/// unspecified.
pub fn for_<I: IntoIterator, F>(iter: I, ref f: F)
    where I::Item: Send, F: Fn(I::Item) + Sync
{
    let _guards: Vec<_> = iter.into_iter().map(|elem| {
        thread::scoped(move || {
            f(elem)
        })
    }).collect();
}

/// Execute `f` on both `x` and `y`, in parallel, returning the
/// result.
///
/// This is the same (including panic semantics) as `(f(x), f(y))`, up
/// to ordering. It is designed to be used for divide-and-conquer
/// algorithms.
pub fn both<T, U, F>(x: T, y: T, ref f: F) -> (U, U)
    where T: Send,
          U: Send,
          F: Sync + Fn(T) -> U
{
    let guard = thread::scoped(move || f(y));
    let a = f(x);
    let b = guard.join();
    (a, b)
}
<commit_msg>Add scoped feature.<commit_after>//! Straight-forward functions and types for basic data parallel
//! operations.
//!
//! This library provides a few building blocks for operating on data
//! in parallel, particularly iterators. At the moment, it is not
//! designed to be robust or eke out every last drop of performance,
//! but rather explore some ways in which Rust's type system allows
//! for some fairly fancy things to be written with a guarantee of
//! safety, all without a garbage collector.
//!
//! The only dependency is `std` and the basic functionality has no
//! `unsafe` code at all (the thread pool does use unsafe). Other than
//! the pool, parallelism is built directly from the functionality
//! provided by `std::thread` and `std::sync` and leverages their
//! correctness to automatically ensure the correctness of this
//! library (at the memory safety level).
//!
//! The core design is to simply allow for operations that could occur
//! on a single thread to execute on many, it is not intending to
//! serve as a hard boundary between threads; in particular, if
//! something (a `panic!`) would take down the main thread when run
//! sequentially, it will also take down the main thread (eventually)
//! when run using the functions in this library.
//!
//! On the point of performance and robustness, the top level
//! functions do no thread pooling and so everything essentially
//! spawns a new thread for each element, which is definitely
//! suboptimal for many reasons. Fortunately, not all is lost, the
//! functionality is designed to be as generic as possible, so the
//! iterator functions work with many many iterators, e.g. instead of
//! executing a thread on every element of a vector individually, a
//! user can divide that vector into disjoint sections and spread
//! those across much fewer threads (e.g. [the `chunks`
//! method](http://doc.rust-lang.org/nightly/std/slice/trait.SliceExt.html#tymethod.chunks)).
//!
//! Further, the thread pooling that does exist has a lot of
//! synchronisation overhead, and so is actually rarely a performance
//! improvement (although it is a robustness improvement over the
//! top-level functions, since it limits the number of threads that
//! will be spawned).
//!
//! Either way, **this is not recommended for general use**.
//!
//! # Usage
//!
//! This is [available on
//! crates.io](https://crates.io/crates/simple_parallel). Add this to
//! your Cargo.toml:
//!
//! ```toml
//! [dependencies]
//! simple_parallel = "0.1"
//! ```
//!
//! The latest development version can be obtained [on
//! GitHub](https://github.com/huonw/simple_parallel).
//!
//! # Examples
//!
//! Initialise an array, in parallel.
//!
//! ```rust
//! let mut data = [0; 10];
//! // fill the array, with one thread for each element:
//! simple_parallel::for_(data.iter_mut().enumerate(), |(i, elem)| {
//!     *elem = i as i32;
//! });
//!
//! // now adjust that data, with a threadpool:
//! let mut pool = simple_parallel::Pool::new(4);
//! pool.for_(data.iter_mut(), |elem| *elem *= 2);
//! ```
//!
//! Transform each element of an ordered map in a fancy way, in
//! parallel, with `map` (`map` ensures the output order matches the
//! input order, unlike `unordered_map`),
//!
//! ```rust
//! use std::collections::BTreeMap;
//!
//! let mut map = BTreeMap::new();
//! map.insert('a', 1);
//! map.insert('x', 55);
//!
//! let f = |(&c, &elem): (&char, _)| {
//!     let mut x = elem  * c as i32;
//!     // ... something complicated and expensive ...
//!     return x as f64
//! };
//!
//! // (`IntoIterator` is used, so "direct" iteration like this is fine.)
//! let par_iter = simple_parallel::map(&map, &f);
//!
//! // the computation is executing on several threads in the
//! // background, so that elements are hopefully ready as soon as
//! // possible.
//!
//! for value in par_iter {
//!     println!("I computed {}", value);
//! }
//! ```
//!
//! Sum an arbitrarily long slice, in parallel, by summing subsections and adding
//! everything to a shared mutex, stored on the stack of the main
//! thread. (A parallel fold is currently missing, hence the mutex.)
//!
//! ```rust
//! use std::sync::Mutex;
//!
//! // limit the spew of thread spawning to something sensible
//! const NUM_CHUNKS: usize = 8;
//!
//! fn sum(x: &[f64]) -> f64 {
//!     // (round up)
//!     let elements_per_chunk = (x.len() + NUM_CHUNKS - 1) / NUM_CHUNKS;
//!
//!     let total = Mutex::new(0.0);
//!     simple_parallel::for_(x.chunks(elements_per_chunk), |chunk| {
//!         // sum up this little subsection
//!         let subsum = chunk.iter().fold(0.0, |a, b| a + *b);
//!         *total.lock().unwrap() += subsum;
//!     });
//!
//!     let answer = *total.lock().unwrap();
//!     answer
//! }
//! ```
//!
//! Alternatively, one could use a thread pool, and assign an absolute
//! number of elements to each subsection and let the pool manage
//! distributing the work among threads, instead of being forced to
//! computing the length of the subsections to limit the number of
//! threads spawned.
//!
//! ```rust
//! use std::sync::Mutex;
//!
//! // limit the spew of thread spawning to something sensible
//! const ELEMS_PER_JOB: usize = 1_000;
//!
//! fn pooled_sum(pool: &mut simple_parallel::Pool, x: &[f64]) -> f64 {
//!     let total = Mutex::new(0.0);
//!     pool.for_(x.chunks(ELEMS_PER_JOB), |chunk| {
//!         // sum up this little subsection
//!         let subsum = chunk.iter().fold(0.0, |a, b| a + *b);
//!         *total.lock().unwrap() += subsum;
//!     });
//!
//!     let answer = *total.lock().unwrap();
//!     answer
//! }
//! ```
//!
//! A sketch of a very simple recursive parallel merge-sort, using
//! `both` to handle the recursion. (A working implementation may
//! really need some temporary buffers to mangle the data, but the key
//! point is `both` naturally running things in parallel.)
//!
//! ```rust
//! /// Merges the two sorted runs `left` and `right`.
//! /// That is, after `merge(left, right)`,
//! ///
//! ///    left[0] <= left[1] <= ... <= left[last] <= right[0] <= ...
//! fn merge<T: Ord>(left: &mut [T], right: &mut [T]) {
//!     // magic (but non-parallel, so boring)
//! }
//!
//! fn parallel_merge_sort<T: Ord + Send>(x: &mut [T]) {
//!    // base case
//!    if x.len() <= 1 { return }
//!
//!    // get two disjoint halves of the `x`,
//!    let half = x.len() / 2;
//!    let (left, right) = x.split_at_mut(half);
//!    // and sort them recursively, in parallel
//!    simple_parallel::both(&mut *left, &mut *right, |v| parallel_merge_sort(v));
//!
//!    // now combine the two sorted halves
//!    merge(left, right)
//! }
//! ```
//!
//! The [`examples`
//! folder](https://github.com/huonw/simple_parallel/tree/master/examples)
//! contains more intricate example(s), such as a parallel fast
//! Fourier transform implementation (it really works, and the
//! parallelism does buy something... when tuned).
#![feature(core, scoped)]

use std::thread;
use std::iter::IntoIterator;

mod maps;

pub mod pool;

pub mod one_to_one {
    pub use maps::{unordered_map, UnorderedParMap, map, ParMap};
}

pub use one_to_one::{map, unordered_map};

pub use pool::Pool;

/// Execute `f` on each element of `iter`, in their own `scoped`
/// thread.
///
/// If `f` panics, so does `for_`. If this occurs, the number of
/// elements of `iter` that have had `f` called on them is
/// unspecified.
pub fn for_<I: IntoIterator, F>(iter: I, ref f: F)
    where I::Item: Send, F: Fn(I::Item) + Sync
{
    let _guards: Vec<_> = iter.into_iter().map(|elem| {
        thread::scoped(move || {
            f(elem)
        })
    }).collect();
}

/// Execute `f` on both `x` and `y`, in parallel, returning the
/// result.
///
/// This is the same (including panic semantics) as `(f(x), f(y))`, up
/// to ordering. It is designed to be used for divide-and-conquer
/// algorithms.
pub fn both<T, U, F>(x: T, y: T, ref f: F) -> (U, U)
    where T: Send,
          U: Send,
          F: Sync + Fn(T) -> U
{
    let guard = thread::scoped(move || f(y));
    let a = f(x);
    let b = guard.join();
    (a, b)
}
<|endoftext|>


<commit_before>// Copyright 2013-2019 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.

//! Terminal formatting library.
//!
//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
//! Terminal][ansi] to provide color printing, among other things. There are two
//! implementations, the `TerminfoTerminal`, which uses control characters from
//! a [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
//! API][win].
//!
//! # Usage
//!
//! This crate is [on crates.io](https://crates.io/crates/term) and can be
//! used by adding `term` to the dependencies in your project's `Cargo.toml`.
//!
//! ```toml
//! [dependencies]
//!
//! term = "0.4.6"
//! ```
//!
//! and this to your crate root:
//!
//! ```rust
//! extern crate term;
//! ```
//!
//! # Examples
//!
//! ```no_run
//! extern crate term;
//! use std::io::prelude::*;
//!
//! fn main() {
//!     let mut t = term::stdout().unwrap();
//!
//!     t.fg(term::color::GREEN).unwrap();
//!     write!(t, "hello, ").unwrap();
//!
//!     t.fg(term::color::RED).unwrap();
//!     writeln!(t, "world!").unwrap();
//!
//!     t.reset().unwrap();
//! }
//! ```
//!
//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
//! [ti]: https://en.wikipedia.org/wiki/Terminfo

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
    html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
    html_root_url = "https://stebalien.github.io/doc/term/term/",
    test(attr(deny(warnings)))
)]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]

use std::io::prelude::*;

pub use crate::terminfo::TerminfoTerminal;
#[cfg(windows)]
pub use win::{WinConsole, WinConsoleInfo};

use std::io::{self, Stderr, Stdout};

pub mod terminfo;

#[cfg(windows)]
mod win;

/// Alias for stdout terminals.
pub type StdoutTerminal = dyn Terminal<Output = Stdout> + Send;
/// Alias for stderr terminals.
pub type StderrTerminal = dyn Terminal<Output = Stderr> + Send;

#[cfg(not(windows))]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<StdoutTerminal>> {
    TerminfoTerminal::new(io::stdout()).map(|t| Box::new(t) as Box<StdoutTerminal>)
}

#[cfg(windows)]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<StdoutTerminal>> {
    TerminfoTerminal::new(io::stdout())
        .map(|t| Box::new(t) as Box<StdoutTerminal>)
        .or_else(|| {
            WinConsole::new(io::stdout())
                .ok()
                .map(|t| Box::new(t) as Box<StdoutTerminal>)
        })
}

#[cfg(not(windows))]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<StderrTerminal>> {
    TerminfoTerminal::new(io::stderr()).map(|t| Box::new(t) as Box<StderrTerminal>)
}

#[cfg(windows)]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<StderrTerminal>> {
    TerminfoTerminal::new(io::stderr())
        .map(|t| Box::new(t) as Box<StderrTerminal>)
        .or_else(|| {
            WinConsole::new(io::stderr())
                .ok()
                .map(|t| Box::new(t) as Box<StderrTerminal>)
        })
}

/// Terminal color definitions
#[allow(missing_docs)]
pub mod color {
    /// Number for a terminal color
    pub type Color = u32;

    pub const BLACK: Color = 0;
    pub const RED: Color = 1;
    pub const GREEN: Color = 2;
    pub const YELLOW: Color = 3;
    pub const BLUE: Color = 4;
    pub const MAGENTA: Color = 5;
    pub const CYAN: Color = 6;
    pub const WHITE: Color = 7;

    pub const BRIGHT_BLACK: Color = 8;
    pub const BRIGHT_RED: Color = 9;
    pub const BRIGHT_GREEN: Color = 10;
    pub const BRIGHT_YELLOW: Color = 11;
    pub const BRIGHT_BLUE: Color = 12;
    pub const BRIGHT_MAGENTA: Color = 13;
    pub const BRIGHT_CYAN: Color = 14;
    pub const BRIGHT_WHITE: Color = 15;
}

/// Terminal attributes for use with term.attr().
///
/// Most attributes can only be turned on and must be turned off with term.reset().
/// The ones that can be turned off explicitly take a boolean value.
/// Color is also represented as an attribute for convenience.
#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
pub enum Attr {
    /// Bold (or possibly bright) mode
    Bold,
    /// Dim mode, also called faint or half-bright. Often not supported
    Dim,
    /// Italics mode. Often not supported
    Italic(bool),
    /// Underline mode
    Underline(bool),
    /// Blink mode
    Blink,
    /// Standout mode. Often implemented as Reverse, sometimes coupled with Bold
    Standout(bool),
    /// Reverse mode, inverts the foreground and background colors
    Reverse,
    /// Secure mode, also called invis mode. Hides the printed text
    Secure,
    /// Convenience attribute to set the foreground color
    ForegroundColor(color::Color),
    /// Convenience attribute to set the background color
    BackgroundColor(color::Color),
}

/// An error arising from interacting with the terminal.
#[derive(Debug)]
pub enum Error {
    /// Indicates an error from any underlying IO
    Io(io::Error),
    /// Indicates an error during terminfo parsing
    TerminfoParsing(terminfo::Error),
    /// Indicates an error expanding a parameterized string from the terminfo database
    ParameterizedExpansion(terminfo::parm::Error),
    /// Indicates that the terminal does not support the requested operation.
    NotSupported,
    /// Indicates that the `TERM` environment variable was unset, and thus we were unable to detect
    /// which terminal we should be using.
    TermUnset,
    /// Indicates that we were unable to find a terminfo entry for the requested terminal.
    TerminfoEntryNotFound,
    /// Indicates that the cursor could not be moved to the requested position.
    CursorDestinationInvalid,
    /// Indicates that the terminal does not support displaying the requested color.
    ///
    /// This is like `NotSupported`, but more specific.
    ColorOutOfRange,
    #[doc(hidden)]
    /// Please don't match against this - if you do, we can't promise we won't break your crate
    /// with a semver-compliant version bump.
    __Nonexhaustive,
}

// manually implemented because std::io::Error does not implement Eq/PartialEq
impl std::cmp::PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        use crate::Error::*;
        match *self {
            Io(_) => false,
            TerminfoParsing(ref inner1) => match *other {
                TerminfoParsing(ref inner2) => inner1 == inner2,
                _ => false,
            },
            ParameterizedExpansion(ref inner1) => match *other {
                ParameterizedExpansion(ref inner2) => inner1 == inner2,
                _ => false,
            },
            NotSupported => match *other {
                NotSupported => true,
                _ => false,
            },
            TermUnset => match *other {
                TermUnset => true,
                _ => false,
            },
            TerminfoEntryNotFound => match *other {
                TerminfoEntryNotFound => true,
                _ => false,
            },
            CursorDestinationInvalid => match *other {
                CursorDestinationInvalid => true,
                _ => false,
            },
            ColorOutOfRange => match *other {
                ColorOutOfRange => true,
                _ => false,
            },
            __Nonexhaustive => match *other {
                __Nonexhaustive => true,
                _ => false,
            },
        }
    }
}

/// The canonical `Result` type using this crate's Error type.
pub type Result<T> = std::result::Result<T, Error>;

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use std::error::Error;
        if let crate::Error::Io(ref e) = *self {
            write!(f, "{}", e)
        } else {
            f.write_str(self.description())
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        use crate::Error::*;
        match *self {
            Io(ref io) => io.description(),
            TerminfoParsing(ref e) => e.description(),
            ParameterizedExpansion(ref e) => e.description(),
            NotSupported => "operation not supported by the terminal",
            TermUnset => "TERM environment variable unset, unable to detect a terminal",
            TerminfoEntryNotFound => "could not find a terminfo entry for this terminal",
            CursorDestinationInvalid => "could not move cursor to requested position",
            ColorOutOfRange => "color not supported by the terminal",
            __Nonexhaustive => "placeholder variant that shouldn't be used",
        }
    }

    fn cause(&self) -> Option<&dyn std::error::Error> {
        match *self {
            Error::Io(ref io) => Some(io),
            Error::TerminfoParsing(ref e) => Some(e),
            Error::ParameterizedExpansion(ref e) => Some(e),
            _ => None,
        }
    }
}

impl From<Error> for io::Error {
    fn from(err: Error) -> io::Error {
        let kind = match err {
            Error::Io(ref e) => e.kind(),
            _ => io::ErrorKind::Other,
        };
        io::Error::new(kind, err)
    }
}

impl std::convert::From<io::Error> for Error {
    fn from(val: io::Error) -> Self {
        Error::Io(val)
    }
}

impl std::convert::From<terminfo::Error> for Error {
    fn from(val: terminfo::Error) -> Self {
        Error::TerminfoParsing(val)
    }
}

impl std::convert::From<terminfo::parm::Error> for Error {
    fn from(val: terminfo::parm::Error) -> Self {
        Error::ParameterizedExpansion(val)
    }
}

/// A terminal with similar capabilities to an ANSI Terminal
/// (foreground/background colors etc).
pub trait Terminal: Write {
    /// The terminal's output writer type.
    type Output: Write;

    /// Sets the foreground color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
    /// was an error.
    fn fg(&mut self, color: color::Color) -> Result<()>;

    /// Sets the background color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
    /// was an error.
    fn bg(&mut self, color: color::Color) -> Result<()>;

    /// Sets the given terminal attribute, if supported.  Returns `Ok(())` if the attribute is
    /// supported and was sent to the terminal, or `Err(e)` if there was an error or the attribute
    /// wasn't supported.
    fn attr(&mut self, attr: Attr) -> Result<()>;

    /// Returns whether the given terminal attribute is supported.
    fn supports_attr(&self, attr: Attr) -> bool;

    /// Resets all terminal attributes and colors to their defaults.
    ///
    /// Returns `Ok(())` if the reset code was printed, or `Err(e)` if there was an error.
    ///
    /// *Note: This does not flush.*
    ///
    /// That means the reset command may get buffered so, if you aren't planning on doing anything
    /// else that might flush stdout's buffer (e.g. writing a line of text), you should flush after
    /// calling reset.
    fn reset(&mut self) -> Result<()>;

    /// Returns true if reset is supported.
    fn supports_reset(&self) -> bool;

    /// Returns true if color is fully supported.
    ///
    /// If this function returns `true`, `bg`, `fg`, and `reset` will never
    /// return `Err(Error::NotSupported)`.
    fn supports_color(&self) -> bool;

    /// Moves the cursor up one line.
    ///
    /// Returns `Ok(())` if the cursor movement code was printed, or `Err(e)` if there was an
    /// error.
    fn cursor_up(&mut self) -> Result<()>;

    /// Deletes the text from the cursor location to the end of the line.
    ///
    /// Returns `Ok(())` if the deletion code was printed, or `Err(e)` if there was an error.
    fn delete_line(&mut self) -> Result<()>;

    /// Moves the cursor to the left edge of the current line.
    ///
    /// Returns `Ok(true)` if the deletion code was printed, or `Err(e)` if there was an error.
    fn carriage_return(&mut self) -> Result<()>;

    /// Gets an immutable reference to the stream inside
    fn get_ref(&self) -> &Self::Output;

    /// Gets a mutable reference to the stream inside
    fn get_mut(&mut self) -> &mut Self::Output;

    /// Returns the contained stream, destroying the `Terminal`
    fn into_inner(self) -> Self::Output
    where
        Self: Sized;
}
<commit_msg>nit: disable field pun warning<commit_after>// Copyright 2013-2019 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.

//! Terminal formatting library.
//!
//! This crate provides the `Terminal` trait, which abstracts over an [ANSI
//! Terminal][ansi] to provide color printing, among other things. There are two
//! implementations, the `TerminfoTerminal`, which uses control characters from
//! a [terminfo][ti] database, and `WinConsole`, which uses the [Win32 Console
//! API][win].
//!
//! # Usage
//!
//! This crate is [on crates.io](https://crates.io/crates/term) and can be
//! used by adding `term` to the dependencies in your project's `Cargo.toml`.
//!
//! ```toml
//! [dependencies]
//!
//! term = "0.4.6"
//! ```
//!
//! and this to your crate root:
//!
//! ```rust
//! extern crate term;
//! ```
//!
//! # Examples
//!
//! ```no_run
//! extern crate term;
//! use std::io::prelude::*;
//!
//! fn main() {
//!     let mut t = term::stdout().unwrap();
//!
//!     t.fg(term::color::GREEN).unwrap();
//!     write!(t, "hello, ").unwrap();
//!
//!     t.fg(term::color::RED).unwrap();
//!     writeln!(t, "world!").unwrap();
//!
//!     t.reset().unwrap();
//! }
//! ```
//!
//! [ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code
//! [win]: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682010%28v=vs.85%29.aspx
//! [ti]: https://en.wikipedia.org/wiki/Terminfo

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
    html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
    html_root_url = "https://stebalien.github.io/doc/term/term/",
    test(attr(deny(warnings)))
)]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
#![allow(clippy::redundant_field_names)]

use std::io::prelude::*;

pub use crate::terminfo::TerminfoTerminal;
#[cfg(windows)]
pub use win::{WinConsole, WinConsoleInfo};

use std::io::{self, Stderr, Stdout};

pub mod terminfo;

#[cfg(windows)]
mod win;

/// Alias for stdout terminals.
pub type StdoutTerminal = dyn Terminal<Output = Stdout> + Send;
/// Alias for stderr terminals.
pub type StderrTerminal = dyn Terminal<Output = Stderr> + Send;

#[cfg(not(windows))]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<StdoutTerminal>> {
    TerminfoTerminal::new(io::stdout()).map(|t| Box::new(t) as Box<StdoutTerminal>)
}

#[cfg(windows)]
/// Return a Terminal wrapping stdout, or None if a terminal couldn't be
/// opened.
pub fn stdout() -> Option<Box<StdoutTerminal>> {
    TerminfoTerminal::new(io::stdout())
        .map(|t| Box::new(t) as Box<StdoutTerminal>)
        .or_else(|| {
            WinConsole::new(io::stdout())
                .ok()
                .map(|t| Box::new(t) as Box<StdoutTerminal>)
        })
}

#[cfg(not(windows))]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<StderrTerminal>> {
    TerminfoTerminal::new(io::stderr()).map(|t| Box::new(t) as Box<StderrTerminal>)
}

#[cfg(windows)]
/// Return a Terminal wrapping stderr, or None if a terminal couldn't be
/// opened.
pub fn stderr() -> Option<Box<StderrTerminal>> {
    TerminfoTerminal::new(io::stderr())
        .map(|t| Box::new(t) as Box<StderrTerminal>)
        .or_else(|| {
            WinConsole::new(io::stderr())
                .ok()
                .map(|t| Box::new(t) as Box<StderrTerminal>)
        })
}

/// Terminal color definitions
#[allow(missing_docs)]
pub mod color {
    /// Number for a terminal color
    pub type Color = u32;

    pub const BLACK: Color = 0;
    pub const RED: Color = 1;
    pub const GREEN: Color = 2;
    pub const YELLOW: Color = 3;
    pub const BLUE: Color = 4;
    pub const MAGENTA: Color = 5;
    pub const CYAN: Color = 6;
    pub const WHITE: Color = 7;

    pub const BRIGHT_BLACK: Color = 8;
    pub const BRIGHT_RED: Color = 9;
    pub const BRIGHT_GREEN: Color = 10;
    pub const BRIGHT_YELLOW: Color = 11;
    pub const BRIGHT_BLUE: Color = 12;
    pub const BRIGHT_MAGENTA: Color = 13;
    pub const BRIGHT_CYAN: Color = 14;
    pub const BRIGHT_WHITE: Color = 15;
}

/// Terminal attributes for use with term.attr().
///
/// Most attributes can only be turned on and must be turned off with term.reset().
/// The ones that can be turned off explicitly take a boolean value.
/// Color is also represented as an attribute for convenience.
#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)]
pub enum Attr {
    /// Bold (or possibly bright) mode
    Bold,
    /// Dim mode, also called faint or half-bright. Often not supported
    Dim,
    /// Italics mode. Often not supported
    Italic(bool),
    /// Underline mode
    Underline(bool),
    /// Blink mode
    Blink,
    /// Standout mode. Often implemented as Reverse, sometimes coupled with Bold
    Standout(bool),
    /// Reverse mode, inverts the foreground and background colors
    Reverse,
    /// Secure mode, also called invis mode. Hides the printed text
    Secure,
    /// Convenience attribute to set the foreground color
    ForegroundColor(color::Color),
    /// Convenience attribute to set the background color
    BackgroundColor(color::Color),
}

/// An error arising from interacting with the terminal.
#[derive(Debug)]
pub enum Error {
    /// Indicates an error from any underlying IO
    Io(io::Error),
    /// Indicates an error during terminfo parsing
    TerminfoParsing(terminfo::Error),
    /// Indicates an error expanding a parameterized string from the terminfo database
    ParameterizedExpansion(terminfo::parm::Error),
    /// Indicates that the terminal does not support the requested operation.
    NotSupported,
    /// Indicates that the `TERM` environment variable was unset, and thus we were unable to detect
    /// which terminal we should be using.
    TermUnset,
    /// Indicates that we were unable to find a terminfo entry for the requested terminal.
    TerminfoEntryNotFound,
    /// Indicates that the cursor could not be moved to the requested position.
    CursorDestinationInvalid,
    /// Indicates that the terminal does not support displaying the requested color.
    ///
    /// This is like `NotSupported`, but more specific.
    ColorOutOfRange,
    #[doc(hidden)]
    /// Please don't match against this - if you do, we can't promise we won't break your crate
    /// with a semver-compliant version bump.
    __Nonexhaustive,
}

// manually implemented because std::io::Error does not implement Eq/PartialEq
impl std::cmp::PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        use crate::Error::*;
        match *self {
            Io(_) => false,
            TerminfoParsing(ref inner1) => match *other {
                TerminfoParsing(ref inner2) => inner1 == inner2,
                _ => false,
            },
            ParameterizedExpansion(ref inner1) => match *other {
                ParameterizedExpansion(ref inner2) => inner1 == inner2,
                _ => false,
            },
            NotSupported => match *other {
                NotSupported => true,
                _ => false,
            },
            TermUnset => match *other {
                TermUnset => true,
                _ => false,
            },
            TerminfoEntryNotFound => match *other {
                TerminfoEntryNotFound => true,
                _ => false,
            },
            CursorDestinationInvalid => match *other {
                CursorDestinationInvalid => true,
                _ => false,
            },
            ColorOutOfRange => match *other {
                ColorOutOfRange => true,
                _ => false,
            },
            __Nonexhaustive => match *other {
                __Nonexhaustive => true,
                _ => false,
            },
        }
    }
}

/// The canonical `Result` type using this crate's Error type.
pub type Result<T> = std::result::Result<T, Error>;

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use std::error::Error;
        if let crate::Error::Io(ref e) = *self {
            write!(f, "{}", e)
        } else {
            f.write_str(self.description())
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        use crate::Error::*;
        match *self {
            Io(ref io) => io.description(),
            TerminfoParsing(ref e) => e.description(),
            ParameterizedExpansion(ref e) => e.description(),
            NotSupported => "operation not supported by the terminal",
            TermUnset => "TERM environment variable unset, unable to detect a terminal",
            TerminfoEntryNotFound => "could not find a terminfo entry for this terminal",
            CursorDestinationInvalid => "could not move cursor to requested position",
            ColorOutOfRange => "color not supported by the terminal",
            __Nonexhaustive => "placeholder variant that shouldn't be used",
        }
    }

    fn cause(&self) -> Option<&dyn std::error::Error> {
        match *self {
            Error::Io(ref io) => Some(io),
            Error::TerminfoParsing(ref e) => Some(e),
            Error::ParameterizedExpansion(ref e) => Some(e),
            _ => None,
        }
    }
}

impl From<Error> for io::Error {
    fn from(err: Error) -> io::Error {
        let kind = match err {
            Error::Io(ref e) => e.kind(),
            _ => io::ErrorKind::Other,
        };
        io::Error::new(kind, err)
    }
}

impl std::convert::From<io::Error> for Error {
    fn from(val: io::Error) -> Self {
        Error::Io(val)
    }
}

impl std::convert::From<terminfo::Error> for Error {
    fn from(val: terminfo::Error) -> Self {
        Error::TerminfoParsing(val)
    }
}

impl std::convert::From<terminfo::parm::Error> for Error {
    fn from(val: terminfo::parm::Error) -> Self {
        Error::ParameterizedExpansion(val)
    }
}

/// A terminal with similar capabilities to an ANSI Terminal
/// (foreground/background colors etc).
pub trait Terminal: Write {
    /// The terminal's output writer type.
    type Output: Write;

    /// Sets the foreground color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
    /// was an error.
    fn fg(&mut self, color: color::Color) -> Result<()>;

    /// Sets the background color to the given color.
    ///
    /// If the color is a bright color, but the terminal only supports 8 colors,
    /// the corresponding normal color will be used instead.
    ///
    /// Returns `Ok(())` if the color change code was sent to the terminal, or `Err(e)` if there
    /// was an error.
    fn bg(&mut self, color: color::Color) -> Result<()>;

    /// Sets the given terminal attribute, if supported.  Returns `Ok(())` if the attribute is
    /// supported and was sent to the terminal, or `Err(e)` if there was an error or the attribute
    /// wasn't supported.
    fn attr(&mut self, attr: Attr) -> Result<()>;

    /// Returns whether the given terminal attribute is supported.
    fn supports_attr(&self, attr: Attr) -> bool;

    /// Resets all terminal attributes and colors to their defaults.
    ///
    /// Returns `Ok(())` if the reset code was printed, or `Err(e)` if there was an error.
    ///
    /// *Note: This does not flush.*
    ///
    /// That means the reset command may get buffered so, if you aren't planning on doing anything
    /// else that might flush stdout's buffer (e.g. writing a line of text), you should flush after
    /// calling reset.
    fn reset(&mut self) -> Result<()>;

    /// Returns true if reset is supported.
    fn supports_reset(&self) -> bool;

    /// Returns true if color is fully supported.
    ///
    /// If this function returns `true`, `bg`, `fg`, and `reset` will never
    /// return `Err(Error::NotSupported)`.
    fn supports_color(&self) -> bool;

    /// Moves the cursor up one line.
    ///
    /// Returns `Ok(())` if the cursor movement code was printed, or `Err(e)` if there was an
    /// error.
    fn cursor_up(&mut self) -> Result<()>;

    /// Deletes the text from the cursor location to the end of the line.
    ///
    /// Returns `Ok(())` if the deletion code was printed, or `Err(e)` if there was an error.
    fn delete_line(&mut self) -> Result<()>;

    /// Moves the cursor to the left edge of the current line.
    ///
    /// Returns `Ok(true)` if the deletion code was printed, or `Err(e)` if there was an error.
    fn carriage_return(&mut self) -> Result<()>;

    /// Gets an immutable reference to the stream inside
    fn get_ref(&self) -> &Self::Output;

    /// Gets a mutable reference to the stream inside
    fn get_mut(&mut self) -> &mut Self::Output;

    /// Returns the contained stream, destroying the `Terminal`
    fn into_inner(self) -> Self::Output
    where
        Self: Sized;
}
<|endoftext|>


<commit_before>//! Tiny OBJ loader, inspired by Syoyo's excellent [tinyobjloader](https://github.com/syoyo/tinyobjloader)

#![allow(dead_code)]

use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
use std::collections::BTreeMap;
use std::str::{FromStr, Words};

/// A mesh for some model containing its triangle geometry
/// This object could be a single polygon group or object within a file
/// that defines multiple groups/objects or be the only mesh within the file
/// if it only contains a single mesh
#[derive(Debug)]
pub struct Mesh {
    positions: Vec<f32>,
    normals: Option<Vec<f32>>,
    texcoords: Option<Vec<f32>>,
    faces: Vec<u32>,
}

impl Mesh {
    /// Create a new mesh specifying the geometry for the mesh
    pub fn new(pos: Vec<f32>, norm: Option<Vec<f32>>, tex: Option<Vec<f32>>, faces: Vec<u32>) -> Mesh {
        Mesh { positions: pos, normals: norm, texcoords: tex, faces: faces }
    }
}

/// A named model within the file
/// This could be a group or object or the single model exported by the file
#[derive(Debug)]
pub struct Model {
    mesh: Mesh,
    name: String,
}

impl Model {
    /// Create a new model, associating a name with a mesh
    pub fn new(mesh: Mesh, name: String) -> Model {
        Model { mesh: mesh, name: name }
    }
}

/// TODO: Decide on various errors we'll return
#[derive(Debug)]
pub enum LoadError {
    OpenFileFailed,
    ReadError,
    UnrecognizedCharacter,
    PositionParseError,
    NormalParseError,
    TexcoordParseError,
    FaceParseError,
    GenericFailure,
}

/// LoadResult is a result containing all the models loaded from the file or any
/// error that occured while loading
pub type LoadResult = Result<Vec<Model>, LoadError>;

/// Struct storing indices corresponding to the vertex
/// Some vertices may not have texcoords or normals, 0 is used to indicate this
/// as OBJ indices begin at 1
/// TODO: Should use std::btree_map::BTreeMap to store the mapping of VertexIndices -> index
#[derive(Eq, PartialEq, PartialOrd, Ord, Debug)]
struct VertexIndices {
    v: u32,
    vt: u32,
    vn: u32,
}

impl VertexIndices {
    /// Parse the vertex indices from the face string
    /// Valid face strings are those that are valid for a Wavefront OBJ file
    /// Returns None if the face string is invalid
    pub fn parse(face_str: &str) -> Option<VertexIndices> {
        println!("Parsing face string {}", face_str);
        let mut indices = [0; 3];
        for i in face_str.split('/').enumerate() {
            println!("Index: {}, element index: {}", i.1, i.0);
            match FromStr::from_str(i.1) {
                Ok(x) => indices[i.0] = x,
                Err(_) => return None,
            }
        }
        Some(VertexIndices { v: indices[0], vt: indices[1], vn: indices[2] })
    }
}

/// Parse the floatn information from the words, words is an iterator over the float strings
/// Returns false if parsing failed
fn parse_floatn(val_str: Words, vals: &mut Vec<f32>, n: usize) -> bool {
    let sz = vals.len();
    for p in val_str {
        match FromStr::from_str(p) {
            Ok(x) => vals.push(x),
            Err(_) => return false,
        }
    }
    // Require that we found an x, y, z coordinate
    sz + n == vals.len()
}

/// Parse vertex indices for a face and create an entry in the vertex map if needed
/// The new index for the face's vertex will be next (and then next + 1 and so on if more are
/// needed). Returns false if parsing a face failed
/// TODO: We actually should take a mesh here and update its values. This method won't work well
fn parse_face(face_str: Words, next: &mut u32, vertex_map: &mut BTreeMap<VertexIndices, u32>) -> bool {
    // TODO: Triangulate faces
    for f in face_str {
        match VertexIndices::parse(f) {
            Some(v) => {
                // TODO: We need the mesh here b/c if we don't find the vertex we need
                // to create it in the mesh and push a new index. Not just stick it in the map
                if !vertex_map.contains_key(&v) {
                    vertex_map.insert(v, *next);
                    *next = *next + 1;
                }
            },
            None => return false,
        }
    }
    true
}

/// Load the various meshes in an OBJ file
pub fn load_obj(file_name: &str) -> LoadResult {
    println!("Loading file {}", file_name);
    let file = match File::open(file_name) {
        Ok(f) => f,
        Err(e) => {
            println!("tobj::load_obj - failed to open {} due to {}", file_name, e);
            return Err(LoadError::OpenFileFailed);
        },
    };
    let mut reader = BufReader::new(file);
    load_obj_buf(&mut reader)
}

/// Load the various meshes in an OBJ buffer
pub fn load_obj_buf<B: BufRead>(reader: &mut B) -> LoadResult {
    let mut models = Vec::new();
    let mut tmp_pos = Vec::new();
    let mut tmp_normals = Vec::new();
    let mut tmp_texcoords = Vec::new();
    let mut tmp_idx_map = BTreeMap::new();
    let mut next = 0;
    for line in reader.lines() {
        // We just need the line for debugging for a bit
        let (line, mut words) = match line {
            Ok(ref line) => (&line[..], line[..].words()),
            Err(e) => {
                println!("tobj::load_obj - failed to read line due to {}", e);
                return Err(LoadError::ReadError);
            },
        };
        match words.next() {
            Some("#") => { println!("Skipping comment"); continue; },
            Some("v") => {
                println!("Will parse vertex {}", line);
                if !parse_floatn(words, &mut tmp_pos, 3) {
                    return Err(LoadError::PositionParseError);
                }
            },
            Some("vt") => {
                println!("Will parse texcoord {}", line);
                if !parse_floatn(words, &mut tmp_texcoords, 2) {
                    return Err(LoadError::TexcoordParseError);
                }
            },
            Some("vn") => {
                println!("Will parse normal {}", line);
                if !parse_floatn(words, &mut tmp_normals, 3) {
                    return Err(LoadError::NormalParseError);
                }
            },
            Some("f") => {
                println!("Will parse face {}", line);
                if !parse_face(words, &mut next, &mut tmp_idx_map) {
                    return Err(LoadError::FaceParseError);
                }
            },
            Some("o") => { println!("Will parse object {}", line); },
            Some("g") => { println!("Will parse group {}", line); },
            Some("mtllib") => { println!("Will parse material lib {}", line); },
            Some("usemtl") => { println!("Will parse usemtl {}", line); },
            None => { println!("Skipping empty line"); continue; },
            Some(_) => { println!("Unrecognized character"); return Err(LoadError::UnrecognizedCharacter) },
        }
    }
    println!("Positions: {:?}", tmp_pos);
    println!("Normals: {:?}", tmp_normals);
    println!("Texcoords: {:?}", tmp_texcoords);
    println!("Index Map: {:?}", tmp_idx_map);
    Ok(models)
}

#[test]
fn test_basic(){
    assert!(load_obj("triangle.obj").is_ok());
}

<commit_msg>Switch to HashMap and compile on latest beta<commit_after>//! Tiny OBJ loader, inspired by Syoyo's excellent [tinyobjloader](https://github.com/syoyo/tinyobjloader)

#![allow(dead_code)]

use std::io::prelude::*;
use std::io::BufReader;
use std::fs::File;
use std::collections::HashMap;
use std::str::{FromStr, Split};

/// A mesh for some model containing its triangle geometry
/// This object could be a single polygon group or object within a file
/// that defines multiple groups/objects or be the only mesh within the file
/// if it only contains a single mesh
#[derive(Debug)]
pub struct Mesh {
    positions: Vec<f32>,
    normals: Option<Vec<f32>>,
    texcoords: Option<Vec<f32>>,
    faces: Vec<u32>,
}

impl Mesh {
    /// Create a new mesh specifying the geometry for the mesh
    pub fn new(pos: Vec<f32>, norm: Option<Vec<f32>>, tex: Option<Vec<f32>>, faces: Vec<u32>) -> Mesh {
        Mesh { positions: pos, normals: norm, texcoords: tex, faces: faces }
    }
}

/// A named model within the file
/// This could be a group or object or the single model exported by the file
#[derive(Debug)]
pub struct Model {
    mesh: Mesh,
    name: String,
}

impl Model {
    /// Create a new model, associating a name with a mesh
    pub fn new(mesh: Mesh, name: String) -> Model {
        Model { mesh: mesh, name: name }
    }
}

/// TODO: Decide on various errors we'll return
#[derive(Debug)]
pub enum LoadError {
    OpenFileFailed,
    ReadError,
    UnrecognizedCharacter,
    PositionParseError,
    NormalParseError,
    TexcoordParseError,
    FaceParseError,
    GenericFailure,
}

/// LoadResult is a result containing all the models loaded from the file or any
/// error that occured while loading
pub type LoadResult = Result<Vec<Model>, LoadError>;

/// Struct storing indices corresponding to the vertex
/// Some vertices may not have texcoords or normals, 0 is used to indicate this
/// as OBJ indices begin at 1
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Debug)]
struct VertexIndices {
    v: u32,
    vt: u32,
    vn: u32,
}

impl VertexIndices {
    /// Parse the vertex indices from the face string
    /// Valid face strings are those that are valid for a Wavefront OBJ file
    /// Returns None if the face string is invalid
    pub fn parse(face_str: &str) -> Option<VertexIndices> {
        println!("Parsing face string {}", face_str);
        let mut indices = [0; 3];
        for i in face_str.split('/').enumerate() {
            println!("Index: {}, element index: {}", i.1, i.0);
            match FromStr::from_str(i.1) {
                Ok(x) => indices[i.0] = x,
                Err(_) => return None,
            }
        }
        Some(VertexIndices { v: indices[0], vt: indices[1], vn: indices[2] })
    }
}

/// Parse the floatn information from the words, words is an iterator over the float strings
/// Returns false if parsing failed
fn parse_floatn(val_str: Split<char>, vals: &mut Vec<f32>, n: usize) -> bool {
    let sz = vals.len();
    for p in val_str {
        match FromStr::from_str(p) {
            Ok(x) => vals.push(x),
            Err(_) => return false,
        }
    }
    // Require that we found an x, y, z coordinate
    sz + n == vals.len()
}

/// Parse vertex indices for a face and create an entry in the vertex map if needed
/// The new index for the face's vertex will be next (and then next + 1 and so on if more are
/// needed). Returns false if parsing a face failed
/// TODO: We actually should take a mesh here and update its values. This method won't work well
/// TODO: Use HashMap<VertexIndices, u32> here instead
fn parse_face(face_str: Split<char>, next: &mut u32, vertex_map: &mut HashMap<VertexIndices, u32>) -> bool {
    // TODO: Triangulate faces
    for f in face_str {
        match VertexIndices::parse(f) {
            Some(v) => {
                // TODO: We need the mesh here b/c if we don't find the vertex we need
                // to create it in the mesh and push a new index. Not just stick it in the map
                if !vertex_map.contains_key(&v) {
                    vertex_map.insert(v, *next);
                    *next = *next + 1;
                }
            },
            None => return false,
        }
    }
    true
}

/// Load the various meshes in an OBJ file
pub fn load_obj(file_name: &str) -> LoadResult {
    println!("Loading file {}", file_name);
    let file = match File::open(file_name) {
        Ok(f) => f,
        Err(e) => {
            println!("tobj::load_obj - failed to open {} due to {}", file_name, e);
            return Err(LoadError::OpenFileFailed);
        },
    };
    let mut reader = BufReader::new(file);
    load_obj_buf(&mut reader)
}

/// Load the various meshes in an OBJ buffer
pub fn load_obj_buf<B: BufRead>(reader: &mut B) -> LoadResult {
    let mut models = Vec::new();
    let mut tmp_pos = Vec::new();
    let mut tmp_normals = Vec::new();
    let mut tmp_texcoords = Vec::new();
    let mut tmp_idx_map = HashMap::new();
    let mut next = 0;
    for line in reader.lines() {
        // We just need the line for debugging for a bit
        // TODO: Switch back to using `words` when it becomes stable
        let (line, mut words) = match line {
            Ok(ref line) => (&line[..], line[..].split(' ')),
            Err(e) => {
                println!("tobj::load_obj - failed to read line due to {}", e);
                return Err(LoadError::ReadError);
            },
        };
        match words.next() {
            Some("#") => { println!("Skipping comment"); continue; },
            Some("v") => {
                println!("Will parse vertex {}", line);
                if !parse_floatn(words, &mut tmp_pos, 3) {
                    return Err(LoadError::PositionParseError);
                }
            },
            Some("vt") => {
                println!("Will parse texcoord {}", line);
                if !parse_floatn(words, &mut tmp_texcoords, 2) {
                    return Err(LoadError::TexcoordParseError);
                }
            },
            Some("vn") => {
                println!("Will parse normal {}", line);
                if !parse_floatn(words, &mut tmp_normals, 3) {
                    return Err(LoadError::NormalParseError);
                }
            },
            Some("f") => {
                println!("Will parse face {}", line);
                if !parse_face(words, &mut next, &mut tmp_idx_map) {
                    return Err(LoadError::FaceParseError);
                }
            },
            Some("o") => { println!("Will parse object {}", line); },
            Some("g") => { println!("Will parse group {}", line); },
            Some("mtllib") => { println!("Will parse material lib {}", line); },
            Some("usemtl") => { println!("Will parse usemtl {}", line); },
            None => { println!("Skipping empty line"); continue; },
            // TODO: throw error on unrecognized character. Currentl with split we get a newline
            // and incorrectly through so this is off temporarily. Blocked until `words` becomes
            // stable
            Some(c) => { println!("Unrecognized character: {}", c); /*return Err(LoadError::UnrecognizedCharacter) */ },
        }
    }
    println!("Positions: {:?}", tmp_pos);
    println!("Normals: {:?}", tmp_normals);
    println!("Texcoords: {:?}", tmp_texcoords);
    println!("Index Map: {:?}", tmp_idx_map);
    Ok(models)
}

#[test]
fn test_basic(){
    assert!(load_obj("triangle.obj").is_ok());
}

<|endoftext|>