<commit_before>#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
use crate::convert::Convert;
use crate::{AHasher};
#[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
use const_random::const_random;
use core::fmt;
use core::hash::BuildHasher;
use core::hash::Hasher;
#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
use once_cell::race::OnceBox;
use core::sync::atomic::{AtomicUsize, Ordering};

#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new();

static COUNTER: AtomicUsize = AtomicUsize::new(0);

pub(crate) const PI: [u64; 4] = [
    0x243f_6a88_85a3_08d3,
    0x1319_8a2e_0370_7344,
    0xa409_3822_299f_31d0,
    0x082e_fa98_ec4e_6c89,
];

#[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
const PI2: [u64; 4] = [
    0x4528_21e6_38d0_1377,
    0xbe54_66cf_34e9_0c6c,
    0xc0ac_29b7_c97c_50dd,
    0x3f84_d5b5_b547_0917,
];

#[inline]
pub(crate) fn seeds() -> [u64; 4] {
    #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
        {
            SEEDS.get_or_init(|| {
                let mut result: [u8; 64] = [0; 64];
                getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.");
                Box::new(result.convert())
            })[1]
        }
    #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
        { [const_random!(u64), const_random!(u64), const_random!(u64), const_random!(u64)] }
    #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
        { PI }
}

/// Provides a [Hasher] factory. This is typically used (e.g. by [HashMap]) to create
/// [AHasher]s in order to hash the keys of the map. See `build_hasher` below.
///
/// [build_hasher]: ahash::
/// [Hasher]: std::hash::Hasher
/// [BuildHasher]: std::hash::BuildHasher
/// [HashMap]: std::collections::HashMap
#[derive(Clone)]
pub struct RandomState {
    pub(crate) k0: u64,
    pub(crate) k1: u64,
    pub(crate) k2: u64,
    pub(crate) k3: u64,
}

impl fmt::Debug for RandomState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("RandomState { .. }")
    }
}

impl RandomState {
    /// Use randomly generated keys
    #[inline]
    pub fn new() -> RandomState {
        #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
        {
            let seeds = SEEDS.get_or_init(|| {
                let mut result: [u8; 64] = [0; 64];
                getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.");
                Box::new(result.convert())
            });
            RandomState::from_keys(seeds[0], seeds[1])
        }
        #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
        {
            RandomState::from_keys(
                [const_random!(u64), const_random!(u64), const_random!(u64), const_random!(u64)],
                [const_random!(u64), const_random!(u64), const_random!(u64), const_random!(u64)],
            )
        }
        #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
        {
            RandomState::from_keys(PI, PI2)
        }
    }

    /// Allows for supplying seeds, but each time it is called the resulting state will be different.
    /// This is done using a static counter, so it can safely be used with a fixed keys.
    #[inline]
    pub fn generate_with(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState {
        RandomState::from_keys(seeds(), [k0, k1, k2, k3])
    }

    fn from_keys(a: [u64; 4], b: [u64; 4]) -> RandomState {
        let [k0, k1, k2, k3] = a;
        let mut hasher = AHasher::from_random_state(&RandomState { k0, k1, k2, k3 });

        let stack_mem_loc = &hasher as *const _ as usize;
        #[cfg(not(all(target_arch="arm", target_os="none")))]
        {
            hasher.write_usize(COUNTER.fetch_add(stack_mem_loc, Ordering::Relaxed));
        }
        #[cfg(all(target_arch="arm", target_os="none"))]
        {
            let previous = COUNTER.load(Ordering::Relaxed);
            let new = previous.wrapping_add(stack_mem_loc);
            COUNTER.store(new, Ordering::Relaxed);
            hasher.write_usize(new);
        }
        #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
        hasher.write_usize(&PI as *const _ as usize);
        let mix = |k: u64| {
            let mut h = hasher.clone();
            h.write_u64(k);
            h.finish()
        };

        RandomState { k0: mix(b[0]), k1: mix(b[1]), k2: mix(b[2]), k3: mix(b[3]) }
    }

    /// Internal. Used by Default.
    #[inline]
    pub(crate) fn with_fixed_keys() -> RandomState {
        let [k0, k1, k2, k3] = seeds();
        RandomState { k0, k1, k2, k3 }
    }

    /// Allows for explicitly setting the seeds to used.
    #[inline]
    pub const fn with_seeds(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState {
        RandomState { k0, k1, k2, k3 }
    }
}

impl Default for RandomState {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl BuildHasher for RandomState {
    type Hasher = AHasher;

    /// Constructs a new [AHasher] with keys based on this [RandomState] object.
    /// This means that two different [RandomState]s will will generate
    /// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher]
    /// will generate the same hashes for the same input data.
    ///
    /// # Examples
    ///
    /// ```
    /// use ahash::{AHasher, RandomState};
    /// use std::hash::{Hasher, BuildHasher};
    ///
    /// let build_hasher = RandomState::new();
    /// let mut hasher_1 = build_hasher.build_hasher();
    /// let mut hasher_2 = build_hasher.build_hasher();
    ///
    /// hasher_1.write_u32(1234);
    /// hasher_2.write_u32(1234);
    ///
    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
    ///
    /// let other_build_hasher = RandomState::new();
    /// let mut different_hasher = other_build_hasher.build_hasher();
    /// different_hasher.write_u32(1234);
    /// assert_ne!(different_hasher.finish(), hasher_1.finish());
    /// ```
    /// [Hasher]: std::hash::Hasher
    /// [BuildHasher]: std::hash::BuildHasher
    /// [HashMap]: std::collections::HashMap
    #[inline]
    fn build_hasher(&self) -> AHasher {
        AHasher::from_random_state(self)
    }
}

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

    #[test]
    fn test_unique() {
        let a = RandomState::new();
        let b = RandomState::new();
        assert_ne!(a.build_hasher().finish(), b.build_hasher().finish());
    }

    #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
    #[test]
    fn test_not_pi() {
        assert_ne!(PI, seeds());
    }

    #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
    #[test]
    fn test_not_pi_const() {
        assert_ne!(PI, seeds());
    }

    #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
    #[test]
    fn test_pi() {
        assert_eq!(PI, seeds());
    }

    #[test]
    fn test_with_seeds_const() {
        const _CONST_RANDOM_STATE: RandomState = RandomState::with_seeds(17, 19, 21, 23);
    }
}
<commit_msg>Import alloc<commit_after>#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
use crate::convert::Convert;
use crate::{AHasher};
#[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
use const_random::const_random;
use core::fmt;
use core::hash::BuildHasher;
use core::hash::Hasher;

#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std as alloc;

#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
use once_cell::race::OnceBox;
#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
use alloc::boxed::Box;
use core::sync::atomic::{AtomicUsize, Ordering};

#[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
static SEEDS: OnceBox<[[u64; 4]; 2]> = OnceBox::new();

static COUNTER: AtomicUsize = AtomicUsize::new(0);

pub(crate) const PI: [u64; 4] = [
    0x243f_6a88_85a3_08d3,
    0x1319_8a2e_0370_7344,
    0xa409_3822_299f_31d0,
    0x082e_fa98_ec4e_6c89,
];

#[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
const PI2: [u64; 4] = [
    0x4528_21e6_38d0_1377,
    0xbe54_66cf_34e9_0c6c,
    0xc0ac_29b7_c97c_50dd,
    0x3f84_d5b5_b547_0917,
];

#[inline]
pub(crate) fn seeds() -> [u64; 4] {
    #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
        {
            SEEDS.get_or_init(|| {
                let mut result: [u8; 64] = [0; 64];
                getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.");
                Box::new(result.convert())
            })[1]
        }
    #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
        { [const_random!(u64), const_random!(u64), const_random!(u64), const_random!(u64)] }
    #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
        { PI }
}

/// Provides a [Hasher] factory. This is typically used (e.g. by [HashMap]) to create
/// [AHasher]s in order to hash the keys of the map. See `build_hasher` below.
///
/// [build_hasher]: ahash::
/// [Hasher]: std::hash::Hasher
/// [BuildHasher]: std::hash::BuildHasher
/// [HashMap]: std::collections::HashMap
#[derive(Clone)]
pub struct RandomState {
    pub(crate) k0: u64,
    pub(crate) k1: u64,
    pub(crate) k2: u64,
    pub(crate) k3: u64,
}

impl fmt::Debug for RandomState {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.pad("RandomState { .. }")
    }
}

impl RandomState {
    /// Use randomly generated keys
    #[inline]
    pub fn new() -> RandomState {
        #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
        {
            let seeds = SEEDS.get_or_init(|| {
                let mut result: [u8; 64] = [0; 64];
                getrandom::getrandom(&mut result).expect("getrandom::getrandom() failed.");
                Box::new(result.convert())
            });
            RandomState::from_keys(seeds[0], seeds[1])
        }
        #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
        {
            RandomState::from_keys(
                [const_random!(u64), const_random!(u64), const_random!(u64), const_random!(u64)],
                [const_random!(u64), const_random!(u64), const_random!(u64), const_random!(u64)],
            )
        }
        #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
        {
            RandomState::from_keys(PI, PI2)
        }
    }

    /// Allows for supplying seeds, but each time it is called the resulting state will be different.
    /// This is done using a static counter, so it can safely be used with a fixed keys.
    #[inline]
    pub fn generate_with(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState {
        RandomState::from_keys(seeds(), [k0, k1, k2, k3])
    }

    fn from_keys(a: [u64; 4], b: [u64; 4]) -> RandomState {
        let [k0, k1, k2, k3] = a;
        let mut hasher = AHasher::from_random_state(&RandomState { k0, k1, k2, k3 });

        let stack_mem_loc = &hasher as *const _ as usize;
        #[cfg(not(all(target_arch="arm", target_os="none")))]
        {
            hasher.write_usize(COUNTER.fetch_add(stack_mem_loc, Ordering::Relaxed));
        }
        #[cfg(all(target_arch="arm", target_os="none"))]
        {
            let previous = COUNTER.load(Ordering::Relaxed);
            let new = previous.wrapping_add(stack_mem_loc);
            COUNTER.store(new, Ordering::Relaxed);
            hasher.write_usize(new);
        }
        #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
        hasher.write_usize(&PI as *const _ as usize);
        let mix = |k: u64| {
            let mut h = hasher.clone();
            h.write_u64(k);
            h.finish()
        };

        RandomState { k0: mix(b[0]), k1: mix(b[1]), k2: mix(b[2]), k3: mix(b[3]) }
    }

    /// Internal. Used by Default.
    #[inline]
    pub(crate) fn with_fixed_keys() -> RandomState {
        let [k0, k1, k2, k3] = seeds();
        RandomState { k0, k1, k2, k3 }
    }

    /// Allows for explicitly setting the seeds to used.
    #[inline]
    pub const fn with_seeds(k0: u64, k1: u64, k2: u64, k3: u64) -> RandomState {
        RandomState { k0, k1, k2, k3 }
    }
}

impl Default for RandomState {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl BuildHasher for RandomState {
    type Hasher = AHasher;

    /// Constructs a new [AHasher] with keys based on this [RandomState] object.
    /// This means that two different [RandomState]s will will generate
    /// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher]
    /// will generate the same hashes for the same input data.
    ///
    /// # Examples
    ///
    /// ```
    /// use ahash::{AHasher, RandomState};
    /// use std::hash::{Hasher, BuildHasher};
    ///
    /// let build_hasher = RandomState::new();
    /// let mut hasher_1 = build_hasher.build_hasher();
    /// let mut hasher_2 = build_hasher.build_hasher();
    ///
    /// hasher_1.write_u32(1234);
    /// hasher_2.write_u32(1234);
    ///
    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
    ///
    /// let other_build_hasher = RandomState::new();
    /// let mut different_hasher = other_build_hasher.build_hasher();
    /// different_hasher.write_u32(1234);
    /// assert_ne!(different_hasher.finish(), hasher_1.finish());
    /// ```
    /// [Hasher]: std::hash::Hasher
    /// [BuildHasher]: std::hash::BuildHasher
    /// [HashMap]: std::collections::HashMap
    #[inline]
    fn build_hasher(&self) -> AHasher {
        AHasher::from_random_state(self)
    }
}

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

    #[test]
    fn test_unique() {
        let a = RandomState::new();
        let b = RandomState::new();
        assert_ne!(a.build_hasher().finish(), b.build_hasher().finish());
    }

    #[cfg(all(feature = "runtime-rng", not(all(feature = "compile-time-rng", test))))]
    #[test]
    fn test_not_pi() {
        assert_ne!(PI, seeds());
    }

    #[cfg(all(feature = "compile-time-rng", any(not(feature = "runtime-rng"), test)))]
    #[test]
    fn test_not_pi_const() {
        assert_ne!(PI, seeds());
    }

    #[cfg(all(not(feature = "runtime-rng"), not(feature = "compile-time-rng")))]
    #[test]
    fn test_pi() {
        assert_eq!(PI, seeds());
    }

    #[test]
    fn test_with_seeds_const() {
        const _CONST_RANDOM_STATE: RandomState = RandomState::with_seeds(17, 19, 21, 23);
    }
}
<|endoftext|>


<commit_before>extern crate rusty_machine as rm;
extern crate rand;

// https://github.com/AtheMathmo/rusty-machine/pull/101/files
use rm::linalg::{Matrix, BaseMatrix};
use rand::{Rng, ThreadRng};
use std::collections::HashMap;

fn generate_arm(content_id: &usize, arms: &HashMap<usize, Arm>, feature_dim: &usize) -> Arm {
    let arm: Arm;
    if !arms.contains_key(content_id) {
        arm = Arm::new(*feature_dim, *content_id, 0.0001, 0.0);
    }else{
        arm = arms.get(content_id).unwrap().clone();
    };
    arm
}

#[derive(Debug, Clone)]
struct Arm{
    content_id : usize,
    alpha : f64,
    norm_mean : Matrix<f64>,
    cov_matrix: Matrix<f64>,
    win_rate : f64,
    win : f64,
    lose : f64,
}

impl Arm {
    fn new(feature_dim: usize, content_id: usize, alpha: f64, win_rate: f64) -> Arm {
        Arm {
            content_id: content_id,
            norm_mean: Matrix::zeros(1, feature_dim),
            cov_matrix: Matrix::ones(1, feature_dim),
            alpha: alpha,
            win_rate: win_rate,
            win: 0.0,
            lose: 0.0,
        }
    }

    fn update(&mut self, features: Matrix<f64>, is_click: bool) {
        // error of diag
        self.cov_matrix += Matrix::new(self.cov_matrix.rows(),
                                        self.cov_matrix.cols(),
                                        (&features.transpose()*&features).diag());
        let cost_of_click = is_click as i8 as f64;

        let feat_mul_cost_vec = features.iter().map(|x| cost_of_click*x).collect::<Vec<f64>>();
        let feat_mul_cost = Matrix::new(features.rows(), features.cols(), feat_mul_cost_vec);
        self.norm_mean += feat_mul_cost;

        if is_click{
            self.win += 1.;
        } else{
            self.lose += 1.;
        }
        self.win_rate = &self.win/(&self.win + &self.lose);
    }

    fn predict(&mut self, features: &Matrix<f64>) -> f64 {
        let one_div_cov_vec = self.cov_matrix.iter().map(|x| 1.0/x).collect::<Vec<f64>>();
        let one_div_cov = Matrix::new(self.cov_matrix.rows(),
                                      self.cov_matrix.cols(), one_div_cov_vec);
        // Since the covariance matrix preserves only the diagonal components,
        // it suffices to take the inverse matrix
        let theta = &one_div_cov.elemul(&self.norm_mean);
        // Again, the inverse matrix of the covariance matrix
        // is computed by taking reciprocal
        let mut tmp: f64 = ((features.elemul(&one_div_cov))*features.transpose()).data()[0];
        tmp = &tmp.sqrt()*&self.alpha;
        (theta*features.transpose()).data()[0] + &tmp
    }

    fn print_result(&mut self) {
        println!("content_id:{}, total_num:{}, win_rate:{}",
                &self.content_id, &self.win+&self.lose, &self.win_rate);
    }
}

struct Viewer{
    gender: String,
    rng: ThreadRng,
}

impl Viewer {
    fn new(gender: String) -> Viewer{
        Viewer{
            gender: gender,
            rng: rand::thread_rng(),
        }
    }

    fn view(&mut self, content_id: &usize) -> bool{
        if &self.gender=="man" {
            // Men are easy to click on ads with id 5 or less
            if *content_id < 6 {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        } else {
            // Women are easy to click on ads with id 6 or higher
            if *content_id > 5{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        }
    }
}

struct Rulet{
    rng: ThreadRng,
}

impl Rulet{
    fn new() -> Rulet {
        Rulet{
            rng: rand::thread_rng(),
        }
    }

    fn generate_features(&mut self, viewer: &Viewer) -> Matrix<f64> {
        let features = Some(&viewer.gender)
            .and_then(|gender|
                if gender=="man" {Some(Matrix::new(1,2,vec![1.,0.]))}
                else {Some(Matrix::new(1,2,vec![0.,1.]))}
            ).unwrap();
        features
    }

    fn generate_content(&mut self) -> usize{
        self.rng.gen_range(0, 10)
    }

    fn generate_gender(&mut self) -> String {
        if self.rng.next_f32() > 0.5{
            return "man".to_string();
        } else{
            return "women".to_string();
        }
    }
}

fn main() {
    // コピーを作るのではなく、本体は一つでそれにアクセスできる変数を増やした感じ
    // 引数に渡した時も所有権の移動が起きる
    // 参照を得るときに&mutを使うことでmutableな参照を得ることができ、それを受け取った側は値を変更できる。mutableな参照を得られるのはmutableな変数に対してのみ。
    // 可変な参照を誰かが借用している間は、それが返却されるまで不変な参照を含め他の参照は一切取得できない
    /*Context is for men and women only
    Men are easy to click on ads with id 5 or less
    Women are easy to click on ads with id 6 or higher
    */
    let feature_dim = 2;
    let num_of_views = 10000;
    let mut rulet = Rulet::new();

    let mut content_id: usize;
    let mut features: Matrix<f64>;
    let mut is_clicked: bool;
    let mut arms: HashMap<usize, Arm> = HashMap::new();

    for step in 0..num_of_views {
        let mut viewer = Viewer::new(rulet.generate_gender());

        content_id = rulet.generate_content();
        features = rulet.generate_features(&viewer);
        is_clicked = viewer.view(&content_id);

        let mut arm = generate_arm(&content_id, &arms, &feature_dim);
        arm.update(features, is_clicked);
        arms.remove(&content_id);
        arms.insert(content_id, arm.clone());

        // Confirmation of prediction accuracy when the number of data is small
        if step<2000 {
            &arm.print_result();
        }
    }

    let man_mat: Matrix<f64> = Matrix::new(1,2, vec![1.,0.]);
    let woman_mat: Matrix<f64> = Matrix::new(1,2, vec![0.,1.]);

    println!("print result======");
    for (_, arm) in arms.iter() {
        let mut arm = arm.clone();  // impl needs mutable self
        &arm.print_result();
        println!("Click rate when men browse: {}", &arm.predict(&man_mat) );
        println!("Click rate when women browse: {}", &arm.predict(&woman_mat) );
    }

}
<commit_msg>remove comment<commit_after>extern crate rusty_machine as rm;
extern crate rand;

use rm::linalg::{Matrix, BaseMatrix};
use rand::{Rng, ThreadRng};
use std::collections::HashMap;

fn generate_arm(content_id: &usize, arms: &HashMap<usize, Arm>, feature_dim: &usize) -> Arm {
    let arm: Arm;
    if !arms.contains_key(content_id) {
        arm = Arm::new(*feature_dim, *content_id, 0.0001, 0.0);
    }else{
        arm = arms.get(content_id).unwrap().clone();
    };
    arm
}

#[derive(Debug, Clone)]
struct Arm{
    content_id : usize,
    alpha : f64,
    norm_mean : Matrix<f64>,
    cov_matrix: Matrix<f64>,
    win_rate : f64,
    win : f64,
    lose : f64,
}

impl Arm {
    fn new(feature_dim: usize, content_id: usize, alpha: f64, win_rate: f64) -> Arm {
        Arm {
            content_id: content_id,
            norm_mean: Matrix::zeros(1, feature_dim),
            cov_matrix: Matrix::ones(1, feature_dim),
            alpha: alpha,
            win_rate: win_rate,
            win: 0.0,
            lose: 0.0,
        }
    }

    fn update(&mut self, features: Matrix<f64>, is_click: bool) {
        // error of diag
        self.cov_matrix += Matrix::new(self.cov_matrix.rows(),
                                        self.cov_matrix.cols(),
                                        (&features.transpose()*&features).diag());
        let cost_of_click = is_click as i8 as f64;

        let feat_mul_cost_vec = features.iter().map(|x| cost_of_click*x).collect::<Vec<f64>>();
        let feat_mul_cost = Matrix::new(features.rows(), features.cols(), feat_mul_cost_vec);
        self.norm_mean += feat_mul_cost;

        if is_click{
            self.win += 1.;
        } else{
            self.lose += 1.;
        }
        self.win_rate = &self.win/(&self.win + &self.lose);
    }

    fn predict(&mut self, features: &Matrix<f64>) -> f64 {
        let one_div_cov_vec = self.cov_matrix.iter().map(|x| 1.0/x).collect::<Vec<f64>>();
        let one_div_cov = Matrix::new(self.cov_matrix.rows(),
                                      self.cov_matrix.cols(), one_div_cov_vec);
        // Since the covariance matrix preserves only the diagonal components,
        // it suffices to take the inverse matrix
        let theta = &one_div_cov.elemul(&self.norm_mean);
        // Again, the inverse matrix of the covariance matrix
        // is computed by taking reciprocal
        let mut tmp: f64 = ((features.elemul(&one_div_cov))*features.transpose()).data()[0];
        tmp = &tmp.sqrt()*&self.alpha;
        (theta*features.transpose()).data()[0] + &tmp
    }

    fn print_result(&mut self) {
        println!("content_id:{}, total_num:{}, win_rate:{}",
                &self.content_id, &self.win+&self.lose, &self.win_rate);
    }
}

struct Viewer{
    gender: String,
    rng: ThreadRng,
}

impl Viewer {
    fn new(gender: String) -> Viewer{
        Viewer{
            gender: gender,
            rng: rand::thread_rng(),
        }
    }

    fn view(&mut self, content_id: &usize) -> bool{
        if &self.gender=="man" {
            // Men are easy to click on ads with id 5 or less
            if *content_id < 6 {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        } else {
            // Women are easy to click on ads with id 6 or higher
            if *content_id > 5{
                return Some(self.rng.next_f32()).and_then(|n| if n>0.3 {Some(true)} else {Some(false)}).unwrap();
            } else {
                return Some(self.rng.next_f32()).and_then(|n| if n>0.7 {Some(true)} else {Some(false)}).unwrap();
            }
        }
    }
}

struct Rulet{
    rng: ThreadRng,
}

impl Rulet{
    fn new() -> Rulet {
        Rulet{
            rng: rand::thread_rng(),
        }
    }

    fn generate_features(&mut self, viewer: &Viewer) -> Matrix<f64> {
        let features = Some(&viewer.gender)
            .and_then(|gender|
                if gender=="man" {Some(Matrix::new(1,2,vec![1.,0.]))}
                else {Some(Matrix::new(1,2,vec![0.,1.]))}
            ).unwrap();
        features
    }

    fn generate_content(&mut self) -> usize{
        self.rng.gen_range(0, 10)
    }

    fn generate_gender(&mut self) -> String {
        if self.rng.next_f32() > 0.5{
            return "man".to_string();
        } else{
            return "women".to_string();
        }
    }
}

fn main() {
    /*Context is for men and women only
    Men are easy to click on ads with id 5 or less
    Women are easy to click on ads with id 6 or higher
    */
    let feature_dim = 2;
    let num_of_views = 10000;
    let mut rulet = Rulet::new();

    let mut content_id: usize;
    let mut features: Matrix<f64>;
    let mut is_clicked: bool;
    let mut arms: HashMap<usize, Arm> = HashMap::new();

    for step in 0..num_of_views {
        let mut viewer = Viewer::new(rulet.generate_gender());

        content_id = rulet.generate_content();
        features = rulet.generate_features(&viewer);
        is_clicked = viewer.view(&content_id);

        let mut arm = generate_arm(&content_id, &arms, &feature_dim);
        arm.update(features, is_clicked);
        arms.remove(&content_id);
        arms.insert(content_id, arm.clone());
    }

    let man_mat: Matrix<f64> = Matrix::new(1,2, vec![1.,0.]);
    let woman_mat: Matrix<f64> = Matrix::new(1,2, vec![0.,1.]);

    println!("print result======");
    for (_, arm) in arms.iter() {
        let mut arm = arm.clone();  // impl needs mutable self
        &arm.print_result();
        println!("Click rate when men browse: {}", &arm.predict(&man_mat) );
        println!("Click rate when women browse: {}", &arm.predict(&woman_mat) );
    }

}
<|endoftext|>


<commit_before>use std::str::raw::c_str_to_static_slice;

enum Selector { }
pub enum Object { }

pub trait Message { }

impl Message for Object { }

pub struct Sel {
	ptr: *mut Selector,
}

pub struct Class {
	ptr: *mut Object,
}

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const i8) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const i8;

	pub fn objc_getClass(name: *const i8) -> Class;
	pub fn class_getName(cls: Class) -> *const i8;
	pub fn object_getClass(obj: *mut Object) -> Class;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel {
		Sel { ptr: self.ptr }
	}
}

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}

impl Class {
	pub fn get(name: &str) -> Class {
		name.with_c_str(|name| unsafe {
			objc_getClass(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl ToMessage for Class {
	fn as_ptr(&self) -> *mut Object {
		self.ptr
	}
}
<commit_msg>Added Imp definition and class_addMethod function.<commit_after>use std::str::raw::c_str_to_static_slice;

enum Selector { }
pub enum Object { }

pub trait Message { }

impl Message for Object { }

pub struct Sel {
	ptr: *mut Selector,
}

pub struct Class {
	ptr: *mut Object,
}

pub type Imp = extern fn(*mut Object, Sel, ...);

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const i8) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const i8;

	pub fn objc_getClass(name: *const i8) -> Class;
	pub fn class_getName(cls: Class) -> *const i8;
	pub fn class_addMethod(cls: Class, name: Sel, imp: Imp, types: *const i8) -> bool;
	pub fn object_getClass(obj: *mut Object) -> Class;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel {
		Sel { ptr: self.ptr }
	}
}

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}

impl Class {
	pub fn get(name: &str) -> Class {
		name.with_c_str(|name| unsafe {
			objc_getClass(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl ToMessage for Class {
	fn as_ptr(&self) -> *mut Object {
		self.ptr
	}
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;
use std::fmt;
use std::io;
use std::io::Write;

use diff;

use crate::config::{Color, Config, Verbosity};

#[derive(Debug, PartialEq)]
pub enum DiffLine {
    Context(String),
    Expected(String),
    Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
    /// The line number in the formatted version.
    pub line_number: u32,
    /// The line number in the original version.
    pub line_number_orig: u32,
    /// The set of lines (context and old/new) in the mismatch.
    pub lines: Vec<DiffLine>,
}

impl Mismatch {
    fn new(line_number: u32, line_number_orig: u32) -> Mismatch {
        Mismatch {
            line_number,
            line_number_orig,
            lines: Vec::new(),
        }
    }
}

/// A single span of changed lines, with 0 or more removed lines
/// and a vector of 0 or more inserted lines.
#[derive(Debug, PartialEq, Eq)]
pub struct ModifiedChunk {
    /// The first to be removed from the original text
    pub line_number_orig: u32,
    /// The number of lines which have been replaced
    pub lines_removed: u32,
    /// The new lines
    pub lines: Vec<String>,
}

/// Set of changed sections of a file.
#[derive(Debug, PartialEq, Eq)]
pub struct ModifiedLines {
    /// The set of changed chunks.
    pub chunks: Vec<ModifiedChunk>,
}

impl From<Vec<Mismatch>> for ModifiedLines {
    fn from(mismatches: Vec<Mismatch>) -> ModifiedLines {
        let chunks = mismatches.into_iter().map(|mismatch| {
            let lines = || mismatch.lines.iter();
            let num_removed = lines()
                .filter(|line| match line {
                    DiffLine::Resulting(_) => true,
                    _ => false,
                })
                .count();

            let new_lines = mismatch.lines.into_iter().filter_map(|line| match line {
                DiffLine::Context(_) | DiffLine::Resulting(_) => None,
                DiffLine::Expected(str) => Some(str),
            });

            ModifiedChunk {
                line_number_orig: mismatch.line_number_orig,
                lines_removed: num_removed as u32,
                lines: new_lines.collect(),
            }
        });

        ModifiedLines {
            chunks: chunks.collect(),
        }
    }
}

// Converts a `Mismatch` into a serialized form, which just includes
// enough information to modify the original file.
// Each section starts with a line with three integers, space separated:
//     lineno num_removed num_added
// followed by (`num_added`) lines of added text. The line numbers are
// relative to the original file.
impl fmt::Display for ModifiedLines {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for chunk in &self.chunks {
            writeln!(
                f,
                "{} {} {}",
                chunk.line_number_orig,
                chunk.lines_removed,
                chunk.lines.iter().count()
            )?;

            for line in &chunk.lines {
                writeln!(f, "{}", line)?;
            }
        }

        Ok(())
    }
}

// Allows to convert `Display`ed `ModifiedLines` back to the structural data.
impl std::str::FromStr for ModifiedLines {
    type Err = ();

    fn from_str(s: &str) -> Result<ModifiedLines, ()> {
        let mut chunks = vec![];

        let mut lines = s.lines();
        while let Some(header) = lines.next() {
            let mut header = header.split_whitespace();
            let (orig, rem, new_lines) = match (header.next(), header.next(), header.next()) {
                (Some(orig), Some(removed), Some(added)) => (orig, removed, added),
                _ => return Err(()),
            };
            eprintln!("{} {} {}", orig, rem, new_lines);
            let (orig, rem, new_lines): (u32, u32, usize) =
                match (orig.parse(), rem.parse(), new_lines.parse()) {
                    (Ok(a), Ok(b), Ok(c)) => (a, b, c),
                    _ => return Err(()),
                };
            eprintln!("{} {} {}", orig, rem, new_lines);
            let lines = lines.by_ref().take(new_lines);
            let lines: Vec<_> = lines.map(ToOwned::to_owned).collect();
            if lines.len() != new_lines {
                return Err(());
            }

            chunks.push(ModifiedChunk {
                line_number_orig: orig,
                lines_removed: rem,
                lines,
            });
        }

        Ok(ModifiedLines { chunks })
    }
}

// This struct handles writing output to stdout and abstracts away the logic
// of printing in color, if it's possible in the executing environment.
pub struct OutputWriter {
    terminal: Option<Box<dyn term::Terminal<Output = io::Stdout>>>,
}

impl OutputWriter {
    // Create a new OutputWriter instance based on the caller's preference
    // for colorized output and the capabilities of the terminal.
    pub fn new(color: Color) -> Self {
        if let Some(t) = term::stdout() {
            if color.use_colored_tty() && t.supports_color() {
                return OutputWriter { terminal: Some(t) };
            }
        }
        OutputWriter { terminal: None }
    }

    // Write output in the optionally specified color. The output is written
    // in the specified color if this OutputWriter instance contains a
    // Terminal in its `terminal` field.
    pub fn writeln(&mut self, msg: &str, color: Option<term::color::Color>) {
        match &mut self.terminal {
            Some(ref mut t) => {
                if let Some(color) = color {
                    t.fg(color).unwrap();
                }
                writeln!(t, "{}", msg).unwrap();
                if color.is_some() {
                    t.reset().unwrap();
                }
            }
            None => println!("{}", msg),
        }
    }
}

// Produces a diff between the expected output and actual output of rustfmt.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
    let mut line_number = 1;
    let mut line_number_orig = 1;
    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
    let mut lines_since_mismatch = context_size + 1;
    let mut results = Vec::new();
    let mut mismatch = Mismatch::new(0, 0);

    for result in diff::lines(expected, actual) {
        match result {
            diff::Result::Left(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(
                        line_number - context_queue.len() as u32,
                        line_number_orig - context_queue.len() as u32,
                    );
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
                line_number_orig += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Right(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(
                        line_number - context_queue.len() as u32,
                        line_number_orig - context_queue.len() as u32,
                    );
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
                line_number += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Both(str, _) => {
                if context_queue.len() >= context_size {
                    let _ = context_queue.pop_front();
                }

                if lines_since_mismatch < context_size {
                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
                } else if context_size > 0 {
                    context_queue.push_back(str);
                }

                line_number += 1;
                line_number_orig += 1;
                lines_since_mismatch += 1;
            }
        }
    }

    results.push(mismatch);
    results.remove(0);

    results
}

pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F, config: &Config)
where
    F: Fn(u32) -> String,
{
    let color = config.color();
    let line_terminator = if config.verbose() == Verbosity::Verbose {
        "⏎"
    } else {
        ""
    };

    let mut writer = OutputWriter::new(color);

    for mismatch in diff {
        let title = get_section_title(mismatch.line_number_orig);
        writer.writeln(&title, None);

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    writer.writeln(&format!(" {}{}", str, line_terminator), None)
                }
                DiffLine::Expected(ref str) => writer.writeln(
                    &format!("+{}{}", str, line_terminator),
                    Some(term::color::GREEN),
                ),
                DiffLine::Resulting(ref str) => writer.writeln(
                    &format!("-{}{}", str, line_terminator),
                    Some(term::color::RED),
                ),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::DiffLine::*;
    use super::{make_diff, Mismatch};
    use super::{ModifiedChunk, ModifiedLines};

    #[test]
    fn diff_simple() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest = "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![Mismatch {
                line_number: 2,
                line_number_orig: 2,
                lines: vec![
                    Context("two".to_owned()),
                    Resulting("three".to_owned()),
                    Expected("trois".to_owned()),
                    Context("four".to_owned()),
                ],
            }]
        );
    }

    #[test]
    fn diff_simple2() {
        let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
        let dest = "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![
                Mismatch {
                    line_number: 2,
                    line_number_orig: 2,
                    lines: vec![
                        Context("two".to_owned()),
                        Resulting("three".to_owned()),
                        Expected("trois".to_owned()),
                        Context("four".to_owned()),
                    ],
                },
                Mismatch {
                    line_number: 5,
                    line_number_orig: 5,
                    lines: vec![
                        Resulting("five".to_owned()),
                        Expected("cinq".to_owned()),
                        Context("six".to_owned()),
                    ],
                },
            ]
        );
    }

    #[test]
    fn diff_zerocontext() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest = "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 0);
        assert_eq!(
            diff,
            vec![Mismatch {
                line_number: 3,
                line_number_orig: 3,
                lines: vec![Resulting("three".to_owned()), Expected("trois".to_owned())],
            }]
        );
    }

    #[test]
    fn diff_trailing_newline() {
        let src = "one\ntwo\nthree\nfour\nfive";
        let dest = "one\ntwo\nthree\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![Mismatch {
                line_number: 5,
                line_number_orig: 5,
                lines: vec![Context("five".to_owned()), Expected("".to_owned())],
            }]
        );
    }

    #[test]
    fn modified_lines_from_str() {
        use std::str::FromStr;

        let src = "1 6 2\nfn some() {}\nfn main() {}\n25 3 1\n  struct Test {}";
        let lines = ModifiedLines::from_str(src).unwrap();
        assert_eq!(
            lines,
            ModifiedLines {
                chunks: vec![
                    ModifiedChunk {
                        line_number_orig: 1,
                        lines_removed: 6,
                        lines: vec!["fn some() {}".to_owned(), "fn main() {}".to_owned(),]
                    },
                    ModifiedChunk {
                        line_number_orig: 25,
                        lines_removed: 3,
                        lines: vec!["  struct Test {}".to_owned()]
                    }
                ]
            }
        );

        let src = "1 5 3";
        assert_eq!(ModifiedLines::from_str(src), Err(()));

        let src = "1 5 3\na\nb";
        assert_eq!(ModifiedLines::from_str(src), Err(()));
    }
}
<commit_msg>Remove eprintln<commit_after>use std::collections::VecDeque;
use std::fmt;
use std::io;
use std::io::Write;

use diff;

use crate::config::{Color, Config, Verbosity};

#[derive(Debug, PartialEq)]
pub enum DiffLine {
    Context(String),
    Expected(String),
    Resulting(String),
}

#[derive(Debug, PartialEq)]
pub struct Mismatch {
    /// The line number in the formatted version.
    pub line_number: u32,
    /// The line number in the original version.
    pub line_number_orig: u32,
    /// The set of lines (context and old/new) in the mismatch.
    pub lines: Vec<DiffLine>,
}

impl Mismatch {
    fn new(line_number: u32, line_number_orig: u32) -> Mismatch {
        Mismatch {
            line_number,
            line_number_orig,
            lines: Vec::new(),
        }
    }
}

/// A single span of changed lines, with 0 or more removed lines
/// and a vector of 0 or more inserted lines.
#[derive(Debug, PartialEq, Eq)]
pub struct ModifiedChunk {
    /// The first to be removed from the original text
    pub line_number_orig: u32,
    /// The number of lines which have been replaced
    pub lines_removed: u32,
    /// The new lines
    pub lines: Vec<String>,
}

/// Set of changed sections of a file.
#[derive(Debug, PartialEq, Eq)]
pub struct ModifiedLines {
    /// The set of changed chunks.
    pub chunks: Vec<ModifiedChunk>,
}

impl From<Vec<Mismatch>> for ModifiedLines {
    fn from(mismatches: Vec<Mismatch>) -> ModifiedLines {
        let chunks = mismatches.into_iter().map(|mismatch| {
            let lines = || mismatch.lines.iter();
            let num_removed = lines()
                .filter(|line| match line {
                    DiffLine::Resulting(_) => true,
                    _ => false,
                })
                .count();

            let new_lines = mismatch.lines.into_iter().filter_map(|line| match line {
                DiffLine::Context(_) | DiffLine::Resulting(_) => None,
                DiffLine::Expected(str) => Some(str),
            });

            ModifiedChunk {
                line_number_orig: mismatch.line_number_orig,
                lines_removed: num_removed as u32,
                lines: new_lines.collect(),
            }
        });

        ModifiedLines {
            chunks: chunks.collect(),
        }
    }
}

// Converts a `Mismatch` into a serialized form, which just includes
// enough information to modify the original file.
// Each section starts with a line with three integers, space separated:
//     lineno num_removed num_added
// followed by (`num_added`) lines of added text. The line numbers are
// relative to the original file.
impl fmt::Display for ModifiedLines {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for chunk in &self.chunks {
            writeln!(
                f,
                "{} {} {}",
                chunk.line_number_orig,
                chunk.lines_removed,
                chunk.lines.iter().count()
            )?;

            for line in &chunk.lines {
                writeln!(f, "{}", line)?;
            }
        }

        Ok(())
    }
}

// Allows to convert `Display`ed `ModifiedLines` back to the structural data.
impl std::str::FromStr for ModifiedLines {
    type Err = ();

    fn from_str(s: &str) -> Result<ModifiedLines, ()> {
        let mut chunks = vec![];

        let mut lines = s.lines();
        while let Some(header) = lines.next() {
            let mut header = header.split_whitespace();
            let (orig, rem, new_lines) = match (header.next(), header.next(), header.next()) {
                (Some(orig), Some(removed), Some(added)) => (orig, removed, added),
                _ => return Err(()),
            };
            let (orig, rem, new_lines): (u32, u32, usize) =
                match (orig.parse(), rem.parse(), new_lines.parse()) {
                    (Ok(a), Ok(b), Ok(c)) => (a, b, c),
                    _ => return Err(()),
                };
            let lines = lines.by_ref().take(new_lines);
            let lines: Vec<_> = lines.map(ToOwned::to_owned).collect();
            if lines.len() != new_lines {
                return Err(());
            }

            chunks.push(ModifiedChunk {
                line_number_orig: orig,
                lines_removed: rem,
                lines,
            });
        }

        Ok(ModifiedLines { chunks })
    }
}

// This struct handles writing output to stdout and abstracts away the logic
// of printing in color, if it's possible in the executing environment.
pub struct OutputWriter {
    terminal: Option<Box<dyn term::Terminal<Output = io::Stdout>>>,
}

impl OutputWriter {
    // Create a new OutputWriter instance based on the caller's preference
    // for colorized output and the capabilities of the terminal.
    pub fn new(color: Color) -> Self {
        if let Some(t) = term::stdout() {
            if color.use_colored_tty() && t.supports_color() {
                return OutputWriter { terminal: Some(t) };
            }
        }
        OutputWriter { terminal: None }
    }

    // Write output in the optionally specified color. The output is written
    // in the specified color if this OutputWriter instance contains a
    // Terminal in its `terminal` field.
    pub fn writeln(&mut self, msg: &str, color: Option<term::color::Color>) {
        match &mut self.terminal {
            Some(ref mut t) => {
                if let Some(color) = color {
                    t.fg(color).unwrap();
                }
                writeln!(t, "{}", msg).unwrap();
                if color.is_some() {
                    t.reset().unwrap();
                }
            }
            None => println!("{}", msg),
        }
    }
}

// Produces a diff between the expected output and actual output of rustfmt.
pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Mismatch> {
    let mut line_number = 1;
    let mut line_number_orig = 1;
    let mut context_queue: VecDeque<&str> = VecDeque::with_capacity(context_size);
    let mut lines_since_mismatch = context_size + 1;
    let mut results = Vec::new();
    let mut mismatch = Mismatch::new(0, 0);

    for result in diff::lines(expected, actual) {
        match result {
            diff::Result::Left(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(
                        line_number - context_queue.len() as u32,
                        line_number_orig - context_queue.len() as u32,
                    );
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Resulting(str.to_owned()));
                line_number_orig += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Right(str) => {
                if lines_since_mismatch >= context_size && lines_since_mismatch > 0 {
                    results.push(mismatch);
                    mismatch = Mismatch::new(
                        line_number - context_queue.len() as u32,
                        line_number_orig - context_queue.len() as u32,
                    );
                }

                while let Some(line) = context_queue.pop_front() {
                    mismatch.lines.push(DiffLine::Context(line.to_owned()));
                }

                mismatch.lines.push(DiffLine::Expected(str.to_owned()));
                line_number += 1;
                lines_since_mismatch = 0;
            }
            diff::Result::Both(str, _) => {
                if context_queue.len() >= context_size {
                    let _ = context_queue.pop_front();
                }

                if lines_since_mismatch < context_size {
                    mismatch.lines.push(DiffLine::Context(str.to_owned()));
                } else if context_size > 0 {
                    context_queue.push_back(str);
                }

                line_number += 1;
                line_number_orig += 1;
                lines_since_mismatch += 1;
            }
        }
    }

    results.push(mismatch);
    results.remove(0);

    results
}

pub fn print_diff<F>(diff: Vec<Mismatch>, get_section_title: F, config: &Config)
where
    F: Fn(u32) -> String,
{
    let color = config.color();
    let line_terminator = if config.verbose() == Verbosity::Verbose {
        "⏎"
    } else {
        ""
    };

    let mut writer = OutputWriter::new(color);

    for mismatch in diff {
        let title = get_section_title(mismatch.line_number_orig);
        writer.writeln(&title, None);

        for line in mismatch.lines {
            match line {
                DiffLine::Context(ref str) => {
                    writer.writeln(&format!(" {}{}", str, line_terminator), None)
                }
                DiffLine::Expected(ref str) => writer.writeln(
                    &format!("+{}{}", str, line_terminator),
                    Some(term::color::GREEN),
                ),
                DiffLine::Resulting(ref str) => writer.writeln(
                    &format!("-{}{}", str, line_terminator),
                    Some(term::color::RED),
                ),
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::DiffLine::*;
    use super::{make_diff, Mismatch};
    use super::{ModifiedChunk, ModifiedLines};

    #[test]
    fn diff_simple() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest = "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![Mismatch {
                line_number: 2,
                line_number_orig: 2,
                lines: vec![
                    Context("two".to_owned()),
                    Resulting("three".to_owned()),
                    Expected("trois".to_owned()),
                    Context("four".to_owned()),
                ],
            }]
        );
    }

    #[test]
    fn diff_simple2() {
        let src = "one\ntwo\nthree\nfour\nfive\nsix\nseven\n";
        let dest = "one\ntwo\ntrois\nfour\ncinq\nsix\nseven\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![
                Mismatch {
                    line_number: 2,
                    line_number_orig: 2,
                    lines: vec![
                        Context("two".to_owned()),
                        Resulting("three".to_owned()),
                        Expected("trois".to_owned()),
                        Context("four".to_owned()),
                    ],
                },
                Mismatch {
                    line_number: 5,
                    line_number_orig: 5,
                    lines: vec![
                        Resulting("five".to_owned()),
                        Expected("cinq".to_owned()),
                        Context("six".to_owned()),
                    ],
                },
            ]
        );
    }

    #[test]
    fn diff_zerocontext() {
        let src = "one\ntwo\nthree\nfour\nfive\n";
        let dest = "one\ntwo\ntrois\nfour\nfive\n";
        let diff = make_diff(src, dest, 0);
        assert_eq!(
            diff,
            vec![Mismatch {
                line_number: 3,
                line_number_orig: 3,
                lines: vec![Resulting("three".to_owned()), Expected("trois".to_owned())],
            }]
        );
    }

    #[test]
    fn diff_trailing_newline() {
        let src = "one\ntwo\nthree\nfour\nfive";
        let dest = "one\ntwo\nthree\nfour\nfive\n";
        let diff = make_diff(src, dest, 1);
        assert_eq!(
            diff,
            vec![Mismatch {
                line_number: 5,
                line_number_orig: 5,
                lines: vec![Context("five".to_owned()), Expected("".to_owned())],
            }]
        );
    }

    #[test]
    fn modified_lines_from_str() {
        use std::str::FromStr;

        let src = "1 6 2\nfn some() {}\nfn main() {}\n25 3 1\n  struct Test {}";
        let lines = ModifiedLines::from_str(src).unwrap();
        assert_eq!(
            lines,
            ModifiedLines {
                chunks: vec![
                    ModifiedChunk {
                        line_number_orig: 1,
                        lines_removed: 6,
                        lines: vec!["fn some() {}".to_owned(), "fn main() {}".to_owned(),]
                    },
                    ModifiedChunk {
                        line_number_orig: 25,
                        lines_removed: 3,
                        lines: vec!["  struct Test {}".to_owned()]
                    }
                ]
            }
        );

        let src = "1 5 3";
        assert_eq!(ModifiedLines::from_str(src), Err(()));

        let src = "1 5 3\na\nb";
        assert_eq!(ModifiedLines::from_str(src), Err(()));
    }
}
<|endoftext|>


<commit_before>fn main() {
    if let Ok(custom) = ::std::env::var("CUSTOM_LIBFUZZER_PATH") {
        let custom_lib_path = ::std::path::PathBuf::from(&custom);
        let custom_lib_dir = custom_lib_path.parent().unwrap().to_string_lossy();

        let custom_lib_name = custom_lib_path.file_stem().unwrap().to_string_lossy();
        let custom_lib_name = custom_lib_name.trim_start_matches("lib");

        println!("cargo:rustc-link-search=native={}", custom_lib_dir);
        println!("cargo:rustc-link-lib=static={}", custom_lib_name);

        // Allow user to specify use of LLVM or GCC C++ stdlib, or none.
        // "cargo:rustc-link-lib=stdc++" is used by default for
        // backward-compatibility.
        // Note: google/oss-fuzz uses LLVM C++ stdlib as of 15 Jan 2020
        if let Ok(std_cxx_flag) = ::std::env::var("CUSTOM_LIBFUZZER_STD_CXX") {
            match std_cxx_flag.as_str() {
                "c++" => println!("cargo:rustc-link-lib=c++"),
                "stdc++" => println!("cargo:rustc-link-lib=stdc++"),
                "none" => (),
                _ => println!("cargo:rustc-link-lib=stdc++"),
            }
        }
    } else {
        let mut build = cc::Build::new();
        let sources = ::std::fs::read_dir("libfuzzer")
            .expect("listable source directory")
            .map(|de| de.expect("file in directory").path())
            .filter(|p| p.extension().map(|ext| ext == "cpp") == Some(true))
            .collect::<Vec<_>>();
        for source in sources.iter() {
            println!("cargo:rerun-if-changed={}", source.display());
            build.file(source.to_str().unwrap());
        }
        build.flag("-std=c++11");
        build.flag("-fno-omit-frame-pointer");
        build.flag("-w");
        build.cpp(true);
        build.compile("libfuzzer.a");
    }
}
<commit_msg>Fix default for c++ std lib env var<commit_after>fn main() {
    if let Ok(custom) = ::std::env::var("CUSTOM_LIBFUZZER_PATH") {
        let custom_lib_path = ::std::path::PathBuf::from(&custom);
        let custom_lib_dir = custom_lib_path.parent().unwrap().to_string_lossy();

        let custom_lib_name = custom_lib_path.file_stem().unwrap().to_string_lossy();
        let custom_lib_name = custom_lib_name.trim_start_matches("lib");

        println!("cargo:rustc-link-search=native={}", custom_lib_dir);
        println!("cargo:rustc-link-lib=static={}", custom_lib_name);

        match std::env::var("CUSTOM_LIBFUZZER_STD_CXX") {
            // Default behavior for backwards compat.
            Err(_) => println!("cargo:rustc-link-lib=stdc++"),
            Ok(s) if s == "none" => (),
            Ok(s) => println!("cargo:rustc-link-lib={}", s),
        }
    } else {
        let mut build = cc::Build::new();
        let sources = ::std::fs::read_dir("libfuzzer")
            .expect("listable source directory")
            .map(|de| de.expect("file in directory").path())
            .filter(|p| p.extension().map(|ext| ext == "cpp") == Some(true))
            .collect::<Vec<_>>();
        for source in sources.iter() {
            println!("cargo:rerun-if-changed={}", source.display());
            build.file(source.to_str().unwrap());
        }
        build.flag("-std=c++11");
        build.flag("-fno-omit-frame-pointer");
        build.flag("-w");
        build.cpp(true);
        build.compile("libfuzzer.a");
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;
use std::str;

fn main() {
    /*
     * If `core::ffi::c_void` exists, libc can just re-export it. Otherwise, it
     * must define an incompatible type to retain backwards-compatibility.
     */
    if rustc_minor_version().expect("Failed to get rustc version") >= 31 {
        println!("cargo:rustc-cfg=core_cvoid");
    }
}

fn rustc_minor_version() -> Option<u32> {
    macro_rules! otry {
        ($e:expr) => {
            match $e {
                Some(e) => e,
                None => return None,
            }
        };
    }

    let rustc = otry!(env::var_os("RUSTC"));
    let output = otry!(Command::new(rustc).arg("--version").output().ok());
    let version = otry!(str::from_utf8(&output.stdout).ok());
    let mut pieces = version.split('.');

    if pieces.next() != Some("rustc 1") {
        return None;
    }

    otry!(pieces.next()).parse().ok()
}
<commit_msg>Auto merge of #1132 - glandium:master, r=alexcrichton<commit_after>use std::env;
use std::process::Command;
use std::str;

fn main() {
    /*
     * If `core::ffi::c_void` exists, libc can just re-export it. Otherwise, it
     * must define an incompatible type to retain backwards-compatibility.
     */
    if rustc_minor_version().expect("Failed to get rustc version") >= 30 {
        println!("cargo:rustc-cfg=core_cvoid");
    }
}

fn rustc_minor_version() -> Option<u32> {
    macro_rules! otry {
        ($e:expr) => {
            match $e {
                Some(e) => e,
                None => return None,
            }
        };
    }

    let rustc = otry!(env::var_os("RUSTC"));
    let output = otry!(Command::new(rustc).arg("--version").output().ok());
    let version = otry!(str::from_utf8(&output.stdout).ok());
    let mut pieces = version.split('.');

    if pieces.next() != Some("rustc 1") {
        return None;
    }

    otry!(pieces.next()).parse().ok()
}
<|endoftext|>


<commit_before>//! An implementation of `loader::Handle`, based on `walkdir` and `notify`.
//!
//! The file watching bits here are untested and quite probably buggy. For this
//! reason, by default we don't watch files and rely on editor's file watching
//! capabilities.
//!
//! Hopefully, one day a reliable file watching/walking crate appears on
//! crates.io, and we can reduce this to trivial glue code.
mod include;

use std::convert::{TryFrom, TryInto};

use crossbeam_channel::{select, unbounded, Receiver, Sender};
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashSet;
use vfs::loader;
use walkdir::WalkDir;

use crate::include::Include;

#[derive(Debug)]
pub struct NotifyHandle {
    // Relative order of fields below is significant.
    sender: Sender<Message>,
    thread: jod_thread::JoinHandle,
}

#[derive(Debug)]
enum Message {
    Config(loader::Config),
    Invalidate(AbsPathBuf),
}

impl loader::Handle for NotifyHandle {
    fn spawn(sender: loader::Sender) -> NotifyHandle {
        let actor = NotifyActor::new(sender);
        let (sender, receiver) = unbounded::<Message>();
        let thread = jod_thread::spawn(move || actor.run(receiver));
        NotifyHandle { sender, thread }
    }
    fn set_config(&mut self, config: loader::Config) {
        self.sender.send(Message::Config(config)).unwrap()
    }
    fn invalidate(&mut self, path: AbsPathBuf) {
        self.sender.send(Message::Invalidate(path)).unwrap();
    }
    fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
        read(path)
    }
}

type NotifyEvent = notify::Result<notify::Event>;

struct NotifyActor {
    sender: loader::Sender,
    config: Vec<(AbsPathBuf, Include, bool)>,
    watched_paths: FxHashSet<AbsPathBuf>,
    // Drop order of fields bellow is significant,
    watcher: Option<RecommendedWatcher>,
    watcher_receiver: Receiver<NotifyEvent>,
}

#[derive(Debug)]
enum Event {
    Message(Message),
    NotifyEvent(NotifyEvent),
}

impl NotifyActor {
    fn new(sender: loader::Sender) -> NotifyActor {
        let (watcher_sender, watcher_receiver) = unbounded();
        let watcher = log_notify_error(Watcher::new_immediate(move |event| {
            watcher_sender.send(event).unwrap()
        }));

        NotifyActor {
            sender,
            config: Vec::new(),
            watched_paths: FxHashSet::default(),
            watcher,
            watcher_receiver,
        }
    }
    fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
        select! {
            recv(receiver) -> it => it.ok().map(Event::Message),
            recv(&self.watcher_receiver) -> it => Some(Event::NotifyEvent(it.unwrap())),
        }
    }
    fn run(mut self, inbox: Receiver<Message>) {
        while let Some(event) = self.next_event(&inbox) {
            log::debug!("vfs-notify event: {:?}", event);
            match event {
                Event::Message(msg) => match msg {
                    Message::Config(config) => {
                        let n_total = config.load.len();
                        self.send(loader::Message::Progress { n_total, n_done: 0 });

                        self.unwatch_all();
                        self.config.clear();

                        for (i, entry) in config.load.into_iter().enumerate() {
                            let watch = config.watch.contains(&i);
                            let files = self.load_entry(entry, watch);
                            self.send(loader::Message::Loaded { files });
                            self.send(loader::Message::Progress { n_total, n_done: i + 1 });
                        }
                        self.config.sort_by(|x, y| x.0.cmp(&y.0));
                    }
                    Message::Invalidate(path) => {
                        let contents = read(path.as_path());
                        let files = vec![(path, contents)];
                        self.send(loader::Message::Loaded { files });
                    }
                },
                Event::NotifyEvent(event) => {
                    if let Some(event) = log_notify_error(event) {
                        let files = event
                            .paths
                            .into_iter()
                            .map(|path| AbsPathBuf::try_from(path).unwrap())
                            .filter_map(|path| {
                                let is_dir = path.is_dir();
                                let is_file = path.is_file();

                                let config_idx =
                                    match self.config.binary_search_by(|it| it.0.cmp(&path)) {
                                        Ok(it) => it,
                                        Err(it) => it.saturating_sub(1),
                                    };
                                let include = self.config.get(config_idx).and_then(|it| {
                                    let rel_path = path.strip_prefix(&it.0)?;
                                    Some((rel_path, &it.1))
                                });

                                if let Some((rel_path, include)) = include {
                                    if is_dir && include.exclude_dir(&rel_path)
                                        || is_file && !include.include_file(&rel_path)
                                    {
                                        return None;
                                    }
                                }

                                if is_dir {
                                    self.watch(path);
                                    return None;
                                }
                                if !is_file {
                                    return None;
                                }
                                let contents = read(&path);
                                Some((path, contents))
                            })
                            .collect();
                        self.send(loader::Message::Loaded { files })
                    }
                }
            }
        }
    }
    fn load_entry(
        &mut self,
        entry: loader::Entry,
        watch: bool,
    ) -> Vec<(AbsPathBuf, Option<Vec<u8>>)> {
        match entry {
            loader::Entry::Files(files) => files
                .into_iter()
                .map(|file| {
                    if watch {
                        self.watch(file.clone())
                    }
                    let contents = read(file.as_path());
                    (file, contents)
                })
                .collect::<Vec<_>>(),
            loader::Entry::Directory { path, include } => {
                let include = Include::new(include);
                self.config.push((path.clone(), include.clone(), watch));

                let files = WalkDir::new(&path)
                    .into_iter()
                    .filter_entry(|entry| {
                        let abs_path: &AbsPath = entry.path().try_into().unwrap();
                        match abs_path.strip_prefix(&path) {
                            Some(rel_path) => {
                                !(entry.file_type().is_dir() && include.exclude_dir(rel_path))
                            }
                            None => false,
                        }
                    })
                    .filter_map(|entry| entry.ok())
                    .filter_map(|entry| {
                        let is_dir = entry.file_type().is_dir();
                        let is_file = entry.file_type().is_file();
                        let abs_path = AbsPathBuf::try_from(entry.into_path()).unwrap();
                        if is_dir && watch {
                            self.watch(abs_path.clone());
                        }
                        let rel_path = abs_path.strip_prefix(&path)?;
                        if is_file && include.include_file(&rel_path) {
                            Some(abs_path)
                        } else {
                            None
                        }
                    });

                files
                    .map(|file| {
                        let contents = read(file.as_path());
                        (file, contents)
                    })
                    .collect()
            }
        }
    }

    fn watch(&mut self, path: AbsPathBuf) {
        if let Some(watcher) = &mut self.watcher {
            log_notify_error(watcher.watch(&path, RecursiveMode::NonRecursive));
            self.watched_paths.insert(path);
        }
    }
    fn unwatch_all(&mut self) {
        if let Some(watcher) = &mut self.watcher {
            for path in self.watched_paths.drain() {
                log_notify_error(watcher.unwatch(path));
            }
        }
    }
    fn send(&mut self, msg: loader::Message) {
        (self.sender)(msg)
    }
}

fn read(path: &AbsPath) -> Option<Vec<u8>> {
    std::fs::read(path).ok()
}

fn log_notify_error<T>(res: notify::Result<T>) -> Option<T> {
    res.map_err(|err| log::warn!("notify error: {}", err)).ok()
}
<commit_msg>Simplify file watcher<commit_after>//! An implementation of `loader::Handle`, based on `walkdir` and `notify`.
//!
//! The file watching bits here are untested and quite probably buggy. For this
//! reason, by default we don't watch files and rely on editor's file watching
//! capabilities.
//!
//! Hopefully, one day a reliable file watching/walking crate appears on
//! crates.io, and we can reduce this to trivial glue code.
mod include;

use std::convert::{TryFrom, TryInto};

use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use paths::{AbsPath, AbsPathBuf};
use vfs::loader;
use walkdir::WalkDir;

use crate::include::Include;

#[derive(Debug)]
pub struct NotifyHandle {
    // Relative order of fields below is significant.
    sender: Sender<Message>,
    thread: jod_thread::JoinHandle,
}

#[derive(Debug)]
enum Message {
    Config(loader::Config),
    Invalidate(AbsPathBuf),
}

impl loader::Handle for NotifyHandle {
    fn spawn(sender: loader::Sender) -> NotifyHandle {
        let actor = NotifyActor::new(sender);
        let (sender, receiver) = unbounded::<Message>();
        let thread = jod_thread::spawn(move || actor.run(receiver));
        NotifyHandle { sender, thread }
    }
    fn set_config(&mut self, config: loader::Config) {
        self.sender.send(Message::Config(config)).unwrap()
    }
    fn invalidate(&mut self, path: AbsPathBuf) {
        self.sender.send(Message::Invalidate(path)).unwrap();
    }
    fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
        read(path)
    }
}

type NotifyEvent = notify::Result<notify::Event>;

struct NotifyActor {
    sender: loader::Sender,
    config: Vec<(AbsPathBuf, Include, bool)>,
    // Drop order is significant.
    watcher: Option<(RecommendedWatcher, Receiver<NotifyEvent>)>,
}

#[derive(Debug)]
enum Event {
    Message(Message),
    NotifyEvent(NotifyEvent),
}

impl NotifyActor {
    fn new(sender: loader::Sender) -> NotifyActor {
        NotifyActor { sender, config: Vec::new(), watcher: None }
    }
    fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
        let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver);
        select! {
            recv(receiver) -> it => it.ok().map(Event::Message),
            recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())),
        }
    }
    fn run(mut self, inbox: Receiver<Message>) {
        while let Some(event) = self.next_event(&inbox) {
            log::debug!("vfs-notify event: {:?}", event);
            match event {
                Event::Message(msg) => match msg {
                    Message::Config(config) => {
                        self.watcher = None;
                        let (watcher_sender, watcher_receiver) = unbounded();
                        let watcher = log_notify_error(Watcher::new_immediate(move |event| {
                            watcher_sender.send(event).unwrap()
                        }));
                        self.watcher = watcher.map(|it| (it, watcher_receiver));

                        let n_total = config.load.len();
                        self.send(loader::Message::Progress { n_total, n_done: 0 });

                        self.config.clear();

                        for (i, entry) in config.load.into_iter().enumerate() {
                            let watch = config.watch.contains(&i);
                            let files = self.load_entry(entry, watch);
                            self.send(loader::Message::Loaded { files });
                            self.send(loader::Message::Progress { n_total, n_done: i + 1 });
                        }
                        self.config.sort_by(|x, y| x.0.cmp(&y.0));
                    }
                    Message::Invalidate(path) => {
                        let contents = read(path.as_path());
                        let files = vec![(path, contents)];
                        self.send(loader::Message::Loaded { files });
                    }
                },
                Event::NotifyEvent(event) => {
                    if let Some(event) = log_notify_error(event) {
                        let files = event
                            .paths
                            .into_iter()
                            .map(|path| AbsPathBuf::try_from(path).unwrap())
                            .filter_map(|path| {
                                let is_dir = path.is_dir();
                                let is_file = path.is_file();

                                let config_idx =
                                    match self.config.binary_search_by(|it| it.0.cmp(&path)) {
                                        Ok(it) => it,
                                        Err(it) => it.saturating_sub(1),
                                    };
                                let include = self.config.get(config_idx).and_then(|it| {
                                    let rel_path = path.strip_prefix(&it.0)?;
                                    Some((rel_path, &it.1))
                                });

                                if let Some((rel_path, include)) = include {
                                    if is_dir && include.exclude_dir(&rel_path)
                                        || is_file && !include.include_file(&rel_path)
                                    {
                                        return None;
                                    }
                                }

                                if is_dir {
                                    self.watch(path);
                                    return None;
                                }
                                if !is_file {
                                    return None;
                                }
                                let contents = read(&path);
                                Some((path, contents))
                            })
                            .collect();
                        self.send(loader::Message::Loaded { files })
                    }
                }
            }
        }
    }
    fn load_entry(
        &mut self,
        entry: loader::Entry,
        watch: bool,
    ) -> Vec<(AbsPathBuf, Option<Vec<u8>>)> {
        match entry {
            loader::Entry::Files(files) => files
                .into_iter()
                .map(|file| {
                    if watch {
                        self.watch(file.clone())
                    }
                    let contents = read(file.as_path());
                    (file, contents)
                })
                .collect::<Vec<_>>(),
            loader::Entry::Directory { path, include } => {
                let include = Include::new(include);
                self.config.push((path.clone(), include.clone(), watch));

                let files = WalkDir::new(&path)
                    .into_iter()
                    .filter_entry(|entry| {
                        let abs_path: &AbsPath = entry.path().try_into().unwrap();
                        match abs_path.strip_prefix(&path) {
                            Some(rel_path) => {
                                !(entry.file_type().is_dir() && include.exclude_dir(rel_path))
                            }
                            None => false,
                        }
                    })
                    .filter_map(|entry| entry.ok())
                    .filter_map(|entry| {
                        let is_dir = entry.file_type().is_dir();
                        let is_file = entry.file_type().is_file();
                        let abs_path = AbsPathBuf::try_from(entry.into_path()).unwrap();
                        if is_dir && watch {
                            self.watch(abs_path.clone());
                        }
                        let rel_path = abs_path.strip_prefix(&path)?;
                        if is_file && include.include_file(&rel_path) {
                            Some(abs_path)
                        } else {
                            None
                        }
                    });

                files
                    .map(|file| {
                        let contents = read(file.as_path());
                        (file, contents)
                    })
                    .collect()
            }
        }
    }

    fn watch(&mut self, path: AbsPathBuf) {
        if let Some((watcher, _)) = &mut self.watcher {
            log_notify_error(watcher.watch(&path, RecursiveMode::NonRecursive));
        }
    }
    fn send(&mut self, msg: loader::Message) {
        (self.sender)(msg)
    }
}

fn read(path: &AbsPath) -> Option<Vec<u8>> {
    std::fs::read(path).ok()
}

fn log_notify_error<T>(res: notify::Result<T>) -> Option<T> {
    res.map_err(|err| log::warn!("notify error: {}", err)).ok()
}
<|endoftext|>