<commit_before>mod target {
    use std::env;
    use std::fs::File;
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

        let mut dst =
            File::create(Path::new(&out_dir).join("host-target.txt")).unwrap();
        dst.write_all(env::var("TARGET").unwrap().as_bytes())
            .unwrap();
    }
}

mod testgen {
    use std::char;
    use std::env;
    use std::ffi::OsStr;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
        let mut dst =
            File::create(Path::new(&out_dir).join("tests.rs")).unwrap();

        let manifest_dir =
            PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let headers_dir = manifest_dir.join("tests").join("headers");

        let headers = match fs::read_dir(headers_dir) {
            Ok(dir) => dir,
            // We may not have headers directory after packaging.
            Err(..) => return,
        };

        let entries =
            headers.map(|result| result.expect("Couldn't read header file"));

        println!("cargo:rerun-if-changed=tests/headers");

        for entry in entries {
            match entry.path().extension().and_then(OsStr::to_str) {
                Some("h") | Some("hpp") => {
                    let func = entry
                        .file_name()
                        .to_str()
                        .unwrap()
                        .replace(|c| !char::is_alphanumeric(c), "_")
                        .replace("__", "_")
                        .to_lowercase();
                    writeln!(
                        dst,
                        "test_header!(header_{}, {:?});",
                        func,
                        entry.path(),
                    )
                    .unwrap();
                }
                _ => {}
            }
        }

        dst.flush().unwrap();
    }
}

fn main() {
    target::main();
    testgen::main();

    // On behalf of clang_sys, rebuild ourselves if important configuration
    // variables change, to ensure that bindings get rebuilt if the
    // underlying libclang changes.
    println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH");
    println!("cargo:rerun-if-env-changed=LIBCLANG_PATH");
    println!("cargo:rerun-if-env-changed=LIBCLANG_STATIC_PATH");
}
<commit_msg>Respect changes to BINDGEN_EXTRA_CLANG_ARGS<commit_after>mod target {
    use std::env;
    use std::fs::File;
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

        let mut dst =
            File::create(Path::new(&out_dir).join("host-target.txt")).unwrap();
        dst.write_all(env::var("TARGET").unwrap().as_bytes())
            .unwrap();
    }
}

mod testgen {
    use std::char;
    use std::env;
    use std::ffi::OsStr;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    pub fn main() {
        let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
        let mut dst =
            File::create(Path::new(&out_dir).join("tests.rs")).unwrap();

        let manifest_dir =
            PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let headers_dir = manifest_dir.join("tests").join("headers");

        let headers = match fs::read_dir(headers_dir) {
            Ok(dir) => dir,
            // We may not have headers directory after packaging.
            Err(..) => return,
        };

        let entries =
            headers.map(|result| result.expect("Couldn't read header file"));

        println!("cargo:rerun-if-changed=tests/headers");

        for entry in entries {
            match entry.path().extension().and_then(OsStr::to_str) {
                Some("h") | Some("hpp") => {
                    let func = entry
                        .file_name()
                        .to_str()
                        .unwrap()
                        .replace(|c| !char::is_alphanumeric(c), "_")
                        .replace("__", "_")
                        .to_lowercase();
                    writeln!(
                        dst,
                        "test_header!(header_{}, {:?});",
                        func,
                        entry.path(),
                    )
                    .unwrap();
                }
                _ => {}
            }
        }

        dst.flush().unwrap();
    }
}

fn main() {
    target::main();
    testgen::main();

    // On behalf of clang_sys, rebuild ourselves if important configuration
    // variables change, to ensure that bindings get rebuilt if the
    // underlying libclang changes.
    println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH");
    println!("cargo:rerun-if-env-changed=LIBCLANG_PATH");
    println!("cargo:rerun-if-env-changed=LIBCLANG_STATIC_PATH");
    println!("cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS");
}
<|endoftext|>


<commit_before>extern crate "pkg-config" as pkg_config;
use std::os;
use std::old_io::{self, fs, Command};
use std::old_io::process::InheritFd;

fn main() {
    match pkg_config::find_library("sass") {
        Ok(_) => return,
        Err(_) => {}
    }
    let src = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let mut cmd = Command::new("make");
    cmd.cwd(&src.join("libsass"));
    run(& mut cmd);

    println!("cargo:rustc-flags=-l sass -L libsass/lib/ -l c++");

}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(InheritFd(1))
               .stderr(InheritFd(2))
               .status()
               .unwrap()
               .success());

}
<commit_msg>Build<commit_after>extern crate "pkg-config" as pkg_config;
use std::os;
use std::old_io::{self, fs, Command};
use std::old_io::process::InheritFd;

fn main() {
    match pkg_config::find_library("sass") {
        Ok(_) => return,
        Err(_) => {}
    }
    let src = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let mut cmd = Command::new("make");
    cmd.cwd(&src.join("libsass"));
    run(& mut cmd);

    println!("cargo:rustc-flags=-l sass -L libsass/lib/ -l dylib=stdc++");

}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(InheritFd(1))
               .stderr(InheritFd(2))
               .status()
               .unwrap()
               .success());

}
<|endoftext|>


<commit_before>use std;
use std::f32::consts::PI;

use arrayvec::ArrayVec;

use consts::MAX_HARMONICS;
use descramble::VoiceDecisions;
use errors::Errors;
use params::BaseParams;
use spectral::Spectrals;

pub struct FrameEnergy {
    pub spectral: f32,
    pub cos: f32,
    pub tracking: f32,
}

impl FrameEnergy {
    pub fn new(spectrals: &Spectrals, prev: &FrameEnergy, params: &BaseParams)
        -> FrameEnergy
    {
        let m0 = spectrals.iter()
            .map(|&m| m.powi(2))
            .fold(0.0, |s, x| s + x);

        let m1 = spectrals.iter().enumerate().map(|(l, &m)| {
            let l = l + 1;
            m.powi(2) * (params.fundamental * l as f32).cos()
        }).fold(0.0, |s, x| s + x);

        FrameEnergy {
            spectral: m0,
            cos: m1,
            tracking: (0.95 * prev.tracking + 0.05 * m0).max(10000.0),
        }
    }
}

impl Default for FrameEnergy {
    fn default() -> FrameEnergy {
        FrameEnergy {
            spectral: 0.0,
            cos: 0.0,
            tracking: 75000.0,
        }
    }
}

#[derive(Clone)]
pub struct EnhancedSpectrals(ArrayVec<[f32; MAX_HARMONICS]>);

impl EnhancedSpectrals {
    pub fn new(spectrals: &Spectrals, energy: &FrameEnergy, params: &BaseParams)
        -> EnhancedSpectrals
    {
        let spectral_sqr = energy.spectral.powi(2);
        let cos_sqr = energy.cos.powi(2);

        let mut enhanced = spectrals.iter().enumerate().map(|(l, &m)| {
            let l = l + 1;

            let weight = m.sqrt() * (
                0.96 * PI * (
                    spectral_sqr + cos_sqr - 2.0 * energy.spectral * energy.cos *
                        (params.fundamental * l as f32).cos()
                ) / (params.fundamental * energy.spectral * (spectral_sqr - cos_sqr))
            ).powf(0.25);

            if 8 * l as u32 <= params.harmonics {
                m
            } else {
                m * weight.max(0.5).min(1.2)
            }
        }).collect::<ArrayVec<[f32; MAX_HARMONICS]>>();

        let scale = (
            energy.spectral / enhanced.iter().fold(0.0, |s, &m| s + m.powi(2))
        ).sqrt();

        for m in enhanced.iter_mut() {
            *m *= scale;
        }

        EnhancedSpectrals(enhanced)
    }

    pub fn get(&self, l: usize) -> f32 {
        assert!(l >= 1);

        match self.0.get(l - 1) {
            Some(&s) => s,
            None => 0.0,
        }
    }
}

impl std::ops::Deref for EnhancedSpectrals {
    type Target = ArrayVec<[f32; MAX_HARMONICS]>;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl std::ops::DerefMut for EnhancedSpectrals {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

impl Default for EnhancedSpectrals {
    fn default() -> EnhancedSpectrals {
        EnhancedSpectrals(ArrayVec::new())
    }
}

pub fn amp_thresh(errors: &Errors, prev: f32) -> f32 {
    if errors.rate <= 0.005 && errors.total <= 6 {
        20480.0
    } else {
        6000.0 - 300.0 * errors.total as f32 + prev
    }
}

pub fn smooth(enhanced: &mut EnhancedSpectrals, voiced: &mut VoiceDecisions,
              errors: &Errors, energy: &FrameEnergy, amp_thresh: f32)
{
    let thresh = if errors.rate <= 0.005 && errors.total <= 4 {
        std::f32::MAX
    } else if errors.rate <= 0.0125 && errors.hamming_init == 0 {
        45.255 * energy.tracking.powf(0.375) / (277.26 * errors.rate).exp()
    } else {
        1.414 * energy.tracking.powf(0.375)
    };

    for (l, &m) in enhanced.iter().enumerate() {
        if m > thresh {
            voiced.force_voiced(l + 1);
        }
    }

    let amp = enhanced.iter().fold(0.0, |s, &m| s + m);
    let scale = (amp_thresh / amp).min(1.0);

    for m in enhanced.iter_mut() {
        *m *= scale;
    }
}

pub fn should_repeat(errors: &Errors) -> bool {
    errors.golay_init >= 2 && errors.total as f32 >= 10.0 + 40.0 * errors.rate
}

pub fn should_mute(errors: &Errors) -> bool {
    errors.rate > 0.0875
}

#[cfg(test)]
mod tests {
    use super::*;
    use descramble::{Bootstrap, descramble};
    use gain::Gains;
    use spectral::Spectrals;
    use coefs::Coefficients;
    use params::BaseParams;
    use prev::{PrevFrame};

    #[test]
    fn test_spectrals() {
        let chunks = [
            0b001000010010,
            0b110011001100,
            0b111000111000,
            0b111111111111,
            0b10100110101,
            0b00101111010,
            0b01110111011,
            0b00001000,
        ];

        let b = Bootstrap::new(&chunks);
        let p = BaseParams::new(b.unwrap_period());
        let (amps, _, gain_idx) = descramble(&chunks, &p);
        let g = Gains::new(gain_idx, &amps, &p);
        let c = Coefficients::new(&g, &amps, &p);
        let prev = PrevFrame::default();
        let s = Spectrals::new(&c, &p, &prev);
        let pfe = FrameEnergy::default();
        let fe = FrameEnergy::new(&s, &pfe, &p);
        let e = EnhancedSpectrals::new(&s, &fe, &p);

        assert_eq!(e.0.len(), 16);

        assert!((e.get(17) - 0.0).abs() < 0.00001);
        assert!((e.get(30) - 0.0).abs() < 0.00001);
        assert!((e.get(56) - 0.0).abs() < 0.00001);
        assert!((e.get(56) - 0.0).abs() < 0.00001);
    }
}
<commit_msg>Revisit enhance module<commit_after>use std;
use std::f32::consts::PI;

use arrayvec::ArrayVec;
use map_in_place::MapInPlace;

use consts::MAX_HARMONICS;
use descramble::VoiceDecisions;
use errors::Errors;
use params::BaseParams;
use spectral::Spectrals;

/// Energy-related parameters for a voice frame.
pub struct FrameEnergy {
    /// Spectral amplitude energy, RM0.
    pub energy: f32,
    /// Scaled energy value, RM1.
    pub scaled: f32,
    /// Moving average energy tracker, SE.
    pub tracking: f32,
}

impl FrameEnergy {
    /// Create a new `FrameEnergy` from the given spectral amplitudes Ml,
    /// previous frame energy values, and current frame parameters.
    pub fn new(spectrals: &Spectrals, prev: &FrameEnergy, params: &BaseParams)
        -> FrameEnergy
    {
        // Compute energy of spectral amplitudes according to Eq 105.
        let energy = spectrals.iter()
            .map(|&m| m.powi(2))
            .fold(0.0, |s, x| s + x);

        // Compute scaled energies according to Eq 106.
        let scaled = spectrals.iter().enumerate()
            .map(|(l, &m)| m.powi(2) * (params.fundamental * (l + 1) as f32).cos())
            .fold(0.0, |s, x| s + x);

        FrameEnergy {
            energy: energy,
            scaled: scaled,
            // Compute energy tracking EWMA according to Eq 111.
            tracking: (0.95 * prev.tracking + 0.05 * energy).max(10000.0),
        }
    }
}

impl Default for FrameEnergy {
    /// Create a new `FrameEnergy` with default initial values.
    fn default() -> FrameEnergy {
        // The first two values are arbitrary as they aren't saved across frames, and the
        // third value is taken from [p64].
        FrameEnergy {
            energy: 0.0,
            scaled: 0.0,
            tracking: 75000.0,
        }
    }
}

/// Enhanced spectral amplitudes, "overbar" Ml, are derived from the decoded
/// spectral amplitudes, "tilde" Ml.
#[derive(Clone)]
pub struct EnhancedSpectrals(ArrayVec<[f32; MAX_HARMONICS]>);

impl EnhancedSpectrals {
    /// Create a new `EnhancedSpectrals` from the given base spectral amplitudes
    /// Ml and current frame energy values and parameters.
    pub fn new(spectrals: &Spectrals, fen: &FrameEnergy, params: &BaseParams)
        -> EnhancedSpectrals
    {
        // Compute R_M0^2.
        let energy_sqr = fen.energy.powi(2);
        // Compute R_M1^2.
        let scaled_sqr = fen.scaled.powi(2);
        // Compute denominator term of Eq 107.
        let denom = params.fundamental * fen.energy * (energy_sqr - scaled_sqr);

        let mut enhanced = spectrals.iter().enumerate().map(|(l, &m)| {
            let l = l + 1;

            // Compute Eq 107.
            let weight = m.sqrt() * (
                0.96 * PI * (
                    energy_sqr + scaled_sqr - 2.0 * fen.energy * fen.scaled *
                        (params.fundamental * l as f32).cos()
                ) / denom
            ).powf(0.25);

            // Scale current spectral amplitude according to Eq 108.
            if 8 * l as u32 <= params.harmonics {
                m
            } else {
                m * weight.max(0.5).min(1.2)
            }
        }).collect::<ArrayVec<[f32; MAX_HARMONICS]>>();

        // Compute root ratio of energies according to Eq 109.
        let scale = (
            fen.energy / enhanced.iter().fold(0.0, |s, &m| s + m.powi(2))
        ).sqrt();

        // Perform second scaling pass according to Eq 110.
        enhanced.map_in_place(|&m| m * scale);

        EnhancedSpectrals(enhanced)
    }

    /// Retrieve the enhanced spectral amplitude Ml, 1 ≤ l ≤ L.
    pub fn get(&self, l: usize) -> f32 {
        assert!(l >= 1);

        match self.0.get(l - 1) {
            Some(&s) => s,
            // Out-of-bounds amplitudes are zero [p60].
            None => 0.0,
        }
    }
}

impl std::ops::Deref for EnhancedSpectrals {
    type Target = ArrayVec<[f32; MAX_HARMONICS]>;
    fn deref(&self) -> &Self::Target { &self.0 }
}

impl std::ops::DerefMut for EnhancedSpectrals {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

impl Default for EnhancedSpectrals {
    /// Create a new `EnhancedSpectrals` with default initial values.
    fn default() -> EnhancedSpectrals {
        // By default, all enhanced amplitudes are 0 [p64].
        EnhancedSpectrals(ArrayVec::new())
    }
}

/// Compute the spectral amplitude threshold τM used in adaptive smoothing from
/// the given error characteristics and previous amplitude threshold.
pub fn amp_thresh(errors: &Errors, prev: f32) -> f32 {
    // Compute Eq 115.
    if errors.rate <= 0.005 && errors.total <= 6 {
        20480.0
    } else {
        6000.0 - 300.0 * errors.total as f32 + prev
    }
}

/// Smooth the given enhanced spectral amplitudes Ml and voiced/unvoiced
/// decisions vl based on the given error characteristics, current frame
/// energy, and spectral amplitude threshold τM for the current frame.
pub fn smooth(enhanced: &mut EnhancedSpectrals, voiced: &mut VoiceDecisions,
              errors: &Errors, fen: &FrameEnergy, amp_thresh: f32)
{
    // Compute Eq 112.
    let thresh = if errors.rate <= 0.005 && errors.total <= 4 {
        std::f32::MAX
    } else if errors.rate <= 0.0125 && errors.hamming_init == 0 {
        45.255 * fen.tracking.powf(0.375) / (277.26 * errors.rate).exp()
    } else {
        1.414 * fen.tracking.powf(0.375)
    };

    // Update voiced/unvoiced decisions according to Eq 113.
    for (l, &m) in enhanced.iter().enumerate() {
        if m > thresh {
            voiced.force_voiced(l + 1);
        }
    }

    // Compute amplitude sum in Eq 114.
    let amp = enhanced.iter().fold(0.0, |s, &m| s + m);
    // Compute scale factor in Eq 116.
    let scale = (amp_thresh / amp).min(1.0);

    // Scale each enhanced M_l [p50].
    enhanced.map_in_place(|&m| m * scale);
}

/// Check whether the current frame should be discarded and the previous repeated based on
/// the given error characteristics of the current frame.
pub fn should_repeat(errors: &Errors) -> bool {
    // Check the conditions in Eqs 97 and 98.
    errors.golay_init >= 2 && errors.total as f32 >= 10.0 + 40.0 * errors.rate
}

/// Check if the current frame should be discarded and replaced with silence/comfort noise
/// based on the given error characteristics of the current frame.
pub fn should_mute(errors: &Errors) -> bool {
    // Check the condition on [p47].
    errors.rate > 0.0875
}

#[cfg(test)]
mod tests {
    use super::*;
    use descramble::{Bootstrap, descramble};
    use gain::Gains;
    use spectral::Spectrals;
    use coefs::Coefficients;
    use params::BaseParams;
    use prev::{PrevFrame};

    #[test]
    fn test_spectrals() {
        let chunks = [
            0b001000010010,
            0b110011001100,
            0b111000111000,
            0b111111111111,
            0b10100110101,
            0b00101111010,
            0b01110111011,
            0b00001000,
        ];

        let b = Bootstrap::new(&chunks);
        let p = BaseParams::new(b.unwrap_period());
        let (amps, _, gain_idx) = descramble(&chunks, &p);
        let g = Gains::new(gain_idx, &amps, &p);
        let c = Coefficients::new(&g, &amps, &p);
        let prev = PrevFrame::default();
        let s = Spectrals::new(&c, &p, &prev);
        let pfe = FrameEnergy::default();
        let fe = FrameEnergy::new(&s, &pfe, &p);
        let e = EnhancedSpectrals::new(&s, &fe, &p);

        assert_eq!(e.0.len(), 16);

        assert!((e.get(17) - 0.0).abs() < 0.00001);
        assert!((e.get(30) - 0.0).abs() < 0.00001);
        assert!((e.get(56) - 0.0).abs() < 0.00001);
        assert!((e.get(56) - 0.0).abs() < 0.00001);
    }
}
<|endoftext|>


<commit_before>//! Starts a timely dataflow execution from configuration information and per-worker logic.

use timely_communication::{initialize, Configuration, Allocator};
use dataflow::scopes::Root;

/// Executes a timely dataflow from a configuration and per-communicator logic.
///
/// The `execute` method takes a `Configuration` and spins up some number of
/// workers threads, each of which execute the supplied closure to construct and run a timely
/// dataflow computation.
///
/// #Examples
/// ```ignore
/// extern crate timely;
///
/// use timely::*;
/// use timely::dataflow::inspect::{Input, Inspect};
///
/// // execute a timely dataflow using three worker threads.
/// timely::execute(Configuration::Process(3), |root| {
///
///     // add an input and base computation off of it
///     let mut input = root.scoped(|scope| {
///         let (input, stream) = scope.new_input();
///         stream.inspect(|x| println!("hello {:?}", x));
///         input
///     });
///
///     // introduce input, advance computation
///     for round in 0..10 {
///         input.send(round);
///         input.advance_to(round + 1);
///         root.step();
///     }
///
///     input.close();          // close the input
///     while root.step() { }   // finish off the computation
/// });
/// ```
pub fn execute<F: Fn(&mut Root<Allocator>)+Send+Sync+'static>(config: Configuration, func: F) {
    initialize(config, move |allocator| {
        func(&mut Root::new(allocator));
    })
}

/// Executes a timely dataflow from supplied arguments and per-communicator logic.
///
/// The `execute` method takes arguments (typically `std::env::args()`) and spins up some number of
/// workers threads, each of which execute the supplied closure to construct and run a timely
/// dataflow computation.
///
/// The arguments `execute` currently understands are:
///
/// `-w, --workers`: number of per-process worker threads.
///
/// `-n, --processes`: number of processes involved in the computation.
///
/// `-p, --process`: identity of this process; from 0 to n-1.
///
/// `-h, --hostfile`: a text file whose lines are "hostname:port" in order of process identity.
/// If not specified, `localhost` will be used, with port numbers increasing from 2101 (chosen
/// arbitrarily).
///
/// #Examples
/// ```ignore
/// extern crate timely;
///
/// use timely::*;
/// use timely::dataflow::inspect::{Input, Inspect};
///
/// // execute a timely dataflow using command line parameters
/// timely::execute_from_args(std::env::args(), |root| {
///
///     // add an input and base computation off of it
///     let mut input = root.scoped(|scope| {
///         let (input, stream) = scope.new_input();
///         stream.inspect(|x| println!("hello {:?}", x));
///         input
///     });
///
///     // introduce input, advance computation
///     for round in 0..10 {
///         input.send(round);
///         input.advance_to(round + 1);
///         root.step();
///     }
///
///     input.close();          // close the input
///     while root.step() { }   // finish off the computation
/// });
/// ```
/// ```ignore
/// host0% cargo run -- -w 2 -n 4 -h hosts.txt -p 0
/// host1% cargo run -- -w 2 -n 4 -h hosts.txt -p 1
/// host2% cargo run -- -w 2 -n 4 -h hosts.txt -p 2
/// host3% cargo run -- -w 2 -n 4 -h hosts.txt -p 3
/// ```
/// ```ignore
/// % cat hosts.txt
/// host0:port
/// host1:port
/// host2:port
/// host3:port
/// ```
pub fn execute_from_args<I: Iterator<Item=String>, F: Fn(&mut Root<Allocator>)+Send+Sync+'static>(iter: I, func: F) {
    if let Some(config) = Configuration::from_args(iter) {
        initialize(config, move |allocator| {
            func(&mut Root::new(allocator));
        })
    }
    else {
        println!("failed to initialize communication fabric");
    }
}
<commit_msg>execute blocks until computation complete<commit_after>//! Starts a timely dataflow execution from configuration information and per-worker logic.

use timely_communication::{initialize, Configuration, Allocator};
use dataflow::scopes::Root;

/// Executes a timely dataflow from a configuration and per-communicator logic.
///
/// The `execute` method takes a `Configuration` and spins up some number of
/// workers threads, each of which execute the supplied closure to construct and run a timely
/// dataflow computation.
///
/// #Examples
/// ```ignore
/// use timely::dataflow::*;
/// use timely::dataflow::operators::{Input, Inspect};
///
/// // execute a timely dataflow using three worker threads.
/// timely::execute(Configuration::Process(3), |root| {
///
///     // add an input and base computation off of it
///     let mut input = root.scoped(|scope| {
///         let (input, stream) = scope.new_input();
///         stream.inspect(|x| println!("hello {:?}", x));
///         input
///     });
///
///     // introduce input, advance computation
///     for round in 0..10 {
///         input.send(round);
///         input.advance_to(round + 1);
///         root.step();
///     }
/// });
/// ```
pub fn execute<F: Fn(&mut Root<Allocator>)+Send+Sync+'static>(config: Configuration, func: F) {
    initialize(config, move |allocator| {
        let mut root = Root::new(allocator);
        func(&mut root);
        while root.step() { }
    })
}

/// Executes a timely dataflow from supplied arguments and per-communicator logic.
///
/// The `execute` method takes arguments (typically `std::env::args()`) and spins up some number of
/// workers threads, each of which execute the supplied closure to construct and run a timely
/// dataflow computation.
///
/// The arguments `execute` currently understands are:
///
/// `-w, --workers`: number of per-process worker threads.
///
/// `-n, --processes`: number of processes involved in the computation.
///
/// `-p, --process`: identity of this process; from 0 to n-1.
///
/// `-h, --hostfile`: a text file whose lines are "hostname:port" in order of process identity.
/// If not specified, `localhost` will be used, with port numbers increasing from 2101 (chosen
/// arbitrarily).
///
/// #Examples
/// ```
/// use timely::dataflow::*;
/// use timely::dataflow::operators::{Input, Inspect};
///
/// // execute a timely dataflow using command line parameters
/// timely::execute_from_args(std::env::args(), |root| {
///
///     // add an input and base computation off of it
///     let mut input = root.scoped(|scope| {
///         let (input, stream) = scope.new_input();
///         stream.inspect(|x| println!("hello {:?}", x));
///         input
///     });
///
///     // introduce input, advance computation
///     for round in 0..10 {
///         input.send(round);
///         input.advance_to(round + 1);
///         root.step();
///     }
/// });
/// ```
/// ```ignore
/// host0% cargo run -- -w 2 -n 4 -h hosts.txt -p 0
/// host1% cargo run -- -w 2 -n 4 -h hosts.txt -p 1
/// host2% cargo run -- -w 2 -n 4 -h hosts.txt -p 2
/// host3% cargo run -- -w 2 -n 4 -h hosts.txt -p 3
/// ```
/// ```ignore
/// % cat hosts.txt
/// host0:port
/// host1:port
/// host2:port
/// host3:port
/// ```
pub fn execute_from_args<I: Iterator<Item=String>, F: Fn(&mut Root<Allocator>)+Send+Sync+'static>(iter: I, func: F) {
    if let Some(config) = Configuration::from_args(iter) {
        execute(config, func);
        // initialize(config, move |allocator| {
        //     func(&mut Root::new(allocator));
        // })
    }
    else {
        println!("failed to initialize communication fabric");
    }
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::os;
use std::fs;
use std::env;
use slack_hook::{Slack, PayloadBuilder};
use time;
use std::path::PathBuf;

macro_rules! done {
    ($v:expr) => {{
        println!(" ...OK");
        $v
    }}
}

macro_rules! pretend {
    ($v:expr) => {
        print!(" ([Pretend] {})", $v);
    }
}

pub struct Facteur {
    basedir: String,
    git: Option<String>,
    ts: String,
    pretend: bool
}

impl Facteur {
    pub fn new(dir: &str, pretend: bool) -> Self {
        Facteur {
            basedir: dir.to_string(),
            git: None,
            ts: Self::ts(),
            pretend: pretend
        }
    }

    pub fn git(mut self, git: &str) -> Self {
        self.git = Some(git.to_string());
        self
    }

    pub fn welcome(self, text: &str) -> Self {
        println!("{}", text);
        self
    }

    pub fn canonicalize_basedir(mut self) -> Self {
        self.basedir = Self::canonicalize(&self.basedir);
        self
    }

    pub fn mkdir_base(self) -> Self {
        print!("Making basedir {}", &self.basedir);
        match self.pretend {
            false => {
                Self::mkdir_or_die(&self.basedir);
                Self::mkdir_or_die(&format!("{}/releases", self.basedir));
                Self::mkdir_or_die(&format!("{}/shared", self.basedir));
            },
            true => {
                pretend!(&format!("mkdir {}", self.basedir));
                pretend!(&format!("mkdir {}/releases", self.basedir));
                pretend!(&format!("mkdir {}/shared", self.basedir));
            }
        }
        done!(self)
    }

    pub fn mkdir_release(self) -> Self {
        print!("Initializing Release dir");
        match self.pretend {
            false => Self::mkdir_or_die(&self.release_dir()),
            true => pretend!(format!("mkdir {}", &self.release_dir()))
        }
        done!(self)
    }

    fn release_dir(&self) -> String {
        format!("{}/releases/{}", self.basedir, &self.ts)
    }

    pub fn clone(self) -> Self {
        print!("Checkout");
        {
            let git = self.git.as_ref().unwrap();

            match self.pretend {
                false => {
                    let output = Command::new("git")
                        .arg("clone")
                        .arg(git)
                        .arg(&self.release_dir())
                        .output()
                        .expect("Failed to clone repo");

                    println!("{}", String::from_utf8_lossy(&output.stdout));
                    println!("{}", String::from_utf8_lossy(&output.stderr));

                    if !output.status.success() {
                        Self::abort("Failed to clone repo");
                    }
                },
                true => pretend!(format ! ("git clone {:?} {}", git, &self.release_dir()))
            }
        }
        done!(self)
    }

    pub fn composer(self) -> Self {
        print!("Composer install");
        match self.pretend {
            false => {
                let output = Command::new("composer")
                    .arg("install")
                    .arg("-d")
                    .arg(&self.release_dir())
                    .arg("--no-dev")
                    .arg("--prefer-dist")
                    .output()
                    .expect("Composer installation failed");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Composer installation failed");
                }
            },
            true => {
                pretend!(format!("composer install -d {} --no-dev --prefer-dist", &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_env(self) -> Self {
        print!("Init .env file");
        match self.pretend {
            false => {
                fs::copy(
                    format!("{}/.env.example", &self.release_dir()),
                    format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/.env.example {}/.env", &self.release_dir(), &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_storage(self) -> Self {
        print!("Init storage");
        match self.pretend {
            false => {
                fs::rename(
                    format!("{}/storage", &self.release_dir()),
                    format!("{}/shared/storage", &self.basedir)).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                });
            },
            true => {
                pretend!(format!("mv {}/storage {}/shared/storage", &self.release_dir(), &self.basedir));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn symlink(self) -> Self {
        print!("Symlink");
        match self.pretend {
            false => {
                fs::remove_file(format!("{}/current", &self.basedir)).ok();
                os::unix::fs::symlink(&self.release_dir(), format!("{}/current", &self.basedir))
                    .unwrap_or_else(|why| { Self::abort(&format!("Cannot create symlink. {:?}", why.kind())); });
            },
            true => {
                pretend!(format!("rm -Rf {}/current", &self.basedir));
                pretend!(format!("ln -s {} {}/current", &self.release_dir(), &self.basedir));
            }
        }
        done!(self)
    }

    pub fn bye(self, text: &str) -> Self {
        println!("{}", text);
        done!(self)
    }
    pub fn copy_env(self) -> Self {
        print!("copy_env");
        match self.pretend {
            false => {
                fs::copy(format!("{}/current/.env", &self.basedir), format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/current/.env {}/.env", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn switch_storage(self) -> Self {
        print!("Link storage dir");
        match self.pretend {
            false => {
                fs::remove_dir_all(format!("{}/storage", &self.release_dir())).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                }
                );
            },
            true => {
                pretend!(format!("rm {}/storage", &self.release_dir()));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn migrate(self) -> Self {
        print!("Artisan migrate");
        match self.pretend {
            false => {
                let output = Command::new("php")
                    .current_dir(&self.release_dir())
                    .arg("artisan")
                    .arg("migrate")
                    .arg("--force")
                    .output()
                    .expect("Failed to migrate");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Artisan migration failed");
                }
            },
            true => {
                pretend!(format!("php artisan migrate --force"));
            }
        }
        done!(self)
    }
    pub fn clean_old_releases(self) -> Self {
        print!("Delete old releases");
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", &self.basedir));
        for _ in 0..3 {
            paths.pop();
        }
        for path in paths {
            println!("Destroying old release: {}", path.path().display());
            match self.pretend {
                false => {
                    fs::remove_dir_all(path.path()).ok();
                },
                true => {
                    pretend!(format!("rm -Rf {}", path.path().display()))
                }
            }
        }
        done!(self)
    }

    pub fn rollback(self) -> Self {
        print!("Rollback");
        let previous = &Self::get_previous_release(&self.basedir);
        fs::remove_file(format!("{}/current", &self.basedir)).ok();
        os::unix::fs::symlink(previous, format!("{}/current", &self.basedir))
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
            });
        done!(self)
    }

    fn canonicalize(dir: &str) -> String {
        match fs::canonicalize(&PathBuf::from(&dir)) {
            Ok(dir) => dir.as_os_str().to_str().unwrap().to_string(),
            Err(_) => format!("{}/{}", env::current_dir().unwrap().display(), dir)
        }
    }

    fn mkdir_or_die(dir: &str) {
        fs::create_dir_all(dir)
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create dir. {:?}", why.kind()));
            });
    }

    fn abort(msg: &str) -> ! {
        panic!("ABORTED: {}", msg)
    }

    fn ts() -> String {
        time::now().strftime("%Y%m%d%H%M%S").unwrap().to_string()
    }

    fn get_sorted_paths(dir: &str) -> Vec<fs::DirEntry> {
        let mut paths: Vec<_> = fs::read_dir(dir).unwrap()
            .map(|r| r.unwrap())
            .collect();
        paths.sort_by_key(|dir| dir.path());
        paths
    }

    fn get_previous_release(dir: &str) -> String {
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", dir));
        paths.pop();
        format!("{}", paths.pop().unwrap().path().display())
    }
}
<commit_msg>Update facteur.rs<commit_after>use std::process::Command;
use std::os;
use std::fs;
use std::env;
use time;
use std::path::PathBuf;

macro_rules! done {
    ($v:expr) => {{
        println!(" ...OK");
        $v
    }}
}

macro_rules! pretend {
    ($v:expr) => {
        print!(" ([Pretend] {})", $v);
    }
}

pub struct Facteur {
    basedir: String,
    git: Option<String>,
    ts: String,
    pretend: bool
}

impl Facteur {
    pub fn new(dir: &str, pretend: bool) -> Self {
        Facteur {
            basedir: dir.to_string(),
            git: None,
            ts: Self::ts(),
            pretend: pretend
        }
    }

    pub fn git(mut self, git: &str) -> Self {
        self.git = Some(git.to_string());
        self
    }

    pub fn welcome(self, text: &str) -> Self {
        println!("{}", text);
        self
    }

    pub fn canonicalize_basedir(mut self) -> Self {
        self.basedir = Self::canonicalize(&self.basedir);
        self
    }

    pub fn mkdir_base(self) -> Self {
        print!("Making basedir {}", &self.basedir);
        match self.pretend {
            false => {
                Self::mkdir_or_die(&self.basedir);
                Self::mkdir_or_die(&format!("{}/releases", self.basedir));
                Self::mkdir_or_die(&format!("{}/shared", self.basedir));
            },
            true => {
                pretend!(&format!("mkdir {}", self.basedir));
                pretend!(&format!("mkdir {}/releases", self.basedir));
                pretend!(&format!("mkdir {}/shared", self.basedir));
            }
        }
        done!(self)
    }

    pub fn mkdir_release(self) -> Self {
        print!("Initializing Release dir");
        match self.pretend {
            false => Self::mkdir_or_die(&self.release_dir()),
            true => pretend!(format!("mkdir {}", &self.release_dir()))
        }
        done!(self)
    }

    fn release_dir(&self) -> String {
        format!("{}/releases/{}", self.basedir, &self.ts)
    }

    pub fn clone(self) -> Self {
        print!("Checkout");
        {
            let git = self.git.as_ref().unwrap();

            match self.pretend {
                false => {
                    let output = Command::new("git")
                        .arg("clone")
                        .arg(git)
                        .arg(&self.release_dir())
                        .output()
                        .expect("Failed to clone repo");

                    println!("{}", String::from_utf8_lossy(&output.stdout));
                    println!("{}", String::from_utf8_lossy(&output.stderr));

                    if !output.status.success() {
                        Self::abort("Failed to clone repo");
                    }
                },
                true => pretend!(format ! ("git clone {:?} {}", git, &self.release_dir()))
            }
        }
        done!(self)
    }

    pub fn composer(self) -> Self {
        print!("Composer install");
        match self.pretend {
            false => {
                let output = Command::new("composer")
                    .arg("install")
                    .arg("-d")
                    .arg(&self.release_dir())
                    .arg("--no-dev")
                    .arg("--prefer-dist")
                    .output()
                    .expect("Composer installation failed");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Composer installation failed");
                }
            },
            true => {
                pretend!(format!("composer install -d {} --no-dev --prefer-dist", &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_env(self) -> Self {
        print!("Init .env file");
        match self.pretend {
            false => {
                fs::copy(
                    format!("{}/.env.example", &self.release_dir()),
                    format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/.env.example {}/.env", &self.release_dir(), &self.release_dir()));
            }
        }
        done!(self)
    }

    pub fn init_storage(self) -> Self {
        print!("Init storage");
        match self.pretend {
            false => {
                fs::rename(
                    format!("{}/storage", &self.release_dir()),
                    format!("{}/shared/storage", &self.basedir)).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                });
            },
            true => {
                pretend!(format!("mv {}/storage {}/shared/storage", &self.release_dir(), &self.basedir));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn symlink(self) -> Self {
        print!("Symlink");
        match self.pretend {
            false => {
                fs::remove_file(format!("{}/current", &self.basedir)).ok();
                os::unix::fs::symlink(&self.release_dir(), format!("{}/current", &self.basedir))
                    .unwrap_or_else(|why| { Self::abort(&format!("Cannot create symlink. {:?}", why.kind())); });
            },
            true => {
                pretend!(format!("rm -Rf {}/current", &self.basedir));
                pretend!(format!("ln -s {} {}/current", &self.release_dir(), &self.basedir));
            }
        }
        done!(self)
    }

    pub fn bye(self, text: &str) -> Self {
        println!("{}", text);
        done!(self)
    }
    pub fn copy_env(self) -> Self {
        print!("copy_env");
        match self.pretend {
            false => {
                fs::copy(format!("{}/current/.env", &self.basedir), format!("{}/.env", &self.release_dir())).ok();
            },
            true => {
                pretend!(format!("cp {}/current/.env {}/.env", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn switch_storage(self) -> Self {
        print!("Link storage dir");
        match self.pretend {
            false => {
                fs::remove_dir_all(format!("{}/storage", &self.release_dir())).ok();
                os::unix::fs::symlink(
                    format!("{}/shared/storage", &self.basedir),
                    format!("{}/storage", &self.release_dir())
                ).unwrap_or_else(|why| {
                    Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
                }
                );
            },
            true => {
                pretend!(format!("rm {}/storage", &self.release_dir()));
                pretend!(format!("ln -s {}/shared/storage {}/storage", &self.basedir, &self.release_dir()));
            }
        }
        done!(self)
    }
    pub fn migrate(self) -> Self {
        print!("Artisan migrate");
        match self.pretend {
            false => {
                let output = Command::new("php")
                    .current_dir(&self.release_dir())
                    .arg("artisan")
                    .arg("migrate")
                    .arg("--force")
                    .output()
                    .expect("Failed to migrate");

                println!("{}", String::from_utf8_lossy(&output.stdout));
                println!("{}", String::from_utf8_lossy(&output.stderr));

                if !output.status.success() {
                    Self::abort("Artisan migration failed");
                }
            },
            true => {
                pretend!(format!("php artisan migrate --force"));
            }
        }
        done!(self)
    }
    pub fn clean_old_releases(self) -> Self {
        print!("Delete old releases");
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", &self.basedir));
        for _ in 0..3 {
            paths.pop();
        }
        for path in paths {
            println!("Destroying old release: {}", path.path().display());
            match self.pretend {
                false => {
                    fs::remove_dir_all(path.path()).ok();
                },
                true => {
                    pretend!(format!("rm -Rf {}", path.path().display()))
                }
            }
        }
        done!(self)
    }

    pub fn rollback(self) -> Self {
        print!("Rollback");
        let previous = &Self::get_previous_release(&self.basedir);
        fs::remove_file(format!("{}/current", &self.basedir)).ok();
        os::unix::fs::symlink(previous, format!("{}/current", &self.basedir))
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create symlink. {:?}", why.kind()));
            });
        done!(self)
    }

    fn canonicalize(dir: &str) -> String {
        match fs::canonicalize(&PathBuf::from(&dir)) {
            Ok(dir) => dir.as_os_str().to_str().unwrap().to_string(),
            Err(_) => format!("{}/{}", env::current_dir().unwrap().display(), dir)
        }
    }

    fn mkdir_or_die(dir: &str) {
        fs::create_dir_all(dir)
            .unwrap_or_else(|why| {
                Self::abort(&format!("Cannot create dir. {:?}", why.kind()));
            });
    }

    fn abort(msg: &str) -> ! {
        panic!("ABORTED: {}", msg)
    }

    fn ts() -> String {
        time::now().strftime("%Y%m%d%H%M%S").unwrap().to_string()
    }

    fn get_sorted_paths(dir: &str) -> Vec<fs::DirEntry> {
        let mut paths: Vec<_> = fs::read_dir(dir).unwrap()
            .map(|r| r.unwrap())
            .collect();
        paths.sort_by_key(|dir| dir.path());
        paths
    }

    fn get_previous_release(dir: &str) -> String {
        let mut paths = Self::get_sorted_paths(&format!("{}/releases", dir));
        paths.pop();
        format!("{}", paths.pop().unwrap().path().display())
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// TODO: add tests

use strings::string_buffer::StringBuffer;

use std::fs::{self, File};
use std::io::{self, Write, Read, BufWriter};

use config::{NewlineStyle, Config, WriteMode};
use rustfmt_diff::{make_diff, print_diff, Mismatch};
use checkstyle::{output_header, output_footer, output_checkstyle_file};

// A map of the files of a crate, with their new content
pub type FileMap = Vec<FileRecord>;

pub type FileRecord = (String, StringBuffer);

// Append a newline to the end of each file.
pub fn append_newline(s: &mut StringBuffer) {
    s.push_str("\n");
}

pub fn write_all_files<T>(file_map: &FileMap, out: &mut T, config: &Config) -> Result<(), io::Error>
    where T: Write
{
    output_header(out, config.write_mode()).ok();
    for &(ref filename, ref text) in file_map {
        write_file(text, filename, out, config)?;
    }
    output_footer(out, config.write_mode()).ok();

    Ok(())
}

// Prints all newlines either as `\n` or as `\r\n`.
pub fn write_system_newlines<T>(writer: T,
                                text: &StringBuffer,
                                config: &Config)
                                -> Result<(), io::Error>
    where T: Write
{
    // Buffer output, since we're writing a since char at a time.
    let mut writer = BufWriter::new(writer);

    let style = if config.newline_style() == NewlineStyle::Native {
        if cfg!(windows) {
            NewlineStyle::Windows
        } else {
            NewlineStyle::Unix
        }
    } else {
        config.newline_style()
    };

    match style {
        NewlineStyle::Unix => write!(writer, "{}", text),
        NewlineStyle::Windows => {
            for (c, _) in text.chars() {
                match c {
                    '\n' => write!(writer, "\r\n")?,
                    '\r' => continue,
                    c => write!(writer, "{}", c)?,
                }
            }
            Ok(())
        }
        NewlineStyle::Native => unreachable!(),
    }
}

pub fn write_file<T>(text: &StringBuffer,
                     filename: &str,
                     out: &mut T,
                     config: &Config)
                     -> Result<bool, io::Error>
    where T: Write
{

    fn source_and_formatted_text(text: &StringBuffer,
                                 filename: &str,
                                 config: &Config)
                                 -> Result<(String, String), io::Error> {
        let mut f = File::open(filename)?;
        let mut ori_text = String::new();
        f.read_to_string(&mut ori_text)?;
        let mut v = Vec::new();
        write_system_newlines(&mut v, text, config)?;
        let fmt_text = String::from_utf8(v).unwrap();
        Ok((ori_text, fmt_text))
    }

    fn create_diff(filename: &str,
                   text: &StringBuffer,
                   config: &Config)
                   -> Result<Vec<Mismatch>, io::Error> {
        let (ori, fmt) = source_and_formatted_text(text, filename, config)?;
        Ok(make_diff(&ori, &fmt, 3))
    }

    match config.write_mode() {
        WriteMode::Replace => {
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                if fmt != ori {
                    // Do a little dance to make writing safer - write to a temp file
                    // rename the original to a .bk, then rename the temp file to the
                    // original.
                    let tmp_name = filename.to_owned() + ".tmp";
                    let bk_name = filename.to_owned() + ".bk";
                    {
                        // Write text to temp file
                        let tmp_file = File::create(&tmp_name)?;
                        write_system_newlines(tmp_file, text, config)?;
                    }

                    fs::rename(filename, bk_name)?;
                    fs::rename(tmp_name, filename)?;
                }
            }
        }
        WriteMode::Overwrite => {
            // Write text directly over original file.
            let file = File::create(filename)?;
            write_system_newlines(file, text, config)?;
        }
        WriteMode::Plain => {
            write_system_newlines(out, text, config)?;
        }
        WriteMode::Display | WriteMode::Coverage => {
            println!("{}:\n", filename);
            write_system_newlines(out, text, config)?;
        }
        WriteMode::Diff => {
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                let mismatch = make_diff(&ori, &fmt, 3);
                let has_diff = !mismatch.is_empty();
                print_diff(mismatch, |line_num| {
                    format!("Diff in {} at line {}:", filename, line_num)
                });
                return Ok(has_diff);
            }
        }
        WriteMode::Checkstyle => {
            let diff = create_diff(filename, text, config)?;
            output_checkstyle_file(out, filename, diff)?;
        }
    }

    // when we are not in diff mode, don't indicate differing files
    Ok(false)
}
<commit_msg>Do not overwrite files when there are no diffs in Overwrite mode<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// TODO: add tests

use strings::string_buffer::StringBuffer;

use std::fs::{self, File};
use std::io::{self, Write, Read, BufWriter};

use config::{NewlineStyle, Config, WriteMode};
use rustfmt_diff::{make_diff, print_diff, Mismatch};
use checkstyle::{output_header, output_footer, output_checkstyle_file};

// A map of the files of a crate, with their new content
pub type FileMap = Vec<FileRecord>;

pub type FileRecord = (String, StringBuffer);

// Append a newline to the end of each file.
pub fn append_newline(s: &mut StringBuffer) {
    s.push_str("\n");
}

pub fn write_all_files<T>(file_map: &FileMap, out: &mut T, config: &Config) -> Result<(), io::Error>
    where T: Write
{
    output_header(out, config.write_mode()).ok();
    for &(ref filename, ref text) in file_map {
        write_file(text, filename, out, config)?;
    }
    output_footer(out, config.write_mode()).ok();

    Ok(())
}

// Prints all newlines either as `\n` or as `\r\n`.
pub fn write_system_newlines<T>(writer: T,
                                text: &StringBuffer,
                                config: &Config)
                                -> Result<(), io::Error>
    where T: Write
{
    // Buffer output, since we're writing a since char at a time.
    let mut writer = BufWriter::new(writer);

    let style = if config.newline_style() == NewlineStyle::Native {
        if cfg!(windows) {
            NewlineStyle::Windows
        } else {
            NewlineStyle::Unix
        }
    } else {
        config.newline_style()
    };

    match style {
        NewlineStyle::Unix => write!(writer, "{}", text),
        NewlineStyle::Windows => {
            for (c, _) in text.chars() {
                match c {
                    '\n' => write!(writer, "\r\n")?,
                    '\r' => continue,
                    c => write!(writer, "{}", c)?,
                }
            }
            Ok(())
        }
        NewlineStyle::Native => unreachable!(),
    }
}

pub fn write_file<T>(text: &StringBuffer,
                     filename: &str,
                     out: &mut T,
                     config: &Config)
                     -> Result<bool, io::Error>
    where T: Write
{

    fn source_and_formatted_text(text: &StringBuffer,
                                 filename: &str,
                                 config: &Config)
                                 -> Result<(String, String), io::Error> {
        let mut f = File::open(filename)?;
        let mut ori_text = String::new();
        f.read_to_string(&mut ori_text)?;
        let mut v = Vec::new();
        write_system_newlines(&mut v, text, config)?;
        let fmt_text = String::from_utf8(v).unwrap();
        Ok((ori_text, fmt_text))
    }

    fn create_diff(filename: &str,
                   text: &StringBuffer,
                   config: &Config)
                   -> Result<Vec<Mismatch>, io::Error> {
        let (ori, fmt) = source_and_formatted_text(text, filename, config)?;
        Ok(make_diff(&ori, &fmt, 3))
    }

    match config.write_mode() {
        WriteMode::Replace => {
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                if fmt != ori {
                    // Do a little dance to make writing safer - write to a temp file
                    // rename the original to a .bk, then rename the temp file to the
                    // original.
                    let tmp_name = filename.to_owned() + ".tmp";
                    let bk_name = filename.to_owned() + ".bk";
                    {
                        // Write text to temp file
                        let tmp_file = File::create(&tmp_name)?;
                        write_system_newlines(tmp_file, text, config)?;
                    }

                    fs::rename(filename, bk_name)?;
                    fs::rename(tmp_name, filename)?;
                }
            }
        }
        WriteMode::Overwrite => {
            // Write text directly over original file if there is a diff.
            let (source, formatted) = source_and_formatted_text(text, filename, config)?;
            if source != formatted {
                let file = File::create(filename)?;
                write_system_newlines(file, text, config)?;
            }
        }
        WriteMode::Plain => {
            write_system_newlines(out, text, config)?;
        }
        WriteMode::Display | WriteMode::Coverage => {
            println!("{}:\n", filename);
            write_system_newlines(out, text, config)?;
        }
        WriteMode::Diff => {
            if let Ok((ori, fmt)) = source_and_formatted_text(text, filename, config) {
                let mismatch = make_diff(&ori, &fmt, 3);
                let has_diff = !mismatch.is_empty();
                print_diff(mismatch, |line_num| {
                    format!("Diff in {} at line {}:", filename, line_num)
                });
                return Ok(has_diff);
            }
        }
        WriteMode::Checkstyle => {
            let diff = create_diff(filename, text, config)?;
            output_checkstyle_file(out, filename, diff)?;
        }
    }

    // when we are not in diff mode, don't indicate differing files
    Ok(false)
}
<|endoftext|>


<commit_before>use std::io::{MemReader, MemWriter};
use amqp_error::{AMQPResult, AMQPError};

#[deriving(Show, Clone, Eq, PartialEq, FromPrimitive)]
pub enum FrameType {
    METHOD = 1,
    HEADERS = 2,
    BODY  = 3,
    HEARTBEAT = 8
}

#[deriving(Show, Clone, Eq, PartialEq)]
pub struct Frame {
    pub frame_type: FrameType,
    pub channel: u16,
    pub payload: Vec<u8>
}

impl Frame {
    pub fn decode(reader: &mut Reader) -> AMQPResult<Frame> {
        let mut header = MemReader::new(try!(reader.read_exact(7)));
        let frame_type_id = try!(header.read_byte());
        let channel = try!(header.read_be_u16());
        let size = try!(header.read_be_u32());
        let payload = try!(reader.read_exact(size as uint));
        let frame_end = try!(reader.read_u8());
        if payload.len() as u32 != size {
            return Err(AMQPError::DecodeError("Payload didn't read the full size"));
        }
        if frame_end != 0xCE {
            return Err(AMQPError::DecodeError("Frame end wasn't right"));
        }
        let frame_type = match FromPrimitive::from_u8(frame_type_id){
            Some(ft) => ft,
            None => return Err(AMQPError::DecodeError("Unknown frame type"))
        };

        let frame = Frame { frame_type: frame_type, channel: channel, payload : payload };
        Ok(frame)
    }

    pub fn encode(&self) -> Vec<u8> {
        let mut writer = MemWriter::new();
        writer.write_u8(self.frame_type as u8).unwrap();
        writer.write_be_u16(self.channel).unwrap();
        writer.write_be_u32(self.payload.len() as u32).unwrap();
        writer.write(self.payload.as_slice()).unwrap();
        writer.write_u8(0xCE).unwrap();
        writer.unwrap()
    }
}

#[deriving(Show, Clone)]
pub struct ContentHeaderFrame {
    pub content_class: u16,
    pub weight: u16,
    pub body_size: u64,
    pub properties_flags: u16,
    pub properties: Vec<u8>
}

impl ContentHeaderFrame {
    pub fn decode(frame: Frame) -> AMQPResult<ContentHeaderFrame> {
        let mut reader = MemReader::new(frame.payload);
        let content_class = try!(reader.read_be_u16());
        let weight = try!(reader.read_be_u16()); //0 all the time for now
        let body_size = try!(reader.read_be_u64());
        let properties_flags = try!(reader.read_be_u16());
        let properties = try!(reader.read_to_end());
        Ok(ContentHeaderFrame {
            content_class: content_class, weight: weight, body_size: body_size,
            properties_flags: properties_flags, properties: properties
        })
    }

    pub fn encode(&self) -> Vec<u8> {
        let mut writer = MemWriter::new();
        writer.write_be_u16(self.content_class).unwrap();
        writer.write_be_u16(self.weight).unwrap(); //0 all the time for now
        writer.write_be_u64(self.body_size).unwrap();
        writer.write_be_u16(self.properties_flags).unwrap();
        writer.write(self.properties.as_slice()).unwrap();
        writer.unwrap()
    }
}

#[test]
fn test_encode_decode(){
    let frame = Frame{ frame_type: METHOD, channel: 5, payload: vec!(1,2,3,4,5) };
    assert_eq!(frame, Frame::decode(&mut MemReader::new(frame.encode())).unwrap());
}<commit_msg>Fix tests.<commit_after>use std::io::{MemReader, MemWriter};
use amqp_error::{AMQPResult, AMQPError};

#[deriving(Show, Clone, Eq, PartialEq, FromPrimitive)]
pub enum FrameType {
    METHOD = 1,
    HEADERS = 2,
    BODY  = 3,
    HEARTBEAT = 8
}

#[deriving(Show, Clone, Eq, PartialEq)]
pub struct Frame {
    pub frame_type: FrameType,
    pub channel: u16,
    pub payload: Vec<u8>
}

impl Frame {
    pub fn decode(reader: &mut Reader) -> AMQPResult<Frame> {
        let mut header = MemReader::new(try!(reader.read_exact(7)));
        let frame_type_id = try!(header.read_byte());
        let channel = try!(header.read_be_u16());
        let size = try!(header.read_be_u32());
        let payload = try!(reader.read_exact(size as uint));
        let frame_end = try!(reader.read_u8());
        if payload.len() as u32 != size {
            return Err(AMQPError::DecodeError("Payload didn't read the full size"));
        }
        if frame_end != 0xCE {
            return Err(AMQPError::DecodeError("Frame end wasn't right"));
        }
        let frame_type = match FromPrimitive::from_u8(frame_type_id){
            Some(ft) => ft,
            None => return Err(AMQPError::DecodeError("Unknown frame type"))
        };

        let frame = Frame { frame_type: frame_type, channel: channel, payload : payload };
        Ok(frame)
    }

    pub fn encode(&self) -> Vec<u8> {
        let mut writer = MemWriter::new();
        writer.write_u8(self.frame_type as u8).unwrap();
        writer.write_be_u16(self.channel).unwrap();
        writer.write_be_u32(self.payload.len() as u32).unwrap();
        writer.write(self.payload.as_slice()).unwrap();
        writer.write_u8(0xCE).unwrap();
        writer.unwrap()
    }
}

#[deriving(Show, Clone)]
pub struct ContentHeaderFrame {
    pub content_class: u16,
    pub weight: u16,
    pub body_size: u64,
    pub properties_flags: u16,
    pub properties: Vec<u8>
}

impl ContentHeaderFrame {
    pub fn decode(frame: Frame) -> AMQPResult<ContentHeaderFrame> {
        let mut reader = MemReader::new(frame.payload);
        let content_class = try!(reader.read_be_u16());
        let weight = try!(reader.read_be_u16()); //0 all the time for now
        let body_size = try!(reader.read_be_u64());
        let properties_flags = try!(reader.read_be_u16());
        let properties = try!(reader.read_to_end());
        Ok(ContentHeaderFrame {
            content_class: content_class, weight: weight, body_size: body_size,
            properties_flags: properties_flags, properties: properties
        })
    }

    pub fn encode(&self) -> Vec<u8> {
        let mut writer = MemWriter::new();
        writer.write_be_u16(self.content_class).unwrap();
        writer.write_be_u16(self.weight).unwrap(); //0 all the time for now
        writer.write_be_u64(self.body_size).unwrap();
        writer.write_be_u16(self.properties_flags).unwrap();
        writer.write(self.properties.as_slice()).unwrap();
        writer.unwrap()
    }
}

#[test]
fn test_encode_decode(){
    let frame = Frame{ frame_type: FrameType::METHOD, channel: 5, payload: vec!(1,2,3,4,5) };
    assert_eq!(frame, Frame::decode(&mut MemReader::new(frame.encode())).unwrap());
}<|endoftext|>


<commit_before>// Test helpers.

extern crate pencil;
extern crate url;
extern crate hyper;

use std::path::PathBuf;

use hyper::header::Location;

use pencil::{PenHTTPError, PenUserError};
use pencil::{abort, redirect, safe_join, escape};

#[test]
fn test_abort() {
    let result = abort(404);
    let pencil_error = result.err().unwrap();
    let http_error = match pencil_error {
        PenHTTPError(e) => Some(e),
        PenUserError(_) => None,
    };
    assert!(http_error.unwrap().code() == 404);
}

#[test]
fn test_redirect() {
    let result = redirect("http://localhost/füübär", 302);
    let response = result.ok().unwrap();
    let location: Option<&Location> = response.headers.get();
    let location_str = url::percent_encoding::lossy_utf8_percent_decode(location.unwrap().as_bytes());
    assert!(location_str.contains("/füübär"));
    assert!(response.status_code == 302);

    let result = redirect("http://example.com/", 301);
    let response = result.ok().unwrap();
    let location: Option<&Location> = response.headers.get();
    assert!(*location.unwrap() == Location("http://example.com/".to_owned()));
    assert!(response.status_code == 301);
}

#[test]
fn test_safe_join() {
    let path = safe_join("foo", "bar/baz").unwrap();
    assert!(path == PathBuf::from("foo/bar/baz"));
    assert!(safe_join("foo", "../bar/baz").is_none());
}

#[test]
fn test_escape() {
    assert!(escape(String::from("42")) == "42");
    assert!(escape(String::from("<>")) == "<>");
    assert!(escape(String::from("\"foo\"")) == ""foo"");
}
<commit_msg>Fixed tests<commit_after>// Test helpers.

extern crate pencil;
extern crate url;
extern crate hyper;

use std::path::PathBuf;

use hyper::header::Location;

use pencil::{PenHTTPError, PenUserError};
use pencil::{abort, redirect, safe_join, escape};

#[test]
fn test_abort() {
    let result = abort(404);
    let pencil_error = result.err().unwrap();
    let http_error = match pencil_error {
        PenHTTPError(e) => Some(e),
        PenUserError(_) => None,
    };
    assert!(http_error.unwrap().code() == 404);
}

#[test]
fn test_redirect() {
    let result = redirect("http://localhost/füübär", 302);
    let response = result.ok().unwrap();
    let location: Option<&Location> = response.headers.get();
    let location_str = url::percent_encoding::percent_decode(location.unwrap().as_bytes()).decode_utf8_lossy();
    assert!(location_str.contains("/füübär"));
    assert!(response.status_code == 302);

    let result = redirect("http://example.com/", 301);
    let response = result.ok().unwrap();
    let location: Option<&Location> = response.headers.get();
    assert!(*location.unwrap() == Location("http://example.com/".to_owned()));
    assert!(response.status_code == 301);
}

#[test]
fn test_safe_join() {
    let path = safe_join("foo", "bar/baz").unwrap();
    assert!(path == PathBuf::from("foo/bar/baz"));
    assert!(safe_join("foo", "../bar/baz").is_none());
}

#[test]
fn test_escape() {
    assert!(escape(String::from("42")) == "42");
    assert!(escape(String::from("<>")) == "<>");
    assert!(escape(String::from("\"foo\"")) == ""foo"");
}
<|endoftext|>


<commit_before>use super::*;

use std::collections::VecDeque;
use std::io::{BufReader, BufRead, Error, ErrorKind};
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::Index;
use std::ops::IndexMut;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Sender};
use std::thread::{sleep, spawn, JoinHandle};
use std::time::Duration;

use bytecount::count;

const DEFAULT_MAX_SIZE: usize = 1000;

/// Structure encapsulating command history
pub struct History {
    // TODO: this should eventually be private
    /// Vector of buffers to store history in
    pub buffers: VecDeque<Buffer>,
    /// Store a filename to save history into; if None don't save history
    file_name: Option<String>,
    /// Maximal number of buffers stored in the memory
    /// TODO: just make this public?
    max_size: usize,
    /// Maximal number of lines stored in the file
    // TODO: just make this public?
    max_file_size: Arc<AtomicUsize>,
    /// Handle to the background thread managing writes to the history file
    bg_handle: Option<JoinHandle<()>>,
    /// Signals the background thread to stop when dropping the struct
    bg_stop: Arc<AtomicBool>,
    /// Sends commands to write to the history file
    sender: Sender<(Buffer, String)>,

    // TODO set from environment variable?
    pub append_duplicate_entries: bool,
}

impl History {
    /// It's important to execute this function before exiting your program, as it will
    /// ensure that all history data has been written to the disk.
    pub fn commit_history(&mut self) {
        // Signal the background thread to stop
        self.bg_stop.store(true, Ordering::Relaxed);
        // Wait for the background thread to stop
        if let Some(handle) = self.bg_handle.take() {
            let _ = handle.join();
        }
    }

    /// Create new History structure.
    pub fn new() -> History {
        let max_file_size = Arc::new(AtomicUsize::new(DEFAULT_MAX_SIZE));
        let bg_stop = Arc::new(AtomicBool::new(false));
        let (sender, receiver) = channel();

        let stop_signal = bg_stop.clone();
        let max_size = max_file_size.clone();
        History {
            buffers: VecDeque::with_capacity(DEFAULT_MAX_SIZE),
            file_name: None,
            sender: sender,
            bg_handle: Some(spawn(move || {
                while !stop_signal.load(Ordering::Relaxed) {
                    if let Ok((command, filepath)) = receiver.try_recv() {
                        let max_file_size = max_size.load(Ordering::Relaxed);
                        let _ = write_to_disk(max_file_size, &command, &filepath);
                    }
                    sleep(Duration::from_millis(100));
                }

                // Deplete the receiver of commands to write, before exiting the thread.
                while let Ok((command, filepath)) = receiver.try_recv() {
                    let max_file_size = max_size.load(Ordering::Relaxed);
                    let _ = write_to_disk(max_file_size, &command, &filepath);
                }
            })),
            bg_stop: bg_stop,
            max_size: DEFAULT_MAX_SIZE,
            max_file_size: max_file_size,
            append_duplicate_entries: false,
        }
    }

    /// Number of items in history.
    pub fn len(&self) -> usize {
        self.buffers.len()
    }

    /// Add a command to the history buffer and remove the oldest commands when the max history
    /// size has been met. If writing to the disk is enabled, this function will be used for
    /// logging history to the designated history file.
    pub fn push(&mut self, new_item: Buffer) -> io::Result<()> {
        self.file_name
            .as_ref()
            .map(|name| {
                let _ = self.sender.send((new_item.clone(), name.to_owned()));
            });

        // buffers[0] is the oldest entry
        // the new entry goes to the end
        if !self.append_duplicate_entries &&
            self.buffers.back().map(|b| b.to_string()) == Some(new_item.to_string())
        {
            return Ok(());
        }

        self.buffers.push_back(new_item);
        while self.buffers.len() > self.max_size {
            self.buffers.pop_front();
        }
        Ok(())
    }

    /// Go through the history and try to find a buffer which starts the same as the new buffer
    /// given to this function as argument.
    pub fn get_newest_match<'a, 'b>(
        &'a self,
        curr_position: Option<usize>,
        new_buff: &'b Buffer,
    ) -> Option<&'a Buffer> {
        let pos = curr_position.unwrap_or(self.buffers.len());
        for iter in (0..pos).rev() {
            if let Some(tested) = self.buffers.get(iter) {
                if tested.starts_with(new_buff) {
                    return self.buffers.get(iter);
                }
            }
        }
        None
    }

    /// Get the history file name.
    pub fn file_name(&self) -> Option<&str> {
        match self.file_name {
            Some(ref s) => Some(&s[..]),
            None => None,
        }
    }

    /// Set history file name. At the same time enable history.
    pub fn set_file_name(&mut self, name: Option<String>) {
        self.file_name = name;
    }

    /// Set maximal number of buffers stored in memory
    pub fn set_max_size(&mut self, size: usize) {
        self.max_size = size;
    }

    /// Set maximal number of entries in history file
    pub fn set_max_file_size(&mut self, size: usize) {
        self.max_file_size.store(size, Ordering::Relaxed);
    }

    /// Load history from given file name
    pub fn load_history(&mut self) -> io::Result<()> {
        let file_name = match self.file_name.clone() {
            Some(name) => name,
            None => {
                return Err(Error::new(
                    ErrorKind::Other,
                    "Liner: file name not specified",
                ))
            }
        };
        let file = try!(OpenOptions::new().read(true).open(file_name));
        let reader = BufReader::new(file);
        for line in reader.lines() {
            match line {
                Ok(line) => self.buffers.push_back(Buffer::from(line)),
                Err(_) => break,
            }
        }
        Ok(())
    }
}

impl Index<usize> for History {
    type Output = Buffer;

    fn index(&self, index: usize) -> &Buffer {
        &self.buffers[index]
    }
}

impl IndexMut<usize> for History {
    fn index_mut(&mut self, index: usize) -> &mut Buffer {
        &mut self.buffers[index]
    }
}

/// Perform write operation. If the history file does not exist, it will be created.
/// This function is not part of the public interface.
/// XXX: include more information in the file (like fish does)
fn write_to_disk(max_file_size: usize, new_item: &Buffer, file_name: &str) -> io::Result<()> {
    let ret = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(file_name) {
        Ok(mut file) => {
            // The metadata contains the length of the file
            let file_length = file.metadata().ok().map_or(0, |m| m.len() as usize);
            // 4K byte buffer for reading chunks of the file at once.
            let mut buffer = [0; 4096];

            // Determine the number of commands stored.
            let commands_stored = {
                let mut commands_stored = 0;
                let mut file = File::open(file_name).unwrap();
                loop {
                    let read = file.read(&mut buffer)?;
                    if read == 0 { break }
                    commands_stored += count(&buffer, b'\n');
                }
                commands_stored
            };

            // If the max history file size has been reached, truncate the file so that only
            // N amount of commands are listed. To truncate the file, the seek point will be
            // discovered by counting the number of bytes until N newlines have been found and
            // then the file will be seeked to that point, copying all data after and rewriting
            // the file with the first N lines removed.
            if commands_stored >= max_file_size {
                let seek_point = {
                    let commands_to_delete = commands_stored - max_file_size + 1;
                    let mut matched = 0;
                    let mut bytes = 0;
                    let file = File::open(file_name).unwrap();
                    for byte in file.bytes() {
                        if byte.unwrap_or(b' ') == b'\n' {
                            matched += 1;
                        }
                        bytes += 1;
                        if matched == commands_to_delete {
                            break;
                        }
                    }
                    bytes as u64
                };

                try!(file.seek(SeekFrom::Start(seek_point)));
                let mut buffer: Vec<u8> = Vec::with_capacity(file_length - seek_point as usize);
                try!(file.read_to_end(&mut buffer));
                try!(file.set_len(0));
                try!(io::copy(&mut buffer.as_slice(), &mut file));
            }

            // Seek to end for appending
            try!(file.seek(SeekFrom::End(0)));
            // Write the command to the history file.
            try!(file.write_all(String::from(new_item.clone()).as_bytes()));
            try!(file.write_all(b"\n"));
            file.flush()?;

            Ok(())
        }
        Err(message) => Err(message),
    };
    ret
}
<commit_msg>Further Disk Writing Optimizations<commit_after>use super::*;

use std::collections::VecDeque;
use std::io::{BufReader, BufRead, Error, ErrorKind};
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::ops::Index;
use std::ops::IndexMut;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Sender};
use std::thread::{sleep, spawn, JoinHandle};
use std::time::Duration;

use bytecount::count;

const DEFAULT_MAX_SIZE: usize = 1000;

/// Structure encapsulating command history
pub struct History {
    // TODO: this should eventually be private
    /// Vector of buffers to store history in
    pub buffers: VecDeque<Buffer>,
    /// Store a filename to save history into; if None don't save history
    file_name: Option<String>,
    /// Maximal number of buffers stored in the memory
    /// TODO: just make this public?
    max_size: usize,
    /// Maximal number of lines stored in the file
    // TODO: just make this public?
    max_file_size: Arc<AtomicUsize>,
    /// Handle to the background thread managing writes to the history file
    bg_handle: Option<JoinHandle<()>>,
    /// Signals the background thread to stop when dropping the struct
    bg_stop: Arc<AtomicBool>,
    /// Sends commands to write to the history file
    sender: Sender<(Buffer, String)>,

    // TODO set from environment variable?
    pub append_duplicate_entries: bool,
}

impl History {
    /// It's important to execute this function before exiting your program, as it will
    /// ensure that all history data has been written to the disk.
    pub fn commit_history(&mut self) {
        // Signal the background thread to stop
        self.bg_stop.store(true, Ordering::Relaxed);
        // Wait for the background thread to stop
        if let Some(handle) = self.bg_handle.take() {
            let _ = handle.join();
        }
    }

    /// Create new History structure.
    pub fn new() -> History {
        let max_file_size = Arc::new(AtomicUsize::new(DEFAULT_MAX_SIZE));
        let bg_stop = Arc::new(AtomicBool::new(false));
        let (sender, receiver) = channel();

        let stop_signal = bg_stop.clone();
        let max_size = max_file_size.clone();
        History {
            buffers: VecDeque::with_capacity(DEFAULT_MAX_SIZE),
            file_name: None,
            sender: sender,
            bg_handle: Some(spawn(move || {
                while !stop_signal.load(Ordering::Relaxed) {
                    if let Ok((command, filepath)) = receiver.try_recv() {
                        let max_file_size = max_size.load(Ordering::Relaxed);
                        let _ = write_to_disk(max_file_size, &command, &filepath);
                    }
                    sleep(Duration::from_millis(100));
                }

                // Deplete the receiver of commands to write, before exiting the thread.
                while let Ok((command, filepath)) = receiver.try_recv() {
                    let max_file_size = max_size.load(Ordering::Relaxed);
                    let _ = write_to_disk(max_file_size, &command, &filepath);
                }
            })),
            bg_stop: bg_stop,
            max_size: DEFAULT_MAX_SIZE,
            max_file_size: max_file_size,
            append_duplicate_entries: false,
        }
    }

    /// Number of items in history.
    pub fn len(&self) -> usize {
        self.buffers.len()
    }

    /// Add a command to the history buffer and remove the oldest commands when the max history
    /// size has been met. If writing to the disk is enabled, this function will be used for
    /// logging history to the designated history file.
    pub fn push(&mut self, new_item: Buffer) -> io::Result<()> {
        self.file_name
            .as_ref()
            .map(|name| {
                let _ = self.sender.send((new_item.clone(), name.to_owned()));
            });

        // buffers[0] is the oldest entry
        // the new entry goes to the end
        if !self.append_duplicate_entries &&
            self.buffers.back().map(|b| b.to_string()) == Some(new_item.to_string())
        {
            return Ok(());
        }

        self.buffers.push_back(new_item);
        while self.buffers.len() > self.max_size {
            self.buffers.pop_front();
        }
        Ok(())
    }

    /// Go through the history and try to find a buffer which starts the same as the new buffer
    /// given to this function as argument.
    pub fn get_newest_match<'a, 'b>(
        &'a self,
        curr_position: Option<usize>,
        new_buff: &'b Buffer,
    ) -> Option<&'a Buffer> {
        let pos = curr_position.unwrap_or(self.buffers.len());
        for iter in (0..pos).rev() {
            if let Some(tested) = self.buffers.get(iter) {
                if tested.starts_with(new_buff) {
                    return self.buffers.get(iter);
                }
            }
        }
        None
    }

    /// Get the history file name.
    pub fn file_name(&self) -> Option<&str> {
        match self.file_name {
            Some(ref s) => Some(&s[..]),
            None => None,
        }
    }

    /// Set history file name. At the same time enable history.
    pub fn set_file_name(&mut self, name: Option<String>) {
        self.file_name = name;
    }

    /// Set maximal number of buffers stored in memory
    pub fn set_max_size(&mut self, size: usize) {
        self.max_size = size;
    }

    /// Set maximal number of entries in history file
    pub fn set_max_file_size(&mut self, size: usize) {
        self.max_file_size.store(size, Ordering::Relaxed);
    }

    /// Load history from given file name
    pub fn load_history(&mut self) -> io::Result<()> {
        let file_name = match self.file_name.clone() {
            Some(name) => name,
            None => {
                return Err(Error::new(
                    ErrorKind::Other,
                    "Liner: file name not specified",
                ))
            }
        };
        let file = try!(OpenOptions::new().read(true).open(file_name));
        let reader = BufReader::new(file);
        for line in reader.lines() {
            match line {
                Ok(line) => self.buffers.push_back(Buffer::from(line)),
                Err(_) => break,
            }
        }
        Ok(())
    }
}

impl Index<usize> for History {
    type Output = Buffer;

    fn index(&self, index: usize) -> &Buffer {
        &self.buffers[index]
    }
}

impl IndexMut<usize> for History {
    fn index_mut(&mut self, index: usize) -> &mut Buffer {
        &mut self.buffers[index]
    }
}

/// Perform write operation. If the history file does not exist, it will be created.
/// This function is not part of the public interface.
/// XXX: include more information in the file (like fish does)
fn write_to_disk(max_file_size: usize, new_item: &Buffer, file_name: &str) -> io::Result<()> {
    let ret = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(file_name) {
        Ok(mut file) => {
            // The metadata contains the length of the file
            let file_length = file.metadata().ok().map_or(0u64, |m| m.len());
            // 4K byte buffer for reading chunks of the file at once.
            let mut buffer = [0; 4096];

            // Determine the number of commands stored.
            {
                let mut seek_point = 0u64;
                let mut stored = 0;
                let mut total_read = 0u64;
                let mut rfile = File::open(file_name).unwrap();
                loop {
                    // Read 4K of bytes all at once into the buffer.
                    let read = rfile.read(&mut buffer)? as u64;
                    // If EOF is found, don't seek at all.
                    if read == 0 { break }
                    // Count the number of commands that were found in the current buffer.
                    let cmds_read = count(&buffer, b'\n');
                    // If stored + read >= max file size, a seek point is in the current buffer.
                    if stored + cmds_read >= max_file_size {
                        for &byte in buffer.iter() {
                            total_read += 1;
                            if byte == b'\n' {
                                stored += 1;
                                if stored == max_file_size {
                                    seek_point = total_read;
                                    break
                                }
                            }
                        }

                        try!(file.seek(SeekFrom::Start(seek_point as u64)));
                        let mut buffer: Vec<u8> = Vec::with_capacity((file_length - seek_point) as usize);
                        try!(file.read_to_end(&mut buffer));
                        try!(file.set_len(0));
                        try!(io::copy(&mut buffer.as_slice(), &mut file));
                    } else {
                        total_read += read;
                        stored += cmds_read;
                    }
                }
            };

            // Seek to end for appending
            try!(file.seek(SeekFrom::End(0)));
            // Write the command to the history file.
            try!(file.write_all(String::from(new_item.clone()).as_bytes()));
            try!(file.write_all(b"\n"));
            file.flush()?;

            Ok(())
        }
        Err(message) => Err(message),
    };
    ret
}
<|endoftext|>