<commit_before>#![feature(test)]

extern crate test;

#[macro_use] extern crate fomat_macros;

use test::{Bencher, black_box};

#[bench]
fn literal_fomat(b: &mut Bencher) {
    b.iter(|| fomat!("Hello"))
}

#[bench]
fn literal_format(b: &mut Bencher) {
    b.iter(|| format!("Hello"))
}

#[bench]
fn short_fomat(b: &mut Bencher) {
    let world = "world";
    b.iter(|| fomat!("Hello, "(black_box(world))"!") )
}

#[bench]
fn short_format(b: &mut Bencher) {
    let world = "world";
    b.iter(|| format!("Hello, {}!", black_box(world)) )
}

#[bench]
fn long_fomat(b: &mut Bencher) {
    let world = "world";
    b.iter(|| fomat!(
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis
        ligula nibh, at maximus diam eleifend sit amet. Suspendisse mattis
        nisi quis eleifend commodo. Praesent ut dui luctus, ullamcorper felis
        et, dictum purus. Nullam elit nunc, dictum ornare arcu vitae, suscipit
        pulvinar felis. Donec malesuada sollicitudin arcu, sit amet laoreet
        dolor elementum eu. Cras quam augue, feugiat ac imperdiet vel, posuere
        et elit. Nunc consequat bibendum dolor at auctor. Cras vel urna
        fermentum, viverra est ut, posuere elit. Suspendisse mattis aliquam
        tincidunt.

        Pellentesque ac risus eu nunc molestie cursus id in risus.
        Pellentesque vel ipsum sed lectus vehicula fermentum vitae vel est.
        Phasellus suscipit dolor eu finibus tincidunt. Duis interdum lectus at
        interdum viverra. Phasellus in mi dapibus, condimentum ligula at,
        rhoncus quam. Morbi pulvinar tortor sit amet fringilla sodales. Duis
        feugiat felis ut ante efficitur, vitae consectetur leo luctus. Vivamus
        congue, ex vel ullamcorper auctor, quam dolor vulputate purus, quis
        dapibus nulla ipsum in ligula. Sed sed vehicula odio. Quisque bibendum
        efficitur sodales. In aliquet sollicitudin venenatis. Sed id euismod
        nulla. Cras risus ligula, mattis in arcu eu, pulvinar aliquet metus.
        Mauris et convallis eros, in tempus sem."
        (world)(1)(2)(3)[vec![4]] "!"
    ) )
}

#[bench]
fn long_format(b: &mut Bencher) {
    let world = "world";
    b.iter(|| format!(
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis
        ligula nibh, at maximus diam eleifend sit amet. Suspendisse mattis
        nisi quis eleifend commodo. Praesent ut dui luctus, ullamcorper felis
        et, dictum purus. Nullam elit nunc, dictum ornare arcu vitae, suscipit
        pulvinar felis. Donec malesuada sollicitudin arcu, sit amet laoreet
        dolor elementum eu. Cras quam augue, feugiat ac imperdiet vel, posuere
        et elit. Nunc consequat bibendum dolor at auctor. Cras vel urna
        fermentum, viverra est ut, posuere elit. Suspendisse mattis aliquam
        tincidunt.

        Pellentesque ac risus eu nunc molestie cursus id in risus.
        Pellentesque vel ipsum sed lectus vehicula fermentum vitae vel est.
        Phasellus suscipit dolor eu finibus tincidunt. Duis interdum lectus at
        interdum viverra. Phasellus in mi dapibus, condimentum ligula at,
        rhoncus quam. Morbi pulvinar tortor sit amet fringilla sodales. Duis
        feugiat felis ut ante efficitur, vitae consectetur leo luctus. Vivamus
        congue, ex vel ullamcorper auctor, quam dolor vulputate purus, quis
        dapibus nulla ipsum in ligula. Sed sed vehicula odio. Quisque bibendum
        efficitur sodales. In aliquet sollicitudin venenatis. Sed id euismod
        nulla. Cras risus ligula, mattis in arcu eu, pulvinar aliquet metus.
        Mauris et convallis eros, in tempus sem.{}{}{}{}{:?}!",
        world, 1, 2, 3, vec![4]
    ) )
}
<commit_msg>Add a few more benches<commit_after>#![feature(test)]

extern crate test;

#[macro_use] extern crate fomat_macros;

use test::{Bencher, black_box};

#[bench]
fn literal_fomat(b: &mut Bencher) {
    b.iter(|| fomat!("Hello"))
}

#[bench]
fn literal_format(b: &mut Bencher) {
    b.iter(|| format!("Hello"))
}

#[bench]
fn short_fomat(b: &mut Bencher) {
    let world = "world";
    b.iter(|| fomat!("Hello, "(black_box(world))"!") )
}

#[bench]
fn short_format(b: &mut Bencher) {
    let world = "world";
    b.iter(|| format!("Hello, {}!", black_box(world)) )
}

#[bench]
fn two_fomat(b: &mut Bencher) {
    b.iter(|| fomat!("One"(3)(2+2)))
}

#[bench]
fn two_format(b: &mut Bencher) {
    b.iter(|| format!("One{}{}", 3, 2+2))
}

#[bench]
fn three_fomat(b: &mut Bencher) {
    b.iter(|| fomat!("One"(2)(3)(2+2)))
}

#[bench]
fn three_format(b: &mut Bencher) {
    b.iter(|| format!("One{}{}{}", 2, 3, 2+2))
}

#[bench]
fn long_fomat(b: &mut Bencher) {
    let world = "world";
    b.iter(|| fomat!(
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis
        ligula nibh, at maximus diam eleifend sit amet. Suspendisse mattis
        nisi quis eleifend commodo. Praesent ut dui luctus, ullamcorper felis
        et, dictum purus. Nullam elit nunc, dictum ornare arcu vitae, suscipit
        pulvinar felis. Donec malesuada sollicitudin arcu, sit amet laoreet
        dolor elementum eu. Cras quam augue, feugiat ac imperdiet vel, posuere
        et elit. Nunc consequat bibendum dolor at auctor. Cras vel urna
        fermentum, viverra est ut, posuere elit. Suspendisse mattis aliquam
        tincidunt.

        Pellentesque ac risus eu nunc molestie cursus id in risus.
        Pellentesque vel ipsum sed lectus vehicula fermentum vitae vel est.
        Phasellus suscipit dolor eu finibus tincidunt. Duis interdum lectus at
        interdum viverra. Phasellus in mi dapibus, condimentum ligula at,
        rhoncus quam. Morbi pulvinar tortor sit amet fringilla sodales. Duis
        feugiat felis ut ante efficitur, vitae consectetur leo luctus. Vivamus
        congue, ex vel ullamcorper auctor, quam dolor vulputate purus, quis
        dapibus nulla ipsum in ligula. Sed sed vehicula odio. Quisque bibendum
        efficitur sodales. In aliquet sollicitudin venenatis. Sed id euismod
        nulla. Cras risus ligula, mattis in arcu eu, pulvinar aliquet metus.
        Mauris et convallis eros, in tempus sem."
        (world)(1)(2)(3)[vec![4]] "!"
    ) )
}

#[bench]
fn long_format(b: &mut Bencher) {
    let world = "world";
    b.iter(|| format!(
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis
        ligula nibh, at maximus diam eleifend sit amet. Suspendisse mattis
        nisi quis eleifend commodo. Praesent ut dui luctus, ullamcorper felis
        et, dictum purus. Nullam elit nunc, dictum ornare arcu vitae, suscipit
        pulvinar felis. Donec malesuada sollicitudin arcu, sit amet laoreet
        dolor elementum eu. Cras quam augue, feugiat ac imperdiet vel, posuere
        et elit. Nunc consequat bibendum dolor at auctor. Cras vel urna
        fermentum, viverra est ut, posuere elit. Suspendisse mattis aliquam
        tincidunt.

        Pellentesque ac risus eu nunc molestie cursus id in risus.
        Pellentesque vel ipsum sed lectus vehicula fermentum vitae vel est.
        Phasellus suscipit dolor eu finibus tincidunt. Duis interdum lectus at
        interdum viverra. Phasellus in mi dapibus, condimentum ligula at,
        rhoncus quam. Morbi pulvinar tortor sit amet fringilla sodales. Duis
        feugiat felis ut ante efficitur, vitae consectetur leo luctus. Vivamus
        congue, ex vel ullamcorper auctor, quam dolor vulputate purus, quis
        dapibus nulla ipsum in ligula. Sed sed vehicula odio. Quisque bibendum
        efficitur sodales. In aliquet sollicitudin venenatis. Sed id euismod
        nulla. Cras risus ligula, mattis in arcu eu, pulvinar aliquet metus.
        Mauris et convallis eros, in tempus sem.{}{}{}{}{:?}!",
        world, 1, 2, 3, vec![4]
    ) )
}
<|endoftext|>


<commit_before>// This benchmark suite contains some benchmarks along a set of dimensions:
//   Hasher: std default (SipHash) and crate default (AHash).
//   Int key distribution: low bit heavy, top bit heavy, and random.
//   Task: basic functionality: insert, insert_erase, lookup, lookup_fail, iter
#![feature(test)]

extern crate test;

use test::{black_box, Bencher};

use hashbrown::hash_map::DefaultHashBuilder;
use hashbrown::{HashMap, HashSet};
use std::{
    collections::hash_map::RandomState,
    sync::atomic::{self, AtomicUsize},
};

const SIZE: usize = 1000;

// The default hashmap when using this crate directly.
type AHashMap<K, V> = HashMap<K, V, DefaultHashBuilder>;
// This uses the hashmap from this crate with the default hasher of the stdlib.
type StdHashMap<K, V> = HashMap<K, V, RandomState>;

// A random key iterator.
#[derive(Clone, Copy)]
struct RandomKeys {
    state: usize,
}

impl RandomKeys {
    fn new() -> Self {
        RandomKeys { state: 0 }
    }
}

impl Iterator for RandomKeys {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        // Add 1 then multiply by some 32 bit prime.
        self.state = self.state.wrapping_add(1).wrapping_mul(3787392781);
        Some(self.state)
    }
}

// Just an arbitrary side effect to make the maps not shortcircuit to the non-dropping path
// when dropping maps/entries (most real world usages likely have drop in the key or value)
lazy_static::lazy_static! {
    static ref SIDE_EFFECT: AtomicUsize = AtomicUsize::new(0);
}

#[derive(Clone)]
struct DropType(usize);
impl Drop for DropType {
    fn drop(&mut self) {
        SIDE_EFFECT.fetch_add(self.0, atomic::Ordering::SeqCst);
    }
}

macro_rules! bench_suite {
    ($bench_macro:ident, $bench_ahash_serial:ident, $bench_std_serial:ident,
     $bench_ahash_highbits:ident, $bench_std_highbits:ident,
     $bench_ahash_random:ident, $bench_std_random:ident) => {
        $bench_macro!($bench_ahash_serial, AHashMap, 0..);
        $bench_macro!($bench_std_serial, StdHashMap, 0..);
        $bench_macro!(
            $bench_ahash_highbits,
            AHashMap,
            (0..).map(usize::swap_bytes)
        );
        $bench_macro!(
            $bench_std_highbits,
            StdHashMap,
            (0..).map(usize::swap_bytes)
        );
        $bench_macro!($bench_ahash_random, AHashMap, RandomKeys::new());
        $bench_macro!($bench_std_random, StdHashMap, RandomKeys::new());
    };
}

macro_rules! bench_insert {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::with_capacity_and_hasher(SIZE, Default::default());
            b.iter(|| {
                m.clear();
                for i in ($keydist).take(SIZE) {
                    m.insert(i, (DropType(i), [i; 20]));
                }
                black_box(&mut m);
            });
            eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
        }
    };
}

bench_suite!(
    bench_insert,
    insert_ahash_serial,
    insert_std_serial,
    insert_ahash_highbits,
    insert_std_highbits,
    insert_ahash_random,
    insert_std_random
);

macro_rules! bench_grow_insert {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            b.iter(|| {
                let mut m = $maptype::default();
                for i in ($keydist).take(SIZE) {
                    m.insert(i, DropType(i));
                }
                black_box(&mut m);
            })
        }
    };
}

bench_suite!(
    bench_grow_insert,
    grow_insert_ahash_serial,
    grow_insert_std_serial,
    grow_insert_ahash_highbits,
    grow_insert_std_highbits,
    grow_insert_ahash_random,
    grow_insert_std_random
);

macro_rules! bench_insert_erase {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut base = $maptype::default();
            for i in ($keydist).take(SIZE) {
                base.insert(i, DropType(i));
            }
            let skip = $keydist.skip(SIZE);
            b.iter(|| {
                let mut m = base.clone();
                let mut add_iter = skip.clone();
                let mut remove_iter = $keydist;
                // While keeping the size constant,
                // replace the first keydist with the second.
                for (add, remove) in (&mut add_iter).zip(&mut remove_iter).take(SIZE) {
                    m.insert(add, DropType(add));
                    black_box(m.remove(&remove));
                }
                black_box(m);
            });
            eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
        }
    };
}

bench_suite!(
    bench_insert_erase,
    insert_erase_ahash_serial,
    insert_erase_std_serial,
    insert_erase_ahash_highbits,
    insert_erase_std_highbits,
    insert_erase_ahash_random,
    insert_erase_std_random
);

macro_rules! bench_lookup {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::default();
            for i in $keydist.take(SIZE) {
                m.insert(i, DropType(i));
            }

            b.iter(|| {
                for i in $keydist.take(SIZE) {
                    black_box(m.get(&i));
                }
            });
            eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
        }
    };
}

bench_suite!(
    bench_lookup,
    lookup_ahash_serial,
    lookup_std_serial,
    lookup_ahash_highbits,
    lookup_std_highbits,
    lookup_ahash_random,
    lookup_std_random
);

macro_rules! bench_lookup_fail {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::default();
            let mut iter = $keydist;
            for i in (&mut iter).take(SIZE) {
                m.insert(i, DropType(i));
            }

            b.iter(|| {
                for i in (&mut iter).take(SIZE) {
                    black_box(m.get(&i));
                }
            })
        }
    };
}

bench_suite!(
    bench_lookup_fail,
    lookup_fail_ahash_serial,
    lookup_fail_std_serial,
    lookup_fail_ahash_highbits,
    lookup_fail_std_highbits,
    lookup_fail_ahash_random,
    lookup_fail_std_random
);

macro_rules! bench_iter {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::default();
            for i in ($keydist).take(SIZE) {
                m.insert(i, DropType(i));
            }

            b.iter(|| {
                for i in &m {
                    black_box(i);
                }
            })
        }
    };
}

bench_suite!(
    bench_iter,
    iter_ahash_serial,
    iter_std_serial,
    iter_ahash_highbits,
    iter_std_highbits,
    iter_ahash_random,
    iter_std_random
);

#[bench]
fn clone_small(b: &mut Bencher) {
    let mut m = HashMap::new();
    for i in 0..10 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        black_box(m.clone());
    })
}

#[bench]
fn clone_from_small(b: &mut Bencher) {
    let mut m = HashMap::new();
    let mut m2 = HashMap::new();
    for i in 0..10 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        m2.clone_from(&m);
        black_box(&mut m2);
    })
}

#[bench]
fn clone_large(b: &mut Bencher) {
    let mut m = HashMap::new();
    for i in 0..1000 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        black_box(m.clone());
    })
}

#[bench]
fn clone_from_large(b: &mut Bencher) {
    let mut m = HashMap::new();
    let mut m2 = HashMap::new();
    for i in 0..1000 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        m2.clone_from(&m);
        black_box(&mut m2);
    })
}

#[bench]
fn rehash_in_place(b: &mut Bencher) {
    b.iter(|| {
        let mut set = HashSet::new();

        // Each loop triggers one rehash
        for _ in 0..10 {
            for i in 0..224 {
                set.insert(i);
            }

            assert_eq!(
                set.capacity(),
                224,
                "The set must be at or close to capacity to trigger a re hashing"
            );

            for i in 100..1400 {
                set.remove(&(i - 100));
                set.insert(i);
            }
            set.clear();
        }
    });
}
<commit_msg>Fix a clippy warning in benches<commit_after>// This benchmark suite contains some benchmarks along a set of dimensions:
//   Hasher: std default (SipHash) and crate default (AHash).
//   Int key distribution: low bit heavy, top bit heavy, and random.
//   Task: basic functionality: insert, insert_erase, lookup, lookup_fail, iter
#![feature(test)]

extern crate test;

use test::{black_box, Bencher};

use hashbrown::hash_map::DefaultHashBuilder;
use hashbrown::{HashMap, HashSet};
use std::{
    collections::hash_map::RandomState,
    sync::atomic::{self, AtomicUsize},
};

const SIZE: usize = 1000;

// The default hashmap when using this crate directly.
type AHashMap<K, V> = HashMap<K, V, DefaultHashBuilder>;
// This uses the hashmap from this crate with the default hasher of the stdlib.
type StdHashMap<K, V> = HashMap<K, V, RandomState>;

// A random key iterator.
#[derive(Clone, Copy)]
struct RandomKeys {
    state: usize,
}

impl RandomKeys {
    fn new() -> Self {
        RandomKeys { state: 0 }
    }
}

impl Iterator for RandomKeys {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        // Add 1 then multiply by some 32 bit prime.
        self.state = self.state.wrapping_add(1).wrapping_mul(3_787_392_781);
        Some(self.state)
    }
}

// Just an arbitrary side effect to make the maps not shortcircuit to the non-dropping path
// when dropping maps/entries (most real world usages likely have drop in the key or value)
lazy_static::lazy_static! {
    static ref SIDE_EFFECT: AtomicUsize = AtomicUsize::new(0);
}

#[derive(Clone)]
struct DropType(usize);
impl Drop for DropType {
    fn drop(&mut self) {
        SIDE_EFFECT.fetch_add(self.0, atomic::Ordering::SeqCst);
    }
}

macro_rules! bench_suite {
    ($bench_macro:ident, $bench_ahash_serial:ident, $bench_std_serial:ident,
     $bench_ahash_highbits:ident, $bench_std_highbits:ident,
     $bench_ahash_random:ident, $bench_std_random:ident) => {
        $bench_macro!($bench_ahash_serial, AHashMap, 0..);
        $bench_macro!($bench_std_serial, StdHashMap, 0..);
        $bench_macro!(
            $bench_ahash_highbits,
            AHashMap,
            (0..).map(usize::swap_bytes)
        );
        $bench_macro!(
            $bench_std_highbits,
            StdHashMap,
            (0..).map(usize::swap_bytes)
        );
        $bench_macro!($bench_ahash_random, AHashMap, RandomKeys::new());
        $bench_macro!($bench_std_random, StdHashMap, RandomKeys::new());
    };
}

macro_rules! bench_insert {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::with_capacity_and_hasher(SIZE, Default::default());
            b.iter(|| {
                m.clear();
                for i in ($keydist).take(SIZE) {
                    m.insert(i, (DropType(i), [i; 20]));
                }
                black_box(&mut m);
            });
            eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
        }
    };
}

bench_suite!(
    bench_insert,
    insert_ahash_serial,
    insert_std_serial,
    insert_ahash_highbits,
    insert_std_highbits,
    insert_ahash_random,
    insert_std_random
);

macro_rules! bench_grow_insert {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            b.iter(|| {
                let mut m = $maptype::default();
                for i in ($keydist).take(SIZE) {
                    m.insert(i, DropType(i));
                }
                black_box(&mut m);
            })
        }
    };
}

bench_suite!(
    bench_grow_insert,
    grow_insert_ahash_serial,
    grow_insert_std_serial,
    grow_insert_ahash_highbits,
    grow_insert_std_highbits,
    grow_insert_ahash_random,
    grow_insert_std_random
);

macro_rules! bench_insert_erase {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut base = $maptype::default();
            for i in ($keydist).take(SIZE) {
                base.insert(i, DropType(i));
            }
            let skip = $keydist.skip(SIZE);
            b.iter(|| {
                let mut m = base.clone();
                let mut add_iter = skip.clone();
                let mut remove_iter = $keydist;
                // While keeping the size constant,
                // replace the first keydist with the second.
                for (add, remove) in (&mut add_iter).zip(&mut remove_iter).take(SIZE) {
                    m.insert(add, DropType(add));
                    black_box(m.remove(&remove));
                }
                black_box(m);
            });
            eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
        }
    };
}

bench_suite!(
    bench_insert_erase,
    insert_erase_ahash_serial,
    insert_erase_std_serial,
    insert_erase_ahash_highbits,
    insert_erase_std_highbits,
    insert_erase_ahash_random,
    insert_erase_std_random
);

macro_rules! bench_lookup {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::default();
            for i in $keydist.take(SIZE) {
                m.insert(i, DropType(i));
            }

            b.iter(|| {
                for i in $keydist.take(SIZE) {
                    black_box(m.get(&i));
                }
            });
            eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
        }
    };
}

bench_suite!(
    bench_lookup,
    lookup_ahash_serial,
    lookup_std_serial,
    lookup_ahash_highbits,
    lookup_std_highbits,
    lookup_ahash_random,
    lookup_std_random
);

macro_rules! bench_lookup_fail {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::default();
            let mut iter = $keydist;
            for i in (&mut iter).take(SIZE) {
                m.insert(i, DropType(i));
            }

            b.iter(|| {
                for i in (&mut iter).take(SIZE) {
                    black_box(m.get(&i));
                }
            })
        }
    };
}

bench_suite!(
    bench_lookup_fail,
    lookup_fail_ahash_serial,
    lookup_fail_std_serial,
    lookup_fail_ahash_highbits,
    lookup_fail_std_highbits,
    lookup_fail_ahash_random,
    lookup_fail_std_random
);

macro_rules! bench_iter {
    ($name:ident, $maptype:ident, $keydist:expr) => {
        #[bench]
        fn $name(b: &mut Bencher) {
            let mut m = $maptype::default();
            for i in ($keydist).take(SIZE) {
                m.insert(i, DropType(i));
            }

            b.iter(|| {
                for i in &m {
                    black_box(i);
                }
            })
        }
    };
}

bench_suite!(
    bench_iter,
    iter_ahash_serial,
    iter_std_serial,
    iter_ahash_highbits,
    iter_std_highbits,
    iter_ahash_random,
    iter_std_random
);

#[bench]
fn clone_small(b: &mut Bencher) {
    let mut m = HashMap::new();
    for i in 0..10 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        black_box(m.clone());
    })
}

#[bench]
fn clone_from_small(b: &mut Bencher) {
    let mut m = HashMap::new();
    let mut m2 = HashMap::new();
    for i in 0..10 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        m2.clone_from(&m);
        black_box(&mut m2);
    })
}

#[bench]
fn clone_large(b: &mut Bencher) {
    let mut m = HashMap::new();
    for i in 0..1000 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        black_box(m.clone());
    })
}

#[bench]
fn clone_from_large(b: &mut Bencher) {
    let mut m = HashMap::new();
    let mut m2 = HashMap::new();
    for i in 0..1000 {
        m.insert(i, DropType(i));
    }

    b.iter(|| {
        m2.clone_from(&m);
        black_box(&mut m2);
    })
}

#[bench]
fn rehash_in_place(b: &mut Bencher) {
    b.iter(|| {
        let mut set = HashSet::new();

        // Each loop triggers one rehash
        for _ in 0..10 {
            for i in 0..224 {
                set.insert(i);
            }

            assert_eq!(
                set.capacity(),
                224,
                "The set must be at or close to capacity to trigger a re hashing"
            );

            for i in 100..1400 {
                set.remove(&(i - 100));
                set.insert(i);
            }
            set.clear();
        }
    });
}
<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg(target_os = "macos")]
use libc::{LC_CTYPE, setlocale};
use std::ffi::{CString, CStr};
use std::os::raw::c_char;
use std::ptr::null;
use std::slice;
use std::str;
use std::env;

use objc::runtime::{Class, Object};

pub fn set_locale_environment() {
    let (language_code, country_code) = unsafe {
        let locale_class = Class::get("NSLocale").unwrap();
        let locale: *const Object = msg_send![locale_class, currentLocale];
        msg_send![locale_class, release];
        let language_code: *const Object = msg_send![locale, languageCode];
        let country_code: *const Object = msg_send![locale, countryCode];
        msg_send![locale, release];
        let language_code_str = nsstring_as_str(language_code).to_owned();
        msg_send![language_code, release];
        let country_code_str = nsstring_as_str(country_code).to_owned();
        msg_send![country_code, release];
        (language_code_str, country_code_str)
    };
    let locale_id = format!("{}_{}.UTF-8", &language_code, &country_code);
    // check if locale_id is valid
    let locale_c_str = CString::new(locale_id.to_owned()).unwrap();
    let locale_ptr = locale_c_str.as_ptr();
    let locale_id = unsafe {
        // save a copy of original setting
        let original = setlocale(LC_CTYPE, null());
        let saved_original = if original.is_null() {
            CString::new("").unwrap()
        } else {
            CStr::from_ptr(original).to_owned()
        };
        // try setting `locale_id`
        let modified = setlocale(LC_CTYPE, locale_ptr);
        let result = if modified.is_null() {
            String::from("")
        } else {
            locale_id
        };
        // restore original setting
        setlocale(LC_CTYPE, saved_original.as_ptr());
        result
    };

    env::set_var("LANG", &locale_id);
}

const UTF8_ENCODING: usize = 4;

unsafe fn nsstring_as_str<'a>(nsstring: *const Object) -> &'a str {
    let cstr: *const c_char = msg_send![nsstring, UTF8String];
    let len: usize = msg_send![nsstring, lengthOfBytesUsingEncoding: UTF8_ENCODING];
    str::from_utf8(slice::from_raw_parts(cstr as *const u8, len)).unwrap()
}

#[cfg(not(target_os = "macos"))]
pub fn set_locale_environment() {}
<commit_msg>handle older macOS versions<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![cfg(target_os = "macos")]
use libc::{LC_CTYPE, setlocale};
use std::ffi::{CString, CStr};
use std::os::raw::c_char;
use std::ptr::null;
use std::slice;
use std::str;
use std::env;

use objc::runtime::{Class, Object};

pub fn set_locale_environment() {
    let locale_id = unsafe {
        let locale_class = Class::get("NSLocale").unwrap();
        let locale: *const Object = msg_send![locale_class, currentLocale];
        let _ : () = msg_send![locale_class, release];
        // `localeIdentifier` returns extra metadata with the locale (including currency and
        // collator) on newer versions of macOS. This is not a valid locale, so we use
        // `languageCode` and `countryCode`, if they're available (macOS 10.12+):
        // https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc
        // https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc
        // https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc
        let is_language_code_supported: bool = msg_send![locale, respondsToSelector:sel!(languageCode)];
        let is_country_code_supported: bool = msg_send![locale, respondsToSelector:sel!(countryCode)];
        let locale_id = if is_language_code_supported && is_country_code_supported {
            let language_code: *const Object = msg_send![locale, languageCode];
            let country_code: *const Object = msg_send![locale, countryCode];
            let language_code_str = nsstring_as_str(language_code).to_owned();
            let _ : () = msg_send![language_code, release];
            let country_code_str = nsstring_as_str(country_code).to_owned();
            let _ : () = msg_send![country_code, release];
            format!("{}_{}.UTF-8", &language_code_str, &country_code_str)
        } else {
            let identifier: *const Object = msg_send![locale, localeIdentifier];
            let identifier_str = nsstring_as_str(identifier).to_owned();
            let _ : () = msg_send![identifier, release];
            identifier_str
        };
        let _ : () = msg_send![locale, release];
        locale_id
    };
    // check if locale_id is valid
    let locale_c_str = CString::new(locale_id.to_owned()).unwrap();
    let locale_ptr = locale_c_str.as_ptr();
    let locale_id = unsafe {
        // save a copy of original setting
        let original = setlocale(LC_CTYPE, null());
        let saved_original = if original.is_null() {
            CString::new("").unwrap()
        } else {
            CStr::from_ptr(original).to_owned()
        };
        // try setting `locale_id`
        let modified = setlocale(LC_CTYPE, locale_ptr);
        let result = if modified.is_null() {
            String::from("")
        } else {
            locale_id
        };
        // restore original setting
        setlocale(LC_CTYPE, saved_original.as_ptr());
        result
    };

    env::set_var("LANG", &locale_id);
}

const UTF8_ENCODING: usize = 4;

unsafe fn nsstring_as_str<'a>(nsstring: *const Object) -> &'a str {
    let cstr: *const c_char = msg_send![nsstring, UTF8String];
    let len: usize = msg_send![nsstring, lengthOfBytesUsingEncoding: UTF8_ENCODING];
    str::from_utf8(slice::from_raw_parts(cstr as *const u8, len)).unwrap()
}

#[cfg(not(target_os = "macos"))]
pub fn set_locale_environment() {}
<|endoftext|>


<commit_before>use libc::c_char;
use std::sync::Once;

use crate::raw;

/// Manually initialize logging.
///
/// It is optional to call this function, and safe to do so more than once.
pub fn init() {
    static mut INIT: Once = Once::new();

    unsafe {
        INIT.call_once(|| {
            init_impl();
        });
    }
}

#[cfg(feature = "log")]
fn init_impl() {
    use log::LogLevelFilter;

    // trace to our callback
    unsafe {
        raw::rs_trace_to(trace);
    }

    // determine log level
    // this is useful because if the setted level is not Debug we can optimize librsync log
    // calls
    let level = match log::max_log_level() {
        LogLevelFilter::Info => raw::RS_LOG_NOTICE,
        LogLevelFilter::Debug | LogLevelFilter::Trace => raw::RS_LOG_DEBUG,
        _ => raw::RS_LOG_WARNING,
    };
    unsafe {
        raw::rs_trace_set_level(level);
    }
}

#[cfg(feature = "log")]
extern "C" fn trace(level: raw::rs_loglevel, msg: *const c_char) {
    use log::LogLevel;
    use std::ffi::CStr;

    let level = match level {
        raw::RS_LOG_EMERG | raw::RS_LOG_ALERT | raw::RS_LOG_CRIT | raw::RS_LOG_ERR => {
            LogLevel::Error
        }
        raw::RS_LOG_WARNING => LogLevel::Warn,
        raw::RS_LOG_NOTICE | raw::RS_LOG_INFO => LogLevel::Info,
        raw::RS_LOG_DEBUG => LogLevel::Debug,
        _ => LogLevel::Error,
    };
    let msg = unsafe { CStr::from_ptr(msg).to_string_lossy() };
    log!(target: "librsync", level, "{}", msg);
}

#[cfg(not(feature = "log"))]
fn init_impl() {
    unsafe {
        raw::rs_trace_to(trace);
        raw::rs_trace_set_level(raw::RS_LOG_EMERG);
    }

    extern "C" fn trace(_level: raw::rs_loglevel, _msg: *const c_char) {}
}
<commit_msg>Upgrade logger.<commit_after>use libc::c_char;
use std::sync::Once;

use crate::raw;

/// Manually initialize logging.
///
/// It is optional to call this function, and safe to do so more than once.
pub fn init() {
    static mut INIT: Once = Once::new();

    unsafe {
        INIT.call_once(|| {
            init_impl();
        });
    }
}

#[cfg(feature = "log")]
fn init_impl() {
    use log::LevelFilter;

    // trace to our callback
    unsafe {
        raw::rs_trace_to(trace);
    }

    // determine log level
    // this is useful because if the setted level is not Debug we can optimize librsync log
    // calls
    let level = match log::max_level() {
        LevelFilter::Info => raw::RS_LOG_NOTICE,
        LevelFilter::Debug | LevelFilter::Trace => raw::RS_LOG_DEBUG,
        _ => raw::RS_LOG_WARNING,
    };
    unsafe {
        raw::rs_trace_set_level(level);
    }
}

#[cfg(feature = "log")]
extern "C" fn trace(level: raw::rs_loglevel, msg: *const c_char) {
    use log::Level;
    use std::ffi::CStr;

    let level = match level {
        raw::RS_LOG_EMERG | raw::RS_LOG_ALERT | raw::RS_LOG_CRIT | raw::RS_LOG_ERR => Level::Error,
        raw::RS_LOG_WARNING => Level::Warn,
        raw::RS_LOG_NOTICE | raw::RS_LOG_INFO => Level::Info,
        raw::RS_LOG_DEBUG => Level::Debug,
        _ => Level::Error,
    };
    let msg = unsafe { CStr::from_ptr(msg).to_string_lossy() };
    log!(target: "librsync", level, "{}", msg);
}

#[cfg(not(feature = "log"))]
fn init_impl() {
    unsafe {
        raw::rs_trace_to(trace);
        raw::rs_trace_set_level(raw::RS_LOG_EMERG);
    }

    extern "C" fn trace(_level: raw::rs_loglevel, _msg: *const c_char) {}
}
<|endoftext|>


<commit_before>//! This tiny build script ensures that rocket_codegen is not compiled with an
//! incompatible version of rust.

extern crate ansi_term;
extern crate version_check;

use ansi_term::Color::{Red, Yellow, Blue, White};
use version_check::{is_nightly, is_min_version, is_min_date};

// Specifies the minimum nightly version needed to compile Rocket's codegen.
const MIN_DATE: &'static str = "2017-03-19";
const MIN_VERSION: &'static str = "1.17.0-nightly";

// Convenience macro for writing to stderr.
macro_rules! printerr {
    ($($arg:tt)*) => ({
        use std::io::prelude::*;
        write!(&mut ::std::io::stderr(), "{}\n", format_args!($($arg)*))
            .expect("Failed to write to stderr.")
    })
}

fn main() {
    let ok_nightly = is_nightly();
    let ok_version = is_min_version(MIN_VERSION);
    let ok_date = is_min_date(MIN_DATE);

    let print_version_err = |version: &str, date: &str| {
        printerr!("{} {}. {} {}.",
                  White.paint("Installed version is:"),
                  Yellow.paint(format!("{} ({})", version, date)),
                  White.paint("Minimum required:"),
                  Yellow.paint(format!("{} ({})", MIN_VERSION, MIN_DATE)));
    };

    match (ok_nightly, ok_version, ok_date) {
        (Some(is_nightly), Some((ok_version, version)), Some((ok_date, date))) => {
            if !is_nightly {
                printerr!("{} {}",
                          Red.bold().paint("Error:"),
                          White.paint("Rocket requires a nightly version of Rust."));
                print_version_err(&*version, &*date);
                printerr!("{}{}{}",
                          Blue.paint("See the getting started guide ("),
                          White.paint("https://rocket.rs/guide/getting-started/"),
                          Blue.paint(") for more information."));
                panic!("Aborting compilation due to incompatible compiler.")
            }

            if !ok_version || !ok_date {
                printerr!("{} {}",
                          Red.bold().paint("Error:"),
                          White.paint("Rocket codegen requires a more recent version of rustc."));
                printerr!("{}{}{}",
                          Blue.paint("Use `"),
                          White.paint("rustup update"),
                          Blue.paint("` or your preferred method to update Rust."));
                print_version_err(&*version, &*date);
                panic!("Aborting compilation due to incompatible compiler.")
            }
        },
        _ => {
            println!("cargo:warning={}", "Rocket was unable to check rustc compatibility.");
            println!("cargo:warning={}", "Build may fail due to incompatible rustc version.");
        }
    }
}
<commit_msg>Update minimum rustc version for codegen.<commit_after>//! This tiny build script ensures that rocket_codegen is not compiled with an
//! incompatible version of rust.

extern crate ansi_term;
extern crate version_check;

use ansi_term::Color::{Red, Yellow, Blue, White};
use version_check::{is_nightly, is_min_version, is_min_date};

// Specifies the minimum nightly version needed to compile Rocket's codegen.
const MIN_DATE: &'static str = "2017-03-22";
const MIN_VERSION: &'static str = "1.17.0-nightly";

// Convenience macro for writing to stderr.
macro_rules! printerr {
    ($($arg:tt)*) => ({
        use std::io::prelude::*;
        write!(&mut ::std::io::stderr(), "{}\n", format_args!($($arg)*))
            .expect("Failed to write to stderr.")
    })
}

fn main() {
    let ok_nightly = is_nightly();
    let ok_version = is_min_version(MIN_VERSION);
    let ok_date = is_min_date(MIN_DATE);

    let print_version_err = |version: &str, date: &str| {
        printerr!("{} {}. {} {}.",
                  White.paint("Installed version is:"),
                  Yellow.paint(format!("{} ({})", version, date)),
                  White.paint("Minimum required:"),
                  Yellow.paint(format!("{} ({})", MIN_VERSION, MIN_DATE)));
    };

    match (ok_nightly, ok_version, ok_date) {
        (Some(is_nightly), Some((ok_version, version)), Some((ok_date, date))) => {
            if !is_nightly {
                printerr!("{} {}",
                          Red.bold().paint("Error:"),
                          White.paint("Rocket requires a nightly version of Rust."));
                print_version_err(&*version, &*date);
                printerr!("{}{}{}",
                          Blue.paint("See the getting started guide ("),
                          White.paint("https://rocket.rs/guide/getting-started/"),
                          Blue.paint(") for more information."));
                panic!("Aborting compilation due to incompatible compiler.")
            }

            if !ok_version || !ok_date {
                printerr!("{} {}",
                          Red.bold().paint("Error:"),
                          White.paint("Rocket codegen requires a more recent version of rustc."));
                printerr!("{}{}{}",
                          Blue.paint("Use `"),
                          White.paint("rustup update"),
                          Blue.paint("` or your preferred method to update Rust."));
                print_version_err(&*version, &*date);
                panic!("Aborting compilation due to incompatible compiler.")
            }
        },
        _ => {
            println!("cargo:warning={}", "Rocket was unable to check rustc compatibility.");
            println!("cargo:warning={}", "Build may fail due to incompatible rustc version.");
        }
    }
}
<|endoftext|>


<commit_before>pub mod value;
pub mod types;

use self::value::*;
use self::types::*;
use parser::ast::*;

pub fn eval(program: Program) -> Result<Value> {
    Ok(Value::Null)
}

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

    fn eval_to(code: &str, expected: Value) {
        let tokens = lexer::tokenize(String::from(code).chars()).unwrap();
        let program = parser::parse(tokens).unwrap();
        let actual = eval(program).unwrap();
        assert_eq!(actual, expected);
    }

    fn fail_with(code: &str, error: &str, pos: (i32, i32)) {
        let tokens = lexer::tokenize(String::from(code).chars()).unwrap();
        let program = parser::parse(tokens).unwrap();
        let actual = eval(program);
        assert_eq!(actual, Err(EvalError(String::from(error), pos)));
    }

    #[test]
    fn empty_program() {
        eval_to("", Value::Null);
    }

    #[test]
    fn simple_int() {
        eval_to("5", Value::Int(5));
        eval_to("10", Value::Int(10));
    }

    #[test]
    fn simple_bool() {
        eval_to("true", Value::Bool(true));
        eval_to("false", Value::Bool(false));
    }

    #[test]
    fn prefix_op() {
        // !, the bang operator
        eval_to("!true", Value::Bool(false));
        eval_to("!false", Value::Bool(true));
        eval_to("!!true", Value::Bool(true));
        eval_to("!!false", Value::Bool(false));
        fail_with("!5", "5 is not a bool", (1, 1));
        fail_with("!1", "1 is not a bool", (1, 1));
        fail_with("!0", "0 is not a bool", (1, 1));
        fail_with("!!5", "5 is not a bool", (1, 2));
        fail_with("!!0", "0 is not a bool", (1, 2));
        // the prefix +
        eval_to("+1", Value::Int(1));
        eval_to("+5", Value::Int(5));
        eval_to("+20", Value::Int(20));
        fail_with("+true", "true is not a number", (1, 1));
        fail_with("+false", "false is not a number", (1, 1));
        // the prefix -
        eval_to("-1", Value::Int((-1)));
        eval_to("-5", Value::Int((-5)));
        eval_to("-20", Value::Int((-20)));
        fail_with("-true", "true is not a number", (1, 1));
        fail_with("-false", "false is not a number", (1, 1));
    }

    #[test]
    fn infix_op() {
        // algebra
        eval_to("5 + 5 + 5 + 5 - 10", Value::Int(10));
        eval_to("2 * 2 * 2 * 2 * 2", Value::Int(32));
        eval_to("-50 + 100 + -50", Value::Int(0));
        eval_to("5 * 2 + 10", Value::Int(20));
        eval_to("5 + 2 * 10", Value::Int(25));
        eval_to("20 + 2 * -10", Value::Int(0));
        eval_to("50 / 2 * 2 + 10", Value::Int(60));
        eval_to("2 * (5 + 10)", Value::Int(30));
        eval_to("3 * 3 * 3 + 10", Value::Int(37));
        eval_to("3 * (3 * 3) + 10", Value::Int(37));
        eval_to("(5 + 10 * 2 + 15 / 3) * 2 + -10", Value::Int(50));
        // logical algebra
        eval_to("1 < 2", Value::Bool(true));
        eval_to("1 > 2", Value::Bool(false));
        eval_to("1 < 1", Value::Bool(false));
        eval_to("1 > 1", Value::Bool(false));
        eval_to("1 == 1", Value::Bool(true));
        eval_to("1 != 1", Value::Bool(false));
        eval_to("1 == 2", Value::Bool(false));
        eval_to("1 != 2", Value::Bool(true));
        // combination
        eval_to("(1 < 2) == true", Value::Bool(true));
        eval_to("(1 < 2) == false", Value::Bool(false));
        eval_to("(1 > 2) == true", Value::Bool(false));
        eval_to("(1 > 2) == false", Value::Bool(true));
    }

    #[test]
    fn conditional_expr() {
        eval_to("if (true) { 10 }", Value::Int(10));
        eval_to("if (false) { 10 }", Value::Null);
        fail_with("if (1) { 10 }", "1 is not a bool", (1, 5));
        eval_to("if (1 < 2) { 10 }", Value::Int(10));
        eval_to("if (1 > 2) { 10 }", Value::Null);
        eval_to("if (1 < 2) { 10 } else { 20 }", Value::Int(10));
        eval_to("if (1 > 2) { 10 } else { 20 }", Value::Int(20));
    }

    static RETURN1: &str = "
if (10 > 1) {
  if (10 > 1) {
    return 10;
  }
  return 1;
}
";

    #[test]
    fn return_stmt() {
        eval_to("return 10", Value::Int(10));
        eval_to("return 10; 9", Value::Int(10));
        eval_to("return 2 * 5; 9", Value::Int(10));
        eval_to("9; return 2 * 5; 9", Value::Int(10));
        eval_to(RETURN1, Value::Int(10));
    }

    #[test]
    fn bindings() {
        eval_to("let a = 5; a;", Value::Int(5));
        eval_to("let a = 5 * 5; a;", Value::Int(25));
        eval_to("let a = 5; let b = a; b;", Value::Int(5));
        eval_to("let a = 5; let b = a; let c = a + b + 5; c;",
                Value::Int(15));
        fail_with("foobar", "identifier not found: foobar", (1, 1))
    }

    static FN1: &str = "
let add = fn(a, b, c, d) { return a + b + c + d; };
add(1, 2, 3, 4);
";

    static FN2: &str = "
let addThree = fn(x) { return x + 3 };
addThree(3);
";

    static FN3: &str = "
let max = fn(x, y) { if (x > y) { x } else { y } };
max(5, 10)
";

    static FN4: &str = "
let factorial = fn(n) {
  if (n == 0) {
    1
  } else {
    n * factorial(n - 1)
  }
}
factorial(5)
";

    static FN5: &str = "
let addThree = fn(x) { return x + 3 };
let callTwoTimes = fn(x, f) { f(f(x)) }
callTwoTimes(3, addThree);
";

    static FN6: &str = "
let callTwoTimes = fn(x, f) { f(f(x)) }
callTwoTimes(3, fn(x) { x + 1 });
";

    static FN7: &str = "
let newAdder = fn(x) { fn(n) { x + n } };
let addTwo = newAdder(2);
addTwo(2);
";

    fn fn_decl_and_eval() {
        eval_to("let identity = fn(x) { x; }; identity(5);", Value::Int(5));
        eval_to("let identity = fn(x) { return x; }; identity(5);",
                Value::Int(5));
        eval_to("let double = fn(x) { x * 2; }; double(5);", Value::Int(10));
        eval_to("let add = fn(x, y) { x + y; }; add(5, 5);", Value::Int(10));
        eval_to("let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));",
                Value::Int(20));
        eval_to("fn(x) { x; }(5)", Value::Int(5));
        fail_with("5();", "5 is not a function", (1, 1));
        fail_with("false();", "false is not a function", (1, 1));
        fail_with("let add = fn(x, y) { x + y; }; add(1);",
                  "wrong number of arguments: 2 expected but 1 given",
                  (1, 32));
        eval_to(FN1, Value::Int(10));
        eval_to(FN2, Value::Int(6));
        eval_to(FN3, Value::Int(10));
        eval_to(FN4, Value::Int(120));
        eval_to(FN5, Value::Int(9));
        eval_to(FN6, Value::Int(5));
        eval_to(FN7, Value::Int(4));
        // special cases
        eval_to("let a = 10; let x = fn () { a; }; x();", Value::Int(10));
        eval_to("let x = fn () { a; }; let a = 10; x();", Value::Int(10));
    }

    static MAP_DECL: &str = "
let map = fn(f, arr) {
  if (len(arr) == 0) {
    []
  } else {
    let h = head(arr);
    cons(f(h), map(f, tail(arr)));
  }
};
";

    static REDUCE_DECL: &str = "
let reduce = fn(f, init, arr) {
  if (len(arr) == 0) {
    init
  } else {
    let newInit = f(init, head(arr));
    reduce(f, newInit, tail(arr));
  }
};
";

    static HASH1: &str = "
let double = fn(x) {
  x * 2;
};
let arr = [1, 2, 3, 4];
let h = {
  \"one\": 10 - 9,
  \"two\": 8 / 4,
  3: arr[2],
  4: double(2),
  true: if (10 > 8) { true } else { false },
  false: \"hello\" == \"world\"
};
";
}
<commit_msg>Add evaluator tests<commit_after>pub mod value;
pub mod types;

use self::value::*;
use self::types::*;
use parser::ast::*;

pub fn eval(program: Program) -> Result<Value> {
    Ok(Value::Null)
}

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

    fn eval_to(code: &str, expected: Value) {
        let tokens = lexer::tokenize(String::from(code).chars()).unwrap();
        let program = parser::parse(tokens).unwrap();
        let actual = eval(program).unwrap();
        assert_eq!(actual, expected);
    }

    fn fail_with(code: &str, error: &str, pos: (i32, i32)) {
        let tokens = lexer::tokenize(String::from(code).chars()).unwrap();
        let program = parser::parse(tokens).unwrap();
        let actual = eval(program);
        assert_eq!(actual, Err(EvalError(String::from(error), pos)));
    }

    #[test]
    fn empty_program() {
        eval_to("", Value::Null);
    }

    #[test]
    fn simple_int() {
        eval_to("5", Value::Int(5));
        eval_to("10", Value::Int(10));
    }

    #[test]
    fn simple_bool() {
        eval_to("true", Value::Bool(true));
        eval_to("false", Value::Bool(false));
    }

    #[test]
    fn prefix_op() {
        // !, the bang operator
        eval_to("!true", Value::Bool(false));
        eval_to("!false", Value::Bool(true));
        eval_to("!!true", Value::Bool(true));
        eval_to("!!false", Value::Bool(false));
        fail_with("!5", "5 is not a bool", (1, 1));
        fail_with("!1", "1 is not a bool", (1, 1));
        fail_with("!0", "0 is not a bool", (1, 1));
        fail_with("!!5", "5 is not a bool", (1, 2));
        fail_with("!!0", "0 is not a bool", (1, 2));
        // the prefix +
        eval_to("+1", Value::Int(1));
        eval_to("+5", Value::Int(5));
        eval_to("+20", Value::Int(20));
        fail_with("+true", "true is not a number", (1, 1));
        fail_with("+false", "false is not a number", (1, 1));
        // the prefix -
        eval_to("-1", Value::Int((-1)));
        eval_to("-5", Value::Int((-5)));
        eval_to("-20", Value::Int((-20)));
        fail_with("-true", "true is not a number", (1, 1));
        fail_with("-false", "false is not a number", (1, 1));
    }

    #[test]
    fn infix_op() {
        // algebra
        eval_to("5 + 5 + 5 + 5 - 10", Value::Int(10));
        eval_to("2 * 2 * 2 * 2 * 2", Value::Int(32));
        eval_to("-50 + 100 + -50", Value::Int(0));
        eval_to("5 * 2 + 10", Value::Int(20));
        eval_to("5 + 2 * 10", Value::Int(25));
        eval_to("20 + 2 * -10", Value::Int(0));
        eval_to("50 / 2 * 2 + 10", Value::Int(60));
        eval_to("2 * (5 + 10)", Value::Int(30));
        eval_to("3 * 3 * 3 + 10", Value::Int(37));
        eval_to("3 * (3 * 3) + 10", Value::Int(37));
        eval_to("(5 + 10 * 2 + 15 / 3) * 2 + -10", Value::Int(50));
        // logical algebra
        eval_to("1 < 2", Value::Bool(true));
        eval_to("1 > 2", Value::Bool(false));
        eval_to("1 < 1", Value::Bool(false));
        eval_to("1 > 1", Value::Bool(false));
        eval_to("1 == 1", Value::Bool(true));
        eval_to("1 != 1", Value::Bool(false));
        eval_to("1 == 2", Value::Bool(false));
        eval_to("1 != 2", Value::Bool(true));
        // combination
        eval_to("(1 < 2) == true", Value::Bool(true));
        eval_to("(1 < 2) == false", Value::Bool(false));
        eval_to("(1 > 2) == true", Value::Bool(false));
        eval_to("(1 > 2) == false", Value::Bool(true));
    }

    #[test]
    fn conditional_expr() {
        eval_to("if (true) { 10 }", Value::Int(10));
        eval_to("if (false) { 10 }", Value::Null);
        fail_with("if (1) { 10 }", "1 is not a bool", (1, 5));
        eval_to("if (1 < 2) { 10 }", Value::Int(10));
        eval_to("if (1 > 2) { 10 }", Value::Null);
        eval_to("if (1 < 2) { 10 } else { 20 }", Value::Int(10));
        eval_to("if (1 > 2) { 10 } else { 20 }", Value::Int(20));
    }

    static RETURN1: &str = "
if (10 > 1) {
  if (10 > 1) {
    return 10;
  }
  return 1;
}
";

    #[test]
    fn return_stmt() {
        eval_to("return 10", Value::Int(10));
        eval_to("return 10; 9", Value::Int(10));
        eval_to("return 2 * 5; 9", Value::Int(10));
        eval_to("9; return 2 * 5; 9", Value::Int(10));
        eval_to(RETURN1, Value::Int(10));
    }

    #[test]
    fn bindings() {
        eval_to("let a = 5; a;", Value::Int(5));
        eval_to("let a = 5 * 5; a;", Value::Int(25));
        eval_to("let a = 5; let b = a; b;", Value::Int(5));
        eval_to("let a = 5; let b = a; let c = a + b + 5; c;",
                Value::Int(15));
        fail_with("foobar", "identifier not found: foobar", (1, 1))
    }

    static FN1: &str = "
let add = fn(a, b, c, d) { return a + b + c + d; };
add(1, 2, 3, 4);
";

    static FN2: &str = "
let addThree = fn(x) { return x + 3 };
addThree(3);
";

    static FN3: &str = "
let max = fn(x, y) { if (x > y) { x } else { y } };
max(5, 10)
";

    static FN4: &str = "
let factorial = fn(n) {
  if (n == 0) {
    1
  } else {
    n * factorial(n - 1)
  }
}
factorial(5)
";

    static FN5: &str = "
let addThree = fn(x) { return x + 3 };
let callTwoTimes = fn(x, f) { f(f(x)) }
callTwoTimes(3, addThree);
";

    static FN6: &str = "
let callTwoTimes = fn(x, f) { f(f(x)) }
callTwoTimes(3, fn(x) { x + 1 });
";

    static FN7: &str = "
let newAdder = fn(x) { fn(n) { x + n } };
let addTwo = newAdder(2);
addTwo(2);
";

    fn fn_decl_and_eval() {
        eval_to("let identity = fn(x) { x; }; identity(5);", Value::Int(5));
        eval_to("let identity = fn(x) { return x; }; identity(5);",
                Value::Int(5));
        eval_to("let double = fn(x) { x * 2; }; double(5);", Value::Int(10));
        eval_to("let add = fn(x, y) { x + y; }; add(5, 5);", Value::Int(10));
        eval_to("let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));",
                Value::Int(20));
        eval_to("fn(x) { x; }(5)", Value::Int(5));
        fail_with("5();", "5 is not a function", (1, 1));
        fail_with("false();", "false is not a function", (1, 1));
        fail_with("let add = fn(x, y) { x + y; }; add(1);",
                  "wrong number of arguments: 2 expected but 1 given",
                  (1, 32));
        eval_to(FN1, Value::Int(10));
        eval_to(FN2, Value::Int(6));
        eval_to(FN3, Value::Int(10));
        eval_to(FN4, Value::Int(120));
        eval_to(FN5, Value::Int(9));
        eval_to(FN6, Value::Int(5));
        eval_to(FN7, Value::Int(4));
        // special cases
        eval_to("let a = 10; let x = fn () { a; }; x();", Value::Int(10));
        eval_to("let x = fn () { a; }; let a = 10; x();", Value::Int(10));
    }

    #[test]
    fn string() {
        eval_to("\"foobar\"", Value::String(String::from("foobar")));
        eval_to("\"foo bar\"", Value::String(String::from("foo bar")));
        eval_to("\"foo\\nbar\"", Value::String(String::from("foo\nbar")));
        eval_to("\"foo\\tbar\"", Value::String(String::from("foo\tbar")));
        eval_to("\"foo\\\"bar\"", Value::String(String::from("foo\"bar")));
        eval_to("\"foo\" + \"bar\"", Value::String(String::from("foobar")));
        eval_to("\"foo\" + \" \" + \"bar\"",
                Value::String(String::from("foo bar")));
        fail_with("\"foo\" - \"bar\"", "\"foo\" is not a number", (1, 1));
    }

    #[test]
    fn array() {
        eval_to("[1, 2, 3, 4]",
                Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]));
        eval_to("let double = fn(x) { x * 2 }; [1, double(2), 3 * 3, 4 - 3]",
                Value::Array(vec![Value::Int(1), Value::Int(4), Value::Int(9), Value::Int(1)]));
        eval_to("[1, 2, 3][0]", Value::Int(1));
        eval_to("[1, 2, 3][1]", Value::Int(2));
        eval_to("[1, 2, 3][2]", Value::Int(3));
        eval_to("let i = 0; [1][i];", Value::Int(1));
        eval_to("[1, 2, 3][1 + 1];", Value::Int(3));
        eval_to("let myArray = [1, 2, 3]; myArray[2];", Value::Int(3));
        eval_to("let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];",
                Value::Int(6));
        eval_to("let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i];",
                Value::Int(2));
        eval_to("[1, 2, 3][3]", Value::Null);
        eval_to("[1, 2, 3][-1]", Value::Null);
    }

    #[test]
    fn built_in_fn() {
        // len
        eval_to("len(\"hello world!\")", Value::Int(12));
        eval_to("len(\"\")", Value::Int(0));
        eval_to("len(\"Hey Bob, how ya doin?\")", Value::Int(21));
        fail_with("len(3)",
                  "invalid arguments for [built-in function: len]: [3]",
                  (1, 1));
        fail_with("len(\"hello\", \"world\")",
                  "wrong number of arguments: 1 expected but 2 given",
                  (1, 1));
        eval_to("len([])", Value::Int(0));
        eval_to("len([1, 2, 3, 4])", Value::Int(4));
        // head
        eval_to("head([1])", Value::Int(1));
        eval_to("head([1, 2, 3, 4])", Value::Int(1));
        fail_with("head([])",
                  "invalid arguments for [built-in function: head]: empty array",
                  (1, 1));
        // tail
        eval_to("tail([1])", Value::Array(vec![]));
        eval_to("tail([1, 2, 3, 4])",
                Value::Array(vec![Value::Int(2), Value::Int(3), Value::Int(4)]));
        fail_with("tail([])",
                  "invalid arguments for [built-in function: tail]: empty array",
                  (1, 1));
        // cons
        eval_to("cons(1, [])", Value::Array(vec![Value::Int(1)]));
        eval_to("cons(1, [2, 3, 4])",
                Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)]));
    }

    static MAP_DECL: &str = "
let map = fn(f, arr) {
  if (len(arr) == 0) {
    []
  } else {
    let h = head(arr);
    cons(f(h), map(f, tail(arr)));
  }
};
";

    static REDUCE_DECL: &str = "
let reduce = fn(f, init, arr) {
  if (len(arr) == 0) {
    init
  } else {
    let newInit = f(init, head(arr));
    reduce(f, newInit, tail(arr));
  }
};
";

    #[test]
    fn map_reduce() {
        eval_to(&(MAP_DECL.to_string() +
                  "let double = fn(x) { x * 2 }; map(double, [1, 2, 3, 4])"),
                Value::Array(vec![Value::Int(2), Value::Int(4), Value::Int(6), Value::Int(8)]));
        eval_to(&(REDUCE_DECL.to_string() +
                  "let add = fn(x, y) { x + y }; reduce(add, 0, [1, 2, 3, 4, 5])"),
                Value::Int(15));
    }

    static HASH1: &str = "
let double = fn(x) {
  x * 2;
};
let arr = [1, 2, 3, 4];
let h = {
  \"one\": 10 - 9,
  \"two\": 8 / 4,
  3: arr[2],
  4: double(2),
  true: if (10 > 8) { true } else { false },
  false: \"hello\" == \"world\"
};
";

    #[test]
    fn hash() {
        eval_to(&(HASH1.to_string() + "h[\"one\"]"), Value::Int(1));
        eval_to(&(HASH1.to_string() + "let s = \"two\"; h[s]"),
                Value::Int(2));
        eval_to(&(HASH1.to_string() + "h[3]"), Value::Int(3));
        eval_to(&(HASH1.to_string() + "h[2 + 2]"), Value::Int(4));
        eval_to(&(HASH1.to_string() + "h[true]"), Value::Bool(true));
        eval_to(&(HASH1.to_string() + "h[5 < 1]"), Value::Bool(false));
        eval_to(&(HASH1.to_string() + "h[100]"), Value::Null);
        fail_with(&(HASH1.to_string() + "h[[]]"), "[] is not hashable", (1, 1));
        fail_with("3[true];", "unexpected index target: 3", (1, 1));
    }
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! default_pub_use {
    () => {
        pub use plygui_api::callbacks;
        pub use plygui_api::controls::*;
        pub use plygui_api::external;
        pub use plygui_api::ids::*;
        pub use plygui_api::layout;
        pub use plygui_api::types::*;
        pub use plygui_api::utils;

        pub mod imp {
            pub use crate::application::Application;
            pub use crate::button::Button;
            pub use crate::frame::Frame;
            pub use crate::image::Image;
            pub use crate::layout_linear::LinearLayout;
            pub use crate::message::Message;
            pub use crate::splitted::Splitted;
            pub use crate::text::Text;
            pub use crate::tray::Tray;
            pub use crate::window::Window;
        }
    };
}

#[macro_export]
macro_rules! default_markup_register_members {
    () => {
        #[cfg(feature = "markup")]
        pub fn register_members(registry: &mut plygui_api::markup::MarkupRegistry) {
            registry.register_member(plygui_api::markup::MEMBER_TYPE_BUTTON.into(), button::spawn).unwrap();
            registry.register_member(plygui_api::markup::MEMBER_TYPE_LINEAR_LAYOUT.into(), layout_linear::spawn).unwrap();
            registry.register_member(plygui_api::markup::MEMBER_TYPE_FRAME.into(), frame::spawn).unwrap();
            registry.register_member(plygui_api::markup::MEMBER_TYPE_SPLITTED.into(), splitted::spawn).unwrap();
        }
    };
}

#[macro_export]
macro_rules! default_impls_as {
    ($typ: ty) => {
        impl_as_any! {$typ}
        impl_as_member! {$typ}
    };
}

#[macro_export]
macro_rules! impl_as_any {
    ($typ: ty) => {
        unsafe fn _as_any_mut(base: &mut ::plygui_api::development::MemberBase) -> &mut ::std::any::Any {
            ::plygui_api::utils::base_to_impl_mut::<$typ>(base)
        }
        unsafe fn _as_any(base: &::plygui_api::development::MemberBase) -> &::std::any::Any {
            ::plygui_api::utils::base_to_impl::<$typ>(base)
        }
    };
}

#[macro_export]
macro_rules! impl_as_member {
    ($typ: ty) => {
        unsafe fn _as_member_mut(base: &mut ::plygui_api::development::MemberBase) -> &mut ::plygui_api::controls::Member {
            ::plygui_api::utils::base_to_impl_mut::<$typ>(base)
        }
        unsafe fn _as_member(base: &::plygui_api::development::MemberBase) -> &::plygui_api::controls::Member {
            ::plygui_api::utils::base_to_impl::<$typ>(base)
        }
    };
}
<commit_msg>Small markup macro update<commit_after>#[macro_export]
macro_rules! default_pub_use {
    () => {
        pub use plygui_api::callbacks;
        pub use plygui_api::controls::*;
        pub use plygui_api::external;
        pub use plygui_api::ids::*;
        pub use plygui_api::layout;
        pub use plygui_api::types::*;
        pub use plygui_api::utils;

        pub mod imp {
            pub use crate::application::Application;
            pub use crate::button::Button;
            pub use crate::frame::Frame;
            pub use crate::image::Image;
            pub use crate::layout_linear::LinearLayout;
            pub use crate::message::Message;
            pub use crate::splitted::Splitted;
            pub use crate::text::Text;
            pub use crate::tray::Tray;
            pub use crate::window::Window;
        }
    };
}

#[macro_export]
macro_rules! default_markup_register_members {
    () => {
        #[cfg(feature = "markup")]
        pub fn register_members(registry: &mut plygui_api::markup::MarkupRegistry) {
            registry.register_member(plygui_api::markup::MEMBER_TYPE_BUTTON.into(), button::spawn).unwrap();
            registry.register_member(plygui_api::markup::MEMBER_TYPE_LINEAR_LAYOUT.into(), layout_linear::spawn).unwrap();
            registry.register_member(plygui_api::markup::MEMBER_TYPE_FRAME.into(), frame::spawn).unwrap();
            registry.register_member(plygui_api::markup::MEMBER_TYPE_SPLITTED.into(), splitted::spawn).unwrap();
            registry.register_member(plygui_api::markup::MEMBER_TYPE_IMAGE.into(), image::spawn).unwrap();
        }
    };
}

#[macro_export]
macro_rules! default_impls_as {
    ($typ: ty) => {
        impl_as_any! {$typ}
        impl_as_member! {$typ}
    };
}

#[macro_export]
macro_rules! impl_as_any {
    ($typ: ty) => {
        unsafe fn _as_any_mut(base: &mut ::plygui_api::development::MemberBase) -> &mut ::std::any::Any {
            ::plygui_api::utils::base_to_impl_mut::<$typ>(base)
        }
        unsafe fn _as_any(base: &::plygui_api::development::MemberBase) -> &::std::any::Any {
            ::plygui_api::utils::base_to_impl::<$typ>(base)
        }
    };
}

#[macro_export]
macro_rules! impl_as_member {
    ($typ: ty) => {
        unsafe fn _as_member_mut(base: &mut ::plygui_api::development::MemberBase) -> &mut ::plygui_api::controls::Member {
            ::plygui_api::utils::base_to_impl_mut::<$typ>(base)
        }
        unsafe fn _as_member(base: &::plygui_api::development::MemberBase) -> &::plygui_api::controls::Member {
            ::plygui_api::utils::base_to_impl::<$typ>(base)
        }
    };
}
<|endoftext|>


<commit_before>
#[macro_export]
macro_rules! approx_eq {
    ($typ:ty, $lhs:expr, $rhs:expr) => {
        {
            let m: <$typ as $crate::ApproxEq>::Margin = Default::default();
            <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
        }
    };
    ($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => {
        {
            let m = <$typ as $crate::ApproxEq>::Margin::zero()$(.$set($val))*;
            <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
        }
    };
    ($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => {
        {
            <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, $marg)
        }
    };
}

// Until saturating_abs() comes out of nightly, we have to code it ourselves.
macro_rules! saturating_abs_i32 {
    ($val:expr) => {
        if $val.is_negative() {
            match $val.checked_neg() {
                Some(v) => v,
                None => core::i32::MAX
            }
        } else {
            $val
        }
    };
}
macro_rules! saturating_abs_i64 {
    ($val:expr) => {
        if $val.is_negative() {
            match $val.checked_neg() {
                Some(v) => v,
                None => core::i64::MAX
            }
        } else {
            $val
        }
    };
}

#[test]
fn test_macro() {
    let a: f32 = 0.15 + 0.15 + 0.15;
    let b: f32 = 0.1 + 0.1 + 0.25;
    assert!( approx_eq!(f32, a, b) ); // uses the default
    assert!( approx_eq!(f32, a, b, ulps = 2) );
    assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
    assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
    assert!( approx_eq!(f32, a, b, (0.0, 2)) );
}

#[test]
fn test_macro_2() {
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, ulps=3) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, epsilon=0.0000000004) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0000000004, 0)) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0, 3)) );
}

#[test]
fn test_macro_3() {
    use crate::F32Margin;

    let a: f32 = 0.15 + 0.15 + 0.15;
    let b: f32 = 0.1 + 0.1 + 0.25;
    assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
    assert!( approx_eq!(f32, a, b, F32Margin::default()) );
}
<commit_msg>Avoid literal use of Default::default<commit_after>
#[macro_export]
macro_rules! approx_eq {
    ($typ:ty, $lhs:expr, $rhs:expr) => {
        {
            let m = <$typ as $crate::ApproxEq>::Margin::default();
            <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
        }
    };
    ($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => {
        {
            let m = <$typ as $crate::ApproxEq>::Margin::zero()$(.$set($val))*;
            <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
        }
    };
    ($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => {
        {
            <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, $marg)
        }
    };
}

// Until saturating_abs() comes out of nightly, we have to code it ourselves.
macro_rules! saturating_abs_i32 {
    ($val:expr) => {
        if $val.is_negative() {
            match $val.checked_neg() {
                Some(v) => v,
                None => core::i32::MAX
            }
        } else {
            $val
        }
    };
}
macro_rules! saturating_abs_i64 {
    ($val:expr) => {
        if $val.is_negative() {
            match $val.checked_neg() {
                Some(v) => v,
                None => core::i64::MAX
            }
        } else {
            $val
        }
    };
}

#[test]
fn test_macro() {
    let a: f32 = 0.15 + 0.15 + 0.15;
    let b: f32 = 0.1 + 0.1 + 0.25;
    assert!( approx_eq!(f32, a, b) ); // uses the default
    assert!( approx_eq!(f32, a, b, ulps = 2) );
    assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
    assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
    assert!( approx_eq!(f32, a, b, (0.0, 2)) );
}

#[test]
fn test_macro_2() {
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, ulps=3) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, epsilon=0.0000000004) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0000000004, 0)) );
    assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0, 3)) );
}

#[test]
fn test_macro_3() {
    use crate::F32Margin;

    let a: f32 = 0.15 + 0.15 + 0.15;
    let b: f32 = 0.1 + 0.1 + 0.25;
    assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
    assert!( approx_eq!(f32, a, b, F32Margin::default()) );
}
<|endoftext|>


<commit_before>use std::fmt;

pub struct Memory {
    rom: u8,
    ram_0x4000_0x7FFF: usize,
    ram_0x8000_0xBFFF: usize,
    ram_0xC000_0xFFFF: usize,

    rom0: Vec<u8>,
    rom1: Vec<u8>,
    bank: [Vec<u8>; 8],
}

impl Memory {
    pub fn new(_rom0: Vec<u8>, _rom1: Vec<u8>) -> Memory {
        Memory {
            rom: 0,
            ram_0x4000_0x7FFF: 5,
            ram_0x8000_0xBFFF: 2,
            ram_0xC000_0xFFFF: 0,

            rom0: _rom0,
            rom1: _rom1,

            bank: [
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024]
            ]
        }
    }

    pub fn read_word(&self, addr: u16) -> u8 {
        if addr >= 0x0000 && addr <= 0x3FFF {
            match self.rom {
                0 => self.rom0[addr as usize],
                1 => self.rom1[addr as usize],
                _ => unreachable!()
            }
        } else if addr >= 0x4000 && addr <= 0x7FFF {
            self.bank[self.ram_0x4000_0x7FFF][(addr - 0x4000) as usize]
        } else if addr >= 0x8000 && addr <= 0xBFFF {
            self.bank[self.ram_0x8000_0xBFFF][(addr - 0x8000) as usize]
        } else if addr >= 0xC000 && addr <= 0xFFFF {
            self.bank[self.ram_0xC000_0xFFFF][(addr - 0xC000) as usize]
        } else {
            panic!("Trying to read from unrecognized address: {:#x}", addr);
        }
    }

    pub fn write_word(&mut self, addr: u16, val: u8) {
        if addr >= 0xC000 && addr <= 0xFFFF {
            self.bank[self.ram_0xC000_0xFFFF][(addr - 0xC000) as usize] = val;
        } else if addr >= 0x8000 && addr <= 0xBFFF {
            self.bank[self.ram_0x8000_0xBFFF][(addr - 0x8000) as usize] = val;
        } else if addr >= 0x4000 && addr <= 0x7FFF {
            self.bank[self.ram_0x4000_0x7FFF][(addr - 0x4000) as usize] = val;
        } else {
            panic!("Trying to write to unrecognized address: {:#x}", addr);
        }
    }

    pub fn change_bank(&mut self, val: u8) {
        self.ram_0xC000_0xFFFF = val as usize;
    }

    pub fn change_rom_bank(&mut self, val: u8) {
        self.rom = val;
    }
}

impl fmt::Debug for Memory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "ram_0xC000_0xFFFF: {}
                     ram_0x8000_0xBFFF: {}
                     ram_0x4000_0x7FFF: {}
                     rom: {}
                    ",
                    self.ram_0xC000_0xFFFF,
                    self.ram_0x8000_0xBFFF,
                    self.ram_0x4000_0x7FFF,
                    self.rom)
    }
}
<commit_msg>Fix some comparison warnings<commit_after>use std::fmt;

pub struct Memory {
    rom: u8,
    ram_0x4000_0x7FFF: usize,
    ram_0x8000_0xBFFF: usize,
    ram_0xC000_0xFFFF: usize,

    rom0: Vec<u8>,
    rom1: Vec<u8>,
    bank: [Vec<u8>; 8],
}

impl Memory {
    pub fn new(_rom0: Vec<u8>, _rom1: Vec<u8>) -> Memory {
        Memory {
            rom: 0,
            ram_0x4000_0x7FFF: 5,
            ram_0x8000_0xBFFF: 2,
            ram_0xC000_0xFFFF: 0,

            rom0: _rom0,
            rom1: _rom1,

            bank: [
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024],
                vec![0; 16 * 1024]
            ]
        }
    }

    pub fn read_word(&self, addr: u16) -> u8 {
        if addr <= 0x3FFF {
            match self.rom {
                0 => self.rom0[addr as usize],
                1 => self.rom1[addr as usize],
                _ => unreachable!()
            }
        } else if addr >= 0x4000 && addr <= 0x7FFF {
            self.bank[self.ram_0x4000_0x7FFF][(addr - 0x4000) as usize]
        } else if addr >= 0x8000 && addr <= 0xBFFF {
            self.bank[self.ram_0x8000_0xBFFF][(addr - 0x8000) as usize]
        } else if addr >= 0xC000 {
            self.bank[self.ram_0xC000_0xFFFF][(addr - 0xC000) as usize]
        } else {
            panic!("Trying to read from unrecognized address: {:#x}", addr);
        }
    }

    pub fn write_word(&mut self, addr: u16, val: u8) {
        if addr >= 0xC000 {
            self.bank[self.ram_0xC000_0xFFFF][(addr - 0xC000) as usize] = val;
        } else if addr >= 0x8000 && addr <= 0xBFFF {
            self.bank[self.ram_0x8000_0xBFFF][(addr - 0x8000) as usize] = val;
        } else if addr >= 0x4000 && addr <= 0x7FFF {
            self.bank[self.ram_0x4000_0x7FFF][(addr - 0x4000) as usize] = val;
        } else {
            panic!("Trying to write to unrecognized address: {:#x}", addr);
        }
    }

    pub fn change_bank(&mut self, val: u8) {
        self.ram_0xC000_0xFFFF = val as usize;
    }

    pub fn change_rom_bank(&mut self, val: u8) {
        self.rom = val;
    }
}

impl fmt::Debug for Memory {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "ram_0xC000_0xFFFF: {}
                     ram_0x8000_0xBFFF: {}
                     ram_0x4000_0x7FFF: {}
                     rom: {}
                    ",
                    self.ram_0xC000_0xFFFF,
                    self.ram_0x8000_0xBFFF,
                    self.ram_0x4000_0x7FFF,
                    self.rom)
    }
}
<|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.

extern crate rustfmt;
extern crate diff;
extern crate regex;
extern crate term;

use std::collections::HashMap;
use std::fs;
use std::io::{self, Read, BufRead, BufReader};
use std::path::Path;

use rustfmt::*;
use rustfmt::filemap::{write_system_newlines, FileMap};
use rustfmt::config::{Config, ReportTactic, WriteMode};
use rustfmt::rustfmt_diff::*;

const DIFF_CONTEXT_SIZE: usize = 3;

fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String {
    let path = dir_entry.expect("Couldn't get DirEntry").path();

    path.to_str().expect("Couldn't stringify path").to_owned()
}

// Integration tests. The files in the tests/source are formatted and compared
// to their equivalent in tests/target. The target file and config can be
// overriden by annotations in the source file. The input and output must match
// exactly.
// FIXME(#28) would be good to check for error messages and fail on them, or at
// least report.
#[test]
fn system_tests() {
    // Get all files in the tests/source directory.
    let files = fs::read_dir("tests/source").expect("Couldn't read source dir");
    // Turn a DirEntry into a String that represents the relative path to the
    // file.
    let files = files.map(get_path_string);
    let (_reports, count, fails) = check_files(files, None);

    // Display results.
    println!("Ran {} system tests.", count);
    assert!(fails == 0, "{} system tests failed", fails);
}

// Do the same for tests/coverage-source directory
// the only difference is the coverage mode
#[test]
fn coverage_tests() {
    let files = fs::read_dir("tests/coverage-source").expect("Couldn't read source dir");
    let files = files.map(get_path_string);
    let (_reports, count, fails) = check_files(files, Some(WriteMode::Coverage));

    println!("Ran {} tests in coverage mode.", count);
    assert!(fails == 0, "{} tests failed", fails);
}

#[test]
fn checkstyle_test() {
    let filename = "tests/source/fn-single-line.rs";
    let expected_filename = "tests/writemode/checkstyle.xml";
    assert_output(filename, expected_filename, Some(WriteMode::Checkstyle));
}

// Helper function for comparing the results of rustfmt
// to a known output file generated by one of the write modes.
fn assert_output(source: &str, expected_filename: &str, write_mode: Option<WriteMode>) {
    let file_map = run_rustfmt(source.to_string(), write_mode);

    let mut config = read_config(&source);
    if let Some(write_mode) = write_mode {
        config.write_mode = write_mode;
    }

    // Populate output by writing to a vec.
    let mut out = vec![];
    let _ = filemap::write_all_files(&file_map, &mut out, &config);
    let output = String::from_utf8(out).unwrap();

    let mut expected_file = fs::File::open(&expected_filename).expect("Couldn't open target");
    let mut expected_text = String::new();
    expected_file.read_to_string(&mut expected_text)
                 .expect("Failed reading target");

    let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE);
    if compare.len() > 0 {
        let mut failures = HashMap::new();
        failures.insert(source.to_string(), compare);
        print_mismatches(failures);
        assert!(false, "Text does not match expected output");
    }
}

// Idempotence tests. Files in tests/target are checked to be unaltered by
// rustfmt.
#[test]
fn idempotence_tests() {
    // Get all files in the tests/target directory.
    let files = fs::read_dir("tests/target")
                    .expect("Couldn't read target dir")
                    .map(get_path_string);
    let (_reports, count, fails) = check_files(files, None);

    // Display results.
    println!("Ran {} idempotent tests.", count);
    assert!(fails == 0, "{} idempotent tests failed", fails);
}

// Run rustfmt on itself. This operation must be idempotent. We also check that
// no warnings are emitted.
#[test]
fn self_tests() {
    let files = fs::read_dir("src/bin")
                    .expect("Couldn't read src dir")
                    .chain(fs::read_dir("tests").expect("Couldn't read tests dir"))
                    .map(get_path_string);
    // Hack because there's no `IntoIterator` impl for `[T; N]`.
    let files = files.chain(Some("src/lib.rs".to_owned()).into_iter());

    let (reports, count, fails) = check_files(files, None);
    let mut warnings = 0;

    // Display results.
    println!("Ran {} self tests.", count);
    assert!(fails == 0, "{} self tests failed", fails);

    for format_report in reports {
        println!("{}", format_report);
        warnings += format_report.warning_count();
    }

    assert!(warnings == 0,
            "Rustfmt's code generated {} warnings",
            warnings);
}

// For each file, run rustfmt and collect the output.
// Returns the number of files checked and the number of failures.
fn check_files<I>(files: I, write_mode: Option<WriteMode>) -> (Vec<FormatReport>, u32, u32)
    where I: Iterator<Item = String>
{
    let mut count = 0;
    let mut fails = 0;
    let mut reports = vec![];

    for file_name in files.filter(|f| f.ends_with(".rs")) {
        println!("Testing '{}'...", file_name);

        match idempotent_check(file_name, write_mode) {
            Ok(report) => reports.push(report),
            Err(msg) => {
                print_mismatches(msg);
                fails += 1;
            }
        }

        count += 1;
    }

    (reports, count, fails)
}

fn print_mismatches(result: HashMap<String, Vec<Mismatch>>) {
    let mut t = term::stdout().unwrap();

    for (file_name, diff) in result {
        print_diff(diff,
                   |line_num| format!("\nMismatch at {}:{}:", file_name, line_num));
    }

    assert!(t.reset().unwrap());
}

fn read_config(filename: &str) -> Config {
    let sig_comments = read_significant_comments(&filename);
    let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..]));

    for (key, val) in &sig_comments {
        if key != "target" && key != "config" {
            config.override_value(key, val);
        }
    }

    // Don't generate warnings for to-do items.
    config.report_todo = ReportTactic::Never;
    config
}

// Simulate run()
fn run_rustfmt(filename: String, write_mode: Option<WriteMode>) -> FileMap {
    let mut config = read_config(&filename);
    if let Some(write_mode) = write_mode {
        config.write_mode = write_mode;
    }
    format(Path::new(&filename), &config)
}

pub fn idempotent_check(filename: String,
                        write_mode: Option<WriteMode>)
                        -> Result<FormatReport, HashMap<String, Vec<Mismatch>>> {
    let sig_comments = read_significant_comments(&filename);
    let config = read_config(&filename);
    let mut file_map = run_rustfmt(filename, write_mode);
    let format_report = fmt_lines(&mut file_map, &config);

    let mut write_result = HashMap::new();
    for (filename, text) in file_map.iter() {
        let mut v = Vec::new();
        // Won't panic, as we're not doing any IO.
        write_system_newlines(&mut v, text, &config).unwrap();
        // Won't panic, we are writing correct utf8.
        let one_result = String::from_utf8(v).unwrap();
        write_result.insert(filename.clone(), one_result);
    }

    let target = sig_comments.get("target").map(|x| &(*x)[..]);

    handle_result(write_result, target, write_mode).map(|_| format_report)
}

// Reads test config file from comments and reads its contents.
fn get_config(config_file: Option<&str>) -> Config {
    let config_file_name = match config_file {
        None => return Default::default(),
        Some(file_name) => {
            let mut full_path = "tests/config/".to_owned();
            full_path.push_str(&file_name);
            full_path
        }
    };

    let mut def_config_file = fs::File::open(config_file_name).expect("Couldn't open config");
    let mut def_config = String::new();
    def_config_file.read_to_string(&mut def_config).expect("Couldn't read config");

    Config::from_toml(&def_config)
}

// Reads significant comments of the form: // rustfmt-key: value
// into a hash map.
fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
    let file = fs::File::open(file_name).expect(&format!("Couldn't read file {}", file_name));
    let reader = BufReader::new(file);
    let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
    let regex = regex::Regex::new(&pattern).expect("Failed creating pattern 1");

    // Matches lines containing significant comments or whitespace.
    let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
                         .expect("Failed creating pattern 2");

    reader.lines()
          .map(|line| line.expect("Failed getting line"))
          .take_while(|line| line_regex.is_match(&line))
          .filter_map(|line| {
              regex.captures_iter(&line).next().map(|capture| {
                  (capture.at(1).expect("Couldn't unwrap capture").to_owned(),
                   capture.at(2).expect("Couldn't unwrap capture").to_owned())
              })
          })
          .collect()
}

// Compare output to input.
// TODO: needs a better name, more explanation.
fn handle_result(result: HashMap<String, String>,
                 target: Option<&str>,
                 write_mode: Option<WriteMode>)
                 -> Result<(), HashMap<String, Vec<Mismatch>>> {
    let mut failures = HashMap::new();

    for (file_name, fmt_text) in result {
        // If file is in tests/source, compare to file with same name in tests/target.
        let target = get_target(&file_name, target, write_mode);
        let mut f = fs::File::open(&target).expect("Couldn't open target");

        let mut text = String::new();
        f.read_to_string(&mut text).expect("Failed reading target");

        if fmt_text != text {
            let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
            assert!(!diff.is_empty(),
                    "Empty diff? Maybe due to a missing a newline at the end of a file?");
            failures.insert(file_name, diff);
        }
    }

    if failures.is_empty() {
        Ok(())
    } else {
        Err(failures)
    }
}

// Map source file paths to their target paths.
fn get_target(file_name: &str, target: Option<&str>, write_mode: Option<WriteMode>) -> String {
    let file_path = Path::new(file_name);
    let (source_path_prefix, target_path_prefix) = match write_mode {
        Some(WriteMode::Coverage) => {
            (Path::new("tests/coverage-source/"), "tests/coverage-target/")
        }
        _ => (Path::new("tests/source/"), "tests/target/"),
    };

    if file_path.starts_with(source_path_prefix) {
        let mut components = file_path.components();
        // Can't skip(2) as the resulting iterator can't as_path()
        components.next();
        components.next();

        let new_target = match components.as_path().to_str() {
            Some(string) => string,
            None => file_name,
        };
        let base = target.unwrap_or(new_target);

        format!("{}{}", target_path_prefix, base)
    } else {
        file_name.to_owned()
    }
}

#[test]
fn rustfmt_diff_make_diff_tests() {
    let diff = make_diff("a\nb\nc\nd", "a\ne\nc\nd", 3);
    assert_eq!(diff,
               vec![Mismatch {
                        line_number: 1,
                        lines: vec![DiffLine::Context("a".into()),
                                    DiffLine::Resulting("b".into()),
                                    DiffLine::Expected("e".into()),
                                    DiffLine::Context("c".into()),
                                    DiffLine::Context("d".into())],
                    }]);
}
<commit_msg>don't read config twice during tests<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.

extern crate rustfmt;
extern crate diff;
extern crate regex;
extern crate term;

use std::collections::HashMap;
use std::fs;
use std::io::{self, Read, BufRead, BufReader};
use std::path::Path;

use rustfmt::*;
use rustfmt::filemap::{write_system_newlines, FileMap};
use rustfmt::config::{Config, ReportTactic, WriteMode};
use rustfmt::rustfmt_diff::*;

const DIFF_CONTEXT_SIZE: usize = 3;

fn get_path_string(dir_entry: io::Result<fs::DirEntry>) -> String {
    let path = dir_entry.expect("Couldn't get DirEntry").path();

    path.to_str().expect("Couldn't stringify path").to_owned()
}

// Integration tests. The files in the tests/source are formatted and compared
// to their equivalent in tests/target. The target file and config can be
// overriden by annotations in the source file. The input and output must match
// exactly.
// FIXME(#28) would be good to check for error messages and fail on them, or at
// least report.
#[test]
fn system_tests() {
    // Get all files in the tests/source directory.
    let files = fs::read_dir("tests/source").expect("Couldn't read source dir");
    // Turn a DirEntry into a String that represents the relative path to the
    // file.
    let files = files.map(get_path_string);
    let (_reports, count, fails) = check_files(files, None);

    // Display results.
    println!("Ran {} system tests.", count);
    assert!(fails == 0, "{} system tests failed", fails);
}

// Do the same for tests/coverage-source directory
// the only difference is the coverage mode
#[test]
fn coverage_tests() {
    let files = fs::read_dir("tests/coverage-source").expect("Couldn't read source dir");
    let files = files.map(get_path_string);
    let (_reports, count, fails) = check_files(files, Some(WriteMode::Coverage));

    println!("Ran {} tests in coverage mode.", count);
    assert!(fails == 0, "{} tests failed", fails);
}

#[test]
fn checkstyle_test() {
    let filename = "tests/source/fn-single-line.rs";
    let expected_filename = "tests/writemode/checkstyle.xml";
    assert_output(filename, expected_filename, Some(WriteMode::Checkstyle));
}

// Helper function for comparing the results of rustfmt
// to a known output file generated by one of the write modes.
fn assert_output(source: &str, expected_filename: &str, write_mode: Option<WriteMode>) {
    let config = read_config(&source, write_mode);
    let file_map = run_rustfmt(source.to_string(), &config);

    // Populate output by writing to a vec.
    let mut out = vec![];
    let _ = filemap::write_all_files(&file_map, &mut out, &config);
    let output = String::from_utf8(out).unwrap();

    let mut expected_file = fs::File::open(&expected_filename).expect("Couldn't open target");
    let mut expected_text = String::new();
    expected_file.read_to_string(&mut expected_text)
                 .expect("Failed reading target");

    let compare = make_diff(&expected_text, &output, DIFF_CONTEXT_SIZE);
    if compare.len() > 0 {
        let mut failures = HashMap::new();
        failures.insert(source.to_string(), compare);
        print_mismatches(failures);
        assert!(false, "Text does not match expected output");
    }
}

// Idempotence tests. Files in tests/target are checked to be unaltered by
// rustfmt.
#[test]
fn idempotence_tests() {
    // Get all files in the tests/target directory.
    let files = fs::read_dir("tests/target")
                    .expect("Couldn't read target dir")
                    .map(get_path_string);
    let (_reports, count, fails) = check_files(files, None);

    // Display results.
    println!("Ran {} idempotent tests.", count);
    assert!(fails == 0, "{} idempotent tests failed", fails);
}

// Run rustfmt on itself. This operation must be idempotent. We also check that
// no warnings are emitted.
#[test]
fn self_tests() {
    let files = fs::read_dir("src/bin")
                    .expect("Couldn't read src dir")
                    .chain(fs::read_dir("tests").expect("Couldn't read tests dir"))
                    .map(get_path_string);
    // Hack because there's no `IntoIterator` impl for `[T; N]`.
    let files = files.chain(Some("src/lib.rs".to_owned()).into_iter());

    let (reports, count, fails) = check_files(files, None);
    let mut warnings = 0;

    // Display results.
    println!("Ran {} self tests.", count);
    assert!(fails == 0, "{} self tests failed", fails);

    for format_report in reports {
        println!("{}", format_report);
        warnings += format_report.warning_count();
    }

    assert!(warnings == 0,
            "Rustfmt's code generated {} warnings",
            warnings);
}

// For each file, run rustfmt and collect the output.
// Returns the number of files checked and the number of failures.
fn check_files<I>(files: I, write_mode: Option<WriteMode>) -> (Vec<FormatReport>, u32, u32)
    where I: Iterator<Item = String>
{
    let mut count = 0;
    let mut fails = 0;
    let mut reports = vec![];

    for file_name in files.filter(|f| f.ends_with(".rs")) {
        println!("Testing '{}'...", file_name);

        match idempotent_check(file_name, write_mode) {
            Ok(report) => reports.push(report),
            Err(msg) => {
                print_mismatches(msg);
                fails += 1;
            }
        }

        count += 1;
    }

    (reports, count, fails)
}

fn print_mismatches(result: HashMap<String, Vec<Mismatch>>) {
    let mut t = term::stdout().unwrap();

    for (file_name, diff) in result {
        print_diff(diff,
                   |line_num| format!("\nMismatch at {}:{}:", file_name, line_num));
    }

    assert!(t.reset().unwrap());
}

fn read_config(filename: &str, write_mode: Option<WriteMode>) -> Config {
    let sig_comments = read_significant_comments(&filename);
    let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..]));

    for (key, val) in &sig_comments {
        if key != "target" && key != "config" {
            config.override_value(key, val);
        }
    }

    // Don't generate warnings for to-do items.
    config.report_todo = ReportTactic::Never;

    if let Some(mode) = write_mode {
        config.write_mode = mode
    }

    config
}

// Simulate run()
fn run_rustfmt(filename: String, config: &Config) -> FileMap {
    format(Path::new(&filename), &config)
}

pub fn idempotent_check(filename: String,
                        write_mode: Option<WriteMode>)
                        -> Result<FormatReport, HashMap<String, Vec<Mismatch>>> {
    let sig_comments = read_significant_comments(&filename);
    let config = read_config(&filename, write_mode);
    let mut file_map = run_rustfmt(filename, &config);
    let format_report = fmt_lines(&mut file_map, &config);

    let mut write_result = HashMap::new();
    for (filename, text) in file_map.iter() {
        let mut v = Vec::new();
        // Won't panic, as we're not doing any IO.
        write_system_newlines(&mut v, text, &config).unwrap();
        // Won't panic, we are writing correct utf8.
        let one_result = String::from_utf8(v).unwrap();
        write_result.insert(filename.clone(), one_result);
    }

    let target = sig_comments.get("target").map(|x| &(*x)[..]);

    handle_result(write_result, target, write_mode).map(|_| format_report)
}

// Reads test config file from comments and reads its contents.
fn get_config(config_file: Option<&str>) -> Config {
    let config_file_name = match config_file {
        None => return Default::default(),
        Some(file_name) => {
            let mut full_path = "tests/config/".to_owned();
            full_path.push_str(&file_name);
            full_path
        }
    };

    let mut def_config_file = fs::File::open(config_file_name).expect("Couldn't open config");
    let mut def_config = String::new();
    def_config_file.read_to_string(&mut def_config).expect("Couldn't read config");

    Config::from_toml(&def_config)
}

// Reads significant comments of the form: // rustfmt-key: value
// into a hash map.
fn read_significant_comments(file_name: &str) -> HashMap<String, String> {
    let file = fs::File::open(file_name).expect(&format!("Couldn't read file {}", file_name));
    let reader = BufReader::new(file);
    let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)";
    let regex = regex::Regex::new(&pattern).expect("Failed creating pattern 1");

    // Matches lines containing significant comments or whitespace.
    let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)")
                         .expect("Failed creating pattern 2");

    reader.lines()
          .map(|line| line.expect("Failed getting line"))
          .take_while(|line| line_regex.is_match(&line))
          .filter_map(|line| {
              regex.captures_iter(&line).next().map(|capture| {
                  (capture.at(1).expect("Couldn't unwrap capture").to_owned(),
                   capture.at(2).expect("Couldn't unwrap capture").to_owned())
              })
          })
          .collect()
}

// Compare output to input.
// TODO: needs a better name, more explanation.
fn handle_result(result: HashMap<String, String>,
                 target: Option<&str>,
                 write_mode: Option<WriteMode>)
                 -> Result<(), HashMap<String, Vec<Mismatch>>> {
    let mut failures = HashMap::new();

    for (file_name, fmt_text) in result {
        // If file is in tests/source, compare to file with same name in tests/target.
        let target = get_target(&file_name, target, write_mode);
        let mut f = fs::File::open(&target).expect("Couldn't open target");

        let mut text = String::new();
        f.read_to_string(&mut text).expect("Failed reading target");

        if fmt_text != text {
            let diff = make_diff(&text, &fmt_text, DIFF_CONTEXT_SIZE);
            assert!(!diff.is_empty(),
                    "Empty diff? Maybe due to a missing a newline at the end of a file?");
            failures.insert(file_name, diff);
        }
    }

    if failures.is_empty() {
        Ok(())
    } else {
        Err(failures)
    }
}

// Map source file paths to their target paths.
fn get_target(file_name: &str, target: Option<&str>, write_mode: Option<WriteMode>) -> String {
    let file_path = Path::new(file_name);
    let (source_path_prefix, target_path_prefix) = match write_mode {
        Some(WriteMode::Coverage) => {
            (Path::new("tests/coverage-source/"), "tests/coverage-target/")
        }
        _ => (Path::new("tests/source/"), "tests/target/"),
    };

    if file_path.starts_with(source_path_prefix) {
        let mut components = file_path.components();
        // Can't skip(2) as the resulting iterator can't as_path()
        components.next();
        components.next();

        let new_target = match components.as_path().to_str() {
            Some(string) => string,
            None => file_name,
        };
        let base = target.unwrap_or(new_target);

        format!("{}{}", target_path_prefix, base)
    } else {
        file_name.to_owned()
    }
}

#[test]
fn rustfmt_diff_make_diff_tests() {
    let diff = make_diff("a\nb\nc\nd", "a\ne\nc\nd", 3);
    assert_eq!(diff,
               vec![Mismatch {
                        line_number: 1,
                        lines: vec![DiffLine::Context("a".into()),
                                    DiffLine::Resulting("b".into()),
                                    DiffLine::Expected("e".into()),
                                    DiffLine::Context("c".into()),
                                    DiffLine::Context("d".into())],
                    }]);
}
<|endoftext|>