<commit_before>use std::sync::Arc;
use std::sync::mpsc::channel;
use std::mem;

use program::Program;
use index_buffer::IndicesSource;
use vertex_buffer::{VerticesSource, AttributeType};
use {DisplayImpl, GlObject};

use {libc, gl};

///
pub struct VertexArrayObject {
    display: Arc<DisplayImpl>,
    id: gl::types::GLuint,
}

impl VertexArrayObject {
    ///
    fn new(display: Arc<DisplayImpl>, vertex_buffer: VerticesSource,
           ib_id: gl::types::GLuint, program: &Program) -> VertexArrayObject
    {
        let VerticesSource::VertexBuffer(vertex_buffer) = vertex_buffer;
        let bindings = vertex_buffer.get_bindings().clone();
        let vb_elementssize = vertex_buffer.get_elements_size();
        let vertex_buffer = GlObject::get_id(vertex_buffer);
        let attributes = ::program::get_attributes(program);

        // checking the attributes types
        for &(ref name, _, ty) in bindings.iter() {
            let attribute = match attributes.get(name) {
                Some(a) => a,
                None => continue
            };

            if !vertex_type_matches(ty, attribute.ty, attribute.size) {
                panic!("The program attribute `{}` does not match the vertex format", name);
            }
        }

        // checking for missing attributes
        for (&ref name, _) in attributes.iter() {
            if bindings.iter().find(|&&(ref n, _, _)| n == name).is_none() {
                panic!("The program attribute `{}` is missing in the vertex bindings", name);
            }
        };

        let (tx, rx) = channel();

        display.context.exec(move |: ctxt| {
            unsafe {
                let id: gl::types::GLuint = mem::uninitialized();
                ctxt.gl.GenVertexArrays(1, mem::transmute(&id));
                tx.send(id).unwrap();

                ctxt.gl.BindVertexArray(id);
                ctxt.state.vertex_array = id;

                // binding vertex buffer
                if ctxt.state.array_buffer_binding != vertex_buffer {
                    ctxt.gl.BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
                    ctxt.state.array_buffer_binding = vertex_buffer;
                }

                // binding index buffer
                ctxt.gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ib_id);

                // binding attributes
                for (name, offset, ty) in bindings.into_iter() {
                    let (data_type, elements_count) = vertex_binding_type_to_gl(ty);

                    let attribute = match attributes.get(&name) {
                        Some(a) => a,
                        None => continue
                    };

                    if attribute.location != -1 {
                        match data_type {
                            gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT |
                            gl::INT | gl::UNSIGNED_INT =>
                                ctxt.gl.VertexAttribIPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type,
                                    vb_elementssize as i32, offset as *const libc::c_void),

                            _ => ctxt.gl.VertexAttribPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type, 0,
                                    vb_elementssize as i32, offset as *const libc::c_void)
                        }

                        ctxt.gl.EnableVertexAttribArray(attribute.location as u32);
                    }
                }
            }
        });

        VertexArrayObject {
            display: display,
            id: rx.recv().unwrap(),
        }
    }
}

impl Drop for VertexArrayObject {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(move |: ctxt| {
            unsafe {
                // unbinding
                if ctxt.state.vertex_array == id {
                    ctxt.gl.BindVertexArray(0);
                    ctxt.state.vertex_array = 0;
                }

                // deleting
                ctxt.gl.DeleteVertexArrays(1, [ id ].as_ptr());
            }
        });
    }
}

impl GlObject for VertexArrayObject {
    fn get_id(&self) -> gl::types::GLuint {
        self.id
    }
}

pub fn get_vertex_array_object<I>(display: &Arc<DisplayImpl>, vertex_buffer: VerticesSource,
                                  indices: &IndicesSource<I>, program: &Program)
                                  -> gl::types::GLuint where I: ::index_buffer::Index
{
    let ib_id = match indices {
        &IndicesSource::Buffer { .. } => 0,
        &IndicesSource::IndexBuffer { ref buffer, .. } => buffer.get_id()
    };

    let vb_id = match vertex_buffer {
        VerticesSource::VertexBuffer(vb) => vb.get_id(),
    };

    let program_id = program.get_id();

    if let Some(value) = display.vertex_array_objects.lock().unwrap()
                                .get(&(vb_id, ib_id, program_id)) {
        return value.id;
    }

    // we create the new VAO without the mutex locked
    let new_vao = VertexArrayObject::new(display.clone(), vertex_buffer.clone(), ib_id, program);
    let new_vao_id = new_vao.id;
    display.vertex_array_objects.lock().unwrap().insert((vb_id, ib_id, program_id), new_vao);
    new_vao_id
}

fn vertex_binding_type_to_gl(ty: AttributeType) -> (gl::types::GLenum, gl::types::GLint) {
    match ty {
        AttributeType::I8 => (gl::BYTE, 1),
        AttributeType::I8I8 => (gl::BYTE, 2),
        AttributeType::I8I8I8 => (gl::BYTE, 3),
        AttributeType::I8I8I8I8 => (gl::BYTE, 4),
        AttributeType::U8 => (gl::UNSIGNED_BYTE, 1),
        AttributeType::U8U8 => (gl::UNSIGNED_BYTE, 2),
        AttributeType::U8U8U8 => (gl::UNSIGNED_BYTE, 3),
        AttributeType::U8U8U8U8 => (gl::UNSIGNED_BYTE, 4),
        AttributeType::I16 => (gl::SHORT, 1),
        AttributeType::I16I16 => (gl::SHORT, 2),
        AttributeType::I16I16I16 => (gl::SHORT, 3),
        AttributeType::I16I16I16I16 => (gl::SHORT, 4),
        AttributeType::U16 => (gl::UNSIGNED_SHORT, 1),
        AttributeType::U16U16 => (gl::UNSIGNED_SHORT, 2),
        AttributeType::U16U16U16 => (gl::UNSIGNED_SHORT, 3),
        AttributeType::U16U16U16U16 => (gl::UNSIGNED_SHORT, 4),
        AttributeType::I32 => (gl::INT, 1),
        AttributeType::I32I32 => (gl::INT, 2),
        AttributeType::I32I32I32 => (gl::INT, 3),
        AttributeType::I32I32I32I32 => (gl::INT, 4),
        AttributeType::U32 => (gl::UNSIGNED_INT, 1),
        AttributeType::U32U32 => (gl::UNSIGNED_INT, 2),
        AttributeType::U32U32U32 => (gl::UNSIGNED_INT, 3),
        AttributeType::U32U32U32U32 => (gl::UNSIGNED_INT, 4),
        AttributeType::F32 => (gl::FLOAT, 1),
        AttributeType::F32F32 => (gl::FLOAT, 2),
        AttributeType::F32F32F32 => (gl::FLOAT, 3),
        AttributeType::F32F32F32F32 => (gl::FLOAT, 4),
    }
}

fn vertex_type_matches(ty: AttributeType, gl_ty: gl::types::GLenum,
                       gl_size: gl::types::GLint) -> bool
{
    match (ty, gl_ty, gl_size) {
        (AttributeType::I8, gl::BYTE, 1) => true,
        (AttributeType::I8I8, gl::BYTE, 2) => true,
        (AttributeType::I8I8I8, gl::BYTE, 3) => true,
        (AttributeType::I8I8I8I8, gl::BYTE, 4) => true,
        (AttributeType::U8, gl::UNSIGNED_BYTE, 1) => true,
        (AttributeType::U8U8, gl::UNSIGNED_BYTE, 2) => true,
        (AttributeType::U8U8U8, gl::UNSIGNED_BYTE, 3) => true,
        (AttributeType::U8U8U8U8, gl::UNSIGNED_BYTE, 4) => true,
        (AttributeType::I16, gl::SHORT, 1) => true,
        (AttributeType::I16I16, gl::SHORT, 2) => true,
        (AttributeType::I16I16I16, gl::SHORT, 3) => true,
        (AttributeType::I16I16I16I16, gl::SHORT, 4) => true,
        (AttributeType::U16, gl::UNSIGNED_SHORT, 1) => true,
        (AttributeType::U16U16, gl::UNSIGNED_SHORT, 2) => true,
        (AttributeType::U16U16U16, gl::UNSIGNED_SHORT, 3) => true,
        (AttributeType::U16U16U16U16, gl::UNSIGNED_SHORT, 4) => true,
        (AttributeType::I32, gl::INT, 1) => true,
        (AttributeType::I32I32, gl::INT, 2) => true,
        (AttributeType::I32I32, gl::INT_VEC2, 1) => true,
        (AttributeType::I32I32I32, gl::INT, 3) => true,
        (AttributeType::I32I32I32, gl::INT_VEC3, 1) => true,
        (AttributeType::I32I32I32I32, gl::INT, 4) => true,
        (AttributeType::I32I32I32I32, gl::INT_VEC4, 1) => true,
        (AttributeType::I32I32I32I32, gl::INT_VEC2, 2) => true,
        (AttributeType::U32, gl::UNSIGNED_INT, 1) => true,
        (AttributeType::U32U32, gl::UNSIGNED_INT, 2) => true,
        (AttributeType::U32U32, gl::UNSIGNED_INT_VEC2, 1) => true,
        (AttributeType::U32U32U32, gl::UNSIGNED_INT, 3) => true,
        (AttributeType::U32U32U32, gl::UNSIGNED_INT_VEC3, 1) => true,
        (AttributeType::U32U32U32U32, gl::UNSIGNED_INT, 4) => true,
        (AttributeType::U32U32U32U32, gl::UNSIGNED_INT_VEC4, 1) => true,
        (AttributeType::U32U32U32U32, gl::UNSIGNED_INT_VEC2, 2) => true,
        (AttributeType::F32, gl::FLOAT, 1) => true,
        (AttributeType::F32F32, gl::FLOAT, 2) => true,
        (AttributeType::F32F32, gl::FLOAT_VEC2, 1) => true,
        (AttributeType::F32F32F32, gl::FLOAT, 3) => true,
        (AttributeType::F32F32F32, gl::FLOAT_VEC3, 1) => true,
        (AttributeType::F32F32F32F32, gl::FLOAT, 4) => true,
        (AttributeType::F32F32F32F32, gl::FLOAT_VEC4, 1) => true,
        (AttributeType::F32F32F32F32, gl::FLOAT_VEC2, 2) => true,
        _ => false,
    }
}
<commit_msg>Add documentation to VAOs<commit_after>use std::sync::Arc;
use std::sync::mpsc::channel;
use std::mem;

use program::Program;
use index_buffer::IndicesSource;
use vertex_buffer::{VerticesSource, AttributeType};
use {DisplayImpl, GlObject};

use {libc, gl};

/// Stores informations about how to bind a vertex buffer, an index buffer and a program.
pub struct VertexArrayObject {
    display: Arc<DisplayImpl>,
    id: gl::types::GLuint,
}

impl VertexArrayObject {
    /// Builds a new `VertexArrayObject`.
    ///
    /// The vertex buffer, index buffer and program must not outlive the
    /// VAO, and the VB & program attributes must not change.
    fn new(display: Arc<DisplayImpl>, vertex_buffer: VerticesSource,
           ib_id: gl::types::GLuint, program: &Program) -> VertexArrayObject
    {
        let VerticesSource::VertexBuffer(vertex_buffer) = vertex_buffer;
        let bindings = vertex_buffer.get_bindings().clone();
        let vb_elementssize = vertex_buffer.get_elements_size();
        let vertex_buffer = GlObject::get_id(vertex_buffer);
        let attributes = ::program::get_attributes(program);

        // checking the attributes types
        for &(ref name, _, ty) in bindings.iter() {
            let attribute = match attributes.get(name) {
                Some(a) => a,
                None => continue
            };

            if !vertex_type_matches(ty, attribute.ty, attribute.size) {
                panic!("The program attribute `{}` does not match the vertex format", name);
            }
        }

        // checking for missing attributes
        for (&ref name, _) in attributes.iter() {
            if bindings.iter().find(|&&(ref n, _, _)| n == name).is_none() {
                panic!("The program attribute `{}` is missing in the vertex bindings", name);
            }
        };

        let (tx, rx) = channel();

        display.context.exec(move |: ctxt| {
            unsafe {
                // building the VAO
                let id: gl::types::GLuint = mem::uninitialized();
                ctxt.gl.GenVertexArrays(1, mem::transmute(&id));
                tx.send(id).unwrap();

                // we don't use DSA as we're going to make multiple calls for this VAO
                // and we're likely going to use the VAO right after it's been created
                ctxt.gl.BindVertexArray(id);
                ctxt.state.vertex_array = id;

                // binding vertex buffer because glVertexAttribPointer uses the current
                // array buffer
                if ctxt.state.array_buffer_binding != vertex_buffer {
                    ctxt.gl.BindBuffer(gl::ARRAY_BUFFER, vertex_buffer);
                    ctxt.state.array_buffer_binding = vertex_buffer;
                }

                // binding index buffer
                // the ELEMENT_ARRAY_BUFFER is part of the state of the VAO
                ctxt.gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ib_id);

                // binding attributes
                for (name, offset, ty) in bindings.into_iter() {
                    let (data_type, elements_count) = vertex_binding_type_to_gl(ty);

                    let attribute = match attributes.get(&name) {
                        Some(a) => a,
                        None => continue
                    };

                    if attribute.location != -1 {
                        match data_type {
                            gl::BYTE | gl::UNSIGNED_BYTE | gl::SHORT | gl::UNSIGNED_SHORT |
                            gl::INT | gl::UNSIGNED_INT =>
                                ctxt.gl.VertexAttribIPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type,
                                    vb_elementssize as i32, offset as *const libc::c_void),

                            _ => ctxt.gl.VertexAttribPointer(attribute.location as u32,
                                    elements_count as gl::types::GLint, data_type, 0,
                                    vb_elementssize as i32, offset as *const libc::c_void)
                        }

                        ctxt.gl.EnableVertexAttribArray(attribute.location as u32);
                    }
                }
            }
        });

        VertexArrayObject {
            display: display,
            id: rx.recv().unwrap(),
        }
    }
}

impl Drop for VertexArrayObject {
    fn drop(&mut self) {
        let id = self.id.clone();
        self.display.context.exec(move |: ctxt| {
            unsafe {
                // unbinding
                if ctxt.state.vertex_array == id {
                    ctxt.gl.BindVertexArray(0);
                    ctxt.state.vertex_array = 0;
                }

                // deleting
                ctxt.gl.DeleteVertexArrays(1, [ id ].as_ptr());
            }
        });
    }
}

impl GlObject for VertexArrayObject {
    fn get_id(&self) -> gl::types::GLuint {
        self.id
    }
}

/// Obtains the id of the VAO corresponding to the vertex buffer, index buffer and program
/// passed as parameters. Creates a new VAO if no existing one matches these.
pub fn get_vertex_array_object<I>(display: &Arc<DisplayImpl>, vertex_buffer: VerticesSource,
                                  indices: &IndicesSource<I>, program: &Program)
                                  -> gl::types::GLuint where I: ::index_buffer::Index
{
    let ib_id = match indices {
        &IndicesSource::Buffer { .. } => 0,
        &IndicesSource::IndexBuffer { ref buffer, .. } => buffer.get_id()
    };

    let vb_id = match vertex_buffer {
        VerticesSource::VertexBuffer(vb) => vb.get_id(),
    };

    let program_id = program.get_id();

    if let Some(value) = display.vertex_array_objects.lock().unwrap()
                                .get(&(vb_id, ib_id, program_id)) {
        return value.id;
    }

    // we create the new VAO without the mutex locked
    let new_vao = VertexArrayObject::new(display.clone(), vertex_buffer.clone(), ib_id, program);
    let new_vao_id = new_vao.id;
    display.vertex_array_objects.lock().unwrap().insert((vb_id, ib_id, program_id), new_vao);
    new_vao_id
}

fn vertex_binding_type_to_gl(ty: AttributeType) -> (gl::types::GLenum, gl::types::GLint) {
    match ty {
        AttributeType::I8 => (gl::BYTE, 1),
        AttributeType::I8I8 => (gl::BYTE, 2),
        AttributeType::I8I8I8 => (gl::BYTE, 3),
        AttributeType::I8I8I8I8 => (gl::BYTE, 4),
        AttributeType::U8 => (gl::UNSIGNED_BYTE, 1),
        AttributeType::U8U8 => (gl::UNSIGNED_BYTE, 2),
        AttributeType::U8U8U8 => (gl::UNSIGNED_BYTE, 3),
        AttributeType::U8U8U8U8 => (gl::UNSIGNED_BYTE, 4),
        AttributeType::I16 => (gl::SHORT, 1),
        AttributeType::I16I16 => (gl::SHORT, 2),
        AttributeType::I16I16I16 => (gl::SHORT, 3),
        AttributeType::I16I16I16I16 => (gl::SHORT, 4),
        AttributeType::U16 => (gl::UNSIGNED_SHORT, 1),
        AttributeType::U16U16 => (gl::UNSIGNED_SHORT, 2),
        AttributeType::U16U16U16 => (gl::UNSIGNED_SHORT, 3),
        AttributeType::U16U16U16U16 => (gl::UNSIGNED_SHORT, 4),
        AttributeType::I32 => (gl::INT, 1),
        AttributeType::I32I32 => (gl::INT, 2),
        AttributeType::I32I32I32 => (gl::INT, 3),
        AttributeType::I32I32I32I32 => (gl::INT, 4),
        AttributeType::U32 => (gl::UNSIGNED_INT, 1),
        AttributeType::U32U32 => (gl::UNSIGNED_INT, 2),
        AttributeType::U32U32U32 => (gl::UNSIGNED_INT, 3),
        AttributeType::U32U32U32U32 => (gl::UNSIGNED_INT, 4),
        AttributeType::F32 => (gl::FLOAT, 1),
        AttributeType::F32F32 => (gl::FLOAT, 2),
        AttributeType::F32F32F32 => (gl::FLOAT, 3),
        AttributeType::F32F32F32F32 => (gl::FLOAT, 4),
    }
}

fn vertex_type_matches(ty: AttributeType, gl_ty: gl::types::GLenum,
                       gl_size: gl::types::GLint) -> bool
{
    match (ty, gl_ty, gl_size) {
        (AttributeType::I8, gl::BYTE, 1) => true,
        (AttributeType::I8I8, gl::BYTE, 2) => true,
        (AttributeType::I8I8I8, gl::BYTE, 3) => true,
        (AttributeType::I8I8I8I8, gl::BYTE, 4) => true,
        (AttributeType::U8, gl::UNSIGNED_BYTE, 1) => true,
        (AttributeType::U8U8, gl::UNSIGNED_BYTE, 2) => true,
        (AttributeType::U8U8U8, gl::UNSIGNED_BYTE, 3) => true,
        (AttributeType::U8U8U8U8, gl::UNSIGNED_BYTE, 4) => true,
        (AttributeType::I16, gl::SHORT, 1) => true,
        (AttributeType::I16I16, gl::SHORT, 2) => true,
        (AttributeType::I16I16I16, gl::SHORT, 3) => true,
        (AttributeType::I16I16I16I16, gl::SHORT, 4) => true,
        (AttributeType::U16, gl::UNSIGNED_SHORT, 1) => true,
        (AttributeType::U16U16, gl::UNSIGNED_SHORT, 2) => true,
        (AttributeType::U16U16U16, gl::UNSIGNED_SHORT, 3) => true,
        (AttributeType::U16U16U16U16, gl::UNSIGNED_SHORT, 4) => true,
        (AttributeType::I32, gl::INT, 1) => true,
        (AttributeType::I32I32, gl::INT, 2) => true,
        (AttributeType::I32I32, gl::INT_VEC2, 1) => true,
        (AttributeType::I32I32I32, gl::INT, 3) => true,
        (AttributeType::I32I32I32, gl::INT_VEC3, 1) => true,
        (AttributeType::I32I32I32I32, gl::INT, 4) => true,
        (AttributeType::I32I32I32I32, gl::INT_VEC4, 1) => true,
        (AttributeType::I32I32I32I32, gl::INT_VEC2, 2) => true,
        (AttributeType::U32, gl::UNSIGNED_INT, 1) => true,
        (AttributeType::U32U32, gl::UNSIGNED_INT, 2) => true,
        (AttributeType::U32U32, gl::UNSIGNED_INT_VEC2, 1) => true,
        (AttributeType::U32U32U32, gl::UNSIGNED_INT, 3) => true,
        (AttributeType::U32U32U32, gl::UNSIGNED_INT_VEC3, 1) => true,
        (AttributeType::U32U32U32U32, gl::UNSIGNED_INT, 4) => true,
        (AttributeType::U32U32U32U32, gl::UNSIGNED_INT_VEC4, 1) => true,
        (AttributeType::U32U32U32U32, gl::UNSIGNED_INT_VEC2, 2) => true,
        (AttributeType::F32, gl::FLOAT, 1) => true,
        (AttributeType::F32F32, gl::FLOAT, 2) => true,
        (AttributeType::F32F32, gl::FLOAT_VEC2, 1) => true,
        (AttributeType::F32F32F32, gl::FLOAT, 3) => true,
        (AttributeType::F32F32F32, gl::FLOAT_VEC3, 1) => true,
        (AttributeType::F32F32F32F32, gl::FLOAT, 4) => true,
        (AttributeType::F32F32F32F32, gl::FLOAT_VEC4, 1) => true,
        (AttributeType::F32F32F32F32, gl::FLOAT_VEC2, 2) => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>/*!
The ignore crate provides a fast recursive directory iterator that respects
various filters such as globs, file types and `.gitignore` files. The precise
matching rules and precedence is explained in the documentation for
`WalkBuilder`.

Secondarily, this crate exposes gitignore and file type matchers for use cases
that demand more fine-grained control.

# Example

This example shows the most basic usage of this crate. This code will
recursively traverse the current directory while automatically filtering out
files and directories according to ignore globs found in files like
`.ignore` and `.gitignore`:

```rust,no_run
use ignore::Walk;

for result in Walk::new("./") {
    // Each item yielded by the iterator is either a directory entry or an
    // error, so either print the path or the error.
    match result {
        Ok(entry) => println!("{}", entry.path().display()),
        Err(err) => println!("ERROR: {}", err),
    }
}
```

# Example: advanced

By default, the recursive directory iterator will ignore hidden files and
directories. This can be disabled by building the iterator with `WalkBuilder`:

```rust,no_run
use ignore::WalkBuilder;

for result in WalkBuilder::new("./").hidden(false).build() {
    println!("{:?}", result);
}
```

See the documentation for `WalkBuilder` for many other options.
*/

#![deny(missing_docs)]

extern crate crossbeam_channel as channel;
extern crate globset;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate memchr;
extern crate regex;
extern crate same_file;
extern crate thread_local;
extern crate walkdir;
#[cfg(windows)]
extern crate winapi_util;

use std::error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

pub use walk::{DirEntry, Walk, WalkBuilder, WalkParallel, WalkState};

mod dir;
pub mod gitignore;
mod pathutil;
pub mod overrides;
pub mod types;
mod walk;

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    /// A collection of "soft" errors. These occur when adding an ignore
    /// file partially succeeded.
    Partial(Vec<Error>),
    /// An error associated with a specific line number.
    WithLineNumber {
        /// The line number.
        line: u64,
        /// The underlying error.
        err: Box<Error>,
    },
    /// An error associated with a particular file path.
    WithPath {
        /// The file path.
        path: PathBuf,
        /// The underlying error.
        err: Box<Error>,
    },
    /// An error associated with a particular directory depth when recursively
    /// walking a directory.
    WithDepth {
        /// The directory depth.
        depth: usize,
        /// The underlying error.
        err: Box<Error>,
    },
    /// An error that occurs when a file loop is detected when traversing
    /// symbolic links.
    Loop {
        /// The ancestor file path in the loop.
        ancestor: PathBuf,
        /// The child file path in the loop.
        child: PathBuf,
    },
    /// An error that occurs when doing I/O, such as reading an ignore file.
    Io(io::Error),
    /// An error that occurs when trying to parse a glob.
    Glob {
        /// The original glob that caused this error. This glob, when
        /// available, always corresponds to the glob provided by an end user.
        /// e.g., It is the glob as written in a `.gitignore` file.
        ///
        /// (This glob may be distinct from the glob that is actually
        /// compiled, after accounting for `gitignore` semantics.)
        glob: Option<String>,
        /// The underlying glob error as a string.
        err: String,
    },
    /// A type selection for a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
}

impl Clone for Error {
    fn clone(&self) -> Error {
        match *self {
            Error::Partial(ref errs) => Error::Partial(errs.clone()),
            Error::WithLineNumber { line, ref err } => {
                Error::WithLineNumber { line: line, err: err.clone() }
            }
            Error::WithPath { ref path, ref err } => {
                Error::WithPath { path: path.clone(), err: err.clone() }
            }
            Error::WithDepth { depth, ref err } => {
                Error::WithDepth { depth: depth, err: err.clone() }
            }
            Error::Loop { ref ancestor, ref child } => {
                Error::Loop {
                    ancestor: ancestor.clone(),
                    child: child.clone()
                }
            }
            Error::Io(ref err) => {
                match err.raw_os_error() {
                    Some(e) => Error::Io(io::Error::from_raw_os_error(e)),
                    None => {
                        Error::Io(io::Error::new(err.kind(), err.to_string()))
                    }
                }
            }
            Error::Glob { ref glob, ref err } => {
                Error::Glob { glob: glob.clone(), err: err.clone() }
            }
            Error::UnrecognizedFileType(ref err) => {
                Error::UnrecognizedFileType(err.clone())
            }
            Error::InvalidDefinition => Error::InvalidDefinition,
        }
    }
}

impl Error {
    /// Returns true if this is a partial error.
    ///
    /// A partial error occurs when only some operations failed while others
    /// may have succeeded. For example, an ignore file may contain an invalid
    /// glob among otherwise valid globs.
    pub fn is_partial(&self) -> bool {
        match *self {
            Error::Partial(_) => true,
            Error::WithLineNumber { ref err, .. } => err.is_partial(),
            Error::WithPath { ref err, .. } => err.is_partial(),
            Error::WithDepth { ref err, .. } => err.is_partial(),
            _ => false,
        }
    }

    /// Returns true if this error is exclusively an I/O error.
    pub fn is_io(&self) -> bool {
        match *self {
            Error::Partial(ref errs) => errs.len() == 1 && errs[0].is_io(),
            Error::WithLineNumber { ref err, .. } => err.is_io(),
            Error::WithPath { ref err, .. } => err.is_io(),
            Error::WithDepth { ref err, .. } => err.is_io(),
            Error::Loop { .. } => false,
            Error::Io(_) => true,
            Error::Glob { .. } => false,
            Error::UnrecognizedFileType(_) => false,
            Error::InvalidDefinition => false,
        }
    }

    /// Returns a depth associated with recursively walking a directory (if
    /// this error was generated from a recursive directory iterator).
    pub fn depth(&self) -> Option<usize> {
        match *self {
            Error::WithPath { ref err, .. } => err.depth(),
            Error::WithDepth { depth, .. } => Some(depth),
            _ => None,
        }
    }

    /// Turn an error into a tagged error with the given file path.
    fn with_path<P: AsRef<Path>>(self, path: P) -> Error {
        Error::WithPath {
            path: path.as_ref().to_path_buf(),
            err: Box::new(self),
        }
    }

    /// Turn an error into a tagged error with the given depth.
    fn with_depth(self, depth: usize) -> Error {
        Error::WithDepth {
            depth: depth,
            err: Box::new(self),
        }
    }

    /// Turn an error into a tagged error with the given file path and line
    /// number. If path is empty, then it is omitted from the error.
    fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error {
        let errline = Error::WithLineNumber {
            line: lineno,
            err: Box::new(self),
        };
        if path.as_ref().as_os_str().is_empty() {
            return errline;
        }
        errline.with_path(path)
    }

    /// Build an error from a walkdir error.
    fn from_walkdir(err: walkdir::Error) -> Error {
        let depth = err.depth();
        if let (Some(anc), Some(child)) = (err.loop_ancestor(), err.path()) {
            return Error::WithDepth {
                depth: depth,
                err: Box::new(Error::Loop {
                    ancestor: anc.to_path_buf(),
                    child: child.to_path_buf(),
                }),
            };
        }
        let path = err.path().map(|p| p.to_path_buf());
        let mut ig_err = Error::Io(io::Error::from(err));
        if let Some(path) = path {
            ig_err = Error::WithPath {
                path: path,
                err: Box::new(ig_err),
            };
        }
        ig_err
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Partial(_) => "partial error",
            Error::WithLineNumber { ref err, .. } => err.description(),
            Error::WithPath { ref err, .. } => err.description(),
            Error::WithDepth { ref err, .. } => err.description(),
            Error::Loop { .. } => "file system loop found",
            Error::Io(ref err) => err.description(),
            Error::Glob { ref err, .. } => err,
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Partial(ref errs) => {
                let msgs: Vec<String> =
                    errs.iter().map(|err| err.to_string()).collect();
                write!(f, "{}", msgs.join("\n"))
            }
            Error::WithLineNumber { line, ref err } => {
                write!(f, "line {}: {}", line, err)
            }
            Error::WithPath { ref path, ref err } => {
                write!(f, "{}: {}", path.display(), err)
            }
            Error::WithDepth { ref err, .. } => err.fmt(f),
            Error::Loop { ref ancestor, ref child } => {
                write!(f, "File system loop found: \
                           {} points to an ancestor {}",
                          child.display(), ancestor.display())
            }
            Error::Io(ref err) => err.fmt(f),
            Error::Glob { glob: None, ref err } => write!(f, "{}", err),
            Error::Glob { glob: Some(ref glob), ref err } => {
                write!(f, "error parsing glob '{}': {}", glob, err)
            }
            Error::UnrecognizedFileType(ref ty) => {
                write!(f, "unrecognized file type: {}", ty)
            }
            Error::InvalidDefinition => {
                write!(f, "invalid definition (format is type:glob, e.g., \
                           html:*.html)")
            }
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

#[derive(Debug, Default)]
struct PartialErrorBuilder(Vec<Error>);

impl PartialErrorBuilder {
    fn push(&mut self, err: Error) {
        self.0.push(err);
    }

    fn push_ignore_io(&mut self, err: Error) {
        if !err.is_io() {
            self.push(err);
        }
    }

    fn maybe_push(&mut self, err: Option<Error>) {
        if let Some(err) = err {
            self.push(err);
        }
    }

    fn maybe_push_ignore_io(&mut self, err: Option<Error>) {
        if let Some(err) = err {
            self.push_ignore_io(err);
        }
    }

    fn into_error_option(mut self) -> Option<Error> {
        if self.0.is_empty() {
            None
        } else if self.0.len() == 1 {
            Some(self.0.pop().unwrap())
        } else {
            Some(Error::Partial(self.0))
        }
    }
}

/// The result of a glob match.
///
/// The type parameter `T` typically refers to a type that provides more
/// information about a particular match. For example, it might identify
/// the specific gitignore file and the specific glob pattern that caused
/// the match.
#[derive(Clone, Debug)]
pub enum Match<T> {
    /// The path didn't match any glob.
    None,
    /// The highest precedent glob matched indicates the path should be
    /// ignored.
    Ignore(T),
    /// The highest precedent glob matched indicates the path should be
    /// whitelisted.
    Whitelist(T),
}

impl<T> Match<T> {
    /// Returns true if the match result didn't match any globs.
    pub fn is_none(&self) -> bool {
        match *self {
            Match::None => true,
            Match::Ignore(_) | Match::Whitelist(_) => false,
        }
    }

    /// Returns true if the match result implies the path should be ignored.
    pub fn is_ignore(&self) -> bool {
        match *self {
            Match::Ignore(_) => true,
            Match::None | Match::Whitelist(_) => false,
        }
    }

    /// Returns true if the match result implies the path should be
    /// whitelisted.
    pub fn is_whitelist(&self) -> bool {
        match *self {
            Match::Whitelist(_) => true,
            Match::None | Match::Ignore(_) => false,
        }
    }

    /// Inverts the match so that `Ignore` becomes `Whitelist` and
    /// `Whitelist` becomes `Ignore`. A non-match remains the same.
    pub fn invert(self) -> Match<T> {
        match self {
            Match::None => Match::None,
            Match::Ignore(t) => Match::Whitelist(t),
            Match::Whitelist(t) => Match::Ignore(t),
        }
    }

    /// Return the value inside this match if it exists.
    pub fn inner(&self) -> Option<&T> {
        match *self {
            Match::None => None,
            Match::Ignore(ref t) => Some(t),
            Match::Whitelist(ref t) => Some(t),
        }
    }

    /// Apply the given function to the value inside this match.
    ///
    /// If the match has no value, then return the match unchanged.
    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Match<U> {
        match self {
            Match::None => Match::None,
            Match::Ignore(t) => Match::Ignore(f(t)),
            Match::Whitelist(t) => Match::Whitelist(f(t)),
        }
    }

    /// Return the match if it is not none. Otherwise, return other.
    pub fn or(self, other: Self) -> Self {
        if self.is_none() {
            other
        } else {
            self
        }
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use std::error;
    use std::fs;
    use std::path::{Path, PathBuf};
    use std::result;

    /// A convenient result type alias.
    pub type Result<T> =
        result::Result<T, Box<dyn error::Error + Send + Sync>>;

    macro_rules! err {
        ($($tt:tt)*) => {
            Box::<dyn error::Error + Send + Sync>::from(format!($($tt)*))
        }
    }

    /// A simple wrapper for creating a temporary directory that is
    /// automatically deleted when it's dropped.
    ///
    /// We use this in lieu of tempfile because tempfile brings in too many
    /// dependencies.
    #[derive(Debug)]
    pub struct TempDir(PathBuf);

    impl Drop for TempDir {
        fn drop(&mut self) {
            fs::remove_dir_all(&self.0).unwrap();
        }
    }

    impl TempDir {
        /// Create a new empty temporary directory under the system's configured
        /// temporary directory.
        pub fn new() -> Result<TempDir> {
            use std::sync::atomic::{AtomicUsize, Ordering};

            static TRIES: usize = 100;
            static COUNTER: AtomicUsize = AtomicUsize::new(0);

            let tmpdir = env::temp_dir();
            for _ in 0..TRIES {
                let count = COUNTER.fetch_add(1, Ordering::SeqCst);
                let path = tmpdir.join("rust-ignore").join(count.to_string());
                if path.is_dir() {
                    continue;
                }
                fs::create_dir_all(&path).map_err(|e| {
                    err!("failed to create {}: {}", path.display(), e)
                })?;
                return Ok(TempDir(path));
            }
            Err(err!("failed to create temp dir after {} tries", TRIES))
        }

        /// Return the underlying path to this temporary directory.
        pub fn path(&self) -> &Path {
            &self.0
        }
    }
}
<commit_msg>ignore: allow use of Error::description<commit_after>/*!
The ignore crate provides a fast recursive directory iterator that respects
various filters such as globs, file types and `.gitignore` files. The precise
matching rules and precedence is explained in the documentation for
`WalkBuilder`.

Secondarily, this crate exposes gitignore and file type matchers for use cases
that demand more fine-grained control.

# Example

This example shows the most basic usage of this crate. This code will
recursively traverse the current directory while automatically filtering out
files and directories according to ignore globs found in files like
`.ignore` and `.gitignore`:

```rust,no_run
use ignore::Walk;

for result in Walk::new("./") {
    // Each item yielded by the iterator is either a directory entry or an
    // error, so either print the path or the error.
    match result {
        Ok(entry) => println!("{}", entry.path().display()),
        Err(err) => println!("ERROR: {}", err),
    }
}
```

# Example: advanced

By default, the recursive directory iterator will ignore hidden files and
directories. This can be disabled by building the iterator with `WalkBuilder`:

```rust,no_run
use ignore::WalkBuilder;

for result in WalkBuilder::new("./").hidden(false).build() {
    println!("{:?}", result);
}
```

See the documentation for `WalkBuilder` for many other options.
*/

#![deny(missing_docs)]

extern crate crossbeam_channel as channel;
extern crate globset;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate memchr;
extern crate regex;
extern crate same_file;
extern crate thread_local;
extern crate walkdir;
#[cfg(windows)]
extern crate winapi_util;

use std::error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

pub use walk::{DirEntry, Walk, WalkBuilder, WalkParallel, WalkState};

mod dir;
pub mod gitignore;
pub mod overrides;
mod pathutil;
pub mod types;
mod walk;

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    /// A collection of "soft" errors. These occur when adding an ignore
    /// file partially succeeded.
    Partial(Vec<Error>),
    /// An error associated with a specific line number.
    WithLineNumber {
        /// The line number.
        line: u64,
        /// The underlying error.
        err: Box<Error>,
    },
    /// An error associated with a particular file path.
    WithPath {
        /// The file path.
        path: PathBuf,
        /// The underlying error.
        err: Box<Error>,
    },
    /// An error associated with a particular directory depth when recursively
    /// walking a directory.
    WithDepth {
        /// The directory depth.
        depth: usize,
        /// The underlying error.
        err: Box<Error>,
    },
    /// An error that occurs when a file loop is detected when traversing
    /// symbolic links.
    Loop {
        /// The ancestor file path in the loop.
        ancestor: PathBuf,
        /// The child file path in the loop.
        child: PathBuf,
    },
    /// An error that occurs when doing I/O, such as reading an ignore file.
    Io(io::Error),
    /// An error that occurs when trying to parse a glob.
    Glob {
        /// The original glob that caused this error. This glob, when
        /// available, always corresponds to the glob provided by an end user.
        /// e.g., It is the glob as written in a `.gitignore` file.
        ///
        /// (This glob may be distinct from the glob that is actually
        /// compiled, after accounting for `gitignore` semantics.)
        glob: Option<String>,
        /// The underlying glob error as a string.
        err: String,
    },
    /// A type selection for a file type that is not defined.
    UnrecognizedFileType(String),
    /// A user specified file type definition could not be parsed.
    InvalidDefinition,
}

impl Clone for Error {
    fn clone(&self) -> Error {
        match *self {
            Error::Partial(ref errs) => Error::Partial(errs.clone()),
            Error::WithLineNumber { line, ref err } => Error::WithLineNumber {
                line: line,
                err: err.clone(),
            },
            Error::WithPath { ref path, ref err } => Error::WithPath {
                path: path.clone(),
                err: err.clone(),
            },
            Error::WithDepth { depth, ref err } => Error::WithDepth {
                depth: depth,
                err: err.clone(),
            },
            Error::Loop {
                ref ancestor,
                ref child,
            } => Error::Loop {
                ancestor: ancestor.clone(),
                child: child.clone(),
            },
            Error::Io(ref err) => match err.raw_os_error() {
                Some(e) => Error::Io(io::Error::from_raw_os_error(e)),
                None => Error::Io(io::Error::new(err.kind(), err.to_string())),
            },
            Error::Glob { ref glob, ref err } => Error::Glob {
                glob: glob.clone(),
                err: err.clone(),
            },
            Error::UnrecognizedFileType(ref err) => Error::UnrecognizedFileType(err.clone()),
            Error::InvalidDefinition => Error::InvalidDefinition,
        }
    }
}

impl Error {
    /// Returns true if this is a partial error.
    ///
    /// A partial error occurs when only some operations failed while others
    /// may have succeeded. For example, an ignore file may contain an invalid
    /// glob among otherwise valid globs.
    pub fn is_partial(&self) -> bool {
        match *self {
            Error::Partial(_) => true,
            Error::WithLineNumber { ref err, .. } => err.is_partial(),
            Error::WithPath { ref err, .. } => err.is_partial(),
            Error::WithDepth { ref err, .. } => err.is_partial(),
            _ => false,
        }
    }

    /// Returns true if this error is exclusively an I/O error.
    pub fn is_io(&self) -> bool {
        match *self {
            Error::Partial(ref errs) => errs.len() == 1 && errs[0].is_io(),
            Error::WithLineNumber { ref err, .. } => err.is_io(),
            Error::WithPath { ref err, .. } => err.is_io(),
            Error::WithDepth { ref err, .. } => err.is_io(),
            Error::Loop { .. } => false,
            Error::Io(_) => true,
            Error::Glob { .. } => false,
            Error::UnrecognizedFileType(_) => false,
            Error::InvalidDefinition => false,
        }
    }

    /// Returns a depth associated with recursively walking a directory (if
    /// this error was generated from a recursive directory iterator).
    pub fn depth(&self) -> Option<usize> {
        match *self {
            Error::WithPath { ref err, .. } => err.depth(),
            Error::WithDepth { depth, .. } => Some(depth),
            _ => None,
        }
    }

    /// Turn an error into a tagged error with the given file path.
    fn with_path<P: AsRef<Path>>(self, path: P) -> Error {
        Error::WithPath {
            path: path.as_ref().to_path_buf(),
            err: Box::new(self),
        }
    }

    /// Turn an error into a tagged error with the given depth.
    fn with_depth(self, depth: usize) -> Error {
        Error::WithDepth {
            depth: depth,
            err: Box::new(self),
        }
    }

    /// Turn an error into a tagged error with the given file path and line
    /// number. If path is empty, then it is omitted from the error.
    fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error {
        let errline = Error::WithLineNumber {
            line: lineno,
            err: Box::new(self),
        };
        if path.as_ref().as_os_str().is_empty() {
            return errline;
        }
        errline.with_path(path)
    }

    /// Build an error from a walkdir error.
    fn from_walkdir(err: walkdir::Error) -> Error {
        let depth = err.depth();
        if let (Some(anc), Some(child)) = (err.loop_ancestor(), err.path()) {
            return Error::WithDepth {
                depth: depth,
                err: Box::new(Error::Loop {
                    ancestor: anc.to_path_buf(),
                    child: child.to_path_buf(),
                }),
            };
        }
        let path = err.path().map(|p| p.to_path_buf());
        let mut ig_err = Error::Io(io::Error::from(err));
        if let Some(path) = path {
            ig_err = Error::WithPath {
                path: path,
                err: Box::new(ig_err),
            };
        }
        ig_err
    }
}

impl error::Error for Error {
    #[allow(deprecated)]
    fn description(&self) -> &str {
        match *self {
            Error::Partial(_) => "partial error",
            Error::WithLineNumber { ref err, .. } => err.description(),
            Error::WithPath { ref err, .. } => err.description(),
            Error::WithDepth { ref err, .. } => err.description(),
            Error::Loop { .. } => "file system loop found",
            Error::Io(ref err) => err.description(),
            Error::Glob { ref err, .. } => err,
            Error::UnrecognizedFileType(_) => "unrecognized file type",
            Error::InvalidDefinition => "invalid definition",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Partial(ref errs) => {
                let msgs: Vec<String> = errs.iter().map(|err| err.to_string()).collect();
                write!(f, "{}", msgs.join("\n"))
            }
            Error::WithLineNumber { line, ref err } => write!(f, "line {}: {}", line, err),
            Error::WithPath { ref path, ref err } => write!(f, "{}: {}", path.display(), err),
            Error::WithDepth { ref err, .. } => err.fmt(f),
            Error::Loop {
                ref ancestor,
                ref child,
            } => write!(
                f,
                "File system loop found: \
                           {} points to an ancestor {}",
                child.display(),
                ancestor.display()
            ),
            Error::Io(ref err) => err.fmt(f),
            Error::Glob {
                glob: None,
                ref err,
            } => write!(f, "{}", err),
            Error::Glob {
                glob: Some(ref glob),
                ref err,
            } => write!(f, "error parsing glob '{}': {}", glob, err),
            Error::UnrecognizedFileType(ref ty) => write!(f, "unrecognized file type: {}", ty),
            Error::InvalidDefinition => write!(
                f,
                "invalid definition (format is type:glob, e.g., \
                           html:*.html)"
            ),
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

#[derive(Debug, Default)]
struct PartialErrorBuilder(Vec<Error>);

impl PartialErrorBuilder {
    fn push(&mut self, err: Error) {
        self.0.push(err);
    }

    fn push_ignore_io(&mut self, err: Error) {
        if !err.is_io() {
            self.push(err);
        }
    }

    fn maybe_push(&mut self, err: Option<Error>) {
        if let Some(err) = err {
            self.push(err);
        }
    }

    fn maybe_push_ignore_io(&mut self, err: Option<Error>) {
        if let Some(err) = err {
            self.push_ignore_io(err);
        }
    }

    fn into_error_option(mut self) -> Option<Error> {
        if self.0.is_empty() {
            None
        } else if self.0.len() == 1 {
            Some(self.0.pop().unwrap())
        } else {
            Some(Error::Partial(self.0))
        }
    }
}

/// The result of a glob match.
///
/// The type parameter `T` typically refers to a type that provides more
/// information about a particular match. For example, it might identify
/// the specific gitignore file and the specific glob pattern that caused
/// the match.
#[derive(Clone, Debug)]
pub enum Match<T> {
    /// The path didn't match any glob.
    None,
    /// The highest precedent glob matched indicates the path should be
    /// ignored.
    Ignore(T),
    /// The highest precedent glob matched indicates the path should be
    /// whitelisted.
    Whitelist(T),
}

impl<T> Match<T> {
    /// Returns true if the match result didn't match any globs.
    pub fn is_none(&self) -> bool {
        match *self {
            Match::None => true,
            Match::Ignore(_) | Match::Whitelist(_) => false,
        }
    }

    /// Returns true if the match result implies the path should be ignored.
    pub fn is_ignore(&self) -> bool {
        match *self {
            Match::Ignore(_) => true,
            Match::None | Match::Whitelist(_) => false,
        }
    }

    /// Returns true if the match result implies the path should be
    /// whitelisted.
    pub fn is_whitelist(&self) -> bool {
        match *self {
            Match::Whitelist(_) => true,
            Match::None | Match::Ignore(_) => false,
        }
    }

    /// Inverts the match so that `Ignore` becomes `Whitelist` and
    /// `Whitelist` becomes `Ignore`. A non-match remains the same.
    pub fn invert(self) -> Match<T> {
        match self {
            Match::None => Match::None,
            Match::Ignore(t) => Match::Whitelist(t),
            Match::Whitelist(t) => Match::Ignore(t),
        }
    }

    /// Return the value inside this match if it exists.
    pub fn inner(&self) -> Option<&T> {
        match *self {
            Match::None => None,
            Match::Ignore(ref t) => Some(t),
            Match::Whitelist(ref t) => Some(t),
        }
    }

    /// Apply the given function to the value inside this match.
    ///
    /// If the match has no value, then return the match unchanged.
    pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Match<U> {
        match self {
            Match::None => Match::None,
            Match::Ignore(t) => Match::Ignore(f(t)),
            Match::Whitelist(t) => Match::Whitelist(f(t)),
        }
    }

    /// Return the match if it is not none. Otherwise, return other.
    pub fn or(self, other: Self) -> Self {
        if self.is_none() {
            other
        } else {
            self
        }
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use std::error;
    use std::fs;
    use std::path::{Path, PathBuf};
    use std::result;

    /// A convenient result type alias.
    pub type Result<T> = result::Result<T, Box<dyn error::Error + Send + Sync>>;

    macro_rules! err {
        ($($tt:tt)*) => {
            Box::<dyn error::Error + Send + Sync>::from(format!($($tt)*))
        }
    }

    /// A simple wrapper for creating a temporary directory that is
    /// automatically deleted when it's dropped.
    ///
    /// We use this in lieu of tempfile because tempfile brings in too many
    /// dependencies.
    #[derive(Debug)]
    pub struct TempDir(PathBuf);

    impl Drop for TempDir {
        fn drop(&mut self) {
            fs::remove_dir_all(&self.0).unwrap();
        }
    }

    impl TempDir {
        /// Create a new empty temporary directory under the system's configured
        /// temporary directory.
        pub fn new() -> Result<TempDir> {
            use std::sync::atomic::{AtomicUsize, Ordering};

            static TRIES: usize = 100;
            static COUNTER: AtomicUsize = AtomicUsize::new(0);

            let tmpdir = env::temp_dir();
            for _ in 0..TRIES {
                let count = COUNTER.fetch_add(1, Ordering::SeqCst);
                let path = tmpdir.join("rust-ignore").join(count.to_string());
                if path.is_dir() {
                    continue;
                }
                fs::create_dir_all(&path)
                    .map_err(|e| err!("failed to create {}: {}", path.display(), e))?;
                return Ok(TempDir(path));
            }
            Err(err!("failed to create temp dir after {} tries", TRIES))
        }

        /// Return the underlying path to this temporary directory.
        pub fn path(&self) -> &Path {
            &self.0
        }
    }
}
<|endoftext|>


<commit_before>pub trait Encode {
    fn encode(&Self) -> Vec<u8>;
}<commit_msg>implement Encode for many primitive types<commit_after>use std::io;

use byteorder::{LittleEndian, WriteBytesExt};

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

    #[test]
    fn it_encodes_a_u8() {
        let mut actual = vec![];
        let _ = 23u8.encode(&mut actual);
        assert_eq!(vec![23], actual);
    }

    #[test]
    fn it_encodes_a_u16() {
        let mut actual = vec![];
        let _ = 23u16.encode(&mut actual);
        assert_eq!(vec![23, 0], actual);
    }

    #[test]
    fn it_encodes_a_u32() {
        let mut actual = vec![];
        let _ = 23u32.encode(&mut actual);
        assert_eq!(vec![23, 0, 0, 0], actual);
    }

    #[test]
    fn it_encodes_a_u64() {
        let mut actual = vec![];
        let _ = 23u64.encode(&mut actual);
        assert_eq!(vec![23, 0, 0, 0, 0, 0, 0, 0], actual);
    }

    #[test]
    fn it_encodes_a_i8() {
        let mut actual = vec![];
        let _ = 23i8.encode(&mut actual);
        assert_eq!(vec![23], actual);
    }

    #[test]
    fn it_encodes_a_i16() {
        let mut actual = vec![];
        let _ = 23i16.encode(&mut actual);
        assert_eq!(vec![23, 0], actual);
    }

    #[test]
    fn it_encodes_a_i32() {
        let mut actual = vec![];
        let _ = 23i32.encode(&mut actual);
        assert_eq!(vec![23, 0, 0, 0], actual);
    }

    #[test]
    fn it_encodes_a_i64() {
        let mut actual = vec![];
        let _ = 23i64.encode(&mut actual);
        assert_eq!(vec![23, 0, 0, 0, 0, 0, 0, 0], actual);
    }

    #[test]
    fn it_encodes_a_true() {
        let mut actual = vec![];
        let _ = true.encode(&mut actual);
        assert_eq!(vec![1], actual);
    }

    #[test]
    fn it_encodes_a_false() {
        let mut actual = vec![];
        let _ = false.encode(&mut actual);
        assert_eq!(vec![0], actual);
    }
}

pub trait Encode {
    fn encode(&self, &mut Vec<u8>) -> Result<(), io::Error>;
}

impl Encode for u8 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_u8(*self)
    }
}

impl Encode for u16 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_u16::<LittleEndian>(*self)
    }
}

impl Encode for u32 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_u32::<LittleEndian>(*self)
    }
}

impl Encode for u64 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_u64::<LittleEndian>(*self)
    }
}

impl Encode for i8 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_i8(*self)
    }
}

impl Encode for i16 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_i16::<LittleEndian>(*self)
    }
}

impl Encode for i32 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_i32::<LittleEndian>(*self)
    }
}

impl Encode for i64 {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        buff.write_i64::<LittleEndian>(*self)
    }
}

impl Encode for bool {
    fn encode(&self, buff: &mut Vec<u8>) -> Result<(), io::Error> {
        match *self {
            true => buff.push(1),
            false => buff.push(0),
        };
        Ok(())
    }
}<|endoftext|>


<commit_before>use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;

use cargo_platform::CfgExpr;
use semver::Version;

use super::BuildContext;
use crate::core::compiler::CompileKind;
use crate::core::{Edition, Package, PackageId, Target};
use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder};

pub struct Doctest {
    /// The package being doc-tested.
    pub package: Package,
    /// The target being tested (currently always the package's lib).
    pub target: Target,
    /// Arguments needed to pass to rustdoc to run this test.
    pub args: Vec<OsString>,
    /// Whether or not -Zunstable-options is needed.
    pub unstable_opts: bool,
}

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// An array of all tests created during this compilation.
    /// `(package, target, path_to_test_exe)`
    pub tests: Vec<(Package, Target, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directories for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    ///
    /// The order should be deterministic.
    pub native_dirs: BTreeSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies.
    /// May be for the host or for a specific target.
    pub deps_output: PathBuf,

    /// Output directory for the rust host dependencies.
    pub host_deps_output: PathBuf,

    /// The path to rustc's own libstd
    pub host_dylib_path: PathBuf,

    /// The path to libstd for the target
    pub target_dylib_path: PathBuf,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    /// Libraries to test with rustdoc.
    pub to_doc_test: Vec<Doctest>,

    /// Features per package enabled during this compilation.
    pub cfgs: HashMap<PackageId, HashSet<String>>,

    /// Flags to pass to rustdoc when invoked from cargo test, per package.
    pub rustdocflags: HashMap<PackageId, Vec<String>>,

    pub host: String,
    pub target: String,

    config: &'cfg Config,
    rustc_process: ProcessBuilder,
    primary_unit_rustc_process: Option<ProcessBuilder>,

    target_runner: Option<(PathBuf, Vec<String>)>,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new<'a>(
        bcx: &BuildContext<'a, 'cfg>,
        default_kind: CompileKind,
    ) -> CargoResult<Compilation<'cfg>> {
        let mut rustc = bcx.rustc.process();

        let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone();

        if bcx.config.extra_verbose() {
            rustc.display_env_vars();

            if let Some(rustc) = primary_unit_rustc_process.as_mut() {
                rustc.display_env_vars();
            }
        }

        Ok(Compilation {
            // TODO: deprecated; remove.
            native_dirs: BTreeSet::new(),
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            host_deps_output: PathBuf::from("/"),
            host_dylib_path: bcx.info(default_kind).sysroot_host_libdir.clone(),
            target_dylib_path: bcx.info(default_kind).sysroot_target_libdir.clone(),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashMap::new(),
            rustdocflags: HashMap::new(),
            config: bcx.config,
            rustc_process: rustc,
            primary_unit_rustc_process,
            host: bcx.host_triple().to_string(),
            target: default_kind.short_name(bcx).to_string(),
            target_runner: target_runner(bcx, default_kind)?,
        })
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package, is_primary: bool) -> CargoResult<ProcessBuilder> {
        let rustc = if is_primary {
            self.primary_unit_rustc_process
                .clone()
                .unwrap_or_else(|| self.rustc_process.clone())
        } else {
            self.rustc_process.clone()
        };

        self.fill_env(rustc, pkg, true)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }

        for crate_type in target.rustc_crate_types() {
            p.arg("--crate-type").arg(crate_type);
        }

        Ok(p)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    pub fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
        &self.target_runner
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
            let mut builder = process(runner);
            builder.args(args);
            builder.arg(cmd);
            builder
        } else {
            process(cmd)
        };
        self.fill_env(builder, pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(
        &self,
        mut cmd: ProcessBuilder,
        pkg: &Package,
        is_host: bool,
    ) -> CargoResult<ProcessBuilder> {
        let mut search_path = if is_host {
            let mut search_path = vec![self.host_deps_output.clone()];
            search_path.push(self.host_dylib_path.clone());
            search_path
        } else {
            let mut search_path =
                super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output);
            search_path.push(self.deps_output.clone());
            search_path.push(self.root_output.clone());
            // For build-std, we don't want to accidentally pull in any shared
            // libs from the sysroot that ships with rustc. This may not be
            // required (at least I cannot craft a situation where it
            // matters), but is here to be safe.
            if self.config.cli_unstable().build_std.is_none() {
                search_path.push(self.target_dylib_path.clone());
            }
            search_path
        };

        let dylib_path = util::dylib_path();
        let dylib_path_is_empty = dylib_path.is_empty();
        search_path.extend(dylib_path.into_iter());
        if cfg!(target_os = "macos") && dylib_path_is_empty {
            // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't
            // set or set to an empty string. Since Cargo is explicitly setting
            // the value, make sure the defaults still work.
            if let Some(home) = env::var_os("HOME") {
                search_path.push(PathBuf::from(home).join("lib"));
            }
            search_path.push(PathBuf::from("/usr/local/lib"));
            search_path.push(PathBuf::from("/usr/lib"));
        }
        let search_path = join_paths(&search_path, util::dylib_path_envvar())?;

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(&pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        let cargo_exe = self.config.cargo_exe()?;
        cmd.env(crate::CARGO_ENV, cargo_exe);

        // When adding new environment variables depending on
        // crate properties which might require rebuild upon change
        // consider adding the corresponding properties to the hash
        // in BuildContext::target_metadata()
        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
            .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
            .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
            .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
            .env(
                "CARGO_PKG_VERSION_PRE",
                &pre_version_component(pkg.version()),
            )
            .env("CARGO_PKG_VERSION", &pkg.version().to_string())
            .env("CARGO_PKG_NAME", &*pkg.name())
            .env(
                "CARGO_PKG_DESCRIPTION",
                metadata.description.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_HOMEPAGE",
                metadata.homepage.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_REPOSITORY",
                metadata.repository.as_ref().unwrap_or(&String::new()),
            )
            .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
            .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 {
            ret.push('.')
        };
        ret.push_str(&x.to_string());
    }

    ret
}

fn target_runner(
    bcx: &BuildContext<'_, '_>,
    kind: CompileKind,
) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
    let target = kind.short_name(bcx);

    // try target.{}.runner
    let key = format!("target.{}.runner", target);
    if let Some(v) = bcx.config.get::<Option<config::PathAndArgs>>(&key)? {
        let path = v.path.resolve_program(bcx.config);
        return Ok(Some((path, v.args)));
    }

    // try target.'cfg(...)'.runner
    let target_cfg = bcx.info(kind).cfg();
    let mut cfgs = bcx
        .config
        .target_cfgs()?
        .iter()
        .filter_map(|(key, cfg)| cfg.runner.as_ref().map(|runner| (key, runner)))
        .filter(|(key, _runner)| CfgExpr::matches_key(key, target_cfg));
    let matching_runner = cfgs.next();
    if let Some((key, runner)) = cfgs.next() {
        anyhow::bail!(
            "several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`\n\
             first match `{}` located in {}\n\
             second match `{}` located in {}",
            matching_runner.unwrap().0,
            matching_runner.unwrap().1.definition,
            key,
            runner.definition
        );
    }
    Ok(matching_runner.map(|(_k, runner)| {
        (
            runner.val.path.clone().resolve_program(bcx.config),
            runner.val.args.clone(),
        )
    }))
}
<commit_msg>Auto merge of #7798 - jnbr:dylib_path, r=alexcrichton<commit_after>use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;

use cargo_platform::CfgExpr;
use semver::Version;

use super::BuildContext;
use crate::core::compiler::CompileKind;
use crate::core::{Edition, Package, PackageId, Target};
use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder};

pub struct Doctest {
    /// The package being doc-tested.
    pub package: Package,
    /// The target being tested (currently always the package's lib).
    pub target: Target,
    /// Arguments needed to pass to rustdoc to run this test.
    pub args: Vec<OsString>,
    /// Whether or not -Zunstable-options is needed.
    pub unstable_opts: bool,
}

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// An array of all tests created during this compilation.
    /// `(package, target, path_to_test_exe)`
    pub tests: Vec<(Package, Target, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directories for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    ///
    /// The order should be deterministic.
    pub native_dirs: BTreeSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies.
    /// May be for the host or for a specific target.
    pub deps_output: PathBuf,

    /// Output directory for the rust host dependencies.
    pub host_deps_output: PathBuf,

    /// The path to rustc's own libstd
    pub host_dylib_path: PathBuf,

    /// The path to libstd for the target
    pub target_dylib_path: PathBuf,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    /// Libraries to test with rustdoc.
    pub to_doc_test: Vec<Doctest>,

    /// Features per package enabled during this compilation.
    pub cfgs: HashMap<PackageId, HashSet<String>>,

    /// Flags to pass to rustdoc when invoked from cargo test, per package.
    pub rustdocflags: HashMap<PackageId, Vec<String>>,

    pub host: String,
    pub target: String,

    config: &'cfg Config,
    rustc_process: ProcessBuilder,
    primary_unit_rustc_process: Option<ProcessBuilder>,

    target_runner: Option<(PathBuf, Vec<String>)>,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new<'a>(
        bcx: &BuildContext<'a, 'cfg>,
        default_kind: CompileKind,
    ) -> CargoResult<Compilation<'cfg>> {
        let mut rustc = bcx.rustc.process();

        let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone();

        if bcx.config.extra_verbose() {
            rustc.display_env_vars();

            if let Some(rustc) = primary_unit_rustc_process.as_mut() {
                rustc.display_env_vars();
            }
        }

        Ok(Compilation {
            // TODO: deprecated; remove.
            native_dirs: BTreeSet::new(),
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            host_deps_output: PathBuf::from("/"),
            host_dylib_path: bcx.info(CompileKind::Host).sysroot_host_libdir.clone(),
            target_dylib_path: bcx.info(default_kind).sysroot_target_libdir.clone(),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashMap::new(),
            rustdocflags: HashMap::new(),
            config: bcx.config,
            rustc_process: rustc,
            primary_unit_rustc_process,
            host: bcx.host_triple().to_string(),
            target: default_kind.short_name(bcx).to_string(),
            target_runner: target_runner(bcx, default_kind)?,
        })
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package, is_primary: bool) -> CargoResult<ProcessBuilder> {
        let rustc = if is_primary {
            self.primary_unit_rustc_process
                .clone()
                .unwrap_or_else(|| self.rustc_process.clone())
        } else {
            self.rustc_process.clone()
        };

        self.fill_env(rustc, pkg, true)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }

        for crate_type in target.rustc_crate_types() {
            p.arg("--crate-type").arg(crate_type);
        }

        Ok(p)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    pub fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
        &self.target_runner
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
            let mut builder = process(runner);
            builder.args(args);
            builder.arg(cmd);
            builder
        } else {
            process(cmd)
        };
        self.fill_env(builder, pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(
        &self,
        mut cmd: ProcessBuilder,
        pkg: &Package,
        is_host: bool,
    ) -> CargoResult<ProcessBuilder> {
        let mut search_path = if is_host {
            let mut search_path = vec![self.host_deps_output.clone()];
            search_path.push(self.host_dylib_path.clone());
            search_path
        } else {
            let mut search_path =
                super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output);
            search_path.push(self.deps_output.clone());
            search_path.push(self.root_output.clone());
            // For build-std, we don't want to accidentally pull in any shared
            // libs from the sysroot that ships with rustc. This may not be
            // required (at least I cannot craft a situation where it
            // matters), but is here to be safe.
            if self.config.cli_unstable().build_std.is_none() {
                search_path.push(self.target_dylib_path.clone());
            }
            search_path
        };

        let dylib_path = util::dylib_path();
        let dylib_path_is_empty = dylib_path.is_empty();
        search_path.extend(dylib_path.into_iter());
        if cfg!(target_os = "macos") && dylib_path_is_empty {
            // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't
            // set or set to an empty string. Since Cargo is explicitly setting
            // the value, make sure the defaults still work.
            if let Some(home) = env::var_os("HOME") {
                search_path.push(PathBuf::from(home).join("lib"));
            }
            search_path.push(PathBuf::from("/usr/local/lib"));
            search_path.push(PathBuf::from("/usr/lib"));
        }
        let search_path = join_paths(&search_path, util::dylib_path_envvar())?;

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(&pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        let cargo_exe = self.config.cargo_exe()?;
        cmd.env(crate::CARGO_ENV, cargo_exe);

        // When adding new environment variables depending on
        // crate properties which might require rebuild upon change
        // consider adding the corresponding properties to the hash
        // in BuildContext::target_metadata()
        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
            .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
            .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
            .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
            .env(
                "CARGO_PKG_VERSION_PRE",
                &pre_version_component(pkg.version()),
            )
            .env("CARGO_PKG_VERSION", &pkg.version().to_string())
            .env("CARGO_PKG_NAME", &*pkg.name())
            .env(
                "CARGO_PKG_DESCRIPTION",
                metadata.description.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_HOMEPAGE",
                metadata.homepage.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_REPOSITORY",
                metadata.repository.as_ref().unwrap_or(&String::new()),
            )
            .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
            .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 {
            ret.push('.')
        };
        ret.push_str(&x.to_string());
    }

    ret
}

fn target_runner(
    bcx: &BuildContext<'_, '_>,
    kind: CompileKind,
) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
    let target = kind.short_name(bcx);

    // try target.{}.runner
    let key = format!("target.{}.runner", target);
    if let Some(v) = bcx.config.get::<Option<config::PathAndArgs>>(&key)? {
        let path = v.path.resolve_program(bcx.config);
        return Ok(Some((path, v.args)));
    }

    // try target.'cfg(...)'.runner
    let target_cfg = bcx.info(kind).cfg();
    let mut cfgs = bcx
        .config
        .target_cfgs()?
        .iter()
        .filter_map(|(key, cfg)| cfg.runner.as_ref().map(|runner| (key, runner)))
        .filter(|(key, _runner)| CfgExpr::matches_key(key, target_cfg));
    let matching_runner = cfgs.next();
    if let Some((key, runner)) = cfgs.next() {
        anyhow::bail!(
            "several matching instances of `target.'cfg(..)'.runner` in `.cargo/config`\n\
             first match `{}` located in {}\n\
             second match `{}` located in {}",
            matching_runner.unwrap().0,
            matching_runner.unwrap().1.definition,
            key,
            runner.definition
        );
    }
    Ok(matching_runner.map(|(_k, runner)| {
        (
            runner.val.path.clone().resolve_program(bcx.config),
            runner.val.args.clone(),
        )
    }))
}
<|endoftext|>


<commit_before>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::Editor;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;

use crate::core::codepointinfo::TextStyle;

use crate::core::view::View;

use crate::core::bench_to_eof;

pub struct RawDataFilter {
    // data
    debug: bool,
    pos: u64,
    max_pos: u64,
    read_max: usize,
    read_size: usize,
    read_count: usize,
}

impl RawDataFilter {
    pub fn new() -> Self {
        RawDataFilter {
            debug: !true,
            pos: 0,
            max_pos: 0,
            read_max: 0,
            read_size: 0,
            read_count: 0,
        }
    }
}

impl ContentFilter<'_> for RawDataFilter {
    fn name(&self) -> &'static str {
        &"RawDataFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        _view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        if self.debug {
            dbg_println!(
                "RawDataFilter w {} h {}",
                env.screen.width(),
                env.screen.height()
            );
        }

        self.pos = env.base_offset;
        self.max_pos = env.max_offset;

        //
        self.read_count = 0;
        self.read_max = env.screen.width() * env.screen.height() * 4;
        self.read_size = env.screen.width(); // * env.screen.height() / 4; // 4: max utf8 encode size

        //        self.read_size = self.read_max;

        if bench_to_eof() {
            let bench_size = 1024 * 32;
            self.read_max = (self.max_pos - self.pos) as usize;
            self.read_max = std::cmp::min(bench_size, self.read_max);
            self.read_size = self.read_max;
        }

        if self.debug {
            dbg_println!("DATA FETCH self.pos         = {}", self.pos);
            dbg_println!("DATA FETCH self.max_pos     = {}", self.max_pos);
            dbg_println!("DATA FETCH self.read_size   = {}", self.read_size);
            dbg_println!("DATA FETCH diff max_pos pos = {}", self.max_pos - self.pos);
            dbg_println!("DATA FETCH diff max_pos pos = {}", self.max_pos - self.pos);
        }
    }

    fn run(
        &mut self,
        view: &View,
        env: &mut LayoutEnv,
        _no_input: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        // dbg_println!("DATA FETCH: run -----------------------");
        // There is no input HERE
        // we convert the document data into  buffer FilterIo

        // we read screen.width() bytes // TODO(ceg): width * codec_max_encode_size() for now
        let doc = view.document.clone();
        if let Some(ref doc) = doc {
            // 1st pass raw_data_filter
            let mut raw_data = vec![]; // TODO(ceg): write directly to next filter input

            // disable this block to test until eof
            if !bench_to_eof() {
                if self.pos + self.read_size as u64 > self.max_pos {
                    self.read_size = self.max_pos.saturating_sub(self.pos) as usize;
                    if self.debug {
                        dbg_println!(
                            "DATA FETCH: pos + rd_size > max_pos adjust read_size : {}",
                            self.read_size
                        );
                    }
                }

                if self.debug {
                    dbg_println!(
                        "DATA FETCH: remaining bytes to read = {}",
                        self.max_pos.saturating_sub(self.pos)
                    );
                }
            }

            if self.debug {
                dbg_println!("DATA FETCH: read_size  = {}", self.read_size);
            }

            if self.debug {
                dbg_println!(
                    "DATA FETCH: try READ from offset({}) {} bytes",
                    self.pos,
                    self.read_size
                );
            }

            //
            let doc = doc.read();

            let rd = doc.read(self.pos, self.read_size, &mut raw_data);

            if self.debug {
                dbg_println!(
                    "DATA FETCH: READ from offset({}) : {} / {} bytes",
                    self.pos,
                    rd,
                    self.read_size
                );
                dbg_println!("DATA FETCH: BUFFER SIZE {}", doc.size());
                dbg_println!(
                    "DATA FETCH: POS {} + RD {}  = {}",
                    self.pos,
                    rd,
                    self.pos + rd as u64
                );
            }
            if false {
                let mut count = 0;
                for i in 0..raw_data.len() {
                    count = count % env.screen.width();
                    if count == 0 {}
                    if raw_data[i] == b'\n' {
                        count = 0;
                        dbg_println!("[ offset {} ] -- LF\n", self.pos + i as u64);
                        continue;
                    } else {
                        dbg_print!(
                            "[ offset {} ] = 0x{:x}\n",
                            self.pos + i as u64,
                            raw_data[i] as usize
                        );
                    }
                    count += 1;
                }
                dbg_println!("");
            }

            if rd > 0 {
                if self.debug {
                    dbg_println!("build byte chunk from offset self.pos {}", self.pos);
                }

                (*filter_out).push(FilterIo {
                    metadata: false,
                    style: TextStyle::new(),
                    offset: Some(self.pos),
                    size: rd,
                    data: FilterData::ByteArray { vec: raw_data },
                });
            }

            if self.debug {
                dbg_println!(
                    "updated POS {} + rd {} = {}",
                    self.pos,
                    rd,
                    self.pos + (rd as u64)
                );
            }

            self.pos += rd as u64;
            self.read_count += rd;

            // TODO(ceg): cache doc size ?
            if self.pos == doc.size() as u64 {
                env.quit = true;

                if true {
                    (*filter_out).push(FilterIo {
                        metadata: true,
                        style: TextStyle::new(),
                        offset: Some(self.pos),
                        size: 0,
                        data: FilterData::EndOfStream,
                    });
                }
                if self.debug {
                    dbg_println!("DATA FETCH: EOF @ offset {}", self.pos);
                }
            }

            if !bench_to_eof() {
                // TODO(ceg): in setup : self.read_to_eof = bench_to_eof()
                if self.pos >= self.max_pos {
                    env.quit = true;
                    if self.debug {
                        dbg_println!("DATA FETCH: MAX POS REACHED");
                    }
                    (*filter_out).push(FilterIo {
                        metadata: true,
                        style: TextStyle::new(),
                        offset: Some(self.pos),
                        size: 0,
                        data: FilterData::CustomLimitReached,
                    });
                }
            }

            // increase read size at every call
            // TODO(ceg): find better default size
            if self.read_size < self.read_max {
                self.read_size += env.screen.width();
                //dbg_println!("updated read size  {} ", self.read_size);
            }
        }
    }
}
<commit_msg>data_fetch: fix self.max_pos<commit_after>use parking_lot::RwLock;
use std::rc::Rc;

use crate::core::Editor;

use crate::core::view::ContentFilter;
use crate::core::view::FilterData;
use crate::core::view::FilterIo;
use crate::core::view::LayoutEnv;

use crate::core::codepointinfo::TextStyle;

use crate::core::view::View;

use crate::core::bench_to_eof;

pub struct RawDataFilter {
    // data
    debug: bool,
    pos: u64,
    max_pos: u64,
    read_max: usize,
    read_size: usize,
    read_count: usize,
}

impl RawDataFilter {
    pub fn new() -> Self {
        RawDataFilter {
            debug: !true,
            pos: 0,
            max_pos: 0,
            read_max: 0,
            read_size: 0,
            read_count: 0,
        }
    }
}

impl ContentFilter<'_> for RawDataFilter {
    fn name(&self) -> &'static str {
        &"RawDataFilter"
    }

    fn setup(
        &mut self,
        _editor: &Editor,
        env: &mut LayoutEnv,
        view: &Rc<RwLock<View>>,
        _parent_view: Option<&View<'static>>,
    ) {
        if self.debug {
            dbg_println!(
                "RawDataFilter w {} h {}",
                env.screen.width(),
                env.screen.height()
            );
        }

        self.pos = env.base_offset;
        self.max_pos = env.max_offset;

        //
        self.read_count = 0;
        self.read_max = env.screen.width() * env.screen.height() * 4;
        self.read_size = env.screen.width(); // * env.screen.height() / 4; // 4: max utf8 encode size

        // EOF
        self.max_pos = view.read().document().unwrap().read().size() as u64;

        if bench_to_eof() {
            let bench_size = 1024 * 32;
            self.read_max = (self.max_pos - self.pos) as usize;
            self.read_max = std::cmp::min(bench_size, self.read_max);
            self.read_size = self.read_max;
        }

        if self.debug {
            dbg_println!("DATA FETCH self.pos         = {}", self.pos);
            dbg_println!("DATA FETCH self.max_pos     = {}", self.max_pos);
            dbg_println!("DATA FETCH self.read_size   = {}", self.read_size);
            dbg_println!("DATA FETCH diff max_pos pos = {}", self.max_pos - self.pos);
            dbg_println!("DATA FETCH diff max_pos pos = {}", self.max_pos - self.pos);
        }
    }

    fn run(
        &mut self,
        view: &View,
        env: &mut LayoutEnv,
        _no_input: &Vec<FilterIo>,
        filter_out: &mut Vec<FilterIo>,
    ) {
        // dbg_println!("DATA FETCH: run -----------------------");
        // There is no input HERE
        // we convert the document data into  buffer FilterIo

        // we read screen.width() bytes // TODO(ceg): width * codec_max_encode_size() for now
        let doc = view.document.clone();
        if let Some(ref doc) = doc {
            // 1st pass raw_data_filter
            let mut raw_data = vec![]; // TODO(ceg): write directly to next filter input

            // disable this block to test until eof
            if !bench_to_eof() {
                if self.pos + self.read_size as u64 > self.max_pos {
                    self.read_size = self.max_pos.saturating_sub(self.pos) as usize;
                    if self.debug {
                        dbg_println!(
                            "DATA FETCH: pos + rd_size > max_pos adjust read_size : {}",
                            self.read_size
                        );
                    }
                }

                if self.debug {
                    dbg_println!(
                        "DATA FETCH: remaining bytes to read = {}",
                        self.max_pos.saturating_sub(self.pos)
                    );
                }
            }

            if self.debug {
                dbg_println!("DATA FETCH: read_size  = {}", self.read_size);
            }

            if self.debug {
                dbg_println!(
                    "DATA FETCH: try READ from offset({}) {} bytes",
                    self.pos,
                    self.read_size
                );
            }

            //
            let doc = doc.read();

            let rd = doc.read(self.pos, self.read_size, &mut raw_data);

            if self.debug {
                dbg_println!(
                    "DATA FETCH: READ from offset({}) : {} / {} bytes",
                    self.pos,
                    rd,
                    self.read_size
                );
                dbg_println!("DATA FETCH: BUFFER SIZE {}", doc.size());
                dbg_println!(
                    "DATA FETCH: POS {} + RD {}  = {}",
                    self.pos,
                    rd,
                    self.pos + rd as u64
                );
            }
            if false {
                let mut count = 0;
                for i in 0..raw_data.len() {
                    count = count % env.screen.width();
                    if count == 0 {}
                    if raw_data[i] == b'\n' {
                        count = 0;
                        dbg_println!("[ offset {} ] -- LF\n", self.pos + i as u64);
                        continue;
                    } else {
                        dbg_print!(
                            "[ offset {} ] = 0x{:x}\n",
                            self.pos + i as u64,
                            raw_data[i] as usize
                        );
                    }
                    count += 1;
                }
                dbg_println!("");
            }

            if rd > 0 {
                if self.debug {
                    dbg_println!("build byte chunk from offset self.pos {}", self.pos);
                }

                (*filter_out).push(FilterIo {
                    metadata: false,
                    style: TextStyle::new(),
                    offset: Some(self.pos),
                    size: rd,
                    data: FilterData::ByteArray { vec: raw_data },
                });
            }

            if self.debug {
                dbg_println!(
                    "updated POS {} + rd {} = {}",
                    self.pos,
                    rd,
                    self.pos + (rd as u64)
                );
            }

            self.pos += rd as u64;
            self.read_count += rd;

            // TODO(ceg): cache doc size ?
            if self.pos == doc.size() as u64 {
                env.quit = true;

                if true {
                    (*filter_out).push(FilterIo {
                        metadata: true,
                        style: TextStyle::new(),
                        offset: Some(self.pos),
                        size: 0,
                        data: FilterData::EndOfStream,
                    });
                }
                if self.debug {
                    dbg_println!("DATA FETCH: EOF @ offset {}", self.pos);
                }
            }

            if !bench_to_eof() {
                // TODO(ceg): in setup : self.read_to_eof = bench_to_eof()
                if self.pos >= self.max_pos {
                    env.quit = true;
                    if self.debug {
                        dbg_println!("DATA FETCH: MAX POS REACHED");
                    }
                    (*filter_out).push(FilterIo {
                        metadata: true,
                        style: TextStyle::new(),
                        offset: Some(self.pos),
                        size: 0,
                        data: FilterData::CustomLimitReached,
                    });
                }
            }

            // increase read size at every call
            // TODO(ceg): find better default size
            if self.read_size < self.read_max {
                self.read_size += env.screen.width();
                //dbg_println!("updated read size  {} ", self.read_size);
            }
        }
    }
}
<|endoftext|>


<commit_before>/// A simple static assertion macro. The first argument should be a unique
/// ALL_CAPS identifier that describes the condition.
#[macro_export]
#[allow_internal_unstable]
macro_rules! static_assert {
    ($name:ident: $test:expr) => {
        // Use the bool to access an array such that if the bool is false, the access
        // is out-of-bounds.
        #[allow(dead_code)]
        static $name: () = [()][!($test: bool) as usize];
    }
}
<commit_msg>Use `allow_internal_unstable` in rustc itself<commit_after>/// A simple static assertion macro. The first argument should be a unique
/// ALL_CAPS identifier that describes the condition.
#[macro_export]
#[cfg_attr(stage0, allow_internal_unstable)]
#[cfg_attr(not(stage0), allow_internal_unstable(type_ascription))]
macro_rules! static_assert {
    ($name:ident: $test:expr) => {
        // Use the bool to access an array such that if the bool is false, the access
        // is out-of-bounds.
        #[allow(dead_code)]
        static $name: () = [()][!($test: bool) as usize];
    }
}
<|endoftext|>