<commit_before>extern crate blas;
extern crate ndarray;

use ndarray::{Data,DataMut,LinalgScalar};
use ndarray::prelude::*;

pub trait GramSchmidt: Sized + Clone + Default {
    fn compute_into<S1,S2,S3>(a: &ArrayBase<S1, Ix2>, orth: &mut ArrayBase<S2, Ix2>, norm: &mut ArrayBase<S3, Ix1>)
        where S1: Data<Elem = Self>,
              S2: DataMut<Elem = Self>,
              S3: DataMut<Elem = Self>;

    fn compute<S>(a: &ArrayBase<S, Ix2>) -> (Array<Self, Ix2>, Array<Self, Ix1>)
        where S: Data<Elem = Self>
    {
        let mut o = a.to_owned();
        let mut n = Array1::default(a.dim().1);

        Self::compute_into(a, &mut o, &mut n);
        (o, n)
    }
}

pub trait ModifiedGramSchmidt: Sized + Clone + Default {
    fn compute_inplace<S1,S2>(orth: &mut ArrayBase<S1, Ix2>, norm: &mut ArrayBase<S2, Ix1>)
        where S1: DataMut<Elem = Self>,
              S2: DataMut<Elem = Self>;

    fn compute_inplace_no_norm<S1>(a: &mut ArrayBase<S1, Ix2>)
        where S1: DataMut<Elem = Self>;

    fn compute_into<S1,S2,S3>(a: &ArrayBase<S1, Ix2>, orth: &mut ArrayBase<S2, Ix2>, norm: &mut ArrayBase<S3, Ix1>)
        where S1: Data<Elem = Self>,
              S2: DataMut<Elem = Self>,
              S3: DataMut<Elem = Self>,
    {
        orth.assign(&a);
        Self::compute_inplace(orth, norm);
    }

    fn compute<S>(a: &ArrayBase<S, Ix2>) -> (Array<Self, Ix2>, Array<Self, Ix1>)
        where S: Data<Elem = Self>
    {
        let mut o = a.to_owned();
        let mut n = Array1::default(a.dim().1);

        Self::compute_into(a, &mut o, &mut n);
        (o, n)
    }
}

impl GramSchmidt for f64 {
    fn compute_into<S1,S2,S3>(a: &ArrayBase<S1, Ix2>, orth: &mut ArrayBase<S2, Ix2>, norm: &mut ArrayBase<S3, Ix1>)
        where S1: Data<Elem = Self>,
              S2: DataMut<Elem = Self>,
              S3: DataMut<Elem = Self>
    {
        let n_rows = orth.shape()[0];

        orth.row_mut(0).assign(&a.row(0));

        for i in 0..n_rows {
            let (done, mut todo) = orth.view_mut().split_at(Axis(0), i);
            let ref_vec = a.row(i);

            let mut v = todo.row_mut(0);

            for w in done.inner_iter() {
                let projection_factor = project(&ref_vec, &w);
                v.zip_mut_with(&w, |ev,ew| { *ev -= projection_factor * ew; });
            }

            norm[i] = normalization(v.as_slice().unwrap());
            v /= norm[i];
        }
    }
}

impl ModifiedGramSchmidt for f64 {
    fn compute_inplace<S1,S2>(orth: &mut ArrayBase<S1, Ix2>, norm: &mut ArrayBase<S2, Ix1>)
        where S1: DataMut<Elem = Self>,
              S2: DataMut<Elem = Self>,
    {
        let n_rows = orth.shape()[0];

        let mut todo = orth.view_mut();

        for i in 0..n_rows {
            let (mut v, rest) = todo.split_at(Axis(0), 1);
            let mut v = v.row_mut(0);
            todo = rest;

            // Normalize the current row and remove its projection from the remaining rows. This
            // partially orthogonalizes the outstanding rows with respect to each current row.
            //
            // Another strategy would have been to use the already orthogonalized rows to
            // orthogonalize the current row in one go and then normalize it.
            //
            // However, the current strategy should be better for cache locality as we will not
            // need to reload the finished rows into cache all the time.
            norm[i] = normalization(v.as_slice().unwrap());
            v /= norm[i];

            for mut w in todo.inner_iter_mut() {
                // w is already normalized
                // let projection_factor = project(&v, &w);
                let projection_factor = v.dot(&w);
                w.zip_mut_with(&v, |ew,ev| { *ew -= projection_factor * ev; });
            }
        }
    }

    fn compute_inplace_no_norm<S1>(orth: &mut ArrayBase<S1, Ix2>)
        where S1: DataMut<Elem = Self>,
    {
        let n_rows = orth.shape()[0];

        let mut todo = orth.view_mut();

        for _i in 0..n_rows {
            let (mut v, rest) = todo.split_at(Axis(0), 1);
            let mut v = v.row_mut(0);
            todo = rest;

            // Same normalization strategy as with compute_inplace; see there for more comments.
            v /= normalization(v.as_slice().unwrap());

            for mut w in todo.inner_iter_mut() {
                // w is already normalized
                // let projection_factor = project(&v, &w);
                let projection_factor = v.dot(&w);
                w.zip_mut_with(&v, |ew,ev| { *ew -= projection_factor * ev; });
            }
        }
    }
}

pub fn orthogonal<S>(a: &ArrayBase<S,Ix2>, tol: f64) -> bool
    where S: Data<Elem=f64>
{
    let b = a.dot(&a.t());
    b.all_close(&Array2::eye(b.shape()[0]), tol)
}

pub fn normalization(v: &[f64]) -> f64 {
    blas::c::dnrm2(v.len() as i32, v, 1)
}

pub fn project<A,S1,S2>(vector: &ArrayBase<S1,Ix1>, onto: &ArrayBase<S2,Ix1>) -> A
    where S1: Data<Elem=A>,
          S2: Data<Elem=A>,
          A: LinalgScalar
{
    vector.dot(onto) / onto.dot(onto)
}

#[cfg(test)]
mod tests {
    use ndarray::{arr1,arr2,Array1,Array2};
    use super::{GramSchmidt,ModifiedGramSchmidt};

    #[test]
    fn normalization() {
        let a = &[1.0,2.0,3.0,4.0,5.0];

        let n_manual = f64::sqrt(1.0 + 4.0 + 9.0 + 16.0 + 25.0);
        let n_calculated = super::normalization(a);

        println!("Norm, manual: {}", n_manual);
        println!("Norm, function: {}", n_calculated);

        assert_eq!(n_manual, n_calculated);
    }

    #[test]
    fn projection() {
        let a = arr1(&[1.0,2.0,3.0,4.0,5.0]);
        let p = super::project(&a,&a);

        println!("Projection: {}", p);
        assert_eq!(p, 1.0);
    }

    #[test]
    fn gram_schmidt() {
        let a = arr2(&[[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [0.0, 0.0, 0.0, 1.0]]);

        let (a_orth, a_norm) = GramSchmidt::compute(&a);

        let b = arr2(&[[2.0, 0.5, 0.0, 0.0],
                       [0.0, 0.3, 0.0, 0.0],
                       [0.0, 1.0, 0.7, 0.0],
                       [0.0, 0.0, 0.0, 3.0]]);

        let (b_orth, b_norm) = GramSchmidt::compute(&b);

        println!("{:?}", b_orth);
        println!("{:?}", b_norm);

        assert!(super::orthogonal(&a_orth, 1e-14));
        assert!(a_norm.all_close(&Array1::from_elem(4, 1.0), 1e-16));

        assert!(super::orthogonal(&b_orth, 1e-14));
        assert!(b_norm.all_close(&arr1(&[2.0615528128088303, 0.2910427500435996, 0.7, 3.0]), 1e-16));
    }

    #[test]
    fn modified_gram_schmidt() {
        let a = arr2(&[[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [0.0, 0.0, 0.0, 1.0]]);

        let (a_orth, a_norm) = ModifiedGramSchmidt::compute(&a);

        let b = arr2(&[[2.0, 0.5, 0.0, 0.0],
                       [0.0, 0.3, 0.0, 0.0],
                       [0.0, 1.0, 0.7, 0.0],
                       [0.0, 0.0, 0.0, 3.0]]);

        let (b_orth, b_norm) = ModifiedGramSchmidt::compute(&b);

        assert!(super::orthogonal(&a_orth,1e-14));
        assert!(a_norm.all_close(&Array1::from_elem(4, 1.0), 1e-16));

        assert!(super::orthogonal(&b_orth,1e-14));
        assert!(b_norm.all_close(&arr1(&[2.0615528128088303, 0.2910427500435996, 0.7, 3.0]), 1e-16));
    }

    #[test]
    fn modified_gram_schmidt_into() {
        let a = arr2(&[[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [0.0, 0.0, 0.0, 1.0]]);

        let mut a_o = Array2::default((4,4));
        let mut a_n = Array1::default(4);

        let b = arr2(&[[2.0, 0.5, 0.0, 0.0],
                       [0.0, 0.3, 0.0, 0.0],
                       [0.0, 1.0, 0.7, 0.0],
                       [0.0, 0.0, 0.0, 3.0]]);

        let mut b_o = Array2::default((4,4));
        let mut b_n = Array1::default(4);

        let c = arr2(&[[-4.079764601288893, 4.831491499921403, -2.9560001027996132, -0.02239325297550033, -0.2672544204261703, -0.07718850306444144],
                       [1.2917480323712418, 0.030479388871438983, 0.604549448561548, 0.013409783846041783, 0.037439247530467186, 0.03153579130305008],
                       [-47.584641085515464, 5.501371846864031, 41.39822251681311, -33.69079455346558, 43.13388644338738, 68.7695035292409],
                       [2.5268795799504997, 25.418530275775225, 33.473125141381374, 77.3391516894698, -44.091836957161426, 45.10932299622911],
                       [-20.383209804181938, -19.163209972229616, 0.09795435026201423, -53.296988576627484, -88.482334971421, 16.757575995918756],
                       [62.270964677492124, -75.82678462673792, -0.6889077708993588, 2.2569901796884064, 9.21906803233946, 44.891962279862234]]);

        let mut c_o = Array2::default((6,6));
        let mut c_n = Array1::default(6);

        ModifiedGramSchmidt::compute_into(&a, &mut a_o, &mut a_n);
        ModifiedGramSchmidt::compute_into(&b, &mut b_o, &mut b_n);
        ModifiedGramSchmidt::compute_into(&c, &mut c_o, &mut c_n);

        assert!(super::orthogonal(&a_o,1e-14));
        assert!(a_n.all_close(&Array1::from_elem(4, 1.0), 1e-16));

        assert!(super::orthogonal(&b_o,1e-14));
        assert!(b_n.all_close(&arr1(&[2.0615528128088303, 0.2910427500435996, 0.7, 3.0]), 1e-16));

        assert!(super::orthogonal(&c_o,1e-14));
    }
}
<commit_msg>Revert 36b720<commit_after>extern crate blas;
extern crate ndarray;

use ndarray::{Data,DataMut,LinalgScalar};
use ndarray::prelude::*;

pub trait GramSchmidt: Sized + Clone + Default {
    fn compute_into<S1,S2,S3>(a: &ArrayBase<S1, Ix2>, orth: &mut ArrayBase<S2, Ix2>, norm: &mut ArrayBase<S3, Ix1>)
        where S1: Data<Elem = Self>,
              S2: DataMut<Elem = Self>,
              S3: DataMut<Elem = Self>;

    fn compute<S>(a: &ArrayBase<S, Ix2>) -> (Array<Self, Ix2>, Array<Self, Ix1>)
        where S: Data<Elem = Self>
    {
        let mut o = a.to_owned();
        let mut n = Array1::default(a.dim().1);

        Self::compute_into(a, &mut o, &mut n);
        (o, n)
    }
}

pub trait ModifiedGramSchmidt: Sized + Clone + Default {
    fn compute_inplace<S1,S2>(orth: &mut ArrayBase<S1, Ix2>, norm: &mut ArrayBase<S2, Ix1>)
        where S1: DataMut<Elem = Self>,
              S2: DataMut<Elem = Self>;

    fn compute_inplace_no_norm<S1>(a: &mut ArrayBase<S1, Ix2>)
        where S1: DataMut<Elem = Self>;

    fn compute_into<S1,S2,S3>(a: &ArrayBase<S1, Ix2>, orth: &mut ArrayBase<S2, Ix2>, norm: &mut ArrayBase<S3, Ix1>)
        where S1: Data<Elem = Self>,
              S2: DataMut<Elem = Self>,
              S3: DataMut<Elem = Self>,
    {
        orth.assign(&a);
        Self::compute_inplace(orth, norm);
    }

    fn compute<S>(a: &ArrayBase<S, Ix2>) -> (Array<Self, Ix2>, Array<Self, Ix1>)
        where S: Data<Elem = Self>
    {
        let mut o = a.to_owned();
        let mut n = Array1::default(a.dim().1);

        Self::compute_into(a, &mut o, &mut n);
        (o, n)
    }
}

impl GramSchmidt for f64 {
    fn compute_into<S1,S2,S3>(a: &ArrayBase<S1, Ix2>, orth: &mut ArrayBase<S2, Ix2>, norm: &mut ArrayBase<S3, Ix1>)
        where S1: Data<Elem = Self>,
              S2: DataMut<Elem = Self>,
              S3: DataMut<Elem = Self>
    {
        let n_rows = orth.shape()[0];

        orth.row_mut(0).assign(&a.row(0));

        for i in 0..n_rows {
            let (done, mut todo) = orth.view_mut().split_at(Axis(0), i);
            let ref_vec = a.row(i);

            let mut v = todo.row_mut(0);

            for w in done.inner_iter() {
                let projection_factor = project(&ref_vec, &w);
                v.zip_mut_with(&w, |ev,ew| { *ev -= projection_factor * ew; });
            }

            norm[i] = normalization(v.as_slice().unwrap());
            v /= norm[i];
        }
    }
}

impl ModifiedGramSchmidt for f64 {
    fn compute_inplace<S1,S2>(orth: &mut ArrayBase<S1, Ix2>, norm: &mut ArrayBase<S2, Ix1>)
        where S1: DataMut<Elem = Self>,
              S2: DataMut<Elem = Self>,
    {
        let n_rows = orth.shape()[0];

        // Orthonormalize the current row with respect to all already orthonormalized rows.
        //
        // Another strategy would have been to normalize the current row, and then remove it
        // from all not-yet-orthonormalized rows. However, benchmarking reveals that the first
        // strategy is about 10% faster.
        for i in 0..n_rows {
            let (done, mut todo) = orth.view_mut().split_at(Axis(0), i);

            let mut v = todo.row_mut(0);

            for w in done.inner_iter() {
                // w is already normalized
                // let projection_factor = project(&v, &w);
                let projection_factor = v.dot(&w);
                v.zip_mut_with(&w, |ev,ew| { *ev -= projection_factor * ew; });
            }

            norm[i] = normalization(v.as_slice().unwrap());
            v /= norm[i];
        }
    }

    fn compute_inplace_no_norm<S1>(orth: &mut ArrayBase<S1, Ix2>)
        where S1: DataMut<Elem = Self>,
    {
        let n_rows = orth.shape()[0];

        for i in 0..n_rows {
            let (done, mut todo) = orth.view_mut().split_at(Axis(0), i);

            let mut v = todo.row_mut(0);

            for w in done.inner_iter() {
                // w is already normalized
                // let projection_factor = project(&v, &w);
                let projection_factor = v.dot(&w);
                v.zip_mut_with(&w, |ev,ew| { *ev -= projection_factor * ew; });
            }

            v /= normalization(v.as_slice().unwrap());
        }
    }
}

pub fn orthogonal<S>(a: &ArrayBase<S,Ix2>, tol: f64) -> bool
    where S: Data<Elem=f64>
{
    let b = a.dot(&a.t());
    b.all_close(&Array2::eye(b.shape()[0]), tol)
}

pub fn normalization(v: &[f64]) -> f64 {
    blas::c::dnrm2(v.len() as i32, v, 1)
}

pub fn project<A,S1,S2>(vector: &ArrayBase<S1,Ix1>, onto: &ArrayBase<S2,Ix1>) -> A
    where S1: Data<Elem=A>,
          S2: Data<Elem=A>,
          A: LinalgScalar
{
    vector.dot(onto) / onto.dot(onto)
}

#[cfg(test)]
mod tests {
    use ndarray::{arr1,arr2,Array1,Array2};
    use super::{GramSchmidt,ModifiedGramSchmidt};

    #[test]
    fn normalization() {
        let a = &[1.0,2.0,3.0,4.0,5.0];

        let n_manual = f64::sqrt(1.0 + 4.0 + 9.0 + 16.0 + 25.0);
        let n_calculated = super::normalization(a);

        println!("Norm, manual: {}", n_manual);
        println!("Norm, function: {}", n_calculated);

        assert_eq!(n_manual, n_calculated);
    }

    #[test]
    fn projection() {
        let a = arr1(&[1.0,2.0,3.0,4.0,5.0]);
        let p = super::project(&a,&a);

        println!("Projection: {}", p);
        assert_eq!(p, 1.0);
    }

    #[test]
    fn gram_schmidt() {
        let a = arr2(&[[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [0.0, 0.0, 0.0, 1.0]]);

        let (a_orth, a_norm) = GramSchmidt::compute(&a);

        let b = arr2(&[[2.0, 0.5, 0.0, 0.0],
                       [0.0, 0.3, 0.0, 0.0],
                       [0.0, 1.0, 0.7, 0.0],
                       [0.0, 0.0, 0.0, 3.0]]);

        let (b_orth, b_norm) = GramSchmidt::compute(&b);

        println!("{:?}", b_orth);
        println!("{:?}", b_norm);

        assert!(super::orthogonal(&a_orth, 1e-14));
        assert!(a_norm.all_close(&Array1::from_elem(4, 1.0), 1e-16));

        assert!(super::orthogonal(&b_orth, 1e-14));
        assert!(b_norm.all_close(&arr1(&[2.0615528128088303, 0.2910427500435996, 0.7, 3.0]), 1e-16));
    }

    #[test]
    fn modified_gram_schmidt() {
        let a = arr2(&[[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [0.0, 0.0, 0.0, 1.0]]);

        let (a_orth, a_norm) = ModifiedGramSchmidt::compute(&a);

        let b = arr2(&[[2.0, 0.5, 0.0, 0.0],
                       [0.0, 0.3, 0.0, 0.0],
                       [0.0, 1.0, 0.7, 0.0],
                       [0.0, 0.0, 0.0, 3.0]]);

        let (b_orth, b_norm) = ModifiedGramSchmidt::compute(&b);

        assert!(super::orthogonal(&a_orth,1e-14));
        assert!(a_norm.all_close(&Array1::from_elem(4, 1.0), 1e-16));

        assert!(super::orthogonal(&b_orth,1e-14));
        assert!(b_norm.all_close(&arr1(&[2.0615528128088303, 0.2910427500435996, 0.7, 3.0]), 1e-16));
    }

    #[test]
    fn modified_gram_schmidt_into() {
        let a = arr2(&[[1.0, 0.0, 0.0, 0.0],
                       [0.0, 1.0, 0.0, 0.0],
                       [0.0, 0.0, 1.0, 0.0],
                       [0.0, 0.0, 0.0, 1.0]]);

        let mut a_o = Array2::default((4,4));
        let mut a_n = Array1::default(4);

        let b = arr2(&[[2.0, 0.5, 0.0, 0.0],
                       [0.0, 0.3, 0.0, 0.0],
                       [0.0, 1.0, 0.7, 0.0],
                       [0.0, 0.0, 0.0, 3.0]]);

        let mut b_o = Array2::default((4,4));
        let mut b_n = Array1::default(4);

        let c = arr2(&[[-4.079764601288893, 4.831491499921403, -2.9560001027996132, -0.02239325297550033, -0.2672544204261703, -0.07718850306444144],
                       [1.2917480323712418, 0.030479388871438983, 0.604549448561548, 0.013409783846041783, 0.037439247530467186, 0.03153579130305008],
                       [-47.584641085515464, 5.501371846864031, 41.39822251681311, -33.69079455346558, 43.13388644338738, 68.7695035292409],
                       [2.5268795799504997, 25.418530275775225, 33.473125141381374, 77.3391516894698, -44.091836957161426, 45.10932299622911],
                       [-20.383209804181938, -19.163209972229616, 0.09795435026201423, -53.296988576627484, -88.482334971421, 16.757575995918756],
                       [62.270964677492124, -75.82678462673792, -0.6889077708993588, 2.2569901796884064, 9.21906803233946, 44.891962279862234]]);

        let mut c_o = Array2::default((6,6));
        let mut c_n = Array1::default(6);

        ModifiedGramSchmidt::compute_into(&a, &mut a_o, &mut a_n);
        ModifiedGramSchmidt::compute_into(&b, &mut b_o, &mut b_n);
        ModifiedGramSchmidt::compute_into(&c, &mut c_o, &mut c_n);

        assert!(super::orthogonal(&a_o,1e-14));
        assert!(a_n.all_close(&Array1::from_elem(4, 1.0), 1e-16));

        assert!(super::orthogonal(&b_o,1e-14));
        assert!(b_n.all_close(&arr1(&[2.0615528128088303, 0.2910427500435996, 0.7, 3.0]), 1e-16));

        assert!(super::orthogonal(&c_o,1e-14));
    }
}
<|endoftext|>


<commit_before>//! Compile-time assertions to ensure that invariants are met.
//!
//! _All_ assertions within this crate are performed at **compile-time**. This
//! allows for finding errors quickly and early when it comes to ensuring
//! certain features or aspects of a codebase.
//!
//! # Usage
//!
//! This crate is available [on crates.io][crate] and can be used by adding the
//! following to your project's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! static_assertions = "0.2.4"
//! ```
//!
//! and this to your crate root:
//!
//! ```
//! #[macro_use]
//! extern crate static_assertions;
//! # fn main() {}
//! ```
//!
//! # Examples
//!
//! Very thorough examples are provided in the docs for each individual macro.
//! Failure case examples are also documented.
//!
//! # Limitations
//!
//! Due to implementation details, some macros can only be used normally from
//! within the context of a function. To use these macros in other contexts, a
//! unique label must be provided.
//!
//! ```compile_fail
//! # #[macro_use] extern crate static_assertions;
//! # fn main() {}
//! // error: expected item after attributes
//! const_assert!(true == true);
//! ```
//!
//! This can be fixed via:
//!
//! ```
//! # #[macro_use] extern crate static_assertions;
//! # fn main() {}
//! const_assert!(label; true == true);
//! ```
//!
//! This issue can be followed [here][issue1]. Feedback and potential solutions
//! are welcome!
//!
//! [issue1]: https://github.com/nvzqz/static-assertions-rs/issues/1
//! [crate]: https://crates.io/crates/static_assertions

#![no_std]

#![deny(unused_macros)]

#[doc(hidden)]
pub extern crate core as _core;

/// Asserts that a given configuration is set.
///
/// # Examples
///
/// A project may not support a set of configurations and thus you may want to
/// report why:
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// // We should only be compiling for Unix or Linux
/// # #[cfg(any(unix, linux))]
/// assert_cfg!(any(unix, linux));
/// # fn main() {}
/// ```
///
/// If users need to specify a database back-end:
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # #[cfg(target_pointer_width = "0")] // Impossible
/// assert_cfg!("Must exclusively use MySQL or MongoDB as database back-end",
///             all(not(all(feature = "mysql", feature = "mongodb")),
///                 any(    feature = "mysql", feature = "mongodb")));
/// # fn main() {}
/// ```
///
/// We can't be compiling for both Unix _and_ Windows simultaneously:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_cfg!(all(unix, windows));
/// # }
/// ```
#[macro_export]
macro_rules! assert_cfg {
    () => {};
    ($msg:expr, $($cfg:tt)*) => {
        #[cfg(not($($cfg)*))]
        compile_error!($msg);
    };
    ($($cfg:tt)*) => {
        #[cfg(not($($cfg)*))]
        compile_error!(concat!("Cfg does not pass: ", stringify!($($cfg)*)));
    };
}

/// Asserts that types are equal in size.
///
/// When performing operations such as pointer casts or dealing with [`usize`]
/// versus [`u64`] versus [`u32`], the size of your types matter. This is where
/// this macro comes into play.
///
/// # Alternatives
///
/// There are also [`assert_eq_size_val`](macro.assert_eq_size_val.html) and
/// [`assert_eq_size_ptr`](macro.assert_eq_size_ptr.html). Instead of specifying
/// types to compare, values' sizes can be directly compared against each other.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// // Can be declared outside of a function if labeled
/// assert_eq_size!(bytes; (u8, u8), u16);
///
/// fn main() {
///     // Supports unlimited arguments:
///     assert_eq_size!([u8; 4], (u16, u16), u32);
/// }
/// ```
///
/// The following produces a compilation failure because `u32` has 4 times the
/// size of `u8`:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_eq_size!(u32, u8);
/// # }
/// ```
///
/// [`usize`]: https://doc.rust-lang.org/std/primitive.usize.html
/// [`u64`]: https://doc.rust-lang.org/std/primitive.u64.html
/// [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html
#[macro_export]
macro_rules! assert_eq_size {
    ($x:ty, $($xs:ty),+ $(,)*) => {
        $(let _ = $crate::_core::mem::transmute::<$x, $xs>;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_eq_size!($($xs)+); }
    };
}

/// Asserts that values pointed to are equal in size.
///
/// This especially is useful for when coercing pointers between different types
/// and ensuring the underlying values are the same size.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// fn operation(x: &(u32, u32), y: &[u16; 4]) {
///     assert_eq_size_ptr!(x, y);
/// }
/// # fn main() {}
/// ```
///
/// Byte arrays of different lengths have different sizes:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// static BYTES: &[u8; 4] = &[
///     /* ... */
///     # 0; 4
/// ];
///
/// static TABLE: &[u8; 16] = &[
///     /* ... */
///     # 0; 16
/// ];
///
/// assert_eq_size_ptr!(BYTES, TABLE);
/// # }
/// ```
#[macro_export]
macro_rules! assert_eq_size_ptr {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, forget_copy, unused_unsafe, useless_transmute)]
        unsafe {
            use $crate::_core::{mem, ptr};
            let mut copy = ptr::read($x);
            $(ptr::write(&mut copy, mem::transmute(ptr::read($xs)));)+
            mem::forget(copy);
        }
    }
}

/// Asserts that values are equal in size.
///
/// This macro doesn't consume its arguments and thus works for
/// non-[`Clone`]able values.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {
/// struct Byte(u8);
///
/// let x = 10u8;
/// let y = Byte(42); // Works for non-cloneable types
///
/// assert_eq_size_val!(x, y);
/// assert_eq_size_val!(x, y, 0u8);
/// # }
/// ```
///
/// Even though both values are 0, they are of types with different sizes:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_eq_size_val!(0u8, 0u32);
/// # }
/// ```
///
/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html
#[macro_export]
macro_rules! assert_eq_size_val {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        assert_eq_size_ptr!(&$x, $(&$xs),+);
    }
}

/// Asserts that constant expressions evaluate to `true`.
///
/// There also exists [`const_assert_eq`](macro.const_assert_eq.html) for
/// validating whether a sequence of expressions are equal to one another.
///
/// # Examples
///
/// Constant expressions can be ensured to have certain properties via this
/// macro If the expression evaluates to `false`, the file will fail to compile.
/// This is synonymous to [`static_assert` in C++][static_assert].
///
/// As a [limitation](index.html#limitations), a unique label is required if
/// the macro is used outside of a function.
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// const FIVE: usize = 5;
///
/// const_assert!(twenty_five; FIVE * FIVE == 25);
///
/// fn main() {
///     const_assert!(2 + 2 == 4);
///     const_assert!(FIVE - FIVE == 0);
/// }
/// ```
///
/// Some expressions are blatantly false:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert!(1 >= 2);
/// # }
/// ```
///
/// [static_assert]: http://en.cppreference.com/w/cpp/language/static_assert
#[macro_export]
macro_rules! const_assert {
    ($($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, eq_op)]
        let _ = [(); 0 - !($($xs)&&+) as usize];
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { const_assert!($($xs)+); }
    };
}

/// Asserts that constants are equal in value.
///
/// # Examples
///
/// Works as a shorthand for `const_assert!(a == b)`:
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// const TWO: usize = 2;
/// const_assert_eq!(two; TWO * TWO, TWO + TWO, 4);
///
/// fn main() {
///     const NUM: usize = 32;
///     const_assert_eq!(NUM + NUM, 64);
/// }
/// ```
///
/// Just because 2 × 2 = 2 + 2 doesn't mean it holds true for other numbers:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert_eq!(4 + 4, 4 * 4);
/// # }
/// ```
#[macro_export]
macro_rules! const_assert_eq {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($($x == $xs),+);
    };
    ($label:ident; $x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($label; $($x == $xs),+);
    };
}

/// Asserts that the traits are object-safe.
///
/// This is useful for when changes are made to a trait that accidentally
/// prevent it from being used as an object. Such a case would be adding a
/// generic method and forgetting to add `where Self: Sized` after it. If left
/// unnoticed, that mistake will affect crate users and break both forward and
/// backward compatibility.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// assert_obj_safe!(basic; Send, Sync, AsRef<str>);
///
/// mod inner {
///     // Works with traits that are not in the calling module
///     pub trait BasicTrait {
///         fn foo(&self);
///     }
/// }
///
/// trait MySafeTrait {
///     fn bar(&self) -> u32;
/// }
///
/// fn main() {
///     assert_obj_safe!(MySafeTrait);
///     assert_obj_safe!(inner::BasicTrait);
/// }
/// ```
///
/// Generics without `where Self: Sized` are not allowed in object-safe traits:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T;
/// }
///
/// # fn main() {
/// assert_obj_safe!(MyUnsafeTrait);
/// # }
/// ```
#[macro_export]
macro_rules! assert_obj_safe {
    ($($xs:ty),+ $(,)*) => {
        $(let _: &$xs;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_obj_safe!($($xs)+); }
    };
}

/// Asserts that the type has the given fields.
///
/// # Examples
///
/// This may be used when types have odd fields as a result of `#[cfg]`.
///
/// ```
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// struct Ty {
///     #[cfg(windows)]
///     value: u8,
///     #[cfg(not(windows))]
///     value: usize,
/// }
///
/// /* ... */
///
/// // Always have `value` regardless of OS
/// assert_fields!(Ty, value);
/// # }
/// ```
///
/// Range does not have a field named `middle`:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// use std::ops::Range;
///
/// assert_fields!(Range<u32>, middle);
/// # }
/// ```
#[macro_export]
macro_rules! assert_fields {
    ($t:path, $($f:ident),+) => {
        $(let $t { $f: _, .. };)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_fields!($($xs)+); }
    };
}

/// Asserts that the type implements the given traits.
///
/// # Examples
///
/// Can be used to ensure types implement [`Send`], [`Sync`], and other traits:
///
/// ```
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {}
/// assert_impl!(str; String, Send, Sync, From<&'static str>);
/// assert_impl!(vec; &'static [u8], Into<Vec<u8>>);
/// ```
///
/// Raw pointers cannot be sent between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_impl!(*const u8, Send);
/// # }
/// ```
///
/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
#[macro_export]
macro_rules! assert_impl {
    ($x:ty, $($t:path),+ $(,)*) => {
        $({
            fn assert_impl<T: ?Sized + $t>() {}
            assert_impl::<$x>();
        })+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_impl!($($xs)+); }
    };
}
<commit_msg>Add message to Unix+Windows compile fail<commit_after>//! Compile-time assertions to ensure that invariants are met.
//!
//! _All_ assertions within this crate are performed at **compile-time**. This
//! allows for finding errors quickly and early when it comes to ensuring
//! certain features or aspects of a codebase.
//!
//! # Usage
//!
//! This crate is available [on crates.io][crate] and can be used by adding the
//! following to your project's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! static_assertions = "0.2.4"
//! ```
//!
//! and this to your crate root:
//!
//! ```
//! #[macro_use]
//! extern crate static_assertions;
//! # fn main() {}
//! ```
//!
//! # Examples
//!
//! Very thorough examples are provided in the docs for each individual macro.
//! Failure case examples are also documented.
//!
//! # Limitations
//!
//! Due to implementation details, some macros can only be used normally from
//! within the context of a function. To use these macros in other contexts, a
//! unique label must be provided.
//!
//! ```compile_fail
//! # #[macro_use] extern crate static_assertions;
//! # fn main() {}
//! // error: expected item after attributes
//! const_assert!(true == true);
//! ```
//!
//! This can be fixed via:
//!
//! ```
//! # #[macro_use] extern crate static_assertions;
//! # fn main() {}
//! const_assert!(label; true == true);
//! ```
//!
//! This issue can be followed [here][issue1]. Feedback and potential solutions
//! are welcome!
//!
//! [issue1]: https://github.com/nvzqz/static-assertions-rs/issues/1
//! [crate]: https://crates.io/crates/static_assertions

#![no_std]

#![deny(unused_macros)]

#[doc(hidden)]
pub extern crate core as _core;

/// Asserts that a given configuration is set.
///
/// # Examples
///
/// A project may not support a set of configurations and thus you may want to
/// report why:
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// // We should only be compiling for Unix or Linux
/// # #[cfg(any(unix, linux))]
/// assert_cfg!(any(unix, linux));
/// # fn main() {}
/// ```
///
/// If users need to specify a database back-end:
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # #[cfg(target_pointer_width = "0")] // Impossible
/// assert_cfg!("Must exclusively use MySQL or MongoDB as database back-end",
///             all(not(all(feature = "mysql", feature = "mongodb")),
///                 any(    feature = "mysql", feature = "mongodb")));
/// # fn main() {}
/// ```
///
/// We can't be compiling for both Unix _and_ Windows simultaneously:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_cfg!("No, that's not how it works! ಠ_ಠ", all(unix, windows));
/// # }
/// ```
#[macro_export]
macro_rules! assert_cfg {
    () => {};
    ($msg:expr, $($cfg:tt)*) => {
        #[cfg(not($($cfg)*))]
        compile_error!($msg);
    };
    ($($cfg:tt)*) => {
        #[cfg(not($($cfg)*))]
        compile_error!(concat!("Cfg does not pass: ", stringify!($($cfg)*)));
    };
}

/// Asserts that types are equal in size.
///
/// When performing operations such as pointer casts or dealing with [`usize`]
/// versus [`u64`] versus [`u32`], the size of your types matter. This is where
/// this macro comes into play.
///
/// # Alternatives
///
/// There are also [`assert_eq_size_val`](macro.assert_eq_size_val.html) and
/// [`assert_eq_size_ptr`](macro.assert_eq_size_ptr.html). Instead of specifying
/// types to compare, values' sizes can be directly compared against each other.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// // Can be declared outside of a function if labeled
/// assert_eq_size!(bytes; (u8, u8), u16);
///
/// fn main() {
///     // Supports unlimited arguments:
///     assert_eq_size!([u8; 4], (u16, u16), u32);
/// }
/// ```
///
/// The following produces a compilation failure because `u32` has 4 times the
/// size of `u8`:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_eq_size!(u32, u8);
/// # }
/// ```
///
/// [`usize`]: https://doc.rust-lang.org/std/primitive.usize.html
/// [`u64`]: https://doc.rust-lang.org/std/primitive.u64.html
/// [`u32`]: https://doc.rust-lang.org/std/primitive.u32.html
#[macro_export]
macro_rules! assert_eq_size {
    ($x:ty, $($xs:ty),+ $(,)*) => {
        $(let _ = $crate::_core::mem::transmute::<$x, $xs>;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_eq_size!($($xs)+); }
    };
}

/// Asserts that values pointed to are equal in size.
///
/// This especially is useful for when coercing pointers between different types
/// and ensuring the underlying values are the same size.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// fn operation(x: &(u32, u32), y: &[u16; 4]) {
///     assert_eq_size_ptr!(x, y);
/// }
/// # fn main() {}
/// ```
///
/// Byte arrays of different lengths have different sizes:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// static BYTES: &[u8; 4] = &[
///     /* ... */
///     # 0; 4
/// ];
///
/// static TABLE: &[u8; 16] = &[
///     /* ... */
///     # 0; 16
/// ];
///
/// assert_eq_size_ptr!(BYTES, TABLE);
/// # }
/// ```
#[macro_export]
macro_rules! assert_eq_size_ptr {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, forget_copy, unused_unsafe, useless_transmute)]
        unsafe {
            use $crate::_core::{mem, ptr};
            let mut copy = ptr::read($x);
            $(ptr::write(&mut copy, mem::transmute(ptr::read($xs)));)+
            mem::forget(copy);
        }
    }
}

/// Asserts that values are equal in size.
///
/// This macro doesn't consume its arguments and thus works for
/// non-[`Clone`]able values.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// # fn main() {
/// struct Byte(u8);
///
/// let x = 10u8;
/// let y = Byte(42); // Works for non-cloneable types
///
/// assert_eq_size_val!(x, y);
/// assert_eq_size_val!(x, y, 0u8);
/// # }
/// ```
///
/// Even though both values are 0, they are of types with different sizes:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_eq_size_val!(0u8, 0u32);
/// # }
/// ```
///
/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html
#[macro_export]
macro_rules! assert_eq_size_val {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        assert_eq_size_ptr!(&$x, $(&$xs),+);
    }
}

/// Asserts that constant expressions evaluate to `true`.
///
/// There also exists [`const_assert_eq`](macro.const_assert_eq.html) for
/// validating whether a sequence of expressions are equal to one another.
///
/// # Examples
///
/// Constant expressions can be ensured to have certain properties via this
/// macro If the expression evaluates to `false`, the file will fail to compile.
/// This is synonymous to [`static_assert` in C++][static_assert].
///
/// As a [limitation](index.html#limitations), a unique label is required if
/// the macro is used outside of a function.
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// const FIVE: usize = 5;
///
/// const_assert!(twenty_five; FIVE * FIVE == 25);
///
/// fn main() {
///     const_assert!(2 + 2 == 4);
///     const_assert!(FIVE - FIVE == 0);
/// }
/// ```
///
/// Some expressions are blatantly false:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert!(1 >= 2);
/// # }
/// ```
///
/// [static_assert]: http://en.cppreference.com/w/cpp/language/static_assert
#[macro_export]
macro_rules! const_assert {
    ($($xs:expr),+ $(,)*) => {
        #[allow(unknown_lints, eq_op)]
        let _ = [(); 0 - !($($xs)&&+) as usize];
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { const_assert!($($xs)+); }
    };
}

/// Asserts that constants are equal in value.
///
/// # Examples
///
/// Works as a shorthand for `const_assert!(a == b)`:
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// const TWO: usize = 2;
/// const_assert_eq!(two; TWO * TWO, TWO + TWO, 4);
///
/// fn main() {
///     const NUM: usize = 32;
///     const_assert_eq!(NUM + NUM, 64);
/// }
/// ```
///
/// Just because 2 × 2 = 2 + 2 doesn't mean it holds true for other numbers:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// const_assert_eq!(4 + 4, 4 * 4);
/// # }
/// ```
#[macro_export]
macro_rules! const_assert_eq {
    ($x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($($x == $xs),+);
    };
    ($label:ident; $x:expr, $($xs:expr),+ $(,)*) => {
        const_assert!($label; $($x == $xs),+);
    };
}

/// Asserts that the traits are object-safe.
///
/// This is useful for when changes are made to a trait that accidentally
/// prevent it from being used as an object. Such a case would be adding a
/// generic method and forgetting to add `where Self: Sized` after it. If left
/// unnoticed, that mistake will affect crate users and break both forward and
/// backward compatibility.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate static_assertions;
/// assert_obj_safe!(basic; Send, Sync, AsRef<str>);
///
/// mod inner {
///     // Works with traits that are not in the calling module
///     pub trait BasicTrait {
///         fn foo(&self);
///     }
/// }
///
/// trait MySafeTrait {
///     fn bar(&self) -> u32;
/// }
///
/// fn main() {
///     assert_obj_safe!(MySafeTrait);
///     assert_obj_safe!(inner::BasicTrait);
/// }
/// ```
///
/// Generics without `where Self: Sized` are not allowed in object-safe traits:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// trait MyUnsafeTrait {
///     fn baz<T>(&self) -> T;
/// }
///
/// # fn main() {
/// assert_obj_safe!(MyUnsafeTrait);
/// # }
/// ```
#[macro_export]
macro_rules! assert_obj_safe {
    ($($xs:ty),+ $(,)*) => {
        $(let _: &$xs;)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_obj_safe!($($xs)+); }
    };
}

/// Asserts that the type has the given fields.
///
/// # Examples
///
/// This may be used when types have odd fields as a result of `#[cfg]`.
///
/// ```
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// struct Ty {
///     #[cfg(windows)]
///     value: u8,
///     #[cfg(not(windows))]
///     value: usize,
/// }
///
/// /* ... */
///
/// // Always have `value` regardless of OS
/// assert_fields!(Ty, value);
/// # }
/// ```
///
/// Range does not have a field named `middle`:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// use std::ops::Range;
///
/// assert_fields!(Range<u32>, middle);
/// # }
/// ```
#[macro_export]
macro_rules! assert_fields {
    ($t:path, $($f:ident),+) => {
        $(let $t { $f: _, .. };)+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_fields!($($xs)+); }
    };
}

/// Asserts that the type implements the given traits.
///
/// # Examples
///
/// Can be used to ensure types implement [`Send`], [`Sync`], and other traits:
///
/// ```
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {}
/// assert_impl!(str; String, Send, Sync, From<&'static str>);
/// assert_impl!(vec; &'static [u8], Into<Vec<u8>>);
/// ```
///
/// Raw pointers cannot be sent between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_impl!(*const u8, Send);
/// # }
/// ```
///
/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
#[macro_export]
macro_rules! assert_impl {
    ($x:ty, $($t:path),+ $(,)*) => {
        $({
            fn assert_impl<T: ?Sized + $t>() {}
            assert_impl::<$x>();
        })+
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_impl!($($xs)+); }
    };
}
<|endoftext|>


<commit_before>//! A memory-safer wrapper around system dynamic library loading primitives.
//!
//! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as
//! shared libraries) as well as use functions and static variables these libraries contain.
//!
//! While the library does expose a cross-platform interface to load a library and find stuff
//! inside it, little is done to paper over the platform differences, especially where library
//! loading is involved. The documentation for each function will attempt to document such
//! differences on the best-effort basis.
//!
//! Less safe, platform specific bindings are also available. See the
//! [`os::platform`](os/index.html) module for details.
//!
//! # Usage
//!
//! Add dependency to this library to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! libloading = "0.3"
//! ```
//!
//! Then inside your project
//!
//! ```no_run
//! extern crate libloading as lib;
//!
//! fn call_dynamic() -> lib::Result<u32> {
//!     let lib = try!(lib::Library::new("/path/to/liblibrary.so"));
//!     unsafe {
//!         let func: lib::Symbol<unsafe extern fn() -> u32> = try!(lib.get(b"my_func"));
//!         Ok(func())
//!     }
//! }
//! ```
//!
//! The compiler will ensure that the loaded `function` will not outlive the `Library` it comes
//! from, preventing a common cause of undefined behaviour and memory safety problems.
use std::ffi::OsStr;
use std::fmt;
use std::ops;
use std::marker;

#[cfg(unix)]
#[macro_use]
extern crate lazy_static;

#[cfg(unix)]
use self::os::unix as imp;
#[cfg(windows)]
use self::os::windows as imp;

pub mod os;
pub mod changelog;
mod util;

pub type Result<T> = ::std::io::Result<T>;

/// A loaded dynamic library.
pub struct Library(imp::Library);

impl Library {
    /// Find and load a dynamic library.
    ///
    /// The `filename` argument may be any of:
    ///
    /// * A library filename;
    /// * Absolute path to the library;
    /// * Relative (to the current working directory) path to the library.
    ///
    /// ## Platform-specific behaviour
    ///
    /// When a plain library filename is supplied, locations where library is searched for is
    /// platform specific and cannot be adjusted in a portable manner.
    ///
    /// Calling this function from multiple threads is not safe if used in conjunction with
    /// path-less filename and library search path is modified (`SetDllDirectory` function on
    /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
    ///
    /// ### Windows
    ///
    /// If the `filename` specifies a library filename without path and with extension omitted,
    /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a
    /// trailing `.` to the `filename`.
    ///
    /// If the library contains thread local variables (MSVC’s `_declspec(thread)`, Rust’s
    /// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows
    /// Vista.
    ///
    /// ## Tips
    ///
    /// Distributing your dynamic libraries under a filename common to all platforms (e.g.
    /// `awesome.module`) allows to avoid code which has to account for platform’s conventional
    /// library filenames.
    ///
    /// Strive to specify absolute or relative path to your library, unless system-wide libraries
    /// are being loaded.  Platform-dependent library search locations combined with various quirks
    /// related to path-less filenames may cause flaky code.
    ///
    /// ## Examples
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// // Any of the following are valid.
    /// let _ = Library::new("/path/to/awesome.module").unwrap();
    /// let _ = Library::new("../awesome.module").unwrap();
    /// let _ = Library::new("libsomelib.so.1").unwrap();
    /// ```
    pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library> {
        imp::Library::new(filename).map(From::from)
    }

    /// Get a pointer to function or static variable by symbol name.
    ///
    /// The `symbol` may not contain any null bytes, with an exception of last byte. A null
    /// terminated `symbol` may avoid a string allocation in some cases.
    ///
    /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
    /// most likely invalid.
    ///
    /// ## Unsafety
    ///
    /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
    /// undefined.
    ///
    /// ## Platform-specific behaviour
    ///
    /// On Linux and Windows, a TLS variable acts just like any regular static variable. OS X uses
    /// some sort of lazy initialization scheme, which makes loading TLS variables this way
    /// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour.
    ///
    /// ## Examples
    ///
    /// Given a loaded library:
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// let lib = Library::new("/path/to/awesome.module").unwrap();
    /// ```
    ///
    /// Loading and using a function looks like this:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
    ///         lib.get(b"awesome_function\0").unwrap();
    ///     awesome_function(0.42);
    /// }
    /// ```
    ///
    /// A static variable may also be loaded and inspected:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap();
    ///     **awesome_variable = 42.0;
    /// };
    /// ```
    pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>> {
        self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
    }
}

impl fmt::Debug for Library {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl From<imp::Library> for Library {
    fn from(lib: imp::Library) -> Library {
        Library(lib)
    }
}

impl From<Library> for imp::Library {
    fn from(lib: Library) -> imp::Library {
        lib.0
    }
}

unsafe impl Send for Library {}
unsafe impl Sync for Library {}

/// Symbol from a library.
///
/// This type is a safeguard against using dynamically loaded symbols after a `Library` is
/// unloaded. Primary method to create an instance of a `Symbol` is via `Library::get`.
///
/// Due to implementation of the `Deref` trait, an instance of `Symbol` may be used as if it was a
/// function or variable directly, without taking care to “extract” function or variable manually
/// most of the time.
///
/// See [`Library::get`] for details.
///
/// [`Library::get`]: ./struct.Library.html#method.get
pub struct Symbol<'lib, T: 'lib> {
    inner: imp::Symbol<T>,
    pd: marker::PhantomData<&'lib T>
}

impl<'lib, T> Symbol<'lib, T> {
    /// Extract the wrapped `os::platform::Symbol`.
    ///
    /// ## Unsafety
    /// Using this function relinquishes all the lifetime guarantees. It is up to programmer to
    /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
    /// was loaded from.
    ///
    /// ## Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    /// }
    /// ```
    pub unsafe fn into_raw(self) -> imp::Symbol<T> {
        self.inner
    }

    /// Wrap the `os::platform::Symbol` into this safe wrapper.
    ///
    /// Note that, in order to create association between the symbol and the library this symbol
    /// came from, this function requires reference to the library provided.
    ///
    /// ## Unsafety
    ///
    /// It is invalid to provide a reference to any other value other than the library the `sym`
    /// was loaded from. Doing so invalidates any lifetime guarantees.
    ///
    /// ## Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    ///     let symbol = Symbol::from_raw(symbol, &lib);
    /// }
    /// ```
    pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, _: &'lib L) -> Symbol<'lib, T> {
        Symbol {
            inner: sym,
            pd: marker::PhantomData
        }
    }
}

impl<'lib, T> Clone for Symbol<'lib, T> {
    fn clone(&self) -> Symbol<'lib, T> {
        Symbol {
            inner: self.inner.clone(),
            pd: marker::PhantomData
        }
    }
}

// FIXME: implement FnOnce for callable stuff instead.
impl<'lib, T> ops::Deref for Symbol<'lib, T> {
    type Target = T;
    fn deref(&self) -> &T {
        ops::Deref::deref(&self.inner)
    }
}

impl<'lib, T> fmt::Debug for Symbol<'lib, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.inner.fmt(f)
    }
}

unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {}
unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {}
<commit_msg>Replace try macro with ? operator in example.<commit_after>//! A memory-safer wrapper around system dynamic library loading primitives.
//!
//! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as
//! shared libraries) as well as use functions and static variables these libraries contain.
//!
//! While the library does expose a cross-platform interface to load a library and find stuff
//! inside it, little is done to paper over the platform differences, especially where library
//! loading is involved. The documentation for each function will attempt to document such
//! differences on the best-effort basis.
//!
//! Less safe, platform specific bindings are also available. See the
//! [`os::platform`](os/index.html) module for details.
//!
//! # Usage
//!
//! Add dependency to this library to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! libloading = "0.3"
//! ```
//!
//! Then inside your project
//!
//! ```no_run
//! extern crate libloading as lib;
//!
//! fn call_dynamic() -> lib::Result<u32> {
//!     let lib = lib::Library::new("/path/to/liblibrary.so")?;
//!     unsafe {
//!         let func: lib::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?;
//!         Ok(func())
//!     }
//! }
//! ```
//!
//! The compiler will ensure that the loaded `function` will not outlive the `Library` it comes
//! from, preventing a common cause of undefined behaviour and memory safety problems.
use std::ffi::OsStr;
use std::fmt;
use std::ops;
use std::marker;

#[cfg(unix)]
#[macro_use]
extern crate lazy_static;

#[cfg(unix)]
use self::os::unix as imp;
#[cfg(windows)]
use self::os::windows as imp;

pub mod os;
pub mod changelog;
mod util;

pub type Result<T> = ::std::io::Result<T>;

/// A loaded dynamic library.
pub struct Library(imp::Library);

impl Library {
    /// Find and load a dynamic library.
    ///
    /// The `filename` argument may be any of:
    ///
    /// * A library filename;
    /// * Absolute path to the library;
    /// * Relative (to the current working directory) path to the library.
    ///
    /// ## Platform-specific behaviour
    ///
    /// When a plain library filename is supplied, locations where library is searched for is
    /// platform specific and cannot be adjusted in a portable manner.
    ///
    /// Calling this function from multiple threads is not safe if used in conjunction with
    /// path-less filename and library search path is modified (`SetDllDirectory` function on
    /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
    ///
    /// ### Windows
    ///
    /// If the `filename` specifies a library filename without path and with extension omitted,
    /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a
    /// trailing `.` to the `filename`.
    ///
    /// If the library contains thread local variables (MSVC’s `_declspec(thread)`, Rust’s
    /// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows
    /// Vista.
    ///
    /// ## Tips
    ///
    /// Distributing your dynamic libraries under a filename common to all platforms (e.g.
    /// `awesome.module`) allows to avoid code which has to account for platform’s conventional
    /// library filenames.
    ///
    /// Strive to specify absolute or relative path to your library, unless system-wide libraries
    /// are being loaded.  Platform-dependent library search locations combined with various quirks
    /// related to path-less filenames may cause flaky code.
    ///
    /// ## Examples
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// // Any of the following are valid.
    /// let _ = Library::new("/path/to/awesome.module").unwrap();
    /// let _ = Library::new("../awesome.module").unwrap();
    /// let _ = Library::new("libsomelib.so.1").unwrap();
    /// ```
    pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library> {
        imp::Library::new(filename).map(From::from)
    }

    /// Get a pointer to function or static variable by symbol name.
    ///
    /// The `symbol` may not contain any null bytes, with an exception of last byte. A null
    /// terminated `symbol` may avoid a string allocation in some cases.
    ///
    /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
    /// most likely invalid.
    ///
    /// ## Unsafety
    ///
    /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
    /// undefined.
    ///
    /// ## Platform-specific behaviour
    ///
    /// On Linux and Windows, a TLS variable acts just like any regular static variable. OS X uses
    /// some sort of lazy initialization scheme, which makes loading TLS variables this way
    /// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour.
    ///
    /// ## Examples
    ///
    /// Given a loaded library:
    ///
    /// ```no_run
    /// # use ::libloading::Library;
    /// let lib = Library::new("/path/to/awesome.module").unwrap();
    /// ```
    ///
    /// Loading and using a function looks like this:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
    ///         lib.get(b"awesome_function\0").unwrap();
    ///     awesome_function(0.42);
    /// }
    /// ```
    ///
    /// A static variable may also be loaded and inspected:
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// # let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap();
    ///     **awesome_variable = 42.0;
    /// };
    /// ```
    pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>> {
        self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
    }
}

impl fmt::Debug for Library {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl From<imp::Library> for Library {
    fn from(lib: imp::Library) -> Library {
        Library(lib)
    }
}

impl From<Library> for imp::Library {
    fn from(lib: Library) -> imp::Library {
        lib.0
    }
}

unsafe impl Send for Library {}
unsafe impl Sync for Library {}

/// Symbol from a library.
///
/// This type is a safeguard against using dynamically loaded symbols after a `Library` is
/// unloaded. Primary method to create an instance of a `Symbol` is via `Library::get`.
///
/// Due to implementation of the `Deref` trait, an instance of `Symbol` may be used as if it was a
/// function or variable directly, without taking care to “extract” function or variable manually
/// most of the time.
///
/// See [`Library::get`] for details.
///
/// [`Library::get`]: ./struct.Library.html#method.get
pub struct Symbol<'lib, T: 'lib> {
    inner: imp::Symbol<T>,
    pd: marker::PhantomData<&'lib T>
}

impl<'lib, T> Symbol<'lib, T> {
    /// Extract the wrapped `os::platform::Symbol`.
    ///
    /// ## Unsafety
    /// Using this function relinquishes all the lifetime guarantees. It is up to programmer to
    /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
    /// was loaded from.
    ///
    /// ## Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    /// }
    /// ```
    pub unsafe fn into_raw(self) -> imp::Symbol<T> {
        self.inner
    }

    /// Wrap the `os::platform::Symbol` into this safe wrapper.
    ///
    /// Note that, in order to create association between the symbol and the library this symbol
    /// came from, this function requires reference to the library provided.
    ///
    /// ## Unsafety
    ///
    /// It is invalid to provide a reference to any other value other than the library the `sym`
    /// was loaded from. Doing so invalidates any lifetime guarantees.
    ///
    /// ## Examples
    ///
    /// ```no_run
    /// # use ::libloading::{Library, Symbol};
    /// let lib = Library::new("/path/to/awesome.module").unwrap();
    /// unsafe {
    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
    ///     let symbol = symbol.into_raw();
    ///     let symbol = Symbol::from_raw(symbol, &lib);
    /// }
    /// ```
    pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, _: &'lib L) -> Symbol<'lib, T> {
        Symbol {
            inner: sym,
            pd: marker::PhantomData
        }
    }
}

impl<'lib, T> Clone for Symbol<'lib, T> {
    fn clone(&self) -> Symbol<'lib, T> {
        Symbol {
            inner: self.inner.clone(),
            pd: marker::PhantomData
        }
    }
}

// FIXME: implement FnOnce for callable stuff instead.
impl<'lib, T> ops::Deref for Symbol<'lib, T> {
    type Target = T;
    fn deref(&self) -> &T {
        ops::Deref::deref(&self.inner)
    }
}

impl<'lib, T> fmt::Debug for Symbol<'lib, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.inner.fmt(f)
    }
}

unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {}
unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {}
<|endoftext|>


<commit_before>//! Defines "provider" traits and implementations for different types of I/O operations, enabling
//! dependency injection that's very helpful for testing.
//!
//! A number of different I/O types are supported:
//!
//! * Process environment (variables, working directy etc), via [`Env`](env/trait.Env.html)
//! * Standard streams (stdin, stdout and stderr), via [`StdStreams`](std_streams/trait.StdStreams.html)
//! * Filesystem access, via [`Fs`](fs/trait.Fs.html)
//!
//! In addition to "native" implementations for each trait, "simulated" implementations are also
//! built-in:
//!
//! * [`SimulatedEnv`](env/trait.SimulatedEnv.html) for faking process environment state
//! * [`SimulatedStdStreams`](std_streams/trait.SimulatedStdStreams.html) for faking standard
//!   stream input and inspecting output
//! * [`TempFs`](fs/trait.TempFs.html) for performing filesystem access in a `chroot`-like sandbox
//!   isolated from the rest of the filesystem
//!
//! Each provider trait can be used independently, however there is also the all-encompassing
//! [`Io`](trait.Io.html) which provides access to all of them. If you have a variety of I/O
//! dependencies, it might be easiest to create and pass around a single `&mut Io`.
//!
//! # Examples
//!
//! ```
//! extern crate io_providers;
//!
//! use std::io::Write;
//! use std::path::Path;
//! use io_providers::{Env, Io, NativeIo, SimulatedIo, StdStreams};
//!
//! /// Gets the current working directory and prints it to stdout.
//! fn do_work<I: Io>(io: &mut I) {
//!     let cur_dir = io.env().current_dir().unwrap();
//!     let stdout = io.std_streams().output();
//!     writeln!(stdout, "The current directory is: {}", cur_dir.to_str().unwrap()).unwrap();
//! }
//!
//! fn main() {
//!     // Test `do_work()` using a simulated I/O environment
//!     let mut simulated_io = SimulatedIo::new().unwrap();
//!     simulated_io.env().set_current_dir(Path::new("/foo/bar")).unwrap();
//!     do_work(&mut simulated_io);
//!     assert_eq!(
//!         "The current directory is: /foo/bar\n",
//!         ::std::str::from_utf8(simulated_io.std_streams().read_output()).unwrap());
//!
//!     // Now use a native I/O provided to access the real system
//!     let mut real_io = NativeIo::new();
//!     do_work(&mut real_io);
//! }
//! ```

extern crate tempfile;

use std::io;

pub mod env;
pub mod fs;
pub mod std_streams;

pub use env::{Env, NativeEnv, SimulatedEnv};
pub use fs::{Fs, NativeFs, TempFs};
pub use std_streams::{NativeStdStreams, SimulatedStdStreams, StdStreams};

/// Provides access to the process environment, filesystem, and standard streams.
///
/// See [`env::Env`](env/trait.Env.html),
/// [`std_streams::StdStreams`](std_streams/trait.StdStreams.html) and
/// [`fs::Fs`](fs/trait.Fs.html) for details.
pub trait Io {
    // The type of the environment provider.
    type E: env::Env;

    // The type of the filesystem provider.
    type F: fs::Fs;

    // The type of the stream provider.
    type S: std_streams::StdStreams;

    /// Gets the [`env::Env`](env/trait.Env.html) provider.
    fn env(&mut self) -> &mut Self::E;

    /// Gets the [`fs::Fs`](fs/trait.Fs.html) provider.
    fn fs(&mut self) -> &mut Self::F;

    /// Gets the [`std_streams::StdStreams`](std_streams/trait.StdStreams.html).
    fn std_streams(&mut self) -> &mut Self::S;
}

/// `Io` implementation using the native system.
///
/// See `env::NativeEnv` and `std_streams::NativeStdStreams` for more information.
#[derive(Default)]
pub struct NativeIo {
    env: env::NativeEnv,
    fs: fs::NativeFs,
    stream: std_streams::NativeStdStreams,
}

impl NativeIo {
    /// Creates a new `LocalIoProvider`.
    pub fn new() -> NativeIo {
        NativeIo {
            env: env::NativeEnv,
            fs: fs::NativeFs,
            stream: std_streams::NativeStdStreams::new(),
        }
    }
}

impl Io for NativeIo {
    type E = env::NativeEnv;
    type F = fs::NativeFs;
    type S = std_streams::NativeStdStreams;

    fn env(&mut self) -> &mut env::NativeEnv {
        &mut self.env
    }

    fn fs(&mut self) -> &mut fs::NativeFs {
        &mut self.fs
    }

    fn std_streams(&mut self) -> &mut std_streams::NativeStdStreams {
        &mut self.stream
    }
}

/// `Io` implementation using a simulated environment.
///
/// See `env::SimulatedEnv` and `std_streams::SimulatedStdStreams` for more information.
pub struct SimulatedIo {
    env: env::SimulatedEnv,
    fs: fs::TempFs,
    stream: std_streams::SimulatedStdStreams,
}

impl SimulatedIo {
    /// Creates a new `SimulatedIo`.
    pub fn new() -> io::Result<SimulatedIo> {
        Ok(SimulatedIo {
            env: env::SimulatedEnv::new(),
            fs: fs::TempFs::new()?,
            stream: std_streams::SimulatedStdStreams::new(),
        })
    }
}

impl Io for SimulatedIo {
    type E = env::SimulatedEnv;
    type F = fs::TempFs;
    type S = std_streams::SimulatedStdStreams;

    fn env(&mut self) -> &mut env::SimulatedEnv {
        &mut self.env
    }

    fn fs(&mut self) -> &mut fs::TempFs {
        &mut self.fs
    }

    fn std_streams(&mut self) -> &mut std_streams::SimulatedStdStreams {
        &mut self.stream
    }
}
<commit_msg>Add pub use of OpenOptions<commit_after>//! Defines "provider" traits and implementations for different types of I/O operations, enabling
//! dependency injection that's very helpful for testing.
//!
//! A number of different I/O types are supported:
//!
//! * Process environment (variables, working directy etc), via [`Env`](env/trait.Env.html)
//! * Standard streams (stdin, stdout and stderr), via [`StdStreams`](std_streams/trait.StdStreams.html)
//! * Filesystem access, via [`Fs`](fs/trait.Fs.html)
//!
//! In addition to "native" implementations for each trait, "simulated" implementations are also
//! built-in:
//!
//! * [`SimulatedEnv`](env/trait.SimulatedEnv.html) for faking process environment state
//! * [`SimulatedStdStreams`](std_streams/trait.SimulatedStdStreams.html) for faking standard
//!   stream input and inspecting output
//! * [`TempFs`](fs/trait.TempFs.html) for performing filesystem access in a `chroot`-like sandbox
//!   isolated from the rest of the filesystem
//!
//! Each provider trait can be used independently, however there is also the all-encompassing
//! [`Io`](trait.Io.html) which provides access to all of them. If you have a variety of I/O
//! dependencies, it might be easiest to create and pass around a single `&mut Io`.
//!
//! # Examples
//!
//! ```
//! extern crate io_providers;
//!
//! use std::io::Write;
//! use std::path::Path;
//! use io_providers::{Env, Io, NativeIo, SimulatedIo, StdStreams};
//!
//! /// Gets the current working directory and prints it to stdout.
//! fn do_work<I: Io>(io: &mut I) {
//!     let cur_dir = io.env().current_dir().unwrap();
//!     let stdout = io.std_streams().output();
//!     writeln!(stdout, "The current directory is: {}", cur_dir.to_str().unwrap()).unwrap();
//! }
//!
//! fn main() {
//!     // Test `do_work()` using a simulated I/O environment
//!     let mut simulated_io = SimulatedIo::new().unwrap();
//!     simulated_io.env().set_current_dir(Path::new("/foo/bar")).unwrap();
//!     do_work(&mut simulated_io);
//!     assert_eq!(
//!         "The current directory is: /foo/bar\n",
//!         ::std::str::from_utf8(simulated_io.std_streams().read_output()).unwrap());
//!
//!     // Now use a native I/O provided to access the real system
//!     let mut real_io = NativeIo::new();
//!     do_work(&mut real_io);
//! }
//! ```

extern crate tempfile;

use std::io;

pub mod env;
pub mod fs;
pub mod std_streams;

pub use env::{Env, NativeEnv, SimulatedEnv};
pub use fs::{Fs, NativeFs, OpenOptions, TempFs};
pub use std_streams::{NativeStdStreams, SimulatedStdStreams, StdStreams};

/// Provides access to the process environment, filesystem, and standard streams.
///
/// See [`env::Env`](env/trait.Env.html),
/// [`std_streams::StdStreams`](std_streams/trait.StdStreams.html) and
/// [`fs::Fs`](fs/trait.Fs.html) for details.
pub trait Io {
    // The type of the environment provider.
    type E: env::Env;

    // The type of the filesystem provider.
    type F: fs::Fs;

    // The type of the stream provider.
    type S: std_streams::StdStreams;

    /// Gets the [`env::Env`](env/trait.Env.html) provider.
    fn env(&mut self) -> &mut Self::E;

    /// Gets the [`fs::Fs`](fs/trait.Fs.html) provider.
    fn fs(&mut self) -> &mut Self::F;

    /// Gets the [`std_streams::StdStreams`](std_streams/trait.StdStreams.html).
    fn std_streams(&mut self) -> &mut Self::S;
}

/// `Io` implementation using the native system.
///
/// See `env::NativeEnv` and `std_streams::NativeStdStreams` for more information.
#[derive(Default)]
pub struct NativeIo {
    env: env::NativeEnv,
    fs: fs::NativeFs,
    stream: std_streams::NativeStdStreams,
}

impl NativeIo {
    /// Creates a new `LocalIoProvider`.
    pub fn new() -> NativeIo {
        NativeIo {
            env: env::NativeEnv,
            fs: fs::NativeFs,
            stream: std_streams::NativeStdStreams::new(),
        }
    }
}

impl Io for NativeIo {
    type E = env::NativeEnv;
    type F = fs::NativeFs;
    type S = std_streams::NativeStdStreams;

    fn env(&mut self) -> &mut env::NativeEnv {
        &mut self.env
    }

    fn fs(&mut self) -> &mut fs::NativeFs {
        &mut self.fs
    }

    fn std_streams(&mut self) -> &mut std_streams::NativeStdStreams {
        &mut self.stream
    }
}

/// `Io` implementation using a simulated environment.
///
/// See `env::SimulatedEnv` and `std_streams::SimulatedStdStreams` for more information.
pub struct SimulatedIo {
    env: env::SimulatedEnv,
    fs: fs::TempFs,
    stream: std_streams::SimulatedStdStreams,
}

impl SimulatedIo {
    /// Creates a new `SimulatedIo`.
    pub fn new() -> io::Result<SimulatedIo> {
        Ok(SimulatedIo {
            env: env::SimulatedEnv::new(),
            fs: fs::TempFs::new()?,
            stream: std_streams::SimulatedStdStreams::new(),
        })
    }
}

impl Io for SimulatedIo {
    type E = env::SimulatedEnv;
    type F = fs::TempFs;
    type S = std_streams::SimulatedStdStreams;

    fn env(&mut self) -> &mut env::SimulatedEnv {
        &mut self.env
    }

    fn fs(&mut self) -> &mut fs::TempFs {
        &mut self.fs
    }

    fn std_streams(&mut self) -> &mut std_streams::SimulatedStdStreams {
        &mut self.stream
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Doug Goldstein
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A simple logger to provide symantics similar to what is expected
//! of most UNIX utilities by logging to stderr and the higher the
//! verbosity the higher the log level. Additionally it supports the
//! ability to provide timestamps at different granularities.
//!
//! ## Simple Use Case
//!
//! ```rust
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! fn main() {
//!     stderrlog::new().module(module_path!()).init().unwrap();
//!
//!     info!("starting up");
//!
//!     // ...
//! }
//! ```
//!
//! ## docopt Example
//!
//! ```rust
//! extern crate docopt;
//! #[macro_use]
//! extern crate log;
//! extern crate rustc_serialize;
//! extern crate stderrlog;
//!
//! use docopt::Docopt;
//!
//! const USAGE: &'static str = "
//! Usage: program [-q] [-v...]
//! ";
//!
//! #[derive(Debug, RustcDecodable)]
//! struct Args {
//!     flag_q: bool,
//!     flag_v: usize,
//! }
//!
//! fn main() {
//!     let args: Args = Docopt::new(USAGE)
//!                             .and_then(|d| d.decode())
//!                             .unwrap_or_else(|e| e.exit());
//!
//!     stderrlog::new()
//!             .module(module_path!())
//!             .quiet(args.flag_q)
//!             .timestamp(stderrlog::Timestamp::Second)
//!             .verbosity(args.flag_v)
//!             .init()
//!             .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//!
//!     // ...
//! }
//! ```
//!
//! # clap Example
//!
//! ```
//! #[macro_use]
//! extern crate clap;
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! use clap::{Arg, App};
//!
//! fn main() {
//!     let m = App::new("stderrlog example")
//!         .version(crate_version!())
//!         .arg(Arg::with_name("verbosity")
//!              .short("v")
//!              .multiple(true)
//!              .help("Increase message verbosity"))
//!         .arg(Arg::with_name("quiet")
//!              .short("q")
//!              .help("Silence all output"))
//!         .arg(Arg::with_name("timestamp")
//!              .short("t")
//!              .help("prepend log lines with a timestamp")
//!              .takes_value(true)
//!              .possible_values(&["none", "sec", "ms", "ns"]))
//!         .get_matches();
//!
//!     let verbose = m.occurrences_of("verbosity") as usize;
//!     let quiet = m.is_present("quiet");
//!     let ts = match m.value_of("timestamp") {
//!         Some("ns") => stderrlog::Timestamp::Nanosecond,
//!         Some("ms") => stderrlog::Timestamp::Microsecond,
//!         Some("sec") => stderrlog::Timestamp::Second,
//!         Some("none") | None => stderrlog::Timestamp::Off,
//!         Some(_) => clap::Error {
//!             message: "invalid value for 'timestamp'".into(),
//!             kind: clap::ErrorKind::InvalidValue,
//!             info: None,
//!         }.exit(),
//!     };
//!
//!     stderrlog::new()
//!         .module(module_path!())
//!         .quiet(quiet)
//!         .verbosity(verbose)
//!         .timestamp(ts)
//!         .init()
//!         .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//! }
//! ```

extern crate chrono;
extern crate log;
extern crate termcolor;
extern crate thread_local;

use chrono::Local;
use log::{LogLevel, LogLevelFilter, LogMetadata};
use std::cell::RefCell;
use std::io::{self, Write};
use std::fmt;
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
use thread_local::CachedThreadLocal;

pub use termcolor::ColorChoice;

/// State of the timestampping in the logger.
#[derive(Clone, Copy, Debug)]
pub enum Timestamp {
    /// Disable timestamping of log messages
    Off,
    /// Timestamp with second granularity
    Second,
    /// Timestamp with microsecond granularity
    Microsecond,
    /// Timestamp with nanosecond granularity
    Nanosecond,
}

/// Data specific to this logger
pub struct StdErrLog {
    verbosity: LogLevelFilter,
    quiet: bool,
    timestamp: Timestamp,
    modules: Vec<String>,
    writer: CachedThreadLocal<RefCell<io::LineWriter<StandardStream>>>,
    color_choice: ColorChoice,
}

impl fmt::Debug for StdErrLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("StdErrLog")
            .field("verbosity", &self.verbosity)
            .field("quiet", &self.quiet)
            .field("timestamp", &self.timestamp)
            .field("modules", &self.modules)
            .field("writer", &"stderr")
            .field("color_choice", &self.color_choice)
            .finish()
    }
}

impl Clone for StdErrLog {
    fn clone(&self) -> StdErrLog {
        StdErrLog {
            modules: self.modules.clone(),
            writer: CachedThreadLocal::new(),
            .. *self
        }
    }
}

impl log::Log for StdErrLog {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.log_level_filter() && self.includes_module(metadata.target())
    }

    fn log(&self, record: &log::LogRecord) {

        // if logging isn't enabled for this level do a quick out
        if !self.enabled(record.metadata()) {
            return;
        }

        let writer =
            self.writer.get_or(|| Box::new(RefCell::new(io::LineWriter::new(StandardStream::stderr(self.color_choice)))));
        let mut writer = writer.borrow_mut();
        let color = match record.metadata().level() {
            LogLevel::Error => Color::Red,
            LogLevel::Warn => Color::Magenta,
            LogLevel::Info => Color::Yellow,
            LogLevel::Debug => Color::Cyan,
            LogLevel::Trace => Color::Blue,
        };
        {
            writer.get_mut().set_color(ColorSpec::new().set_fg(Some(color))).expect("failed to set color");
        }
        match self.timestamp {
            Timestamp::Second => {
                let fmt = "%Y-%m-%dT%H:%M:%S%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Microsecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.6f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Nanosecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.9f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Off => {},
        }
        let _ = writeln!(writer, "{} - {}", record.level(), record.args());
        {
            writer.get_mut().reset().expect("failed to reset the color");
        }
    }
}

impl StdErrLog {
    /// creates a new stderr logger
    pub fn new() -> StdErrLog {
        StdErrLog {
            verbosity: LogLevelFilter::Error,
            quiet: false,
            timestamp: Timestamp::Off,
            modules: Vec::new(),
            writer: CachedThreadLocal::new(),
            color_choice: ColorChoice::Auto,
        }
    }

    /// Sets the verbosity level of messages that will be displayed
    pub fn verbosity(&mut self, verbosity: usize) -> &mut StdErrLog {
        let log_lvl = match verbosity {
            0 => LogLevelFilter::Error,
            1 => LogLevelFilter::Warn,
            2 => LogLevelFilter::Info,
            3 => LogLevelFilter::Debug,
            _ => LogLevelFilter::Trace,
        };

        self.verbosity = log_lvl;
        self
    }

    /// silence all output, no matter the value of verbosity
    pub fn quiet(&mut self, quiet: bool) -> &mut StdErrLog {
        self.quiet = quiet;
        self
    }

    /// Enables or disables the use of timestamps in log messages
    pub fn timestamp(&mut self, timestamp: Timestamp) -> &mut StdErrLog {
        self.timestamp = timestamp;
        self
    }

    /// Enables or disables the use of color in log messages
    pub fn color(&mut self, choice: ColorChoice) -> &mut StdErrLog {
        self.color_choice = choice;
        self
    }

    /// specify a module to allow to log to stderr
    pub fn module<T: Into<String>>(&mut self, module: T) -> &mut StdErrLog {
        self._module(module.into())
    }

    fn _module(&mut self, module: String) -> &mut StdErrLog {
        // If Ok, the module was already found
        if let Err(i) = self.modules.binary_search(&module) {
            // If a super-module of the current module already exists, don't insert this module
            if i == 0 || !is_submodule(&self.modules[i - 1], &module) {
                // Remove any submodules of the module we're inserting
                let submodule_count = self.modules[i..]
                    .iter()
                    .take_while(|possible_submodule|
                        is_submodule(&module, possible_submodule)
                    )
                    .count();
                self.modules.drain(i..i+submodule_count);
                self.modules.insert(i, module);
            }
        }
        self
    }

    /// specifiy modules to allow to log to stderr
    pub fn modules<T: Into<String>, I: IntoIterator<Item = T>>(&mut self,
                                                               modules: I)
                                                               -> &mut StdErrLog {
        for module in modules {
            self.module(module);
        }
        self
    }

    fn log_level_filter(&self) -> LogLevelFilter {
        if self.quiet {
            LogLevelFilter::Off
        } else {
            self.verbosity
        }
    }

    fn includes_module(&self, module_path: &str) -> bool {
        // If modules is empty, include all module paths
        if self.modules.is_empty() {
            return true;
        }
        // if a prefix of module_path is in `self.modules`, it must
        // be located at the first location before
        // where module_path would be.
        match self.modules.binary_search_by(|module| module.as_str().cmp(&module_path)) {
            Ok(_) => {
                // Found exact module: return true
                true
            }
            Err(0) => {
                // if there's no item which would be located before module_path, no prefix is there
                false
            }
            Err(i) => {
                is_submodule(&self.modules[i - 1], module_path)
            }
        }
    }

    /// sets the the logger as active
    pub fn init(&self) -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
                            max_log_level.set(self.log_level_filter());

                            Box::new(self.clone())
                        })
    }
}

impl Default for StdErrLog {
    fn default() -> Self {
        StdErrLog::new()
    }
}

/// creates a new stderr logger
pub fn new() -> StdErrLog {
    StdErrLog::new()
}

fn is_submodule(parent: &str, possible_child: &str) -> bool {
    parent.split("::").zip(possible_child.split("::")).all(|(a, b)| a == b)
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_default_level() {
        extern crate log;

        super::new().module(module_path!()).init().unwrap();

        assert_eq!(log::LogLevel::Error, log::max_log_level())
    }
}
<commit_msg>Make the submodule check better<commit_after>// Copyright 2016 Doug Goldstein
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A simple logger to provide symantics similar to what is expected
//! of most UNIX utilities by logging to stderr and the higher the
//! verbosity the higher the log level. Additionally it supports the
//! ability to provide timestamps at different granularities.
//!
//! ## Simple Use Case
//!
//! ```rust
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! fn main() {
//!     stderrlog::new().module(module_path!()).init().unwrap();
//!
//!     info!("starting up");
//!
//!     // ...
//! }
//! ```
//!
//! ## docopt Example
//!
//! ```rust
//! extern crate docopt;
//! #[macro_use]
//! extern crate log;
//! extern crate rustc_serialize;
//! extern crate stderrlog;
//!
//! use docopt::Docopt;
//!
//! const USAGE: &'static str = "
//! Usage: program [-q] [-v...]
//! ";
//!
//! #[derive(Debug, RustcDecodable)]
//! struct Args {
//!     flag_q: bool,
//!     flag_v: usize,
//! }
//!
//! fn main() {
//!     let args: Args = Docopt::new(USAGE)
//!                             .and_then(|d| d.decode())
//!                             .unwrap_or_else(|e| e.exit());
//!
//!     stderrlog::new()
//!             .module(module_path!())
//!             .quiet(args.flag_q)
//!             .timestamp(stderrlog::Timestamp::Second)
//!             .verbosity(args.flag_v)
//!             .init()
//!             .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//!
//!     // ...
//! }
//! ```
//!
//! # clap Example
//!
//! ```
//! #[macro_use]
//! extern crate clap;
//! #[macro_use]
//! extern crate log;
//! extern crate stderrlog;
//!
//! use clap::{Arg, App};
//!
//! fn main() {
//!     let m = App::new("stderrlog example")
//!         .version(crate_version!())
//!         .arg(Arg::with_name("verbosity")
//!              .short("v")
//!              .multiple(true)
//!              .help("Increase message verbosity"))
//!         .arg(Arg::with_name("quiet")
//!              .short("q")
//!              .help("Silence all output"))
//!         .arg(Arg::with_name("timestamp")
//!              .short("t")
//!              .help("prepend log lines with a timestamp")
//!              .takes_value(true)
//!              .possible_values(&["none", "sec", "ms", "ns"]))
//!         .get_matches();
//!
//!     let verbose = m.occurrences_of("verbosity") as usize;
//!     let quiet = m.is_present("quiet");
//!     let ts = match m.value_of("timestamp") {
//!         Some("ns") => stderrlog::Timestamp::Nanosecond,
//!         Some("ms") => stderrlog::Timestamp::Microsecond,
//!         Some("sec") => stderrlog::Timestamp::Second,
//!         Some("none") | None => stderrlog::Timestamp::Off,
//!         Some(_) => clap::Error {
//!             message: "invalid value for 'timestamp'".into(),
//!             kind: clap::ErrorKind::InvalidValue,
//!             info: None,
//!         }.exit(),
//!     };
//!
//!     stderrlog::new()
//!         .module(module_path!())
//!         .quiet(quiet)
//!         .verbosity(verbose)
//!         .timestamp(ts)
//!         .init()
//!         .unwrap();
//!     trace!("trace message");
//!     debug!("debug message");
//!     info!("info message");
//!     warn!("warn message");
//!     error!("error message");
//! }
//! ```

extern crate chrono;
extern crate log;
extern crate termcolor;
extern crate thread_local;

use chrono::Local;
use log::{LogLevel, LogLevelFilter, LogMetadata};
use std::cell::RefCell;
use std::io::{self, Write};
use std::fmt;
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
use thread_local::CachedThreadLocal;

pub use termcolor::ColorChoice;

/// State of the timestampping in the logger.
#[derive(Clone, Copy, Debug)]
pub enum Timestamp {
    /// Disable timestamping of log messages
    Off,
    /// Timestamp with second granularity
    Second,
    /// Timestamp with microsecond granularity
    Microsecond,
    /// Timestamp with nanosecond granularity
    Nanosecond,
}

/// Data specific to this logger
pub struct StdErrLog {
    verbosity: LogLevelFilter,
    quiet: bool,
    timestamp: Timestamp,
    modules: Vec<String>,
    writer: CachedThreadLocal<RefCell<io::LineWriter<StandardStream>>>,
    color_choice: ColorChoice,
}

impl fmt::Debug for StdErrLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("StdErrLog")
            .field("verbosity", &self.verbosity)
            .field("quiet", &self.quiet)
            .field("timestamp", &self.timestamp)
            .field("modules", &self.modules)
            .field("writer", &"stderr")
            .field("color_choice", &self.color_choice)
            .finish()
    }
}

impl Clone for StdErrLog {
    fn clone(&self) -> StdErrLog {
        StdErrLog {
            modules: self.modules.clone(),
            writer: CachedThreadLocal::new(),
            .. *self
        }
    }
}

impl log::Log for StdErrLog {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.log_level_filter() && self.includes_module(metadata.target())
    }

    fn log(&self, record: &log::LogRecord) {

        // if logging isn't enabled for this level do a quick out
        if !self.enabled(record.metadata()) {
            return;
        }

        let writer =
            self.writer.get_or(|| Box::new(RefCell::new(io::LineWriter::new(StandardStream::stderr(self.color_choice)))));
        let mut writer = writer.borrow_mut();
        let color = match record.metadata().level() {
            LogLevel::Error => Color::Red,
            LogLevel::Warn => Color::Magenta,
            LogLevel::Info => Color::Yellow,
            LogLevel::Debug => Color::Cyan,
            LogLevel::Trace => Color::Blue,
        };
        {
            writer.get_mut().set_color(ColorSpec::new().set_fg(Some(color))).expect("failed to set color");
        }
        match self.timestamp {
            Timestamp::Second => {
                let fmt = "%Y-%m-%dT%H:%M:%S%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Microsecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.6f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Nanosecond => {
                let fmt = "%Y-%m-%dT%H:%M:%S%.9f%:z";
                let _ = write!(writer, "{} - ", Local::now().format(fmt));
            },
            Timestamp::Off => {},
        }
        let _ = writeln!(writer, "{} - {}", record.level(), record.args());
        {
            writer.get_mut().reset().expect("failed to reset the color");
        }
    }
}

impl StdErrLog {
    /// creates a new stderr logger
    pub fn new() -> StdErrLog {
        StdErrLog {
            verbosity: LogLevelFilter::Error,
            quiet: false,
            timestamp: Timestamp::Off,
            modules: Vec::new(),
            writer: CachedThreadLocal::new(),
            color_choice: ColorChoice::Auto,
        }
    }

    /// Sets the verbosity level of messages that will be displayed
    pub fn verbosity(&mut self, verbosity: usize) -> &mut StdErrLog {
        let log_lvl = match verbosity {
            0 => LogLevelFilter::Error,
            1 => LogLevelFilter::Warn,
            2 => LogLevelFilter::Info,
            3 => LogLevelFilter::Debug,
            _ => LogLevelFilter::Trace,
        };

        self.verbosity = log_lvl;
        self
    }

    /// silence all output, no matter the value of verbosity
    pub fn quiet(&mut self, quiet: bool) -> &mut StdErrLog {
        self.quiet = quiet;
        self
    }

    /// Enables or disables the use of timestamps in log messages
    pub fn timestamp(&mut self, timestamp: Timestamp) -> &mut StdErrLog {
        self.timestamp = timestamp;
        self
    }

    /// Enables or disables the use of color in log messages
    pub fn color(&mut self, choice: ColorChoice) -> &mut StdErrLog {
        self.color_choice = choice;
        self
    }

    /// specify a module to allow to log to stderr
    pub fn module<T: Into<String>>(&mut self, module: T) -> &mut StdErrLog {
        self._module(module.into())
    }

    fn _module(&mut self, module: String) -> &mut StdErrLog {
        // If Ok, the module was already found
        if let Err(i) = self.modules.binary_search(&module) {
            // If a super-module of the current module already exists, don't insert this module
            if i == 0 || !is_submodule(&self.modules[i - 1], &module) {
                // Remove any submodules of the module we're inserting
                let submodule_count = self.modules[i..]
                    .iter()
                    .take_while(|possible_submodule|
                        is_submodule(&module, possible_submodule)
                    )
                    .count();
                self.modules.drain(i..i+submodule_count);
                self.modules.insert(i, module);
            }
        }
        self
    }

    /// specifiy modules to allow to log to stderr
    pub fn modules<T: Into<String>, I: IntoIterator<Item = T>>(&mut self,
                                                               modules: I)
                                                               -> &mut StdErrLog {
        for module in modules {
            self.module(module);
        }
        self
    }

    fn log_level_filter(&self) -> LogLevelFilter {
        if self.quiet {
            LogLevelFilter::Off
        } else {
            self.verbosity
        }
    }

    fn includes_module(&self, module_path: &str) -> bool {
        // If modules is empty, include all module paths
        if self.modules.is_empty() {
            return true;
        }
        // if a prefix of module_path is in `self.modules`, it must
        // be located at the first location before
        // where module_path would be.
        match self.modules.binary_search_by(|module| module.as_str().cmp(&module_path)) {
            Ok(_) => {
                // Found exact module: return true
                true
            }
            Err(0) => {
                // if there's no item which would be located before module_path, no prefix is there
                false
            }
            Err(i) => {
                is_submodule(&self.modules[i - 1], module_path)
            }
        }
    }

    /// sets the the logger as active
    pub fn init(&self) -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
                            max_log_level.set(self.log_level_filter());

                            Box::new(self.clone())
                        })
    }
}

impl Default for StdErrLog {
    fn default() -> Self {
        StdErrLog::new()
    }
}

/// creates a new stderr logger
pub fn new() -> StdErrLog {
    StdErrLog::new()
}

fn is_submodule(parent: &str, possible_child: &str) -> bool {
    // Treat as bytes, because we'll be doing slicing, and we only care about ':' chars
    let parent = parent.as_bytes();
    let possible_child = possible_child.as_bytes();

    // a longer module path cannot be a parent of a shorter module path
    if parent.len() > possible_child.len() {
        return false;
    }

    // If the path up to the parent isn't the same as the child,
    if parent != &possible_child[..parent.len()] {
        return false;
    }

    // Either the path is exactly the same, or the sub module should have a "::" after
    // the length of the parent path. This prevents things like 'a::bad' being considered
    // a submodule of 'a::b'
    parent.len() == possible_child.len() ||
        possible_child.get(parent.len()..parent.len() + 2) == Some(b"::")
}

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

    #[test]
    fn submodule() {
        assert!(is_submodule("a", "a::b::c::d"));
        assert!(is_submodule("a::b::c", "a::b::c::d"));
        assert!(is_submodule("a::b::c", "a::b::c"));
        assert!(!is_submodule("a::b::c", "a::bad::c"));
        assert!(!is_submodule("a::b::c", "a::b::cab"));
        assert!(!is_submodule("a::b::c", "a::b::cab::d"));
        assert!(!is_submodule("a::b::c", "a::b"));
        assert!(!is_submodule("a::b::c", "a::bad"));
    }

    #[test]
    fn test_default_level() {
        extern crate log;

        super::new().module(module_path!()).init().unwrap();

        assert_eq!(log::LogLevel::Error, log::max_log_level())
    }
}
<|endoftext|>