<commit_before>#![allow(non_camel_case_types)]

use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem::transmute;

use static_assertions::assert_eq_size;

// TODO static assertion

// Minimal ELF support, sufficient for a very simple 64-bit static Linux
// executable.

// Sources:
// * /usr/include/elf.h
// * https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
// * https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html

type Elf64_Half = u16;
type Elf64_Word = u32;
type Elf64_Xword = u64;
type Elf64_Addr = u64;
type Elf64_Off = u64;

const ELFOSABI_LINUX: u8 = 3;
const ET_EXEC: u16 = 2;
const EM_X86_64: u16 = 62;
const PT_LOAD: u32 = 1;

const PF_X: u32 = 1 << 0;
const PF_W: u32 = 1 << 1;
const PF_R: u32 = 1 << 2;

const EHDR_SIZE: usize = 64;
const PHDR_SIZE: usize = 56;
const SHDR_SIZE: usize = 64;

assert_eq_size!(ehdr_size_assert; Elf64_Ehdr, [u8; EHDR_SIZE]);
assert_eq_size!(phdr_size_assert; Elf64_Phdr, [u8; PHDR_SIZE]);
assert_eq_size!(shdr_size_assert; Elf64_Shdr, [u8; SHDR_SIZE]);

// ELF header
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Elf64_Ehdr {
    pub e_ident: [u8; 16],
    pub e_type: Elf64_Half,
    pub e_machine: Elf64_Half,
    pub e_version: Elf64_Word,
    pub e_entry: Elf64_Addr,
    pub e_phoff: Elf64_Off,
    pub e_shoff: Elf64_Off,
    pub e_flags: Elf64_Word,
    pub e_ehsize: Elf64_Half,
    pub e_phentsize: Elf64_Half,
    pub e_phnum: Elf64_Half,
    pub e_shentsize: Elf64_Half,
    pub e_shnum: Elf64_Half,
    pub e_shstrndx: Elf64_Half,
}

// Program header
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Elf64_Phdr {
    pub p_type: Elf64_Word,
    pub p_flags: Elf64_Word,
    pub p_offset: Elf64_Off,
    pub p_vaddr: Elf64_Addr,
    pub p_paddr: Elf64_Addr,
    pub p_filesz: Elf64_Xword,
    pub p_memsz: Elf64_Xword,
    pub p_align: Elf64_Xword,
}

// Symbol header
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Elf64_Shdr {
    pub sh_name: Elf64_Word,
    pub sh_type: Elf64_Word,
    pub sh_flags: Elf64_Xword,
    pub sh_addr: Elf64_Addr,
    pub sh_offset: Elf64_Off,
    pub sh_size: Elf64_Xword,
    pub sh_link: Elf64_Word,
    pub sh_info: Elf64_Word,
    pub sh_addralign: Elf64_Xword,
    pub sh_entsize: Elf64_Xword,
}

pub fn elf64_write(f: &mut impl Write, text: &[u8], bss_size: u64) -> io::Result<()> {
    let size = text.len() as u64;
    let hdr_size = (EHDR_SIZE + 2 * PHDR_SIZE) as u64;
    let hdr_size_padded = (hdr_size + 0x1000 - 1) & !(0x1000 - 1);

    let ehdr = Elf64_Ehdr {
        e_ident: [0x7f, b'E', b'L', b'F', 2, 1, 1, ELFOSABI_LINUX, 0, 0, 0, 0, 0, 0, 0, 0 ],
        e_type: ET_EXEC,
        e_machine: EM_X86_64,
        e_version: 1,
        e_entry: 0x401000,
        // Put program header immediately after ELF header
        e_phoff: EHDR_SIZE as u64,
        // Don't include a section header table
        e_shoff: 0,
        e_flags: 0,
        e_ehsize: EHDR_SIZE as u16,
        e_phentsize: PHDR_SIZE as u16,
        e_phnum: 2,
        e_shentsize: SHDR_SIZE as u16,
        e_shnum: 0,
        e_shstrndx: 0,
    };

    let phdr_text = Elf64_Phdr {
        p_type: PT_LOAD,
        p_flags: PF_R | PF_X,
        p_offset: hdr_size_padded,
        p_vaddr: 0x401000,
        p_paddr: 0x401000,
        p_filesz: size,
        p_memsz: size,
        p_align: 0x1000,
    };

    let bss_offset = (size + 0x1000 - 1) & !(0x1000 - 1);

    let phdr_bss = Elf64_Phdr {
        p_type: PT_LOAD,
        p_flags: PF_R | PF_W,
        p_offset: hdr_size_padded + bss_offset,
        p_vaddr: 0x401000 + bss_offset,
        p_paddr: 0x401000 + bss_offset,
        p_filesz: 0,
        p_memsz: bss_size,
        p_align: 0x1000,
    };

    unsafe {
        f.write(&transmute::<_, [u8; EHDR_SIZE]>(ehdr))?;
        f.write(&transmute::<_, [u8; PHDR_SIZE]>(phdr_text))?;
        f.write(&transmute::<_, [u8; PHDR_SIZE]>(phdr_bss))?;
    }
    for _ in 0..(hdr_size_padded - hdr_size) {
        f.write(b"0")?;
    }
    f.write(text)?;
    Ok(())
}

fn elf64_read_strtab(f: &mut (impl Read + Seek), ehdr: &Elf64_Ehdr) -> io::Result<Vec<u8>> {
    // Read section header for the string table
    let mut shdr_buf = [0; SHDR_SIZE];
    f.seek(SeekFrom::Start(ehdr.e_shoff + ehdr.e_shstrndx as u64 * SHDR_SIZE as u64))?;
    f.read(&mut shdr_buf)?;
    let shdr: Elf64_Shdr = unsafe { transmute(shdr_buf) };

    // Read string table section
    let mut strtab = Vec::with_capacity(shdr.sh_size as usize);
    strtab.resize(shdr.sh_size as usize, 0);
    f.seek(SeekFrom::Start(shdr.sh_offset))?;
    f.read(&mut strtab)?;

    Ok(strtab)
}

pub fn elf64_get_section(f: &mut (impl Read + Seek), name: &[u8]) -> io::Result<Option<Elf64_Shdr>> {
    let mut ehdr_buf = [0; EHDR_SIZE];
    f.seek(SeekFrom::Start(0))?;
    f.read(&mut ehdr_buf)?;
    let ehdr: Elf64_Ehdr = unsafe { transmute(ehdr_buf) };

    let strtab = elf64_read_strtab(f, &ehdr)?;

    f.seek(SeekFrom::Start(ehdr.e_shoff))?;
    for _ in 0..ehdr.e_shnum {
        let mut shdr_buf = [0; SHDR_SIZE];
        f.read(&mut shdr_buf)?;
        let shdr: Elf64_Shdr = unsafe { transmute(shdr_buf) };

        let mut cur_name = &strtab[shdr.sh_name as usize..];
        if let Some(idx) = cur_name.iter().position(|c| *c == b'\0') {
            cur_name = &cur_name[..idx];
        }
        if cur_name == name {
            return Ok(Some(shdr));
        }
    }

    Ok(None)
}
<commit_msg>Use more constants in elf.rs, borrowed from elf.h<commit_after>#![allow(non_camel_case_types)]

use std::ops::Range;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem::transmute;

use static_assertions::assert_eq_size;

// Minimal ELF support, sufficient for a very simple 64-bit static Linux
// executable.

// Sources:
// * /usr/include/elf.h
// * https://wiki.osdev.org/ELF_Tutorial
// * https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
// * https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
// * https://unix.stackexchange.com/questions/132036/why-does-readelf-show-system-v-as-my-os-instead-of-linux
// * http://www.sco.com/developers/gabi/latest/ch4.eheader.html

type Elf64_Half = u16;
type Elf64_Word = u32;
type Elf64_Xword = u64;
type Elf64_Addr = u64;
type Elf64_Off = u64;

const EI_MAG: Range<usize> = 0..4;
const EI_CLASS: usize = 4;
const EI_DATA: usize = 5;
const EI_VERSION: usize = 6;
const EI_OSABI: usize = 7;

const ELFCLASS64: u8 = 2;
const ELFDATA2LSB: u8 = 1;
const ELFOSABI_SYSV: u8 = 0;
const ET_EXEC: u16 = 2;
const EM_X86_64: u16 = 62;
const PT_LOAD: u32 = 1;

const PF_X: u32 = 1 << 0;
const PF_W: u32 = 1 << 1;
const PF_R: u32 = 1 << 2;

const EHDR_SIZE: usize = 64;
const PHDR_SIZE: usize = 56;
const SHDR_SIZE: usize = 64;

assert_eq_size!(ehdr_size_assert; Elf64_Ehdr, [u8; EHDR_SIZE]);
assert_eq_size!(phdr_size_assert; Elf64_Phdr, [u8; PHDR_SIZE]);
assert_eq_size!(shdr_size_assert; Elf64_Shdr, [u8; SHDR_SIZE]);

// ELF header
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Elf64_Ehdr {
    pub e_ident: [u8; 16],
    pub e_type: Elf64_Half,
    pub e_machine: Elf64_Half,
    pub e_version: Elf64_Word,
    pub e_entry: Elf64_Addr,
    pub e_phoff: Elf64_Off,
    pub e_shoff: Elf64_Off,
    pub e_flags: Elf64_Word,
    pub e_ehsize: Elf64_Half,
    pub e_phentsize: Elf64_Half,
    pub e_phnum: Elf64_Half,
    pub e_shentsize: Elf64_Half,
    pub e_shnum: Elf64_Half,
    pub e_shstrndx: Elf64_Half,
}

// Program header
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct Elf64_Phdr {
    pub p_type: Elf64_Word,
    pub p_flags: Elf64_Word,
    pub p_offset: Elf64_Off,
    pub p_vaddr: Elf64_Addr,
    pub p_paddr: Elf64_Addr,
    pub p_filesz: Elf64_Xword,
    pub p_memsz: Elf64_Xword,
    pub p_align: Elf64_Xword,
}

// Symbol header
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Elf64_Shdr {
    pub sh_name: Elf64_Word,
    pub sh_type: Elf64_Word,
    pub sh_flags: Elf64_Xword,
    pub sh_addr: Elf64_Addr,
    pub sh_offset: Elf64_Off,
    pub sh_size: Elf64_Xword,
    pub sh_link: Elf64_Word,
    pub sh_info: Elf64_Word,
    pub sh_addralign: Elf64_Xword,
    pub sh_entsize: Elf64_Xword,
}

pub fn elf64_write(f: &mut impl Write, text: &[u8], bss_size: u64) -> io::Result<()> {
    let size = text.len() as u64;
    let hdr_size = (EHDR_SIZE + 2 * PHDR_SIZE) as u64;
    let hdr_size_padded = (hdr_size + 0x1000 - 1) & !(0x1000 - 1);

    let mut e_ident = [0u8; 16];
    e_ident[EI_MAG].copy_from_slice(b"\x7fELF");
    e_ident[EI_CLASS] = ELFCLASS64;
    e_ident[EI_DATA] = ELFDATA2LSB;
    e_ident[EI_VERSION] = 1;
    e_ident[EI_OSABI] = ELFOSABI_SYSV;

    let ehdr = Elf64_Ehdr {
        e_ident,
        e_type: ET_EXEC,
        e_machine: EM_X86_64,
        e_version: 1,
        e_entry: 0x401000,
        // Put program header immediately after ELF header
        e_phoff: EHDR_SIZE as u64,
        // Don't include a section header table
        e_shoff: 0,
        e_flags: 0,
        e_ehsize: EHDR_SIZE as u16,
        e_phentsize: PHDR_SIZE as u16,
        e_phnum: 2,
        e_shentsize: SHDR_SIZE as u16,
        e_shnum: 0,
        e_shstrndx: 0,
    };

    let phdr_text = Elf64_Phdr {
        p_type: PT_LOAD,
        p_flags: PF_R | PF_X,
        p_offset: hdr_size_padded,
        p_vaddr: 0x401000,
        p_paddr: 0x401000,
        p_filesz: size,
        p_memsz: size,
        p_align: 0x1000,
    };

    let bss_offset = (size + 0x1000 - 1) & !(0x1000 - 1);

    let phdr_bss = Elf64_Phdr {
        p_type: PT_LOAD,
        p_flags: PF_R | PF_W,
        p_offset: hdr_size_padded + bss_offset,
        p_vaddr: 0x401000 + bss_offset,
        p_paddr: 0x401000 + bss_offset,
        p_filesz: 0,
        p_memsz: bss_size,
        p_align: 0x1000,
    };

    unsafe {
        f.write(&transmute::<_, [u8; EHDR_SIZE]>(ehdr))?;
        f.write(&transmute::<_, [u8; PHDR_SIZE]>(phdr_text))?;
        f.write(&transmute::<_, [u8; PHDR_SIZE]>(phdr_bss))?;
    }
    for _ in 0..(hdr_size_padded - hdr_size) {
        f.write(b"0")?;
    }
    f.write(text)?;
    Ok(())
}

fn elf64_read_strtab(f: &mut (impl Read + Seek), ehdr: &Elf64_Ehdr) -> io::Result<Vec<u8>> {
    // Read section header for the string table
    let mut shdr_buf = [0; SHDR_SIZE];
    f.seek(SeekFrom::Start(ehdr.e_shoff + ehdr.e_shstrndx as u64 * SHDR_SIZE as u64))?;
    f.read(&mut shdr_buf)?;
    let shdr: Elf64_Shdr = unsafe { transmute(shdr_buf) };

    // Read string table section
    let mut strtab = Vec::with_capacity(shdr.sh_size as usize);
    strtab.resize(shdr.sh_size as usize, 0);
    f.seek(SeekFrom::Start(shdr.sh_offset))?;
    f.read(&mut strtab)?;

    Ok(strtab)
}

pub fn elf64_get_section(f: &mut (impl Read + Seek), name: &[u8]) -> io::Result<Option<Elf64_Shdr>> {
    let mut ehdr_buf = [0; EHDR_SIZE];
    f.seek(SeekFrom::Start(0))?;
    f.read(&mut ehdr_buf)?;
    let ehdr: Elf64_Ehdr = unsafe { transmute(ehdr_buf) };

    let strtab = elf64_read_strtab(f, &ehdr)?;

    f.seek(SeekFrom::Start(ehdr.e_shoff))?;
    for _ in 0..ehdr.e_shnum {
        let mut shdr_buf = [0; SHDR_SIZE];
        f.read(&mut shdr_buf)?;
        let shdr: Elf64_Shdr = unsafe { transmute(shdr_buf) };

        let mut cur_name = &strtab[shdr.sh_name as usize..];
        if let Some(idx) = cur_name.iter().position(|c| *c == b'\0') {
            cur_name = &cur_name[..idx];
        }
        if cur_name == name {
            return Ok(Some(shdr));
        }
    }

    Ok(None)
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::LinkedList;

use ::expr::{Expr, Proc, Error as E, Result};

pub struct Env {
    global: HashMap<String, Expr>,
    local: LinkedList<HashMap<String, Expr>>,
    fglobal: HashMap<String, Proc>,
    flocal: LinkedList<HashMap<String, Proc>>,
}

impl Env {
    pub fn new() -> Env {
        Env {
            global: HashMap::new(),
            local: LinkedList::new(),
            fglobal: HashMap::new(),
            flocal: LinkedList::new()
        }
    }

    pub fn new_local(&mut self) {
        self.local.push_front(HashMap::new());
        self.flocal.push_front(HashMap::new());
    }

    pub fn end_local(&mut self) {
        self.local.pop_front();
        self.flocal.pop_front();
    }

    pub fn register<S: Into<String>>(&mut self, name: S, value: Expr) {
        match self.local.front_mut() {
            Some(l) => l.insert(name.into(), value),
            None => self.global.insert(name.into(), value)
        };
    }

    pub fn fregister<S: Into<String>>(&mut self, name: S, value: Proc) {
        match self.flocal.front_mut() {
            Some(l) => l.insert(name.into(), value),
            None => self.fglobal.insert(name.into(), value)
        };
    }

    pub fn find(&self, name: &String)  -> Result<&Expr> {
        for m in self.local.iter() {
            match m.get(name) {
                Some(v) => return Ok(v),
                None => ()
            }
        };
        match self.global.get(name) {
            Some(v) => Ok(v),
            None => Err(E::Unbound(name.clone()))
        }
    }

    pub fn ffind(&self, name: &String)  -> Result<&Proc> {
        for m in self.flocal.iter() {
            match m.get(name) {
                Some(v) => return Ok(v),
                None => ()
            }
        };
        println!("{:?}", self.fglobal);
        match self.fglobal.get(name) {
            Some(v) => Ok(v),
            None => Err(E::Unbound(name.clone()))
        }
    }

}
<commit_msg>remove debug print<commit_after>use std::collections::HashMap;
use std::collections::LinkedList;

use ::expr::{Expr, Proc, Error as E, Result};

pub struct Env {
    global: HashMap<String, Expr>,
    local: LinkedList<HashMap<String, Expr>>,
    fglobal: HashMap<String, Proc>,
    flocal: LinkedList<HashMap<String, Proc>>,
}

impl Env {
    pub fn new() -> Env {
        Env {
            global: HashMap::new(),
            local: LinkedList::new(),
            fglobal: HashMap::new(),
            flocal: LinkedList::new()
        }
    }

    pub fn new_local(&mut self) {
        self.local.push_front(HashMap::new());
        self.flocal.push_front(HashMap::new());
    }

    pub fn end_local(&mut self) {
        self.local.pop_front();
        self.flocal.pop_front();
    }

    pub fn register<S: Into<String>>(&mut self, name: S, value: Expr) {
        match self.local.front_mut() {
            Some(l) => l.insert(name.into(), value),
            None => self.global.insert(name.into(), value)
        };
    }

    pub fn fregister<S: Into<String>>(&mut self, name: S, value: Proc) {
        match self.flocal.front_mut() {
            Some(l) => l.insert(name.into(), value),
            None => self.fglobal.insert(name.into(), value)
        };
    }

    pub fn find(&self, name: &String)  -> Result<&Expr> {
        for m in self.local.iter() {
            match m.get(name) {
                Some(v) => return Ok(v),
                None => ()
            }
        };
        match self.global.get(name) {
            Some(v) => Ok(v),
            None => Err(E::Unbound(name.clone()))
        }
    }

    pub fn ffind(&self, name: &String)  -> Result<&Proc> {
        for m in self.flocal.iter() {
            match m.get(name) {
                Some(v) => return Ok(v),
                None => ()
            }
        };
        match self.fglobal.get(name) {
            Some(v) => Ok(v),
            None => Err(E::Unbound(name.clone()))
        }
    }

}
<|endoftext|>


<commit_before>//! Defines types to use with the geospatial commands.

use super::{ErrorKind, RedisResult};
use types::{FromRedisValue, RedisWrite, ToRedisArgs, Value};

macro_rules! invalid_type_error {
    ($v:expr, $det:expr) => {{
        fail!((
            ErrorKind::TypeError,
            "Response was of incompatible type",
            format!("{:?} (response was {:?})", $det, $v)
        ));
    }};
}

/// Units used by [`geo_dist`][1] and [`geo_radius`][2].
///
/// [1]: ../trait.Commands.html#method.geo_dist
/// [2]: ../trait.Commands.html#method.geo_radius
pub enum Unit {
    Meters,
    Kilometers,
    Miles,
    Feet,
}

impl ToRedisArgs for Unit {
    fn write_redis_args<W>(&self, out: &mut W)
    where
        W: ?Sized + RedisWrite,
    {
        let unit = match *self {
            Unit::Meters => "m",
            Unit::Kilometers => "km",
            Unit::Miles => "mi",
            Unit::Feet => "ft",
        };
        out.write_arg(unit.as_bytes());
    }
}

/// A coordinate (longitude, latitude). Can be used with [`geo_pos`][1]
/// to parse response from Redis.
///
/// [1]: ../trait.Commands.html#method.geo_pos
///
/// `T` is the type of the every value.
///
/// * You may want to use either `f64` or `f32` if you want to perform mathematical operations.
/// * To keep the raw value from Redis, use `String`.
#[derive(Debug, PartialEq)]
pub struct Coord<T> {
    pub longitude: T,
    pub latitude: T,
}

impl<T> Coord<T> {
    /// Create a new Coord with the (longitude, latitude)
    pub fn lon_lat(longitude: T, latitude: T) -> Coord<T> {
        Coord {
            longitude,
            latitude,
        }
    }
}

impl<T: FromRedisValue> FromRedisValue for Coord<T> {
    fn from_redis_value(v: &Value) -> RedisResult<Self> {
        let values: Vec<T> = FromRedisValue::from_redis_value(v)?;
        let mut values = values.into_iter();
        let (longitude, latitude) = match (values.next(), values.next(), values.next()) {
            (Some(longitude), Some(latitude), None) => (longitude, latitude),
            _ => invalid_type_error!(v, "Expect a pair of numbers"),
        };
        Ok(Coord { longitude, latitude })
    }
}

impl<T: ToRedisArgs> ToRedisArgs for Coord<T> {
    fn write_redis_args<W>(&self, out: &mut W)
    where
        W: ?Sized + RedisWrite,
    {
        ToRedisArgs::write_redis_args(&self.longitude, out);
        ToRedisArgs::write_redis_args(&self.latitude, out);
    }

    fn is_single_arg(&self) -> bool {
        false
    }
}

/// Options to sort results from [GEORADIUS][1] and [GEORADIUSBYMEMBER][2] commands
///
/// [1]: https://redis.io/commands/georadius
/// [2]: https://redis.io/commands/georadiusbymember
pub enum RadiusOrder {
    /// Don't sort the results
    Unsorted,

    /// Sort returned items from the nearest to the farthest, relative to the center.
    Asc,

    /// Sort returned items from the farthest to the nearest, relative to the center.
    Desc,
}

impl Default for RadiusOrder {
    fn default() -> RadiusOrder {
        RadiusOrder::Unsorted
    }
}

/// Options for the [GEORADIUS][1] and [GEORADIUSBYMEMBER][2] commands
///
/// [1]: https://redis.io/commands/georadius
/// [2]: https://redis.io/commands/georadiusbymember
///
/// # Example
///
/// ```rust,no_run
/// use redis::{Commands, RedisResult};
/// use redis::geo::{RadiusSearchResult, RadiusOptions, RadiusOrder, Unit};
/// fn nearest_in_radius(
///     con: &mut redis::Connection,
///     key: &str,
///     longitude: f64,
///     latitude: f64,
///     meters: f64,
///     limit: usize,
/// ) -> RedisResult<Vec<RadiusSearchResult>> {
///     let opts = RadiusOptions::default()
///         .order(RadiusOrder::Asc)
///         .limit(limit);
///     con.geo_radius(key, longitude, latitude, meters, Unit::Meters, opts)
/// }
/// ```
#[derive(Default)]
pub struct RadiusOptions {
    with_coord: bool,
    with_dist: bool,
    count: Option<usize>,
    order: RadiusOrder,
    store: Option<Vec<Vec<u8>>>,
    store_dist: Option<Vec<Vec<u8>>>,
}

impl RadiusOptions {
    /// Limit the results to the first N matching items.
    pub fn limit(mut self, n: usize) -> Self {
        self.count = Some(n);
        self
    }

    /// Return the distance of the returned items from the specified center.
    /// The distance is returned in the same unit as the unit specified as the
    /// radius argument of the command.
    pub fn with_dist(mut self) -> Self {
        self.with_dist = true;
        self
    }

    /// Return the `longitude, latitude` coordinates of the matching items.
    pub fn with_coord(mut self) -> Self {
        self.with_coord = true;
        self
    }

    /// Sort the returned items
    pub fn order(mut self, o: RadiusOrder) -> Self {
        self.order = o;
        self
    }

    /// Store the results in a sorted set at `key`, instead of returning them.
    ///
    /// This feature can't be used with any `with_*` method.
    pub fn store<K: ToRedisArgs>(mut self, key: K) -> Self {
        self.store = Some(ToRedisArgs::to_redis_args(&key));
        self
    }

    /// Store the results in a sorted set at `key`, with the distance from the
    /// center as its score. This feature can't be used with any `with_*` method.
    pub fn store_dist<K: ToRedisArgs>(mut self, key: K) -> Self {
        self.store_dist = Some(ToRedisArgs::to_redis_args(&key));
        self
    }
}

impl ToRedisArgs for RadiusOptions {
    fn write_redis_args<W>(&self, out: &mut W)
    where
        W: ?Sized + RedisWrite,
    {
        if self.with_coord {
            out.write_arg("WITHCOORD".as_bytes());
        }

        if self.with_dist {
            out.write_arg("WITHDIST".as_bytes());
        }

        if let Some(n) = self.count {
            out.write_arg("COUNT".as_bytes());
            out.write_arg(format!("{}", n).as_bytes());
        }

        match self.order {
            RadiusOrder::Asc => out.write_arg("ASC".as_bytes()),
            RadiusOrder::Desc => out.write_arg("DESC".as_bytes()),
            _ => (),
        };

        if let Some(ref store) = self.store {
            out.write_arg("STORE".as_bytes());
            for i in store {
                out.write_arg(i);
            }
        }

        if let Some(ref store_dist) = self.store_dist {
            out.write_arg("STOREDIST".as_bytes());
            for i in store_dist {
                out.write_arg(i);
            }
        }
    }

    fn is_single_arg(&self) -> bool {
        false
    }
}

/// Contain an item returned by [`geo_radius`][1] and [`geo_radius_by_member`][2].
///
/// [1]: ../trait.Commands.html#method.geo_radius
/// [2]: ../trait.Commands.html#method.geo_radius_by_member
pub struct RadiusSearchResult {
    pub name: String,
    pub coord: Option<Coord<f64>>,
    pub dist: Option<f64>,
}

impl FromRedisValue for RadiusSearchResult {
    fn from_redis_value(v: &Value) -> RedisResult<Self> {
        // If we receive only the member name, it will be a plain string
        if let Ok(name) = FromRedisValue::from_redis_value(v) {
            return Ok(RadiusSearchResult {
                name: name,
                coord: None,
                dist: None,
            });
        }

        // Try to parse the result from multitple values
        if let Value::Bulk(ref items) = *v {
            if let Some(result) = RadiusSearchResult::parse_multi_values(items) {
                return Ok(result);
            }
        }

        invalid_type_error!(v, "Response type not RadiusSearchResult compatible.");
    }
}

impl RadiusSearchResult {
    fn parse_multi_values(items: &[Value]) -> Option<Self> {
        let mut iter = items.iter();

        // First item is always the member name
        let name: String = match iter.next().map(FromRedisValue::from_redis_value) {
            Some(Ok(n)) => n,
            _ => return None,
        };

        let mut next = iter.next();

        // Next element, if present, will be the distance.
        let dist = match next.map(FromRedisValue::from_redis_value) {
            Some(Ok(c)) => {
                next = iter.next();
                Some(c)
            }
            _ => None,
        };

        // Finally, if present, the last item will be the coordinates

        let coord = match next.map(FromRedisValue::from_redis_value) {
            Some(Ok(c)) => Some(c),
            _ => None,
        };

        Some(RadiusSearchResult { name, coord, dist })
    }
}

#[cfg(test)]
mod tests {
    use super::{Coord, RadiusOptions, RadiusOrder};
    use std::str;
    use types::ToRedisArgs;

    macro_rules! assert_args {
        ($value:expr, $($args:expr),+) => {
            let args = $value.to_redis_args();
            let strings: Vec<_> = args.iter()
                                      .map(|a| str::from_utf8(a.as_ref()).unwrap())
                                      .collect();
            assert_eq!(strings, vec![$($args),+]);
        }
    }

    #[test]
    fn test_coord_to_args() {
        let member = ("Palermo", Coord::lon_lat("13.361389", "38.115556"));
        assert_args!(&member, "Palermo", "13.361389", "38.115556");
    }

    #[test]
    fn test_radius_options() {
        // Without options, should not generate any argument
        let empty = RadiusOptions::default();
        assert_eq!(ToRedisArgs::to_redis_args(&empty).len(), 0);

        // Some combinations with WITH* options
        let opts = RadiusOptions::default;

        assert_args!(opts().with_coord().with_dist(), "WITHCOORD", "WITHDIST");

        assert_args!(opts().limit(50), "COUNT", "50");

        assert_args!(opts().limit(50).store("x"), "COUNT", "50", "STORE", "x");

        assert_args!(
            opts().limit(100).store_dist("y"),
            "COUNT",
            "100",
            "STOREDIST",
            "y"
        );

        assert_args!(
            opts().order(RadiusOrder::Asc).limit(10).with_dist(),
            "WITHDIST",
            "COUNT",
            "10",
            "ASC"
        );
    }
}
<commit_msg>Rust cargo fmt<commit_after>//! Defines types to use with the geospatial commands.

use super::{ErrorKind, RedisResult};
use types::{FromRedisValue, RedisWrite, ToRedisArgs, Value};

macro_rules! invalid_type_error {
    ($v:expr, $det:expr) => {{
        fail!((
            ErrorKind::TypeError,
            "Response was of incompatible type",
            format!("{:?} (response was {:?})", $det, $v)
        ));
    }};
}

/// Units used by [`geo_dist`][1] and [`geo_radius`][2].
///
/// [1]: ../trait.Commands.html#method.geo_dist
/// [2]: ../trait.Commands.html#method.geo_radius
pub enum Unit {
    Meters,
    Kilometers,
    Miles,
    Feet,
}

impl ToRedisArgs for Unit {
    fn write_redis_args<W>(&self, out: &mut W)
    where
        W: ?Sized + RedisWrite,
    {
        let unit = match *self {
            Unit::Meters => "m",
            Unit::Kilometers => "km",
            Unit::Miles => "mi",
            Unit::Feet => "ft",
        };
        out.write_arg(unit.as_bytes());
    }
}

/// A coordinate (longitude, latitude). Can be used with [`geo_pos`][1]
/// to parse response from Redis.
///
/// [1]: ../trait.Commands.html#method.geo_pos
///
/// `T` is the type of the every value.
///
/// * You may want to use either `f64` or `f32` if you want to perform mathematical operations.
/// * To keep the raw value from Redis, use `String`.
#[derive(Debug, PartialEq)]
pub struct Coord<T> {
    pub longitude: T,
    pub latitude: T,
}

impl<T> Coord<T> {
    /// Create a new Coord with the (longitude, latitude)
    pub fn lon_lat(longitude: T, latitude: T) -> Coord<T> {
        Coord {
            longitude,
            latitude,
        }
    }
}

impl<T: FromRedisValue> FromRedisValue for Coord<T> {
    fn from_redis_value(v: &Value) -> RedisResult<Self> {
        let values: Vec<T> = FromRedisValue::from_redis_value(v)?;
        let mut values = values.into_iter();
        let (longitude, latitude) = match (values.next(), values.next(), values.next()) {
            (Some(longitude), Some(latitude), None) => (longitude, latitude),
            _ => invalid_type_error!(v, "Expect a pair of numbers"),
        };
        Ok(Coord {
            longitude,
            latitude,
        })
    }
}

impl<T: ToRedisArgs> ToRedisArgs for Coord<T> {
    fn write_redis_args<W>(&self, out: &mut W)
    where
        W: ?Sized + RedisWrite,
    {
        ToRedisArgs::write_redis_args(&self.longitude, out);
        ToRedisArgs::write_redis_args(&self.latitude, out);
    }

    fn is_single_arg(&self) -> bool {
        false
    }
}

/// Options to sort results from [GEORADIUS][1] and [GEORADIUSBYMEMBER][2] commands
///
/// [1]: https://redis.io/commands/georadius
/// [2]: https://redis.io/commands/georadiusbymember
pub enum RadiusOrder {
    /// Don't sort the results
    Unsorted,

    /// Sort returned items from the nearest to the farthest, relative to the center.
    Asc,

    /// Sort returned items from the farthest to the nearest, relative to the center.
    Desc,
}

impl Default for RadiusOrder {
    fn default() -> RadiusOrder {
        RadiusOrder::Unsorted
    }
}

/// Options for the [GEORADIUS][1] and [GEORADIUSBYMEMBER][2] commands
///
/// [1]: https://redis.io/commands/georadius
/// [2]: https://redis.io/commands/georadiusbymember
///
/// # Example
///
/// ```rust,no_run
/// use redis::{Commands, RedisResult};
/// use redis::geo::{RadiusSearchResult, RadiusOptions, RadiusOrder, Unit};
/// fn nearest_in_radius(
///     con: &mut redis::Connection,
///     key: &str,
///     longitude: f64,
///     latitude: f64,
///     meters: f64,
///     limit: usize,
/// ) -> RedisResult<Vec<RadiusSearchResult>> {
///     let opts = RadiusOptions::default()
///         .order(RadiusOrder::Asc)
///         .limit(limit);
///     con.geo_radius(key, longitude, latitude, meters, Unit::Meters, opts)
/// }
/// ```
#[derive(Default)]
pub struct RadiusOptions {
    with_coord: bool,
    with_dist: bool,
    count: Option<usize>,
    order: RadiusOrder,
    store: Option<Vec<Vec<u8>>>,
    store_dist: Option<Vec<Vec<u8>>>,
}

impl RadiusOptions {
    /// Limit the results to the first N matching items.
    pub fn limit(mut self, n: usize) -> Self {
        self.count = Some(n);
        self
    }

    /// Return the distance of the returned items from the specified center.
    /// The distance is returned in the same unit as the unit specified as the
    /// radius argument of the command.
    pub fn with_dist(mut self) -> Self {
        self.with_dist = true;
        self
    }

    /// Return the `longitude, latitude` coordinates of the matching items.
    pub fn with_coord(mut self) -> Self {
        self.with_coord = true;
        self
    }

    /// Sort the returned items
    pub fn order(mut self, o: RadiusOrder) -> Self {
        self.order = o;
        self
    }

    /// Store the results in a sorted set at `key`, instead of returning them.
    ///
    /// This feature can't be used with any `with_*` method.
    pub fn store<K: ToRedisArgs>(mut self, key: K) -> Self {
        self.store = Some(ToRedisArgs::to_redis_args(&key));
        self
    }

    /// Store the results in a sorted set at `key`, with the distance from the
    /// center as its score. This feature can't be used with any `with_*` method.
    pub fn store_dist<K: ToRedisArgs>(mut self, key: K) -> Self {
        self.store_dist = Some(ToRedisArgs::to_redis_args(&key));
        self
    }
}

impl ToRedisArgs for RadiusOptions {
    fn write_redis_args<W>(&self, out: &mut W)
    where
        W: ?Sized + RedisWrite,
    {
        if self.with_coord {
            out.write_arg("WITHCOORD".as_bytes());
        }

        if self.with_dist {
            out.write_arg("WITHDIST".as_bytes());
        }

        if let Some(n) = self.count {
            out.write_arg("COUNT".as_bytes());
            out.write_arg(format!("{}", n).as_bytes());
        }

        match self.order {
            RadiusOrder::Asc => out.write_arg("ASC".as_bytes()),
            RadiusOrder::Desc => out.write_arg("DESC".as_bytes()),
            _ => (),
        };

        if let Some(ref store) = self.store {
            out.write_arg("STORE".as_bytes());
            for i in store {
                out.write_arg(i);
            }
        }

        if let Some(ref store_dist) = self.store_dist {
            out.write_arg("STOREDIST".as_bytes());
            for i in store_dist {
                out.write_arg(i);
            }
        }
    }

    fn is_single_arg(&self) -> bool {
        false
    }
}

/// Contain an item returned by [`geo_radius`][1] and [`geo_radius_by_member`][2].
///
/// [1]: ../trait.Commands.html#method.geo_radius
/// [2]: ../trait.Commands.html#method.geo_radius_by_member
pub struct RadiusSearchResult {
    pub name: String,
    pub coord: Option<Coord<f64>>,
    pub dist: Option<f64>,
}

impl FromRedisValue for RadiusSearchResult {
    fn from_redis_value(v: &Value) -> RedisResult<Self> {
        // If we receive only the member name, it will be a plain string
        if let Ok(name) = FromRedisValue::from_redis_value(v) {
            return Ok(RadiusSearchResult {
                name: name,
                coord: None,
                dist: None,
            });
        }

        // Try to parse the result from multitple values
        if let Value::Bulk(ref items) = *v {
            if let Some(result) = RadiusSearchResult::parse_multi_values(items) {
                return Ok(result);
            }
        }

        invalid_type_error!(v, "Response type not RadiusSearchResult compatible.");
    }
}

impl RadiusSearchResult {
    fn parse_multi_values(items: &[Value]) -> Option<Self> {
        let mut iter = items.iter();

        // First item is always the member name
        let name: String = match iter.next().map(FromRedisValue::from_redis_value) {
            Some(Ok(n)) => n,
            _ => return None,
        };

        let mut next = iter.next();

        // Next element, if present, will be the distance.
        let dist = match next.map(FromRedisValue::from_redis_value) {
            Some(Ok(c)) => {
                next = iter.next();
                Some(c)
            }
            _ => None,
        };

        // Finally, if present, the last item will be the coordinates

        let coord = match next.map(FromRedisValue::from_redis_value) {
            Some(Ok(c)) => Some(c),
            _ => None,
        };

        Some(RadiusSearchResult { name, coord, dist })
    }
}

#[cfg(test)]
mod tests {
    use super::{Coord, RadiusOptions, RadiusOrder};
    use std::str;
    use types::ToRedisArgs;

    macro_rules! assert_args {
        ($value:expr, $($args:expr),+) => {
            let args = $value.to_redis_args();
            let strings: Vec<_> = args.iter()
                                      .map(|a| str::from_utf8(a.as_ref()).unwrap())
                                      .collect();
            assert_eq!(strings, vec![$($args),+]);
        }
    }

    #[test]
    fn test_coord_to_args() {
        let member = ("Palermo", Coord::lon_lat("13.361389", "38.115556"));
        assert_args!(&member, "Palermo", "13.361389", "38.115556");
    }

    #[test]
    fn test_radius_options() {
        // Without options, should not generate any argument
        let empty = RadiusOptions::default();
        assert_eq!(ToRedisArgs::to_redis_args(&empty).len(), 0);

        // Some combinations with WITH* options
        let opts = RadiusOptions::default;

        assert_args!(opts().with_coord().with_dist(), "WITHCOORD", "WITHDIST");

        assert_args!(opts().limit(50), "COUNT", "50");

        assert_args!(opts().limit(50).store("x"), "COUNT", "50", "STORE", "x");

        assert_args!(
            opts().limit(100).store_dist("y"),
            "COUNT",
            "100",
            "STOREDIST",
            "y"
        );

        assert_args!(
            opts().order(RadiusOrder::Asc).limit(10).with_dist(),
            "WITHDIST",
            "COUNT",
            "10",
            "ASC"
        );
    }
}
<|endoftext|>


<commit_before>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;

use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub trait DatabaseEntry<A> : Send {
    fn get_base(&self) -> A;
}

impl<A : Send + Clone> DatabaseEntry<A> for A {
    fn get_base(&self) -> A { self.clone() }
}

/// Type A is some key
pub trait Database<A, B : DatabaseEntry<A>> : Sync + Send {
    fn add_pending(&self, entry: A);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<B>;

    fn add_test_results(&self, entry: B, results: BuildResult);

    fn results_for_entry(&self, entry: &B) -> Option<String>;
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate pg_typeprovider;

    use self::pg_typeprovider::util::Joinable;

    use std::sync::Mutex;

    use super::postgres::{Connection, SslMode, ToSql};

    use builder::BuildResult;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::{Database, DatabaseEntry};

    pg_table!(builds)

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    let lock = db.db.lock();
                    lock.execute(
                        "DELETE FROM users", &[]).unwrap();
                    lock.execute(
                        "DELETE FROM builds", &[]).unwrap();
                    },
                None => ()
            };
            retval
        }
    }

    impl DatabaseEntry<BuildInsert> for Build {
        fn get_base(&self) -> BuildInsert {
            BuildInsert {
                status: self.status,
                clone_url: self.clone_url.clone(),
                branch: self.branch.clone(),
                results: self.results.clone()
            }
        }
    }

    fn get_one_build(conn: &Connection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &Connection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    impl Database<BuildInsert, Build> for PostgresDatabase {
        fn add_pending(&self, entry: BuildInsert) {
            entry.insert(&*self.db.lock());
        }

        fn get_pending(&self) -> Option<Build> {
            let conn: &Connection = &*self.db.lock();
            loop {
                match get_one_build(conn) {
                    Some(b) => {
                        if try_lock_build(conn, &b) {
                            return Some(b);
                        }
                    },
                    None => { return None; }
                }
            }
        }

        fn add_test_results(&self, entry: Build, results: BuildResult) {
            let num_updated =
                BuildUpdate::new()
                .status_to((&Done).to_int())
                .results_to(results.to_string())
                .where_id(entry.id)
                .update(&*self.db.lock());
            assert_eq!(num_updated, 1);
        }

        fn results_for_entry(&self, entry: &Build) -> Option<String> {
            BuildSearch::new()
                .where_id(entry.id)
                .where_status((&Done).to_int())
                .search(&*self.db.lock(), Some(1))
                .pop()
                .map(|b| b.results)
        }
    }
}

/// For integration tests.
pub mod testing {
    use std::collections::HashMap;
    use std::hash::Hash;
    use std::sync::mpmc_bounded_queue::Queue;
    use std::sync::RWLock;

    use builder::BuildResult;
    use super::Database;

    /// Simply a directory to a status.
    pub struct TestDatabase<A> {
        pending: Queue<A>,
        results: RWLock<HashMap<A, BuildResult>>,
    }

    impl<A : Clone + Eq + Send + Hash> TestDatabase<A> {
        pub fn new<A : Eq + Send + Hash>() -> TestDatabase<A> {
            TestDatabase {
                pending: Queue::with_capacity(10),
                results: RWLock::new(HashMap::new())
            }
        }
    }

    impl<A : Clone + Eq + Send + Hash> Database<A, A> for TestDatabase<A> {
        fn add_pending(&self, entry: A) {
            self.pending.push(entry);
        }

        fn get_pending(&self) -> Option<A> {
            self.pending.pop()
        }

        fn add_test_results(&self, entry: A, results: BuildResult) {
            let mut val = self.results.write();
            val.insert(entry, results);
            val.downgrade();
        }

        fn results_for_entry(&self, entry: &A) -> Option<String> {
            self.results.read().get(entry).map(|res| res.to_string())
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::Database;
    use super::testing::TestDatabase;

    use builder::BuildResult::TestSuccess;

    use std::collections::HashMap;

    static KEY : &'static str = "foobar";

    fn add_get_pending<D : Database<Path, Path>>(db: &D) {
        db.add_pending(Path::new(KEY));
        let actual = db.get_pending().and_then(|pending| {
            pending.as_str().map(|s| s.to_string())
        });
        let expected = Path::new(KEY).as_str().map(|s| s.to_string());
        assert_eq!(actual, expected);
    }

    fn add_test_results<D : Database<Path, Path>>(db: &D) {
        add_get_pending(db);
        db.add_test_results(Path::new(KEY),
                            TestSuccess(HashMap::new()));
    }

    #[test]
    fn memory_add_get_pending() {
        add_get_pending(&TestDatabase::<Path>::new());
    }

    #[test]
    fn memory_add_test_results() {
        add_test_results(&TestDatabase::<Path>::new());
    }
}
<commit_msg>Added method to handle db locking as opposed to making downstream code repeatedly lock it.<commit_after>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;

use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub trait DatabaseEntry<A> : Send {
    fn get_base(&self) -> A;
}

impl<A : Send + Clone> DatabaseEntry<A> for A {
    fn get_base(&self) -> A { self.clone() }
}

/// Type A is some key
pub trait Database<A, B : DatabaseEntry<A>> : Sync + Send {
    fn add_pending(&self, entry: A);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<B>;

    fn add_test_results(&self, entry: B, results: BuildResult);

    fn results_for_entry(&self, entry: &B) -> Option<String>;
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate pg_typeprovider;

    use self::pg_typeprovider::util::Joinable;

    use std::sync::Mutex;

    use super::postgres::{Connection, SslMode, ToSql};

    use builder::BuildResult;
    use super::EntryStatus::{Pending, InProgress, Done};
    use super::{Database, DatabaseEntry};

    pg_table!(builds)

    pub struct PostgresDatabase {
        db: Mutex<Connection>
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: Mutex::new(db)
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        conn.execute(
                            "DELETE FROM users", &[]).unwrap();
                        conn.execute(
                            "DELETE FROM builds", &[]).unwrap();
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&*self.db.lock())
        }
    }

    impl DatabaseEntry<BuildInsert> for Build {
        fn get_base(&self) -> BuildInsert {
            BuildInsert {
                status: self.status,
                clone_url: self.clone_url.clone(),
                branch: self.branch.clone(),
                results: self.results.clone()
            }
        }
    }

    fn get_one_build(conn: &Connection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &Connection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    impl Database<BuildInsert, Build> for PostgresDatabase {
        fn add_pending(&self, entry: BuildInsert) {
            self.with_connection(|conn| entry.insert(conn));
        }

        fn get_pending(&self) -> Option<Build> {
            self.with_connection(|conn| {
                loop {
                    match get_one_build(conn) {
                        Some(b) => {
                            if try_lock_build(conn, &b) {
                                return Some(b);
                            }
                        },
                        None => { return None; }
                    }
                }
            })
        }

        fn add_test_results(&self, entry: Build, results: BuildResult) {
            self.with_connection(|conn| {
                let num_updated =
                    BuildUpdate::new()
                    .status_to((&Done).to_int())
                    .results_to(results.to_string())
                    .where_id(entry.id)
                    .update(conn);
                assert_eq!(num_updated, 1);
            });
        }

        fn results_for_entry(&self, entry: &Build) -> Option<String> {
            self.with_connection(|conn| {
                BuildSearch::new()
                    .where_id(entry.id)
                    .where_status((&Done).to_int())
                    .search(conn, Some(1))
                    .pop()
                    .map(|b| b.results)
            })
        }
    }
}

/// For integration tests.
pub mod testing {
    use std::collections::HashMap;
    use std::hash::Hash;
    use std::sync::mpmc_bounded_queue::Queue;
    use std::sync::RWLock;

    use builder::BuildResult;
    use super::Database;

    /// Simply a directory to a status.
    pub struct TestDatabase<A> {
        pending: Queue<A>,
        results: RWLock<HashMap<A, BuildResult>>,
    }

    impl<A : Clone + Eq + Send + Hash> TestDatabase<A> {
        pub fn new<A : Eq + Send + Hash>() -> TestDatabase<A> {
            TestDatabase {
                pending: Queue::with_capacity(10),
                results: RWLock::new(HashMap::new())
            }
        }
    }

    impl<A : Clone + Eq + Send + Hash> Database<A, A> for TestDatabase<A> {
        fn add_pending(&self, entry: A) {
            self.pending.push(entry);
        }

        fn get_pending(&self) -> Option<A> {
            self.pending.pop()
        }

        fn add_test_results(&self, entry: A, results: BuildResult) {
            let mut val = self.results.write();
            val.insert(entry, results);
            val.downgrade();
        }

        fn results_for_entry(&self, entry: &A) -> Option<String> {
            self.results.read().get(entry).map(|res| res.to_string())
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::Database;
    use super::testing::TestDatabase;

    use builder::BuildResult::TestSuccess;

    use std::collections::HashMap;

    static KEY : &'static str = "foobar";

    fn add_get_pending<D : Database<Path, Path>>(db: &D) {
        db.add_pending(Path::new(KEY));
        let actual = db.get_pending().and_then(|pending| {
            pending.as_str().map(|s| s.to_string())
        });
        let expected = Path::new(KEY).as_str().map(|s| s.to_string());
        assert_eq!(actual, expected);
    }

    fn add_test_results<D : Database<Path, Path>>(db: &D) {
        add_get_pending(db);
        db.add_test_results(Path::new(KEY),
                            TestSuccess(HashMap::new()));
    }

    #[test]
    fn memory_add_get_pending() {
        add_get_pending(&TestDatabase::<Path>::new());
    }

    #[test]
    fn memory_add_test_results() {
        add_test_results(&TestDatabase::<Path>::new());
    }
}
<|endoftext|>


<commit_before>use antidote::RwLock;
use irc::client::data::User;
use irc::client::prelude::*;

use chrono::NaiveDateTime;
use humantime::format_duration;
use std::time::Duration;
use time;

use plugin::*;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::Fail;
use failure::ResultExt;

pub mod database;
use self::database::Database;

#[derive(PluginName)]
pub struct Tell<T: Database> {
    tells: RwLock<T>,
}

impl<T: Database> Tell<T> {
    pub fn new(db: T) -> Tell<T> {
        Tell {
            tells: RwLock::new(db),
        }
    }

    fn tell_command(
        &self,
        client: &IrcClient,
        command: PluginCommand,
    ) -> Result<String, TellError> {
        if command.tokens.len() < 2 {
            return Ok(self.invalid_command().to_owned());
        }

        let mut online = Vec::new();

        let receivers = command.tokens[0].split(',').filter(|&s| !s.is_empty());
        let sender = command.source;

        let mut no_receiver = true;
        for receiver in receivers {
            if receiver.eq_ignore_ascii_case(client.current_nickname())
                || receiver.eq_ignore_ascii_case(&sender)
            {
                online.push(receiver);
                // TODO Change this when https://github.com/aatxe/irc/issues/136 gets resolved
                // continue;
            }

            let channels = client
                .list_channels()
                .expect("The irc crate should not be compiled with the \"nochanlists\" feature");

            let find_receiver = |option: Option<Vec<User>>| {
                option.and_then(|users| {
                    users
                        .into_iter()
                        .find(|user| user.get_nickname().eq_ignore_ascii_case(&receiver))
                })
            };

            if channels
                .iter()
                .map(|channel| client.list_users(&channel))
                .map(find_receiver)
                .any(|option| option.is_some())
            {
                online.push(receiver);
                continue;
            }

            let tm = time::now().to_timespec();
            let message = command.tokens[1..].join(" ");
            let tell = database::NewTellMessage {
                sender: &sender,
                receiver: &receiver.to_lowercase(),
                time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
                message: &message,
            };

            debug!("Saving tell for {:?}", receiver);
            self.tells.write().insert_tell(&tell)?;
            no_receiver = false;
        }

        Ok(if no_receiver && online.is_empty() {
            String::from("Invalid receiver.")
        } else {
            match online.len() {
                0 => format!("Got it!"),
                1 => format!("{} is currently online.", online[0]),
                _ => format!("{} are currently online.", online.join(", ")),
            }
        })
    }

    fn on_namelist(&self, client: &IrcClient, channel: &str) -> Result<(), FrippyError> {
        let receivers = self.tells
            .read()
            .get_receivers()
            .context(FrippyErrorKind::Tell)?;

        if let Some(users) = client.list_users(channel) {
            debug!("Outstanding tells for {:?}", receivers);

            for receiver in users
                .iter()
                .map(|u| u.get_nickname())
                .filter(|u| receivers.iter().any(|r| r == &u.to_lowercase()))
            {
                self.send_tells(client, receiver)?;
            }

            Ok(())
        } else {
            Ok(())
        }
    }

    fn send_tells(&self, client: &IrcClient, receiver: &str) -> Result<(), FrippyError> {
        if client.current_nickname() == receiver {
            return Ok(());
        }

        let mut tells = self.tells.write();

        let tell_messages = match tells.get_tells(&receiver.to_lowercase()) {
            Ok(t) => t,
            Err(e) => {
                // This warning only occurs if frippy is built without a database
                #[allow(unreachable_patterns)]
                return match e.kind() {
                    ErrorKind::NotFound => Ok(()),
                    _ => Err(e.context(FrippyErrorKind::Tell))?,
                };
            }
        };

        for tell in tell_messages {
            let now = Duration::new(time::now().to_timespec().sec as u64, 0);
            let dur = now - Duration::new(tell.time.timestamp() as u64, 0);
            let human_dur = format_duration(dur);

            client
                .send_notice(
                    receiver,
                    &format!(
                        "Tell from {} {} ago: {}",
                        tell.sender, human_dur, tell.message
                    ),
                )
                .context(FrippyErrorKind::Connection)?;

            debug!(
                "Sent {:?} from {:?} to {:?}",
                tell.message, tell.sender, receiver
            );
        }

        tells
            .delete_tells(&receiver.to_lowercase())
            .context(FrippyErrorKind::Tell)?;

        Ok(())
    }

    fn invalid_command(&self) -> &str {
        "Incorrect Command. \
         Send \"tell help\" for help."
    }

    fn help(&self) -> &str {
        "Used to send messages to offline users which they will receive when they come online.\r\n
         usage: tell user message\r\n\
         example: tell Foobar Hello!"
    }
}

impl<T: Database> Plugin for Tell<T> {
    fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
        let res = match message.command {
            Command::JOIN(_, _, _) => self.send_tells(client, message.source_nickname().unwrap()),
            Command::NICK(ref nick) => self.send_tells(client, nick),
            Command::PRIVMSG(ref nick, _) => self.send_tells(client, nick),
            Command::Response(resp, ref chan_info, _) => {
                if resp == Response::RPL_NAMREPLY {
                    debug!("NAMREPLY info: {:?}", chan_info);

                    self.on_namelist(client, &chan_info[chan_info.len() - 1])
                } else {
                    Ok(())
                }
            }
            _ => Ok(()),
        };

        match res {
            Ok(_) => ExecutionStatus::Done,
            Err(e) => ExecutionStatus::Err(e),
        }
    }

    fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
        panic!("Tell should not use threading")
    }

    fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            client
                .send_notice(&command.source, &self.invalid_command())
                .context(FrippyErrorKind::Connection)?;
            return Ok(());
        }

        let sender = command.source.to_owned();

        match command.tokens[0].as_ref() {
            "help" => client
                .send_notice(&command.source, &self.help())
                .context(FrippyErrorKind::Connection),
            _ => match self.tell_command(client, command) {
                Ok(msg) => client
                    .send_notice(&sender, &msg)
                    .context(FrippyErrorKind::Connection),
                Err(e) => client
                    .send_notice(&sender, &e.to_string())
                    .context(FrippyErrorKind::Connection),
            },
        }?;

        Ok(())
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from("This Plugin does not implement any commands."))
    }
}

use std::fmt;
impl<T: Database> fmt::Debug for Tell<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Tell {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "TellError"]
    pub enum ErrorKind {
        /// Not found command error
        #[fail(display = "Tell was not found")]
        NotFound,

        /// MySQL error
        #[cfg(feature = "mysql")]
        #[fail(display = "Failed to execute MySQL Query")]
        MysqlError,

        /// No connection error
        #[cfg(feature = "mysql")]
        #[fail(display = "No connection to the database")]
        NoConnection,
    }
}
<commit_msg>Tell: Fix tell accept condition<commit_after>use antidote::RwLock;
use irc::client::data::User;
use irc::client::prelude::*;

use chrono::NaiveDateTime;
use humantime::format_duration;
use std::time::Duration;
use time;

use plugin::*;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::Fail;
use failure::ResultExt;

pub mod database;
use self::database::Database;

#[derive(PluginName)]
pub struct Tell<T: Database> {
    tells: RwLock<T>,
}

impl<T: Database> Tell<T> {
    pub fn new(db: T) -> Tell<T> {
        Tell {
            tells: RwLock::new(db),
        }
    }

    fn tell_command(
        &self,
        client: &IrcClient,
        command: PluginCommand,
    ) -> Result<String, TellError> {
        if command.tokens.len() < 2 {
            return Ok(self.invalid_command().to_owned());
        }

        let mut online = Vec::new();

        let receivers = command.tokens[0].split(',').filter(|&s| !s.is_empty());
        let sender = command.source;

        let mut no_receiver = true;
        for receiver in receivers {
            if receiver.eq_ignore_ascii_case(client.current_nickname())
                || receiver.eq_ignore_ascii_case(&sender)
            {
                if !online.contains(&receiver) {
                    online.push(receiver);
                }
                continue;
            }

            let channels = client
                .list_channels()
                .expect("The irc crate should not be compiled with the \"nochanlists\" feature");

            let find_receiver = |option: Option<Vec<User>>| {
                option.and_then(|users| {
                    users
                        .into_iter()
                        .find(|user| user.get_nickname().eq_ignore_ascii_case(&receiver))
                })
            };

            if channels
                .iter()
                .map(|channel| client.list_users(&channel))
                .map(find_receiver)
                .any(|option| option.is_some())
            {
                if !online.contains(&receiver) {
                    online.push(receiver);
                }
                // TODO Change this when https://github.com/aatxe/irc/issues/136 gets resolved
                //continue;
            }

            let tm = time::now().to_timespec();
            let message = command.tokens[1..].join(" ");
            let tell = database::NewTellMessage {
                sender: &sender,
                receiver: &receiver.to_lowercase(),
                time: NaiveDateTime::from_timestamp(tm.sec, 0u32),
                message: &message,
            };

            debug!("Saving tell for {:?}", receiver);
            self.tells.write().insert_tell(&tell)?;
            no_receiver = false;
        }

        Ok(if no_receiver && online.is_empty() {
            String::from("Invalid receiver.")
        } else {
            match online.len() {
                0 => format!("Got it!"),
                1 => format!("{} is currently online.", online[0]),
                _ => format!("{} are currently online.", online.join(", ")),
            }
        })
    }

    fn on_namelist(&self, client: &IrcClient, channel: &str) -> Result<(), FrippyError> {
        let receivers = self.tells
            .read()
            .get_receivers()
            .context(FrippyErrorKind::Tell)?;

        if let Some(users) = client.list_users(channel) {
            debug!("Outstanding tells for {:?}", receivers);

            for receiver in users
                .iter()
                .map(|u| u.get_nickname())
                .filter(|u| receivers.iter().any(|r| r == &u.to_lowercase()))
            {
                self.send_tells(client, receiver)?;
            }

            Ok(())
        } else {
            Ok(())
        }
    }

    fn send_tells(&self, client: &IrcClient, receiver: &str) -> Result<(), FrippyError> {
        if client.current_nickname() == receiver {
            return Ok(());
        }

        let mut tells = self.tells.write();

        let tell_messages = match tells.get_tells(&receiver.to_lowercase()) {
            Ok(t) => t,
            Err(e) => {
                // This warning only occurs if frippy is built without a database
                #[allow(unreachable_patterns)]
                return match e.kind() {
                    ErrorKind::NotFound => Ok(()),
                    _ => Err(e.context(FrippyErrorKind::Tell))?,
                };
            }
        };

        for tell in tell_messages {
            let now = Duration::new(time::now().to_timespec().sec as u64, 0);
            let dur = now - Duration::new(tell.time.timestamp() as u64, 0);
            let human_dur = format_duration(dur);

            client
                .send_notice(
                    receiver,
                    &format!(
                        "Tell from {} {} ago: {}",
                        tell.sender, human_dur, tell.message
                    ),
                )
                .context(FrippyErrorKind::Connection)?;

            debug!(
                "Sent {:?} from {:?} to {:?}",
                tell.message, tell.sender, receiver
            );
        }

        tells
            .delete_tells(&receiver.to_lowercase())
            .context(FrippyErrorKind::Tell)?;

        Ok(())
    }

    fn invalid_command(&self) -> &str {
        "Incorrect Command. \
         Send \"tell help\" for help."
    }

    fn help(&self) -> &str {
        "Used to send messages to offline users which they will receive when they come online.\r\n
         usage: tell user message\r\n\
         example: tell Foobar Hello!"
    }
}

impl<T: Database> Plugin for Tell<T> {
    fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
        let res = match message.command {
            Command::JOIN(_, _, _) => self.send_tells(client, message.source_nickname().unwrap()),
            Command::NICK(ref nick) => self.send_tells(client, nick),
            Command::PRIVMSG(ref nick, _) => self.send_tells(client, nick),
            Command::Response(resp, ref chan_info, _) => {
                if resp == Response::RPL_NAMREPLY {
                    debug!("NAMREPLY info: {:?}", chan_info);

                    self.on_namelist(client, &chan_info[chan_info.len() - 1])
                } else {
                    Ok(())
                }
            }
            _ => Ok(()),
        };

        match res {
            Ok(_) => ExecutionStatus::Done,
            Err(e) => ExecutionStatus::Err(e),
        }
    }

    fn execute_threaded(&self, _: &IrcClient, _: &Message) -> Result<(), FrippyError> {
        panic!("Tell should not use threading")
    }

    fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
        if command.tokens.is_empty() {
            client
                .send_notice(&command.source, &self.invalid_command())
                .context(FrippyErrorKind::Connection)?;
            return Ok(());
        }

        let sender = command.source.to_owned();

        match command.tokens[0].as_ref() {
            "help" => client
                .send_notice(&command.source, &self.help())
                .context(FrippyErrorKind::Connection),
            _ => match self.tell_command(client, command) {
                Ok(msg) => client
                    .send_notice(&sender, &msg)
                    .context(FrippyErrorKind::Connection),
                Err(e) => client
                    .send_notice(&sender, &e.to_string())
                    .context(FrippyErrorKind::Connection),
            },
        }?;

        Ok(())
    }

    fn evaluate(&self, _: &IrcClient, _: PluginCommand) -> Result<String, String> {
        Err(String::from("This Plugin does not implement any commands."))
    }
}

use std::fmt;
impl<T: Database> fmt::Debug for Tell<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Tell {{ ... }}")
    }
}

pub mod error {
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "TellError"]
    pub enum ErrorKind {
        /// Not found command error
        #[fail(display = "Tell was not found")]
        NotFound,

        /// MySQL error
        #[cfg(feature = "mysql")]
        #[fail(display = "Failed to execute MySQL Query")]
        MysqlError,

        /// No connection error
        #[cfg(feature = "mysql")]
        #[fail(display = "No connection to the database")]
        NoConnection,
    }
}
<|endoftext|>


<commit_before>#![crate_type = "bin"]
#![feature(collections)]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::{WindowTrait, ContainerTrait, WidgetTrait, ButtonSignals, BoxTrait};
use gtk::{Orientation};
use sysinfo::*;
use gtk::signal::Inhibit;
use gtk::signal::{WidgetSignals, TreeViewSignals};
use std::collections::VecMap;
use std::str::FromStr;
use std::rc::Rc;
use std::cell::{RefCell};

struct NoteBook {
    notebook: gtk::NoteBook,
    tabs: Vec<gtk::Box>
}

impl NoteBook {
    fn new() -> NoteBook {
        NoteBook {
            notebook: gtk::NoteBook::new().unwrap(),
            tabs: Vec::new()
        }
    }

    fn create_tab<'a, Widget: gtk::WidgetTrait>(&mut self, title: &'a str, widget: &Widget) -> Option<u32> {
        let label = gtk::Label::new(title).unwrap();
        let tab = gtk::Box::new(Orientation::Horizontal, 0).unwrap();

        tab.pack_start(&label, false, false, 0);
        tab.show_all();

        let index = match self.notebook.append_page(widget, Some(&tab)) {
            Some(index) => index,
            _ => return None
        };

        self.tabs.push(tab);

        Some(index)
    }
}

struct Procs {
    left_tree: gtk::TreeView,
    scroll: gtk::ScrolledWindow,
    current_pid: Rc<RefCell<Option<String>>>,
    kill_button: Rc<RefCell<gtk::Button>>,
    vertical_layout: gtk::Box,
    list_store: gtk::ListStore
}

impl Procs {
    pub fn new<'a>(proc_list: &VecMap<Processus>) -> Procs {
        let left_tree = gtk::TreeView::new().unwrap();
        let scroll = gtk::ScrolledWindow::new(None, None).unwrap();
        let current_pid = Rc::new(RefCell::new(None));
        let kill_button = Rc::new(RefCell::new(gtk::Button::new_with_label("End task").unwrap()));
        let current_pid1 = current_pid.clone();
        let current_pid2 = current_pid.clone();
        let kill_button1 = kill_button.clone();

        scroll.set_min_content_height(800);
        scroll.set_min_content_width(600);

        let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

        append_column("pid", &mut columns);
        append_column("process name", &mut columns);
        append_column("cpu usage", &mut columns);
        append_column("memory usage", &mut columns);

        for i in columns {
            left_tree.append_column(&i);
        }

        let mut list_store = gtk::ListStore::new(&[glib::Type::String, glib::Type::String, glib::Type::String, glib::Type::String]).unwrap();
        for (_, pro) in proc_list {
            create_and_fill_model(&mut list_store, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
        }

        left_tree.set_model(&list_store.get_model().unwrap());
        left_tree.set_headers_visible(true);
        scroll.add(&left_tree);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();

        left_tree.connect_cursor_changed(move |tree_view| {
            match tree_view.get_selection() {
                Some(selection) => {
                    let model = tree_view.get_model().unwrap();
                    let mut iter = gtk::TreeIter::new();

                    if selection.get_selected(&model, &mut iter) {
                        let pid = model.get_value(&iter, 0).get_string();
                        let mut tmp = current_pid1.borrow_mut();
                        *tmp = pid;
                    }
                    (*kill_button1.borrow_mut()).set_sensitive((*current_pid.borrow()).is_some());
                }
                None => {
                    let mut tmp = current_pid1.borrow_mut();
                    *tmp = None;
                }
            }
        });
        (*kill_button.borrow_mut()).set_sensitive(false);

        vertical_layout.add(&scroll);
        vertical_layout.add(&(*kill_button.borrow_mut()));
        Procs {
            left_tree: left_tree,
            scroll: scroll,
            current_pid: current_pid2.clone(),
            kill_button: kill_button,
            vertical_layout: vertical_layout,
            list_store: list_store
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>) {
    let l = v.len();
    let renderer = gtk::CellRendererText::new().unwrap();

    v.push(gtk::TreeViewColumn::new().unwrap());
    let tmp = v.get_mut(l).unwrap();

    tmp.set_title(title);
    tmp.pack_start(&renderer, true);
    tmp.add_attribute(&renderer, "text", l as i32);
}

fn create_and_fill_model(list_store: &mut gtk::ListStore, pid: i64, name: &str, cpu: f32, memory: u64) {
    if name.len() < 1 {
        return;
    }
    let mut top_level = gtk::TreeIter::new();

    list_store.append(&mut top_level);
    list_store.set_string(&top_level, 0, &format!("{}", pid));
    list_store.set_string(&top_level, 1, name);
    list_store.set_string(&top_level, 2, &format!("{}", cpu));
    list_store.set_string(&top_level, 3, &format!("{}", memory));
}

fn update_window(w: &mut (&mut gtk::ListStore, Rc<RefCell<sysinfo::System>>, Rc<RefCell<DisplaySysInfo>>)) -> i32 {
    let model = w.0.get_model().unwrap();
    let mut iter = gtk::TreeIter::new();

    (*w.1.borrow_mut()).refresh();
    let mut entries : VecMap<Processus> = ((*w.1.borrow()).get_processus_list()).clone();
    let mut nb = model.iter_n_children(None);

    let sys = w.1.clone();
    (*w.2.borrow_mut()).update_ram_display(sys.clone());
    (*w.2.borrow_mut()).update_process_display(sys);

    let mut i = 0;
    while i < nb {
        if model.iter_nth_child(&mut iter, None, i) {
            let pid : i64 = i64::from_str(&model.get_value(&iter, 0).get_string().unwrap()).unwrap();
            let mut to_delete = false;

            match entries.get(&(pid as usize)) {
                Some(p) => {
                    //w.0.set_string(&iter, 1, &p.name);
                    w.0.set_string(&iter, 2, &format!("{}", p.cpu_usage));
                    w.0.set_string(&iter, 3, &format!("{}", p.memory));
                    to_delete = true;
                }
                None => {
                    w.0.remove(&iter);
                }
            }
            if to_delete {
                entries.remove(&(pid as usize));
                nb = model.iter_n_children(None);
                i += 1;
            }
        } else {
            i += 1;
        }
    }
    for (_, pro) in entries {
        create_and_fill_model(&mut w.0, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }
    1
}

struct DisplaySysInfo {
    procs : Rc<RefCell<Vec<gtk::ProgressBar>>>,
    ram : Rc<RefCell<gtk::ProgressBar>>,
    swap : Rc<RefCell<gtk::ProgressBar>>,
    vertical_layout : gtk::Box
}

impl DisplaySysInfo {
    pub fn new(sys1: Rc<RefCell<sysinfo::System>>) -> DisplaySysInfo {
        let mut tmp = DisplaySysInfo {
            procs: Rc::new(RefCell::new(Vec::new())),
            ram: Rc::new(RefCell::new(gtk::ProgressBar::new().unwrap())),
            swap: Rc::new(RefCell::new(gtk::ProgressBar::new().unwrap())),
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap()
        };

        let sys2 = sys1.clone();
        let v = tmp.procs.clone();

        (*tmp.ram.borrow_mut()).set_show_text(true);
        (*tmp.swap.borrow_mut()).set_show_text(true);

        tmp.vertical_layout.set_spacing(5);
        let mut i = 0;
        let mut total = false;

        tmp.vertical_layout.pack_start(&gtk::Label::new("Memory usage").unwrap(), false, false, 15);
        tmp.vertical_layout.add(&(*tmp.ram.borrow()));
        tmp.vertical_layout.pack_start(&gtk::Label::new("Swap usage").unwrap(), false, false, 15);
        tmp.vertical_layout.add(&(*tmp.swap.borrow()));
        tmp.vertical_layout.pack_start(&gtk::Label::new("Total CPU usage").unwrap(), false, false, 15);
        for pro in (*sys1.borrow()).get_process_list() {
            if total {
                (*v.borrow_mut()).push(gtk::ProgressBar::new().unwrap());
                let p : &gtk::ProgressBar = &(*v.borrow())[i];
                let l = gtk::Label::new(&format!("{}", i)).unwrap();
                let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();

                p.set_text(&format!("{:.2} %", pro.get_cpu_usage() * 100.));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);
                horizontal_layout.pack_start(&l, false, false, 5);
                horizontal_layout.pack_start(p, true, true, 5);
                tmp.vertical_layout.add(&horizontal_layout);
            } else {
                (*v.borrow_mut()).push(gtk::ProgressBar::new().unwrap());
                let p : &gtk::ProgressBar = &(*v.borrow())[i];

                p.set_text(&format!("{:.2} %", pro.get_cpu_usage() * 100.));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);

                tmp.vertical_layout.add(p);
                tmp.vertical_layout.pack_start(&gtk::Label::new("Process usage").unwrap(), false, false, 15);
                total = true;
            }
            i += 1;
        }
        tmp.update_ram_display(sys2);
        tmp
    }

    pub fn update_ram_display(&mut self, sys: Rc<RefCell<sysinfo::System>>) {
        let total = (*sys.borrow()).get_total_memory();
        let used = (*sys.borrow()).get_free_memory();
        let disp = if total < 100000 {
            format!("{} / {}B", used, total)
        } else if total < 10000000 {
            format!("{} / {}KB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}MB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}GB", used / 1000000000, total / 1000000000)
        };

        (*self.ram.borrow_mut()).set_text(&disp);
        (*self.ram.borrow_mut()).set_fraction(used as f64 / total as f64);

        let total = (*sys.borrow()).get_total_swap();
        let used = total - (*sys.borrow()).get_free_swap();
        let disp = if total < 100000 {
            format!("{} / {}B", used, total)
        } else if total < 10000000 {
            format!("{} / {}KB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}MB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}GB", used / 1000000000, total / 1000000000)
        };

        (*self.swap.borrow_mut()).set_text(&disp);
        (*self.swap.borrow_mut()).set_fraction(used as f64 / total as f64);
    }

    pub fn update_process_display(&mut self, sys: Rc<RefCell<sysinfo::System>>) {
        let v = &*self.procs.borrow_mut();
        let mut i = 0;

        for pro in (*sys.borrow()).get_process_list() {
            v[i].set_text(&format!("{:.1} %", pro.get_cpu_usage() * 100.));
            v[i].set_show_text(true);
            v[i].set_fraction(pro.get_cpu_usage() as f64);
            i += 1;
        }
    }
}

fn main() {
    gtk::init();

    let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let sys : Rc<RefCell<sysinfo::System>> = Rc::new(RefCell::new(sysinfo::System::new()));
    let mut note = NoteBook::new();
    (*sys.borrow_mut()).refresh();
    let mut procs = Procs::new((*sys.borrow()).get_processus_list());
    let current_pid2 = procs.current_pid.clone();
    let sys1 = sys.clone();
    let sys2 = sys.clone();

    window.set_title("Processus viewer");
    window.set_window_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    (*procs.kill_button.borrow_mut()).connect_clicked(move |_| {
        let tmp = (*current_pid2.borrow_mut()).is_some() ;

        if tmp {
            let s = (*current_pid2.borrow()).clone();
            match (*sys.borrow_mut()).get_processus(i64::from_str(&s.unwrap()).unwrap()) {
                Some(p) => {
                    p.kill(Signal::Kill);
                },
                None => {}
            };
        }
    });

    let display_tab = Rc::new(RefCell::new(DisplaySysInfo::new(sys2)));

    glib::timeout::add(1500, update_window, &mut (&mut procs.list_store, sys1.clone(), display_tab.clone()));
    //window.add(&vertical_layout);
    note.create_tab("Processus list", &procs.vertical_layout);
    //let t = gtk::Button::new_with_label("test").unwrap();
    note.create_tab("System usage", &(*display_tab.borrow()).vertical_layout);
    window.add(&note.notebook);
    window.show_all();
    gtk::main();
}
<commit_msg>Update to last sysinfo version<commit_after>#![crate_type = "bin"]
#![feature(collections)]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::{WindowTrait, ContainerTrait, WidgetTrait, ButtonSignals, BoxTrait};
use gtk::{Orientation};
use sysinfo::*;
use gtk::signal::Inhibit;
use gtk::signal::{WidgetSignals, TreeViewSignals};
use std::collections::VecMap;
use std::str::FromStr;
use std::rc::Rc;
use std::cell::{RefCell};

struct NoteBook {
    notebook: gtk::NoteBook,
    tabs: Vec<gtk::Box>
}

impl NoteBook {
    fn new() -> NoteBook {
        NoteBook {
            notebook: gtk::NoteBook::new().unwrap(),
            tabs: Vec::new()
        }
    }

    fn create_tab<'a, Widget: gtk::WidgetTrait>(&mut self, title: &'a str, widget: &Widget) -> Option<u32> {
        let label = gtk::Label::new(title).unwrap();
        let tab = gtk::Box::new(Orientation::Horizontal, 0).unwrap();

        tab.pack_start(&label, false, false, 0);
        tab.show_all();

        let index = match self.notebook.append_page(widget, Some(&tab)) {
            Some(index) => index,
            _ => return None
        };

        self.tabs.push(tab);

        Some(index)
    }
}

struct Procs {
    left_tree: gtk::TreeView,
    scroll: gtk::ScrolledWindow,
    current_pid: Rc<RefCell<Option<String>>>,
    kill_button: Rc<RefCell<gtk::Button>>,
    vertical_layout: gtk::Box,
    list_store: gtk::ListStore
}

impl Procs {
    pub fn new<'a>(proc_list: &VecMap<Processus>) -> Procs {
        let left_tree = gtk::TreeView::new().unwrap();
        let scroll = gtk::ScrolledWindow::new(None, None).unwrap();
        let current_pid = Rc::new(RefCell::new(None));
        let kill_button = Rc::new(RefCell::new(gtk::Button::new_with_label("End task").unwrap()));
        let current_pid1 = current_pid.clone();
        let current_pid2 = current_pid.clone();
        let kill_button1 = kill_button.clone();

        scroll.set_min_content_height(800);
        scroll.set_min_content_width(600);

        let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

        append_column("pid", &mut columns);
        append_column("process name", &mut columns);
        append_column("cpu usage", &mut columns);
        append_column("memory usage", &mut columns);

        for i in columns {
            left_tree.append_column(&i);
        }

        let mut list_store = gtk::ListStore::new(&[glib::Type::String, glib::Type::String, glib::Type::String, glib::Type::String]).unwrap();
        for (_, pro) in proc_list {
            create_and_fill_model(&mut list_store, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
        }

        left_tree.set_model(&list_store.get_model().unwrap());
        left_tree.set_headers_visible(true);
        scroll.add(&left_tree);
        let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();

        left_tree.connect_cursor_changed(move |tree_view| {
            match tree_view.get_selection() {
                Some(selection) => {
                    let model = tree_view.get_model().unwrap();
                    let mut iter = gtk::TreeIter::new();

                    if selection.get_selected(&model, &mut iter) {
                        let pid = model.get_value(&iter, 0).get_string();
                        let mut tmp = current_pid1.borrow_mut();
                        *tmp = pid;
                    }
                    (*kill_button1.borrow_mut()).set_sensitive((*current_pid.borrow()).is_some());
                }
                None => {
                    let mut tmp = current_pid1.borrow_mut();
                    *tmp = None;
                }
            }
        });
        (*kill_button.borrow_mut()).set_sensitive(false);

        vertical_layout.add(&scroll);
        vertical_layout.add(&(*kill_button.borrow_mut()));
        Procs {
            left_tree: left_tree,
            scroll: scroll,
            current_pid: current_pid2.clone(),
            kill_button: kill_button,
            vertical_layout: vertical_layout,
            list_store: list_store
        }
    }
}

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>) {
    let l = v.len();
    let renderer = gtk::CellRendererText::new().unwrap();

    v.push(gtk::TreeViewColumn::new().unwrap());
    let tmp = v.get_mut(l).unwrap();

    tmp.set_title(title);
    tmp.pack_start(&renderer, true);
    tmp.add_attribute(&renderer, "text", l as i32);
}

fn create_and_fill_model(list_store: &mut gtk::ListStore, pid: i64, name: &str, cpu: f32, memory: u64) {
    if name.len() < 1 {
        return;
    }
    let mut top_level = gtk::TreeIter::new();

    list_store.append(&mut top_level);
    list_store.set_string(&top_level, 0, &format!("{}", pid));
    list_store.set_string(&top_level, 1, name);
    list_store.set_string(&top_level, 2, &format!("{}", cpu));
    list_store.set_string(&top_level, 3, &format!("{}", memory));
}

fn update_window(w: &mut (&mut gtk::ListStore, Rc<RefCell<sysinfo::System>>, Rc<RefCell<DisplaySysInfo>>)) -> i32 {
    let model = w.0.get_model().unwrap();
    let mut iter = gtk::TreeIter::new();

    (*w.1.borrow_mut()).refresh();
    let mut entries : VecMap<Processus> = ((*w.1.borrow()).get_processus_list()).clone();
    let mut nb = model.iter_n_children(None);

    let sys = w.1.clone();
    (*w.2.borrow_mut()).update_ram_display(sys.clone());
    (*w.2.borrow_mut()).update_process_display(sys);

    let mut i = 0;
    while i < nb {
        if model.iter_nth_child(&mut iter, None, i) {
            let pid : i64 = i64::from_str(&model.get_value(&iter, 0).get_string().unwrap()).unwrap();
            let mut to_delete = false;

            match entries.get(&(pid as usize)) {
                Some(p) => {
                    //w.0.set_string(&iter, 1, &p.name);
                    w.0.set_string(&iter, 2, &format!("{}", p.cpu_usage));
                    w.0.set_string(&iter, 3, &format!("{}", p.memory));
                    to_delete = true;
                }
                None => {
                    w.0.remove(&iter);
                }
            }
            if to_delete {
                entries.remove(&(pid as usize));
                nb = model.iter_n_children(None);
                i += 1;
            }
        } else {
            i += 1;
        }
    }
    for (_, pro) in entries {
        create_and_fill_model(&mut w.0, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }
    1
}

struct DisplaySysInfo {
    procs : Rc<RefCell<Vec<gtk::ProgressBar>>>,
    ram : Rc<RefCell<gtk::ProgressBar>>,
    swap : Rc<RefCell<gtk::ProgressBar>>,
    vertical_layout : gtk::Box
}

impl DisplaySysInfo {
    pub fn new(sys1: Rc<RefCell<sysinfo::System>>) -> DisplaySysInfo {
        let mut tmp = DisplaySysInfo {
            procs: Rc::new(RefCell::new(Vec::new())),
            ram: Rc::new(RefCell::new(gtk::ProgressBar::new().unwrap())),
            swap: Rc::new(RefCell::new(gtk::ProgressBar::new().unwrap())),
            vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap()
        };

        let sys2 = sys1.clone();
        let v = tmp.procs.clone();

        (*tmp.ram.borrow_mut()).set_show_text(true);
        (*tmp.swap.borrow_mut()).set_show_text(true);

        tmp.vertical_layout.set_spacing(5);
        let mut i = 0;
        let mut total = false;

        tmp.vertical_layout.pack_start(&gtk::Label::new("Memory usage").unwrap(), false, false, 15);
        tmp.vertical_layout.add(&(*tmp.ram.borrow()));
        tmp.vertical_layout.pack_start(&gtk::Label::new("Swap usage").unwrap(), false, false, 15);
        tmp.vertical_layout.add(&(*tmp.swap.borrow()));
        tmp.vertical_layout.pack_start(&gtk::Label::new("Total CPU usage").unwrap(), false, false, 15);
        for pro in (*sys1.borrow()).get_process_list() {
            if total {
                (*v.borrow_mut()).push(gtk::ProgressBar::new().unwrap());
                let p : &gtk::ProgressBar = &(*v.borrow())[i];
                let l = gtk::Label::new(&format!("{}", i)).unwrap();
                let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();

                p.set_text(&format!("{:.2} %", pro.get_cpu_usage() * 100.));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);
                horizontal_layout.pack_start(&l, false, false, 5);
                horizontal_layout.pack_start(p, true, true, 5);
                tmp.vertical_layout.add(&horizontal_layout);
            } else {
                (*v.borrow_mut()).push(gtk::ProgressBar::new().unwrap());
                let p : &gtk::ProgressBar = &(*v.borrow())[i];

                p.set_text(&format!("{:.2} %", pro.get_cpu_usage() * 100.));
                p.set_show_text(true);
                p.set_fraction(pro.get_cpu_usage() as f64);

                tmp.vertical_layout.add(p);
                tmp.vertical_layout.pack_start(&gtk::Label::new("Process usage").unwrap(), false, false, 15);
                total = true;
            }
            i += 1;
        }
        tmp.update_ram_display(sys2);
        tmp
    }

    pub fn update_ram_display(&mut self, sys: Rc<RefCell<sysinfo::System>>) {
        let total = (*sys.borrow()).get_total_memory();
        let used = (*sys.borrow()).get_used_memory();
        let disp = if total < 100000 {
            format!("{} / {}B", used, total)
        } else if total < 10000000 {
            format!("{} / {}KB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}MB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}GB", used / 1000000000, total / 1000000000)
        };

        (*self.ram.borrow_mut()).set_text(&disp);
        (*self.ram.borrow_mut()).set_fraction(used as f64 / total as f64);

        let total = (*sys.borrow()).get_total_swap();
        let used = total - (*sys.borrow()).get_used_swap();
        let disp = if total < 100000 {
            format!("{} / {}B", used, total)
        } else if total < 10000000 {
            format!("{} / {}KB", used / 1000, total / 1000)
        } else if total < 10000000000 {
            format!("{} / {}MB", used / 1000000, total / 1000000)
        } else {
            format!("{} / {}GB", used / 1000000000, total / 1000000000)
        };

        (*self.swap.borrow_mut()).set_text(&disp);
        (*self.swap.borrow_mut()).set_fraction(used as f64 / total as f64);
    }

    pub fn update_process_display(&mut self, sys: Rc<RefCell<sysinfo::System>>) {
        let v = &*self.procs.borrow_mut();
        let mut i = 0;

        for pro in (*sys.borrow()).get_process_list() {
            v[i].set_text(&format!("{:.1} %", pro.get_cpu_usage() * 100.));
            v[i].set_show_text(true);
            v[i].set_fraction(pro.get_cpu_usage() as f64);
            i += 1;
        }
    }
}

fn main() {
    gtk::init();

    let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let sys : Rc<RefCell<sysinfo::System>> = Rc::new(RefCell::new(sysinfo::System::new()));
    let mut note = NoteBook::new();
    (*sys.borrow_mut()).refresh();
    let mut procs = Procs::new((*sys.borrow()).get_processus_list());
    let current_pid2 = procs.current_pid.clone();
    let sys1 = sys.clone();
    let sys2 = sys.clone();

    window.set_title("Processus viewer");
    window.set_window_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    (*procs.kill_button.borrow_mut()).connect_clicked(move |_| {
        let tmp = (*current_pid2.borrow_mut()).is_some() ;

        if tmp {
            let s = (*current_pid2.borrow()).clone();
            match (*sys.borrow_mut()).get_processus(i64::from_str(&s.unwrap()).unwrap()) {
                Some(p) => {
                    p.kill(Signal::Kill);
                },
                None => {}
            };
        }
    });

    let display_tab = Rc::new(RefCell::new(DisplaySysInfo::new(sys2)));

    glib::timeout::add(1500, update_window, &mut (&mut procs.list_store, sys1.clone(), display_tab.clone()));
    //window.add(&vertical_layout);
    note.create_tab("Processus list", &procs.vertical_layout);
    //let t = gtk::Button::new_with_label("test").unwrap();
    note.create_tab("System usage", &(*display_tab.borrow()).vertical_layout);
    window.add(&note.notebook);
    window.show_all();
    gtk::main();
}
<|endoftext|>


<commit_before>use std::process::{Command, Stdio};
#[cfg(unix)]use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::File;
use std::fmt::Display;

/// convenience function to print a Cargo metadata pair
fn print_meta<A: Display, B: Display>(key: A, val: B) {
    println!("cargo:{}={}", key, val);
}

/// convenience macro to print a Cargo metadata pair with formatting in the value
macro_rules! format_meta {
    ($key:expr, $($val:tt)*) => {
        print_meta($key, format!($($val)*))
    }
}

#[cfg(not(unix))]
/// KLT only works on *nix
fn build_klt() {
    panic!("Unix only");
}

#[cfg(unix)]
/// Build KLT library dependency and print relevant metadata
fn build_klt() {
    const NAME: &'static str = "klt";
    const DIR: &'static str = "lib/klt";

    print_meta("rerun-if-changed", DIR);

    let log = File::create(format!("build/{}.log", NAME))
                   .unwrap_or_else(|e| panic!("could not create {} build log: {}", NAME, e));
    let status = Command::new("make")
                         .args(&["-C", DIR])
                         .stdout(unsafe { Stdio::from_raw_fd(log.as_raw_fd()) })
                         .status()
                         .unwrap_or_else(|e| panic!("could not run make for {}: {}", NAME, e));
    if !status.success() {
        panic!("failed to build {}: {}", NAME, status);
    }

    format_meta!("rustc-link-lib", "static={}", NAME);
    print_meta("rustc-link-search", DIR);
}

fn main() {
    // build script depends on itself
    print_meta("rerun-if-changed", "build/build.rs");

    // build KLT dependency
    build_klt();
}

<commit_msg>ll: correct build script output<commit_after>use std::process::{Command, Stdio};
#[cfg(unix)]use std::os::unix::io::{AsRawFd, FromRawFd};
use std::fs::File;
use std::fmt::Display;
use std::env;

/// convenience function to print a Cargo metadata pair
fn print_meta<A: Display, B: Display>(key: A, val: B) {
    println!("cargo:{}={}", key, val);
}

/// convenience macro to print a Cargo metadata pair with formatting in the value
macro_rules! format_meta {
    ($key:expr, $($val:tt)*) => {
        print_meta($key, format!($($val)*))
    }
}

#[cfg(not(unix))]
/// KLT only works on *nix
fn build_klt() {
    panic!("Unix only");
}

#[cfg(unix)]
/// Build KLT library dependency and print relevant metadata
fn build_klt() {
    const NAME: &'static str = "klt";
    const DIR: &'static str = "lib/klt";

    print_meta("rerun-if-changed", DIR);

    let log = File::create(format!("build/{}.log", NAME))
                   .unwrap_or_else(|e| panic!("could not create {} build log: {}", NAME, e));
    let status = Command::new("make")
                         .args(&["-C", DIR])
                         .stdout(unsafe { Stdio::from_raw_fd(log.as_raw_fd()) })
                         .status()
                         .unwrap_or_else(|e| panic!("could not run make for {}: {}", NAME, e));
    if !status.success() {
        panic!("failed to build {}: {}", NAME, status);
    }

    format_meta!("rustc-link-lib", "static={}", NAME);
    format_meta!("rustc-link-search", "{}/{}", env::var("CARGO_MANIFEST_DIR").unwrap(), DIR);
}

fn main() {
    // build script depends on itself
    print_meta("rerun-if-changed", "build/build.rs");

    // build KLT dependency
    build_klt();
}

<|endoftext|>


<commit_before>extern crate prng;
use prng::{PRNG, FloatPointsIterator};

#[cfg(not(target_os = "emscripten"))]
extern crate rayon;
#[cfg(not(target_os = "emscripten"))]
use rayon::prelude::*;

pub fn estimate(points: f64) -> f64 {
  let mut inside: f64 = 0.;
  let randseed = 1.;

  let mut generator = PRNG::new(randseed);

  for _ in 0..points as usize {
    let x = generator.next_float();
    let y = generator.next_float();

    if (x * x) + (y * y) <= 1. {
      inside += 1.;
    }
  }

  inside / points * 4.
}

pub fn functional_estimate(points: f64) -> f64 {
  let randseed = 1.;

  let inside = FloatPointsIterator::new(randseed)
    .take(points as usize)
    .map(|(x,y)| (x * x) + (y * y))
    .filter(|&point| point <= 1.)
    .fold(0., |acc, _| acc + 1.);

  inside / points * 4.
}

#[cfg(not(target_os = "emscripten"))]
pub fn ws_estimate(points: f64) -> f64 {
  let chunks = 16;
  let total = (1..chunks + 1)
    .into_par_iter()
    .map(|chunk| {
      FloatPointsIterator::new(chunk as f64)
        .take((points / chunks as f64) as usize)
        .map(|(x,y)| (x * x) + (y * y))
        .filter(|&point| point <= 1.)
        .fold(0., |acc, _| acc + 1.)
    })
    .map(|inside| inside / points * 4.)
    .reduce_with(std::ops::Add::add);

  total.unwrap()
}

#[cfg(test)]
mod estimate_tests {
  use estimate;

  #[test]
  fn pi_estimation_test() {
    let pi = estimate(1e6);
    assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }

  #[cfg(not(target_os = "emscripten"))]
  use ws_estimate;

  #[cfg(not(target_os = "emscripten"))]
  #[test]
  #[cfg(not(target_os = "emscripten"))]
  fn ws_estimation_test() {
    let pi = ws_estimate(1e6);
    assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }
}
<commit_msg>Modified Rust code to ignore work stealing implementation when compiled as an emscripten target<commit_after>extern crate prng;
use prng::{PRNG, FloatPointsIterator};

pub fn estimate(points: f64) -> f64 {
  let mut inside: f64 = 0.;
  let randseed = 1.;

  let mut generator = PRNG::new(randseed);

  for _ in 0..points as usize {
    let x = generator.next_float();
    let y = generator.next_float();

    if (x * x) + (y * y) <= 1. {
      inside += 1.;
    }
  }

  inside / points * 4.
}

pub fn functional_estimate(points: f64) -> f64 {
  let randseed = 1.;

  let inside = FloatPointsIterator::new(randseed)
    .take(points as usize)
    .map(|(x,y)| (x * x) + (y * y))
    .filter(|&point| point <= 1.)
    .fold(0., |acc, _| acc + 1.);

  inside / points * 4.
}

#[cfg(not(target_os = "emscripten"))]
pub mod ws {
  extern crate rayon;
  use self::rayon::prelude::*;
  use prng::{FloatPointsIterator};
  use std;

  pub fn estimate(points: f64) -> f64 {
    let chunks = 16;
    let total = (1..chunks + 1)
      .into_par_iter()
      .map(|chunk| {
        FloatPointsIterator::new(chunk as f64)
          .take((points / chunks as f64) as usize)
          .map(|(x,y)| (x * x) + (y * y))
          .filter(|&point| point <= 1.)
          .fold(0., |acc, _| acc + 1.)
      })
      .map(|inside| inside / points * 4.)
      .reduce_with(std::ops::Add::add);

    total.unwrap()
  }
}

#[cfg(test)]
mod estimate_tests {
  use estimate;

  #[test]
  fn pi_estimation_test() {
    let pi = estimate(1e6);
    assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }
}

#[cfg(test)]
#[cfg(not(target_os = "emscripten"))]
mod work_stealing_estimation {
  use ws::estimate;
  #[test]
  fn ws_estimation_test() {
      let pi = estimate(1e6);
      assert!((pi > 2.64) && (pi < 3.64), "estimated pi value is more than 0.5 off");
  }
}
<|endoftext|>