<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Communicating with the running kernel using devicemapper.

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use std::fs::File;
use std::io;
use std::io::{BufReader, BufRead};
use std::os::unix::io::AsRawFd;
use std::mem;
use std::slice;
use std::slice::bytes::copy_memory;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::BTreeSet;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm::dm_ioctl as dmi;
use lv::LV;
use vg::VG;
use util::align_to;
use pv::Device;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

/// Context needed for communicating with devicemapper.
pub struct DM<'a> {
    file: File,
    vg: &'a VG,
}

impl <'a> DM<'a> {
    /// Create a new context for communicating about a given VG with DM.
    pub fn new(vg: &'a VG) -> io::Result<Self> {
        Ok(DM {
            file: try!(File::open(DM_CTL_PATH)),
            vg: vg,
        })
    }

    /// Determine if a major number is a major number used by DM.
    pub fn is_dm_major(major: u32) -> bool {
        let mut set = BTreeSet::new();

        let f = File::open("/proc/devices")
            .ok().expect("Could not open /proc/devices");

        let reader = BufReader::new(f);

        for line in reader.lines()
            .filter_map(|x| x.ok())
            .skip_while(|x| x != "Block devices:")
            .skip(1) {
                let spl: Vec<_> = line.split_whitespace().collect();

                if spl[1] == "device-mapper" {
                    set.insert(spl[0].parse::<u32>().unwrap());
                }
            }

        set.contains(&major)
    }

    fn initialize_hdr(hdr: &mut dmi::Struct_dm_ioctl) -> () {
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
    }

    fn hdr_set_name(hdr: &mut dmi::Struct_dm_ioctl, vg_name: &str, lv_name: &str) -> () {
        let name = format!("{}-{}", vg_name.replace("-", "--"),
                           lv_name.replace("-", "--"));
        let name_dest: &mut [u8; 128] = unsafe { mem::transmute(&mut hdr.name) };
        copy_memory(name.as_bytes(), &mut name_dest[..]);
    }

    fn hdr_set_uuid(hdr: &mut dmi::Struct_dm_ioctl, vg_uuid: &str, lv_uuid: &str) -> () {
        let uuid = format!("LVM-{}{}", vg_uuid.replace("-", ""), lv_uuid.replace("-", ""));
        let uuid_dest: &mut [u8; 129] = unsafe { mem::transmute(&mut hdr.uuid) };
        copy_memory(uuid.as_bytes(), &mut uuid_dest[..]);
    }

    /// Devicemapper version information: Major, Minor, and patchlevel versions.
    pub fn get_version(&self) -> io::Result<(u32, u32, u32)> {

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
    }

    /// Returns a list of tuples containing DM device names and their major/minor
    /// device numbers.
    pub fn list_devices(&self) -> io::Result<Vec<(String, Device)>> {
        let mut buf = [0u8; 16 * 1024];
        let mut hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = buf.len() as u32;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut buf) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        let mut devs = Vec::new();
        if (hdr.data_size - hdr.data_start as u32) != 0 {
            let mut result = &buf[hdr.data_start as usize..];

            loop {
                let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
                let devno = LittleEndian::read_u64(&result[..8]);
                devs.push((String::from_utf8_lossy(slc).into_owned(), Device::from(devno)));

                let next = LittleEndian::read_u32(&result[8..12]);
                if next == 0 { break }

                result = &result[next as usize..];
            }
        }

        Ok(devs)
    }

    fn create_device(&self, lv: &mut LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);
        Self::hdr_set_uuid(&mut hdr, &self.vg.id, &lv.id);
        hdr.data_size = hdr.data_start;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_CREATE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => { }
        };

        lv.device = Some(Device::from(hdr.dev));

        Ok(())
    }

    fn load_device(&self, lv: &LV) -> io::Result<()> {
        let sectors_per_extent = self.vg.extent_size;
        let mut targs = Vec::new();

        // Construct targets first, since we need to know how many & size
        // before initializing the header.
        for seg in &lv.segments {
            for &(ref pvname, ref loc) in &seg.stripes {
                let err = || Error::new(Other, "dm load_device error");
                let pv = try!(self.vg.pvs.get(pvname).ok_or(err()));

                let mut targ: dmi::Struct_dm_target_spec = Default::default();
                targ.sector_start = seg.start_extent * sectors_per_extent;
                targ.length = seg.extent_count * sectors_per_extent;
                targ.status = 0;

                let mut dst: &mut [u8] = unsafe {
                    mem::transmute(&mut targ.target_type[..])
                };
                copy_memory(seg.ty.as_bytes(), &mut dst);

                let mut params = Vec::new();
                // TODO: only works for linear
                params.extend(
                    format!("{}:{} {}",
                            pv.device.major,
                            pv.device.minor,
                            (loc * sectors_per_extent) + pv.pe_start).as_bytes());

                let pad_bytes = align_to(
                    params.len() + 1usize, 8usize) - params.len();
                params.extend(vec![0; pad_bytes]);

                targ.next = (mem::size_of::<dmi::Struct_dm_target_spec>()
                           + params.len()) as u32;

                targs.push((targ, params));
            }
        }

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
        hdr.data_size = hdr.data_start + targs.iter()
            .map(|&(t, _)| t.next)
            .sum::<u32>();
        hdr.target_count = targs.len() as u32;

        // Flatten into buf
        let mut buf: Vec<u8> = Vec::with_capacity(hdr.data_size as usize);
        unsafe {
            let ptr: *mut u8 = mem::transmute(&mut hdr);
            let slc = slice::from_raw_parts(ptr, hdr.data_start as usize);
            buf.extend(slc);
        }

        for (targ, param) in targs {
            unsafe {
                let ptr: *mut u8 = mem::transmute(&targ);
                let slc = slice::from_raw_parts(
                    ptr, mem::size_of::<dmi::Struct_dm_target_spec>());
                buf.extend(slc);
            }

            buf.extend(&param);
        }

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_TABLE_LOAD_CMD as u8, buf.len());

        match unsafe { ioctl::read_into_ptr(self.file.as_raw_fd(), op, buf.as_mut_ptr()) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn suspend_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);
        hdr.flags = DM_SUSPEND_FLAG;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn resume_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);
        // DM_SUSPEND_FLAG not set = resume

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    /// Activate a Logical Volume.
    ///
    /// Also populates the LV's device field.
    pub fn activate_device(&self, lv: &mut LV) -> io::Result<()> {

        // TODO: name/uuid mangle?

        try!(self.create_device(lv));

        try!(self.load_device(lv));

        self.resume_device(lv)
    }

    /// Remove a Logical Volume.
    // UNTESTED
    pub fn remove_device(&self, lv: LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_REMOVE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }

    }
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}
<commit_msg>Fix segment type for linear.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Communicating with the running kernel using devicemapper.

#[allow(dead_code, non_camel_case_types)]
mod dm_ioctl;

use std::fs::File;
use std::io;
use std::io::{BufReader, BufRead};
use std::os::unix::io::AsRawFd;
use std::mem;
use std::slice;
use std::slice::bytes::copy_memory;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::BTreeSet;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm::dm_ioctl as dmi;
use lv::LV;
use vg::VG;
use util::align_to;
use pv::Device;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

/// Context needed for communicating with devicemapper.
pub struct DM<'a> {
    file: File,
    vg: &'a VG,
}

impl <'a> DM<'a> {
    /// Create a new context for communicating about a given VG with DM.
    pub fn new(vg: &'a VG) -> io::Result<Self> {
        Ok(DM {
            file: try!(File::open(DM_CTL_PATH)),
            vg: vg,
        })
    }

    /// Determine if a major number is a major number used by DM.
    pub fn is_dm_major(major: u32) -> bool {
        let mut set = BTreeSet::new();

        let f = File::open("/proc/devices")
            .ok().expect("Could not open /proc/devices");

        let reader = BufReader::new(f);

        for line in reader.lines()
            .filter_map(|x| x.ok())
            .skip_while(|x| x != "Block devices:")
            .skip(1) {
                let spl: Vec<_> = line.split_whitespace().collect();

                if spl[1] == "device-mapper" {
                    set.insert(spl[0].parse::<u32>().unwrap());
                }
            }

        set.contains(&major)
    }

    fn initialize_hdr(hdr: &mut dmi::Struct_dm_ioctl) -> () {
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
    }

    fn hdr_set_name(hdr: &mut dmi::Struct_dm_ioctl, vg_name: &str, lv_name: &str) -> () {
        let name = format!("{}-{}", vg_name.replace("-", "--"),
                           lv_name.replace("-", "--"));
        let name_dest: &mut [u8; 128] = unsafe { mem::transmute(&mut hdr.name) };
        copy_memory(name.as_bytes(), &mut name_dest[..]);
    }

    fn hdr_set_uuid(hdr: &mut dmi::Struct_dm_ioctl, vg_uuid: &str, lv_uuid: &str) -> () {
        let uuid = format!("LVM-{}{}", vg_uuid.replace("-", ""), lv_uuid.replace("-", ""));
        let uuid_dest: &mut [u8; 129] = unsafe { mem::transmute(&mut hdr.uuid) };
        copy_memory(uuid.as_bytes(), &mut uuid_dest[..]);
    }

    /// Devicemapper version information: Major, Minor, and patchlevel versions.
    pub fn get_version(&self) -> io::Result<(u32, u32, u32)> {

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
    }

    /// Returns a list of tuples containing DM device names and their major/minor
    /// device numbers.
    pub fn list_devices(&self) -> io::Result<Vec<(String, Device)>> {
        let mut buf = [0u8; 16 * 1024];
        let mut hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = buf.len() as u32;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut buf) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        let mut devs = Vec::new();
        if (hdr.data_size - hdr.data_start as u32) != 0 {
            let mut result = &buf[hdr.data_start as usize..];

            loop {
                let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
                let devno = LittleEndian::read_u64(&result[..8]);
                devs.push((String::from_utf8_lossy(slc).into_owned(), Device::from(devno)));

                let next = LittleEndian::read_u32(&result[8..12]);
                if next == 0 { break }

                result = &result[next as usize..];
            }
        }

        Ok(devs)
    }

    fn create_device(&self, lv: &mut LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);
        Self::hdr_set_uuid(&mut hdr, &self.vg.id, &lv.id);
        hdr.data_size = hdr.data_start;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_CREATE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => { }
        };

        lv.device = Some(Device::from(hdr.dev));

        Ok(())
    }

    fn load_device(&self, lv: &LV) -> io::Result<()> {
        let sectors_per_extent = self.vg.extent_size;
        let mut targs = Vec::new();

        // Construct targets first, since we need to know how many & size
        // before initializing the header.
        for seg in &lv.segments {

            let seg_ty = {
                if seg.ty == "striped" && seg.stripes.len() == 1 {
                    &b"linear"[..]
                } else {
                    seg.ty.as_bytes()
                }
            };

            for &(ref pvname, ref loc) in &seg.stripes {
                let err = || Error::new(Other, "dm load_device error");
                let pv = try!(self.vg.pvs.get(pvname).ok_or(err()));

                let mut targ: dmi::Struct_dm_target_spec = Default::default();
                targ.sector_start = seg.start_extent * sectors_per_extent;
                targ.length = seg.extent_count * sectors_per_extent;
                targ.status = 0;

                let mut dst: &mut [u8] = unsafe {
                    mem::transmute(&mut targ.target_type[..])
                };
                copy_memory(seg_ty, &mut dst);

                let mut params = Vec::new();
                // TODO: only works for linear
                params.extend(
                    format!("{}:{} {}",
                            pv.device.major,
                            pv.device.minor,
                            (loc * sectors_per_extent) + pv.pe_start).as_bytes());

                let pad_bytes = align_to(
                    params.len() + 1usize, 8usize) - params.len();
                params.extend(vec![0; pad_bytes]);

                targ.next = (mem::size_of::<dmi::Struct_dm_target_spec>()
                           + params.len()) as u32;

                targs.push((targ, params));
            }
        }

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);

        hdr.data_start = mem::size_of::<dmi::Struct_dm_ioctl>() as u32;
        hdr.data_size = hdr.data_start + targs.iter()
            .map(|&(t, _)| t.next)
            .sum::<u32>();
        hdr.target_count = targs.len() as u32;

        // Flatten into buf
        let mut buf: Vec<u8> = Vec::with_capacity(hdr.data_size as usize);
        unsafe {
            let ptr: *mut u8 = mem::transmute(&mut hdr);
            let slc = slice::from_raw_parts(ptr, hdr.data_start as usize);
            buf.extend(slc);
        }

        for (targ, param) in targs {
            unsafe {
                let ptr: *mut u8 = mem::transmute(&targ);
                let slc = slice::from_raw_parts(
                    ptr, mem::size_of::<dmi::Struct_dm_target_spec>());
                buf.extend(slc);
            }

            buf.extend(&param);
        }

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_TABLE_LOAD_CMD as u8, buf.len());

        match unsafe { ioctl::read_into_ptr(self.file.as_raw_fd(), op, buf.as_mut_ptr()) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn suspend_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);
        hdr.flags = DM_SUSPEND_FLAG;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn resume_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, &self.vg.name, &lv.name);
        // DM_SUSPEND_FLAG not set = resume

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_SUSPEND_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    /// Activate a Logical Volume.
    ///
    /// Also populates the LV's device field.
    pub fn activate_device(&self, lv: &mut LV) -> io::Result<()> {

        // TODO: name/uuid mangle?

        try!(self.create_device(lv));

        try!(self.load_device(lv));

        self.resume_device(lv)
    }

    /// Remove a Logical Volume.
    // UNTESTED
    pub fn remove_device(&self, lv: LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(&mut hdr);
        hdr.data_size = hdr.data_start;
        Self::hdr_set_name(&mut hdr, lv.name.as_bytes());

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_REMOVE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }

    }
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}
<|endoftext|>


<commit_before>
use super::internal::*;
use super::*;
use std::cmp;
use std::iter;

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct ZipEq<A: IndexedParallelIterator, B: IndexedParallelIterator> {
    zip: Zip<A, B>
}

/// Create a new `ZipEq` iterator.
///
/// NB: a free fn because it is NOT part of the end-user API.
#[inline]
pub fn new<A, B>(a: A, b: B) -> ZipEq<A, B>
    where A: IndexedParallelIterator,
          B: IndexedParallelIterator
{
    ZipEq { zip: zip::new(a, b) }
}

impl<A, B> ParallelIterator for ZipEq<A, B>
    where A: IndexedParallelIterator,
          B: IndexedParallelIterator
{
    type Item = (A::Item, B::Item);

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: UnindexedConsumer<Self::Item>
    {
        bridge(self.zip, consumer)
    }

    fn opt_len(&mut self) -> Option<usize> {
        Some(self.zip.len())
    }
}

impl<A, B> IndexedParallelIterator for ZipEq<A, B>
    where A: IndexedParallelIterator,
          B: IndexedParallelIterator
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self.zip, consumer)
    }

    fn len(&mut self) -> usize {
        self.zip.len()
    }

    fn with_producer<CB>(self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        self.zip.with_producer(callback)
    }
}
<commit_msg>Fix 1.12.0 build<commit_after>
use super::internal::*;
use super::*;
use std::cmp;
use std::iter;

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct ZipEq<A: IndexedParallelIterator, B: IndexedParallelIterator> {
    zip: Zip<A, B>
}

/// Create a new `ZipEq` iterator.
///
/// NB: a free fn because it is NOT part of the end-user API.
#[inline]
pub fn new<A, B>(a: A, b: B) -> ZipEq<A, B>
    where A: IndexedParallelIterator,
          B: IndexedParallelIterator
{
    ZipEq { zip: super::zip::new(a, b) }
}

impl<A, B> ParallelIterator for ZipEq<A, B>
    where A: IndexedParallelIterator,
          B: IndexedParallelIterator
{
    type Item = (A::Item, B::Item);

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: UnindexedConsumer<Self::Item>
    {
        bridge(self.zip, consumer)
    }

    fn opt_len(&mut self) -> Option<usize> {
        Some(self.zip.len())
    }
}

impl<A, B> IndexedParallelIterator for ZipEq<A, B>
    where A: IndexedParallelIterator,
          B: IndexedParallelIterator
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self.zip, consumer)
    }

    fn len(&mut self) -> usize {
        self.zip.len()
    }

    fn with_producer<CB>(self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        self.zip.with_producer(callback)
    }
}
<|endoftext|>


<commit_before>use ffi::h5i::{hid_t, H5I_type_t, H5Iget_type, H5Iis_valid, H5Iinc_ref, H5Idec_ref,
               H5Iget_ref, H5I_INVALID_HID};
use ffi::h5i::H5I_type_t::*;

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};

fn get_id_type(id: hid_t) -> H5I_type_t {
    let tp = h5lock!(H5Iget_type(id));
    let valid = id > 0 && tp > H5I_BADID && tp < H5I_NTYPES;
    if valid { tp } else { H5I_BADID }
}

fn is_valid_id(id: hid_t) -> bool {
    let tp = get_id_type(id);
    tp > H5I_BADID && tp < H5I_NTYPES
}

fn is_valid_user_id(id: hid_t) -> bool {
    h5lock!(H5Iis_valid(id)) == 1
}

pub struct Handle {
    id: Arc<RwLock<hid_t>>,
}

impl Handle {
    pub fn new(id: hid_t) -> Handle {
        lazy_static! {
            static ref REGISTRY: Registry = Registry::new();
        }
        Handle {
            // if the id is not registered with the library, do not share it
            id: match is_valid_user_id(id) {
                false => Arc::new(RwLock::new(id)),
                true  => REGISTRY.new_handle(id),
            }
        }
    }

    pub fn id(&self) -> hid_t {
        *self.id.read().unwrap()
    }

    pub fn invalidate(&self) {
        *self.id.write().unwrap() = H5I_INVALID_HID;
    }

    fn incref(&self) {
        if self.is_valid() {
            h5lock!(H5Iinc_ref(self.id()));
        }
    }

    fn decref(&self) {
        if self.is_valid() {
            h5lock!(H5Idec_ref(self.id()));
        }
        // must invalidate all linked IDs because the library reuses them internally
        if !self.is_valid() && !is_valid_id(self.id()) {
            self.invalidate();
        }
    }
}

impl Clone for Handle {
    fn clone(&self) -> Handle {
        self.incref();
        Handle::new(self.id())
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        self.decref();
    }
}

struct Registry {
    registry: Mutex<HashMap<hid_t, Arc<RwLock<hid_t>>>>,
}

impl Registry {
    pub fn new() -> Registry {
        Registry { registry: Mutex::new(HashMap::new()) }
    }

    pub fn new_handle(&self, id: hid_t) -> Arc<RwLock<hid_t>> {
        let mut registry = self.registry.lock().unwrap();
        let handle = registry.entry(id).or_insert(Arc::new(RwLock::new(id)));
        if *handle.read().unwrap() != id {
            // an id may be left dangling by previous invalidation of a linked handle
            *handle = Arc::new(RwLock::new(id));
        }
        handle.clone()
    }
}

pub trait Object {
    fn id(&self) -> hid_t;
    fn from_id(id: hid_t) -> Self;

    /// Returns reference count if the handle is valid, 0 otherwise.
    fn refcount(&self) -> u32 {
        if self.is_valid() {
            match h5call!(H5Iget_ref(self.id())) {
                Ok(count) if count >= 0 => count as u32,
                _                       => 0,
            }
        } else {
            0
        }
    }

    fn is_valid(&self) -> bool {
        is_valid_user_id(self.id())
    }

    fn id_type(&self) -> H5I_type_t {
        get_id_type(self.id())
    }

    fn is_file(&self) -> bool          { self.id_type() == H5I_FILE }
    fn is_group(&self) -> bool         { self.id_type() == H5I_GROUP }
    fn is_datatype(&self) -> bool      { self.id_type() == H5I_DATATYPE }
    fn is_dataspace(&self) -> bool     { self.id_type() == H5I_DATASPACE }
    fn is_dataset(&self) -> bool       { self.id_type() == H5I_DATASET }
    fn is_attribute(&self) -> bool     { self.id_type() == H5I_ATTR }
    fn is_reference(&self) -> bool     { self.id_type() == H5I_REFERENCE }
    fn is_vfl(&self) -> bool           { self.id_type() == H5I_VFL }
    fn is_plist_class(&self) -> bool   { self.id_type() == H5I_GENPROP_CLS }
    fn is_plist(&self) -> bool         { self.id_type() == H5I_GENPROP_LST }
    fn is_error_class(&self) -> bool   { self.id_type() == H5I_ERROR_CLASS }
    fn is_error_message(&self) -> bool { self.id_type() == H5I_ERROR_MSG }
    fn is_error_stack(&self) -> bool   { self.id_type() == H5I_ERROR_STACK }
}

impl Object for Handle {
    fn id(&self) -> hid_t {
        self.id()
    }

    fn from_id(id: hid_t) -> Handle {
        Handle::new(id)
    }
}

#[test]
pub fn test_handle() {
    use ffi::h5i::H5I_INVALID_HID;
    use ffi::h5p::H5Pcreate;
    use globals::{H5P_ROOT, H5P_FILE_ACCESS};

    #[derive(Clone)]
    struct TestObject {
        handle: Handle,
    }

    impl TestObject {
        fn incref(&self) {
            self.handle.incref()
        }

        fn decref(&self) {
            self.handle.decref()
        }
    }

    impl Object for TestObject {
        fn id(&self) -> hid_t {
            self.handle.id()
        }

        fn from_id(id: hid_t) -> TestObject {
            TestObject { handle: Handle::new(id) }
        }
    }

    // invalid id
    let mut obj = TestObject::from_id(H5I_INVALID_HID);
    assert_eq!(obj.id(), H5I_INVALID_HID);
    assert!(!obj.is_valid());
    assert!(!is_valid_id(obj.id()));
    assert!(!is_valid_user_id(obj.id()));
    assert_eq!(obj.id_type(), H5I_BADID);
    assert_eq!(obj.refcount(), 0);

    // existing generic id
    obj = TestObject::from_id(*H5P_ROOT);
    assert_eq!(obj.id(), *H5P_ROOT);
    assert!(is_valid_id(obj.id()));
    assert!(!is_valid_user_id(obj.id()));
    assert!(!obj.is_valid());
    assert!(obj.is_plist_class());
    assert_eq!(obj.refcount(), 0);
    obj.decref();
    assert!(is_valid_id(obj.id()));

    // new user id
    obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap());
    assert!(obj.id() > 0);
    assert!(obj.is_valid());
    assert!(is_valid_id(obj.id()));
    assert!(is_valid_user_id(obj.id()));

    assert!(!obj.is_file());
    assert!(!obj.is_group());
    assert!(!obj.is_datatype());
    assert!(!obj.is_dataspace());
    assert!(!obj.is_dataset());
    assert!(!obj.is_attribute());
    assert!(!obj.is_reference());
    assert!(!obj.is_vfl());
    assert!(!obj.is_plist_class());
    assert!(obj.is_plist());
    assert!(!obj.is_error_class());
    assert!(!obj.is_error_message());
    assert!(!obj.is_error_stack());

    assert_eq!(obj.refcount(), 1);
    obj.incref();
    assert_eq!(obj.refcount(), 2);
    obj.decref();
    assert_eq!(obj.refcount(), 1);
    obj.decref();
    obj.decref();
    assert_eq!(obj.refcount(), 0);
    assert!(!obj.is_valid());
    assert!(!is_valid_user_id(obj.id()));
    assert!(!is_valid_id(obj.id()));

    // cloning and dropping
    obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap());
    let obj_id = obj.id();
    obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap());
    assert!(!is_valid_id(obj_id));
    assert!(!is_valid_user_id(obj_id));
    assert!(obj.id() > 0);
    assert!(obj.is_valid());
    assert!(is_valid_id(obj.id()));
    assert!(is_valid_user_id(obj.id()));
    assert_eq!(obj.refcount(), 1);
    let mut obj2 = obj.clone();
    assert_eq!(obj.refcount(), 2);
    assert_eq!(obj2.refcount(), 2);
    drop(obj2);
    assert!(obj.is_valid());
    assert_eq!(obj.refcount(), 1);
    obj2 = obj.clone();
    obj.decref();
    obj.decref();
    assert_eq!(obj.id(), H5I_INVALID_HID);
    assert_eq!(obj2.id(), H5I_INVALID_HID);
}
<commit_msg>Synchronize Object::decref()<commit_after>use ffi::h5i::{hid_t, H5I_type_t, H5Iget_type, H5Iis_valid, H5Iinc_ref, H5Idec_ref,
               H5Iget_ref, H5I_INVALID_HID};
use ffi::h5i::H5I_type_t::*;

use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};

fn get_id_type(id: hid_t) -> H5I_type_t {
    let tp = h5lock!(H5Iget_type(id));
    let valid = id > 0 && tp > H5I_BADID && tp < H5I_NTYPES;
    if valid { tp } else { H5I_BADID }
}

fn is_valid_id(id: hid_t) -> bool {
    let tp = get_id_type(id);
    tp > H5I_BADID && tp < H5I_NTYPES
}

fn is_valid_user_id(id: hid_t) -> bool {
    h5lock!(H5Iis_valid(id)) == 1
}

pub struct Handle {
    id: Arc<RwLock<hid_t>>,
}

impl Handle {
    pub fn new(id: hid_t) -> Handle {
        lazy_static! {
            static ref REGISTRY: Registry = Registry::new();
        }
        Handle {
            // if the id is not registered with the library, do not share it
            id: match is_valid_user_id(id) {
                false => Arc::new(RwLock::new(id)),
                true  => REGISTRY.new_handle(id),
            }
        }
    }

    pub fn id(&self) -> hid_t {
        *self.id.read().unwrap()
    }

    pub fn invalidate(&self) {
        *self.id.write().unwrap() = H5I_INVALID_HID;
    }

    fn incref(&self) {
        if self.is_valid() {
            h5lock!(H5Iinc_ref(self.id()));
        }
    }

    fn decref(&self) {
        h5lock!({
            if self.is_valid() {
                H5Idec_ref(self.id());
            }
            // must invalidate all linked IDs because the library reuses them internally
            if !self.is_valid() && !is_valid_id(self.id()) {
                self.invalidate();
            }
        })
    }
}

impl Clone for Handle {
    fn clone(&self) -> Handle {
        self.incref();
        Handle::new(self.id())
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        h5lock_s!(self.decref());
    }
}

struct Registry {
    registry: Mutex<HashMap<hid_t, Arc<RwLock<hid_t>>>>,
}

impl Registry {
    pub fn new() -> Registry {
        Registry { registry: Mutex::new(HashMap::new()) }
    }

    pub fn new_handle(&self, id: hid_t) -> Arc<RwLock<hid_t>> {
        let mut registry = self.registry.lock().unwrap();
        let handle = registry.entry(id).or_insert(Arc::new(RwLock::new(id)));
        if *handle.read().unwrap() != id {
            // an id may be left dangling by previous invalidation of a linked handle
            *handle = Arc::new(RwLock::new(id));
        }
        handle.clone()
    }
}

pub trait Object {
    fn id(&self) -> hid_t;
    fn from_id(id: hid_t) -> Self;

    /// Returns reference count if the handle is valid, 0 otherwise.
    fn refcount(&self) -> u32 {
        if self.is_valid() {
            match h5call!(H5Iget_ref(self.id())) {
                Ok(count) if count >= 0 => count as u32,
                _                       => 0,
            }
        } else {
            0
        }
    }

    fn is_valid(&self) -> bool {
        is_valid_user_id(self.id())
    }

    fn id_type(&self) -> H5I_type_t {
        get_id_type(self.id())
    }

    fn is_file(&self) -> bool          { self.id_type() == H5I_FILE }
    fn is_group(&self) -> bool         { self.id_type() == H5I_GROUP }
    fn is_datatype(&self) -> bool      { self.id_type() == H5I_DATATYPE }
    fn is_dataspace(&self) -> bool     { self.id_type() == H5I_DATASPACE }
    fn is_dataset(&self) -> bool       { self.id_type() == H5I_DATASET }
    fn is_attribute(&self) -> bool     { self.id_type() == H5I_ATTR }
    fn is_reference(&self) -> bool     { self.id_type() == H5I_REFERENCE }
    fn is_vfl(&self) -> bool           { self.id_type() == H5I_VFL }
    fn is_plist_class(&self) -> bool   { self.id_type() == H5I_GENPROP_CLS }
    fn is_plist(&self) -> bool         { self.id_type() == H5I_GENPROP_LST }
    fn is_error_class(&self) -> bool   { self.id_type() == H5I_ERROR_CLASS }
    fn is_error_message(&self) -> bool { self.id_type() == H5I_ERROR_MSG }
    fn is_error_stack(&self) -> bool   { self.id_type() == H5I_ERROR_STACK }
}

impl Object for Handle {
    fn id(&self) -> hid_t {
        self.id()
    }

    fn from_id(id: hid_t) -> Handle {
        Handle::new(id)
    }
}

#[test]
pub fn test_handle() {
    use ffi::h5i::H5I_INVALID_HID;
    use ffi::h5p::H5Pcreate;
    use globals::{H5P_ROOT, H5P_FILE_ACCESS};

    #[derive(Clone)]
    struct TestObject {
        handle: Handle,
    }

    impl TestObject {
        fn incref(&self) {
            self.handle.incref()
        }

        fn decref(&self) {
            self.handle.decref()
        }
    }

    impl Object for TestObject {
        fn id(&self) -> hid_t {
            self.handle.id()
        }

        fn from_id(id: hid_t) -> TestObject {
            TestObject { handle: Handle::new(id) }
        }
    }

    // invalid id
    let mut obj = TestObject::from_id(H5I_INVALID_HID);
    assert_eq!(obj.id(), H5I_INVALID_HID);
    assert!(!obj.is_valid());
    assert!(!is_valid_id(obj.id()));
    assert!(!is_valid_user_id(obj.id()));
    assert_eq!(obj.id_type(), H5I_BADID);
    assert_eq!(obj.refcount(), 0);

    // existing generic id
    obj = TestObject::from_id(*H5P_ROOT);
    assert_eq!(obj.id(), *H5P_ROOT);
    assert!(is_valid_id(obj.id()));
    assert!(!is_valid_user_id(obj.id()));
    assert!(!obj.is_valid());
    assert!(obj.is_plist_class());
    assert_eq!(obj.refcount(), 0);
    obj.decref();
    assert!(is_valid_id(obj.id()));

    // new user id
    obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap());
    assert!(obj.id() > 0);
    assert!(obj.is_valid());
    assert!(is_valid_id(obj.id()));
    assert!(is_valid_user_id(obj.id()));

    assert!(!obj.is_file());
    assert!(!obj.is_group());
    assert!(!obj.is_datatype());
    assert!(!obj.is_dataspace());
    assert!(!obj.is_dataset());
    assert!(!obj.is_attribute());
    assert!(!obj.is_reference());
    assert!(!obj.is_vfl());
    assert!(!obj.is_plist_class());
    assert!(obj.is_plist());
    assert!(!obj.is_error_class());
    assert!(!obj.is_error_message());
    assert!(!obj.is_error_stack());

    assert_eq!(obj.refcount(), 1);
    obj.incref();
    assert_eq!(obj.refcount(), 2);
    obj.decref();
    assert_eq!(obj.refcount(), 1);
    obj.decref();
    obj.decref();
    assert_eq!(obj.refcount(), 0);
    assert!(!obj.is_valid());
    assert!(!is_valid_user_id(obj.id()));
    assert!(!is_valid_id(obj.id()));

    // cloning and dropping
    obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap());
    let obj_id = obj.id();
    obj = TestObject::from_id(h5call!(H5Pcreate(*H5P_FILE_ACCESS)).unwrap());
    assert!(!is_valid_id(obj_id));
    assert!(!is_valid_user_id(obj_id));
    assert!(obj.id() > 0);
    assert!(obj.is_valid());
    assert!(is_valid_id(obj.id()));
    assert!(is_valid_user_id(obj.id()));
    assert_eq!(obj.refcount(), 1);
    let mut obj2 = obj.clone();
    assert_eq!(obj.refcount(), 2);
    assert_eq!(obj2.refcount(), 2);
    drop(obj2);
    assert!(obj.is_valid());
    assert_eq!(obj.refcount(), 1);
    obj2 = obj.clone();
    obj.decref();
    obj.decref();
    assert_eq!(obj.id(), H5I_INVALID_HID);
    assert_eq!(obj2.id(), H5I_INVALID_HID);
}
<|endoftext|>


<commit_before>extern crate docopt;

docopt!(pub Args derive Debug, "
Usage:
  logian output <plugin> <schema-dir> <output-dir>
  logian generate (log|type) <log-name> [<field>...]
  logian (-h | --help)
  logian --version

Options:
  -h --help     Show this screen.
  --version     Show version.
");

pub fn parse() -> Args {
    Args::docopt().deserialize().unwrap_or_else(|e| e.exit())
}

pub struct Output {
    pub plugin_name: String,
    pub schema_dir: String,
    pub output_dir: String,
}

pub struct GeneratedField {
    pub field_name: String,
    pub data_type: String,
    pub nullable: bool,
}

pub enum GeneratedKind {
    Log,
    Type,
}

pub struct Generate {
    pub kind: GeneratedKind,
    pub name: String,
    pub fields: Vec<GeneratedField>,
}

impl Args {
    pub fn is_output(&self) -> bool {
        self.cmd_output
    }

    pub fn as_output(&self) -> Option<Output> {
        if !self.is_output() {
            return Option::None;
        }

        Some(Output {
            plugin_name: self.arg_plugin.clone(),
            schema_dir: self.arg_schema_dir.clone(),
            output_dir: self.arg_output_dir.clone(),
        })
    }

    pub fn is_generate(&self) -> bool {
        self.cmd_generate
    }

    pub fn as_generate(&self) -> Option<Generate> {
        if !self.is_generate() {
            return Option::None;
        }

        let fields = self.clone().arg_field.iter().map(|field| {
            let field_and_type: Vec<&str> = field.split(':').collect();

            if field_and_type.len() != 2 {
                panic!("Found invalid field format: {}.", field);
            }

            let mut data_type = field_and_type[1].to_string();
            let mut nullable = false;
            if data_type.chars().last().unwrap() == '?' {
                nullable = true;
                data_type.pop();
            }

            GeneratedField {
                field_name: field_and_type[0].to_string(),
                data_type: data_type,
                nullable: nullable,
            }

        }).collect();

        let mut kind = GeneratedKind::Log;
        if self.cmd_type {
            kind = GeneratedKind::Type;
        }

        Some(Generate {
            kind: kind,
            name: self.arg_log_name.clone(),
            fields: fields,
        })
    }
}

<commit_msg>Add specified plugin directory option<commit_after>extern crate docopt;

docopt!(pub Args derive Debug, "
Usage:
  logian output <plugin> <schema-dir> <output-dir> [--plugin-dir=<pd>]
  logian generate (log|type) <log-name> [<field>...]
  logian (-h | --help)
  logian --version

Options:
  -h --help         Show this screen.
  --version         Show version.
  --plugin-dir=<pd>  Plugin directory [default: ].
");

pub fn parse() -> Args {
    Args::docopt().deserialize().unwrap_or_else(|e| e.exit())
}

pub struct Output {
    pub plugin_name: String,
    pub schema_dir: String,
    pub output_dir: String,
    pub plugin_dir: String,
}

pub struct GeneratedField {
    pub field_name: String,
    pub data_type: String,
    pub nullable: bool,
}

pub enum GeneratedKind {
    Log,
    Type,
}

pub struct Generate {
    pub kind: GeneratedKind,
    pub name: String,
    pub fields: Vec<GeneratedField>,
}

impl Args {
    pub fn is_output(&self) -> bool {
        self.cmd_output
    }

    pub fn as_output(&self) -> Option<Output> {
        if !self.is_output() {
            return Option::None;
        }

        Some(Output {
            plugin_name: self.arg_plugin.clone(),
            schema_dir: self.arg_schema_dir.clone(),
            output_dir: self.arg_output_dir.clone(),
            plugin_dir: self.flag_plugin_dir.to_string(),
        })
    }

    pub fn is_generate(&self) -> bool {
        self.cmd_generate
    }

    pub fn as_generate(&self) -> Option<Generate> {
        if !self.is_generate() {
            return Option::None;
        }

        let fields = self.clone().arg_field.iter().map(|field| {
            let field_and_type: Vec<&str> = field.split(':').collect();

            if field_and_type.len() != 2 {
                panic!("Found invalid field format: {}.", field);
            }

            let mut data_type = field_and_type[1].to_string();
            let mut nullable = false;
            if data_type.chars().last().unwrap() == '?' {
                nullable = true;
                data_type.pop();
            }

            GeneratedField {
                field_name: field_and_type[0].to_string(),
                data_type: data_type,
                nullable: nullable,
            }

        }).collect();

        let mut kind = GeneratedKind::Log;
        if self.cmd_type {
            kind = GeneratedKind::Type;
        }

        Some(Generate {
            kind: kind,
            name: self.arg_log_name.clone(),
            fields: fields,
        })
    }
}

<|endoftext|>


<commit_before>#![allow(unused_imports)]

use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use rand;
use rand::distributions::{Sample, Range};

pub const VERSION: &'static str = "0.1";

pub struct DummyEngine {
    position: Position,
    replies: Vec<EngineReply>,
    is_thinking: bool,
    infinite: bool,
    ponder: bool,
    best_move: String,
    move_stack: Vec<Move>,
}

impl DummyEngine {
    pub fn new() -> DummyEngine {
        DummyEngine {
            position: Position::from_history("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk \
                                              - 0 1",
                                             &mut vec![].into_iter())
                          .ok()
                          .unwrap(),
            replies: vec![],
            is_thinking: false,
            infinite: false,
            ponder: false,
            best_move: "0000".to_string(),
            move_stack: Vec::new(),
        }
    }
}

impl UciEngine for DummyEngine {
    #[allow(unused_variables)]
    fn set_option(&mut self, name: &str, value: &str) {
        self.replies.push(EngineReply::Info(vec![("string".to_string(),
                                                  format!("{} -> {}", name, value))]));
    }

    fn new_game(&mut self) {}

    #[allow(unused_variables)]
    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if self.is_thinking {
            return;
        }
        self.position = Position::from_history(fen, moves).ok().unwrap();
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if self.is_thinking {
            return;
        }
        let s = &mut self.move_stack;
        let p = &mut self.position;
        p.generate_moves(s);
        // let mut legal_moves = vec![];
        let mut best_score = -20000;
        while let Some(m) = s.pop() {
            if p.do_move(m) {
                let score = -p.evaluate_quiescence(-20000, 20000);
                if score >= best_score {
                    best_score = score;
                    // self.replies.push(EngineReply::Info(vec![("info".to_string(),
                    //                                           format!("{} -> {}",
                    //                                                   m.notation(),
                    //                                                   score))]));
                    self.best_move = m.notation();
                }
                p.undo_move();
                // legal_moves.push(m.notation());
            }
        }
        // let mut rng = rand::thread_rng();
        // let mut between = Range::new(0, legal_moves.len());
        // self.best_move = legal_moves[between.sample(&mut rng)].clone();

        self.ponder = ponder;
        self.infinite = infinite;
        self.is_thinking = infinite | ponder;
        if !self.is_thinking {
            self.replies.push(EngineReply::BestMove {
                best_move: self.best_move.clone(),
                ponder_move: None,
            });
            self.best_move = "0000".to_string();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.ponder {
            self.ponder = false;
            if !self.infinite {
                self.stop()
            }
        }
    }

    fn stop(&mut self) {
        if !self.is_thinking {
            return;
        }
        self.replies.push(EngineReply::BestMove {
            best_move: self.best_move.clone(),
            ponder_move: None,
        });
        self.best_move = "0000".to_string();
        self.is_thinking = false;
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        self.replies.pop()
    }
}

pub struct DummyEngineFactory {
    name: String,
}

impl DummyEngineFactory {
    pub fn new() -> DummyEngineFactory {
        DummyEngineFactory { name: format!("Alcibiades {}", VERSION) }
    }
}

impl UciEngineFactory<DummyEngine> for DummyEngineFactory {
    fn name(&self) -> &str {
        self.name.as_str()
    }

    fn author(&self) -> &str {
        "Evgeni Pandurski"
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        // vec![
        //     ("Nullmove".to_string(), OptionDescription::Check { default: true }),
        //     ("Selectivity".to_string(), OptionDescription::Spin { default: 2, min: 0, max: 4 }),
        //     ("NalimovPath".to_string(), OptionDescription::String { default: "c:\\".to_string() }),
        //     ("Clear Hash".to_string(), OptionDescription::Button),
        // ]

        vec![
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Style".to_string(),
             OptionDescription::Combo {
                 default: "Normal".to_string(),
                 list: vec![
                     "Solid".to_string(),
                     "Normal".to_string(),
                     "Risky".to_string()
                 ]
             }),
        ]
    }

    fn create(&self) -> DummyEngine {
        DummyEngine::new()
    }
}
<commit_msg>make DummyEngin play more randomly<commit_after>#![allow(unused_imports)]

use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use rand;
use rand::distributions::{Sample, Range};

pub const VERSION: &'static str = "0.1";

pub struct DummyEngine {
    position: Position,
    replies: Vec<EngineReply>,
    is_thinking: bool,
    infinite: bool,
    ponder: bool,
    best_move: String,
    move_stack: Vec<Move>,
}

impl DummyEngine {
    pub fn new() -> DummyEngine {
        DummyEngine {
            position: Position::from_history("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk \
                                              - 0 1",
                                             &mut vec![].into_iter())
                          .ok()
                          .unwrap(),
            replies: vec![],
            is_thinking: false,
            infinite: false,
            ponder: false,
            best_move: "0000".to_string(),
            move_stack: Vec::new(),
        }
    }
}

impl UciEngine for DummyEngine {
    #[allow(unused_variables)]
    fn set_option(&mut self, name: &str, value: &str) {
        self.replies.push(EngineReply::Info(vec![("string".to_string(),
                                                  format!("{} -> {}", name, value))]));
    }

    fn new_game(&mut self) {}

    #[allow(unused_variables)]
    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if self.is_thinking {
            return;
        }
        self.position = Position::from_history(fen, moves).ok().unwrap();
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if self.is_thinking {
            return;
        }
        let s = &mut self.move_stack;
        let p = &mut self.position;
        p.generate_moves(s);
        // let mut legal_moves = vec![];
        let mut rng = rand::thread_rng();
        let mut between = Range::new(0, 10);
        let mut best_score = -20000;
        while let Some(m) = s.pop() {
            if p.do_move(m) {
                let score = -p.evaluate_quiescence(-20000, 20000) + between.sample(&mut rng);
                if score >= best_score {
                    best_score = score;
                    // self.replies.push(EngineReply::Info(vec![("info".to_string(),
                    //                                           format!("{} -> {}",
                    //                                                   m.notation(),
                    //                                                   score))]));
                    self.best_move = m.notation();
                }
                p.undo_move();
                // legal_moves.push(m.notation());
            }
        }
        // let mut rng = rand::thread_rng();
        // let mut between = Range::new(0, legal_moves.len());
        // self.best_move = legal_moves[between.sample(&mut rng)].clone();

        self.ponder = ponder;
        self.infinite = infinite;
        self.is_thinking = infinite | ponder;
        if !self.is_thinking {
            self.replies.push(EngineReply::BestMove {
                best_move: self.best_move.clone(),
                ponder_move: None,
            });
            self.best_move = "0000".to_string();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.ponder {
            self.ponder = false;
            if !self.infinite {
                self.stop()
            }
        }
    }

    fn stop(&mut self) {
        if !self.is_thinking {
            return;
        }
        self.replies.push(EngineReply::BestMove {
            best_move: self.best_move.clone(),
            ponder_move: None,
        });
        self.best_move = "0000".to_string();
        self.is_thinking = false;
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        self.replies.pop()
    }
}

pub struct DummyEngineFactory {
    name: String,
}

impl DummyEngineFactory {
    pub fn new() -> DummyEngineFactory {
        DummyEngineFactory { name: format!("Alcibiades {}", VERSION) }
    }
}

impl UciEngineFactory<DummyEngine> for DummyEngineFactory {
    fn name(&self) -> &str {
        self.name.as_str()
    }

    fn author(&self) -> &str {
        "Evgeni Pandurski"
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        // vec![
        //     ("Nullmove".to_string(), OptionDescription::Check { default: true }),
        //     ("Selectivity".to_string(), OptionDescription::Spin { default: 2, min: 0, max: 4 }),
        //     ("NalimovPath".to_string(), OptionDescription::String { default: "c:\\".to_string() }),
        //     ("Clear Hash".to_string(), OptionDescription::Button),
        // ]

        vec![
            ("Clear Hash".to_string(), OptionDescription::Button),
            ("Style".to_string(),
             OptionDescription::Combo {
                 default: "Normal".to_string(),
                 list: vec![
                     "Solid".to_string(),
                     "Normal".to_string(),
                     "Risky".to_string()
                 ]
             }),
        ]
    }

    fn create(&self) -> DummyEngine {
        DummyEngine::new()
    }
}
<|endoftext|>


<commit_before>//! This module imports errors generated by `StaticDir` and which are provided as response to following Iron middlewares.

use std::error::Error;
use std::fmt;
use std::io;

use iron::status::Status;
use iron::prelude::IronError;

/// Generated when target entry is not a directory, i.e. a file or symlink.
#[derive(Debug)]
pub struct NotADir;

impl Error for NotADir {
    fn description(&self) -> &str { "Requested entry is file" }
}

impl fmt::Display for NotADir {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

#[inline]
pub fn status_by_err_code(err: &io::Error) -> Status {
    const NOT_A_DIR_STATUS_CODE: i32 = 20;

    match err.raw_os_error() {
        Some(NOT_A_DIR_STATUS_CODE) => Status::NotFound,
        _                           => Status::InternalServerError,
    }
}

/// Maps `std::io::Error` to `iron::prelude::IronError`.
#[inline]
pub fn io_to_iron(err: io::Error) -> IronError {
    let status = match err.kind() {

        io::ErrorKind::NotFound         => Status::NotFound,
        io::ErrorKind::PermissionDenied => Status::Forbidden,
        io::ErrorKind::Other            => status_by_err_code(&err),
        _                               => Status::InternalServerError,
    };
    IronError::new(err, status)
}

/// Generated when failed to convert `OsString` to `String`
#[derive(Debug)]
pub struct BadString {
    desc: String,
}

impl BadString {
    pub fn new(desc: &str) -> BadString {
        BadString{ desc: desc.into() }
    }
}

impl Error for BadString {
    fn description(&self) -> &str { &self.desc }
}

impl fmt::Display for BadString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}
<commit_msg>removed wrong pub modifier<commit_after>//! This module imports errors generated by `StaticDir` and which are provided as response to following Iron middlewares.

use std::error::Error;
use std::fmt;
use std::io;

use iron::status::Status;
use iron::prelude::IronError;

/// Generated when target entry is not a directory, i.e. a file or symlink.
#[derive(Debug)]
pub struct NotADir;

impl Error for NotADir {
    fn description(&self) -> &str { "Requested entry is file" }
}

impl fmt::Display for NotADir {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

#[inline]
fn status_by_err_code(err: &io::Error) -> Status {
    const NOT_A_DIR_STATUS_CODE: i32 = 20;

    match err.raw_os_error() {
        Some(NOT_A_DIR_STATUS_CODE) => Status::NotFound,
        _                           => Status::InternalServerError,
    }
}

/// Maps `std::io::Error` to `iron::prelude::IronError`.
#[inline]
pub fn io_to_iron(err: io::Error) -> IronError {
    let status = match err.kind() {

        io::ErrorKind::NotFound         => Status::NotFound,
        io::ErrorKind::PermissionDenied => Status::Forbidden,
        io::ErrorKind::Other            => status_by_err_code(&err),
        _                               => Status::InternalServerError,
    };
    IronError::new(err, status)
}

/// Generated when failed to convert `OsString` to `String`
#[derive(Debug)]
pub struct BadString {
    desc: String,
}

impl BadString {
    pub fn new(desc: &str) -> BadString {
        BadString{ desc: desc.into() }
    }
}

impl Error for BadString {
    fn description(&self) -> &str { &self.desc }
}

impl fmt::Display for BadString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}
<|endoftext|>


<commit_before>use nom::{self, IResult};
use std::str;
use proto::{Address, Attribute, Envelope, MailboxDatum};
use proto::{RequestId, Response, ResponseCode, Status};

fn crlf(c: u8) -> bool {
    c == b'\r' || c == b'\n'
}

fn list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

fn quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

fn resp_specials(c: u8) -> bool {
    c == b']'
}

fn atom_specials(c: u8) -> bool {
    c == b'(' || c == b')' || c == b'{' || c == b' ' || c < 32 ||
    list_wildcards(c) || quoted_specials(c) || resp_specials(c)
}

fn atom_char(c: u8) -> bool {
    !atom_specials(c)
}

fn astring_char(c: u8) -> bool {
    atom_char(c) || resp_specials(c)
}

fn tag_char(c: u8) -> bool {
    c != b'+' && astring_char(c)
}

named!(quoted_data<&[u8], &str, u32>, map!(
    //escaped!(take_till_s!(quoted_specials), '\\', one_of!("\"\\")),
    take_till_s!(quoted_specials),
    |s| str::from_utf8(s).unwrap()
));

named!(quoted<&str>, do_parse!(
    tag_s!("\"") >>
    data: quoted_data >>
    tag_s!("\"") >>
    (data)
));

named!(status_ok<Status>, map!(tag_no_case!("OK"),
    |s| Status::Ok
));
named!(status_no<Status>, map!(tag_no_case!("NO"),
    |s| Status::No
));
named!(status_bad<Status>, map!(tag_no_case!("BAD"),
    |s| Status::Bad
));
named!(status_preauth<Status>, map!(tag_no_case!("PREAUTH"),
    |s| Status::PreAuth
));
named!(status_bye<Status>, map!(tag_no_case!("BYE"),
    |s| Status::Bye
));

named!(status<Status>, alt!(
    status_ok |
    status_no |
    status_bad |
    status_preauth |
    status_bye
));

named!(number<u32>, map!(nom::digit,
    |s| str::parse(str::from_utf8(s).unwrap()).unwrap()
));

named!(number_64<u64>, map!(nom::digit,
    |s| str::parse(str::from_utf8(s).unwrap()).unwrap()
));

named!(text<&str>, map!(take_till_s!(crlf),
    |s| str::from_utf8(s).unwrap()
));

named!(atom<&str>, map!(take_while1_s!(atom_char),
    |s| str::from_utf8(s).unwrap()
));

fn flag_extension(i: &[u8]) -> IResult<&[u8], &str> {
    if i.len() < 1 || i[0] != b'\\' {
        return IResult::Error(nom::ErrorKind::Custom(0));
    }
    let mut last = 0;
    for (idx, c) in i[1..].iter().enumerate() {
        last = idx;
        if !atom_char(*c) {
            break;
        }
    }
    IResult::Done(&i[last + 1..], str::from_utf8(&i[..last + 1]).unwrap())
}

named!(flag<&str>, alt!(flag_extension | atom));

named!(flag_list<Vec<&str>>, do_parse!(
    tag_s!("(") >>
    elements: opt!(do_parse!(
        flag0: flag >>
        flags: many0!(do_parse!(
            tag_s!(" ") >>
            flag: flag >>
            (flag)
        )) >> ({
            let mut res = vec![flag0];
            res.extend(flags);
            res
        })
    )) >>
    tag_s!(")") >> ({
       if elements.is_some() {
           elements.unwrap()
       } else {
           Vec::new()
       }
    })
));

named!(flag_perm<&str>, alt!(
    map!(tag_s!("\\*"), |s| str::from_utf8(s).unwrap()) |
    flag
));

named!(resp_text_code_permanent_flags<ResponseCode>, do_parse!(
    tag_s!("PERMANENTFLAGS (") >>
    elements: dbg_dmp!(opt!(do_parse!(
        flag0: flag_perm >>
        flags: many0!(do_parse!(
            tag_s!(" ") >>
            flag: flag_perm >>
            (flag)
        )) >> ({
            let mut res = vec![flag0];
            res.extend(flags);
            res
        })
    ))) >>
    tag_s!(")") >> ({
        ResponseCode::PermanentFlags(if elements.is_some() {
            elements.unwrap()
        } else {
            Vec::new()
        })
    })
));

named!(resp_text_code_highest_mod_seq<ResponseCode>, dbg_dmp!(do_parse!(
    tag_s!("HIGHESTMODSEQ ") >>
    num: number_64 >>
    (ResponseCode::HighestModSeq(num))
)));

named!(resp_text_code_read_only<ResponseCode>, do_parse!(
    tag_s!("READ-ONLY") >>
    (ResponseCode::ReadOnly)
));

named!(resp_text_code_read_write<ResponseCode>, do_parse!(
    tag_s!("READ-WRITE") >>
    (ResponseCode::ReadWrite)
));

named!(resp_text_code_try_create<ResponseCode>, do_parse!(
    tag_s!("TRYCREATE") >>
    (ResponseCode::TryCreate)
));

named!(resp_text_code_uid_validity<ResponseCode>, do_parse!(
    tag_s!("UIDVALIDITY ") >>
    num: number >>
    (ResponseCode::UidValidity(num))
));

named!(resp_text_code_uid_next<ResponseCode>, do_parse!(
    tag_s!("UIDNEXT ") >>
    num: number >>
    (ResponseCode::UidNext(num))
));

named!(resp_text_code<ResponseCode>, do_parse!(
    tag_s!("[") >>
    coded: alt!(
        resp_text_code_permanent_flags |
        resp_text_code_uid_validity |
        resp_text_code_uid_next |
        resp_text_code_read_only |
        resp_text_code_read_write |
        resp_text_code_try_create |
        resp_text_code_highest_mod_seq
    ) >>
    // Per the spec, the closing tag should be "] ".
    // See `resp_text` for more on why this is done differently.
    tag_s!("]") >>
    (coded)
));

named!(capability<&str>, do_parse!(
    tag_s!(" ") >>
    atom: take_till1_s!(atom_specials) >>
    (str::from_utf8(atom).unwrap())
));

named!(capability_data<Response>, do_parse!(
    tag_s!("CAPABILITY") >>
    capabilities: many1!(capability) >>
    (Response::Capabilities(capabilities))
));

named!(mailbox_data_flags<Response>, do_parse!(
    tag_s!("FLAGS ") >>
    flags: flag_list >>
    (Response::MailboxData(MailboxDatum::Flags(flags)))
));

named!(mailbox_data_exists<Response>, do_parse!(
    num: number >>
    tag_s!(" EXISTS") >>
    (Response::MailboxData(MailboxDatum::Exists(num)))
));

named!(mailbox_data_recent<Response>, do_parse!(
    num: number >>
    tag_s!(" RECENT") >>
    (Response::MailboxData(MailboxDatum::Recent(num)))
));

named!(mailbox_data<Response>, alt!(
    mailbox_data_flags |
    mailbox_data_exists |
    mailbox_data_recent
));

named!(nstring<Option<&str>>, map!(
    alt!(
        map!(tag_s!("NIL"), |s| str::from_utf8(s).unwrap()) |
        quoted
    ),
    |s| if s == "NIL" { None } else { Some(s) }
));

named!(address<Address>, do_parse!(
    tag_s!("(") >>
    name: nstring >>
    tag_s!(" ") >>
    adl: nstring >>
    tag_s!(" ") >>
    mailbox: nstring >>
    tag_s!(" ") >>
    host: nstring >>
    tag_s!(")") >>
    (Address { name, adl, mailbox, host })
));

named!(opt_addresses<Option<Vec<Address>>>, alt!(
    map!(tag_s!("NIL"), |s| None) |
    do_parse!(
        tag_s!("(") >>
        addrs: many1!(address) >>
        tag_s!(")") >>
        (Some(addrs))
    )
));

named!(msg_att_envelope<Attribute>, do_parse!(
    tag_s!("ENVELOPE (") >>
    date: nstring >>
    tag_s!(" ") >>
    subject: nstring >>
    tag_s!(" ") >>
    from: opt_addresses >>
    tag_s!(" ") >>
    sender: opt_addresses >>
    tag_s!(" ") >>
    reply_to: opt_addresses >>
    tag_s!(" ") >>
    to: opt_addresses >>
    tag_s!(" ") >>
    cc: opt_addresses >>
    tag_s!(" ") >>
    bcc: opt_addresses >>
    tag_s!(" ") >>
    in_reply_to: nstring >>
    tag_s!(" ") >>
    message_id: nstring >>
    tag_s!(")") >> ({
        Attribute::Envelope(Envelope {
            date, subject, from, sender, reply_to, to, cc, bcc, in_reply_to, message_id
        })
    })
));

named!(msg_att_internal_date<Attribute>, do_parse!(
    tag_s!("INTERNALDATE ") >>
    date: nstring >>
    (Attribute::InternalDate(date.unwrap()))
));

named!(msg_att_flags<Attribute>, do_parse!(
    tag_s!("FLAGS ") >>
    flags: flag_list >>
    (Attribute::Flags(flags))
));

named!(msg_att_rfc822_size<Attribute>, do_parse!(
    tag_s!("RFC822.SIZE ") >>
    num: number >>
    (Attribute::Rfc822Size(num))
));

named!(msg_att_mod_seq<Attribute>, do_parse!(
    tag_s!("MODSEQ (") >>
    num: number_64 >>
    tag_s!(")") >>
    (Attribute::ModSeq(num))
));

named!(msg_att<Attribute>, alt!(
    msg_att_envelope |
    msg_att_internal_date |
    msg_att_flags |
    msg_att_mod_seq |
    msg_att_rfc822_size
));

named!(msg_att_list<Vec<Attribute>>, do_parse!(
    tag_s!("(") >>
    elements: do_parse!(
        attr0: msg_att >>
        attrs: many0!(do_parse!(
            tag_s!(" ") >>
            attr: msg_att >>
            (attr)
        )) >> ({
            let mut res = vec![attr0];
            res.extend(attrs);
            res
        })
    ) >>
    tag_s!(")") >>
    (elements)
));

named!(message_data_fetch<Response>, do_parse!(
    num: number >>
    tag_s!(" FETCH ") >>
    attrs: msg_att_list >>
    (Response::Fetch(num, attrs))
));

named!(message_data_expunge<Response>, do_parse!(
    num: number >>
    tag_s!(" EXPUNGE") >>
    (Response::Expunge(num))
));

named!(tag<RequestId>, map!(take_while1_s!(tag_char),
    |s| RequestId(str::from_utf8(s).unwrap().to_string())
));

// This is not quite according to spec, which mandates the following:
//     ["[" resp-text-code "]" SP] text
// However, examples in RFC 4551 (Conditional STORE) counteract this by giving
// examples of `resp-text` that do not include the trailing space and text.
named!(resp_text<(Option<ResponseCode>, Option<&str>)>, do_parse!(
    code: opt!(resp_text_code) >>
    text: text >>
    ({
        let res = if text.len() < 1 {
            None
        } else if code.is_some() {
            Some(&text[1..])
        } else {
            Some(text)
        };
        (code, res)
    })
));

named!(response_tagged<Response>, do_parse!(
    tag: tag >>
    tag_s!(" ") >>
    status: status >>
    tag_s!(" ") >>
    text: resp_text >>
    (Response::Done(tag, status, text.0, text.1))
));

named!(resp_cond<Response>, do_parse!(
    status: status >>
    tag_s!(" ") >>
    text: resp_text >>
    (Response::Data(status, text.0, text.1))
));

named!(response_data<Response>, do_parse!(
    tag_s!("* ") >>
    contents: alt!(
        resp_cond |
        mailbox_data |
        message_data_expunge |
        message_data_fetch |
        capability_data
    ) >>
    (contents)
));

named!(response<Response>, alt!(
    response_data |
    response_tagged
));

pub fn parse(msg: &str) -> Response {
    match response(msg.as_bytes()) {
        IResult::Done(_, res) => res,
        IResult::Error(err) => panic!("problems parsing template source: {}", err),
        IResult::Incomplete(_) => panic!("parsing incomplete: {:?}", msg),
    }
}
<commit_msg>Improve parser error message<commit_after>use nom::{self, IResult};
use std::str;
use proto::{Address, Attribute, Envelope, MailboxDatum};
use proto::{RequestId, Response, ResponseCode, Status};

fn crlf(c: u8) -> bool {
    c == b'\r' || c == b'\n'
}

fn list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

fn quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

fn resp_specials(c: u8) -> bool {
    c == b']'
}

fn atom_specials(c: u8) -> bool {
    c == b'(' || c == b')' || c == b'{' || c == b' ' || c < 32 ||
    list_wildcards(c) || quoted_specials(c) || resp_specials(c)
}

fn atom_char(c: u8) -> bool {
    !atom_specials(c)
}

fn astring_char(c: u8) -> bool {
    atom_char(c) || resp_specials(c)
}

fn tag_char(c: u8) -> bool {
    c != b'+' && astring_char(c)
}

named!(quoted_data<&[u8], &str, u32>, map!(
    //escaped!(take_till_s!(quoted_specials), '\\', one_of!("\"\\")),
    take_till_s!(quoted_specials),
    |s| str::from_utf8(s).unwrap()
));

named!(quoted<&str>, do_parse!(
    tag_s!("\"") >>
    data: quoted_data >>
    tag_s!("\"") >>
    (data)
));

named!(status_ok<Status>, map!(tag_no_case!("OK"),
    |s| Status::Ok
));
named!(status_no<Status>, map!(tag_no_case!("NO"),
    |s| Status::No
));
named!(status_bad<Status>, map!(tag_no_case!("BAD"),
    |s| Status::Bad
));
named!(status_preauth<Status>, map!(tag_no_case!("PREAUTH"),
    |s| Status::PreAuth
));
named!(status_bye<Status>, map!(tag_no_case!("BYE"),
    |s| Status::Bye
));

named!(status<Status>, alt!(
    status_ok |
    status_no |
    status_bad |
    status_preauth |
    status_bye
));

named!(number<u32>, map!(nom::digit,
    |s| str::parse(str::from_utf8(s).unwrap()).unwrap()
));

named!(number_64<u64>, map!(nom::digit,
    |s| str::parse(str::from_utf8(s).unwrap()).unwrap()
));

named!(text<&str>, map!(take_till_s!(crlf),
    |s| str::from_utf8(s).unwrap()
));

named!(atom<&str>, map!(take_while1_s!(atom_char),
    |s| str::from_utf8(s).unwrap()
));

fn flag_extension(i: &[u8]) -> IResult<&[u8], &str> {
    if i.len() < 1 || i[0] != b'\\' {
        return IResult::Error(nom::ErrorKind::Custom(0));
    }
    let mut last = 0;
    for (idx, c) in i[1..].iter().enumerate() {
        last = idx;
        if !atom_char(*c) {
            break;
        }
    }
    IResult::Done(&i[last + 1..], str::from_utf8(&i[..last + 1]).unwrap())
}

named!(flag<&str>, alt!(flag_extension | atom));

named!(flag_list<Vec<&str>>, do_parse!(
    tag_s!("(") >>
    elements: opt!(do_parse!(
        flag0: flag >>
        flags: many0!(do_parse!(
            tag_s!(" ") >>
            flag: flag >>
            (flag)
        )) >> ({
            let mut res = vec![flag0];
            res.extend(flags);
            res
        })
    )) >>
    tag_s!(")") >> ({
       if elements.is_some() {
           elements.unwrap()
       } else {
           Vec::new()
       }
    })
));

named!(flag_perm<&str>, alt!(
    map!(tag_s!("\\*"), |s| str::from_utf8(s).unwrap()) |
    flag
));

named!(resp_text_code_permanent_flags<ResponseCode>, do_parse!(
    tag_s!("PERMANENTFLAGS (") >>
    elements: dbg_dmp!(opt!(do_parse!(
        flag0: flag_perm >>
        flags: many0!(do_parse!(
            tag_s!(" ") >>
            flag: flag_perm >>
            (flag)
        )) >> ({
            let mut res = vec![flag0];
            res.extend(flags);
            res
        })
    ))) >>
    tag_s!(")") >> ({
        ResponseCode::PermanentFlags(if elements.is_some() {
            elements.unwrap()
        } else {
            Vec::new()
        })
    })
));

named!(resp_text_code_highest_mod_seq<ResponseCode>, dbg_dmp!(do_parse!(
    tag_s!("HIGHESTMODSEQ ") >>
    num: number_64 >>
    (ResponseCode::HighestModSeq(num))
)));

named!(resp_text_code_read_only<ResponseCode>, do_parse!(
    tag_s!("READ-ONLY") >>
    (ResponseCode::ReadOnly)
));

named!(resp_text_code_read_write<ResponseCode>, do_parse!(
    tag_s!("READ-WRITE") >>
    (ResponseCode::ReadWrite)
));

named!(resp_text_code_try_create<ResponseCode>, do_parse!(
    tag_s!("TRYCREATE") >>
    (ResponseCode::TryCreate)
));

named!(resp_text_code_uid_validity<ResponseCode>, do_parse!(
    tag_s!("UIDVALIDITY ") >>
    num: number >>
    (ResponseCode::UidValidity(num))
));

named!(resp_text_code_uid_next<ResponseCode>, do_parse!(
    tag_s!("UIDNEXT ") >>
    num: number >>
    (ResponseCode::UidNext(num))
));

named!(resp_text_code<ResponseCode>, do_parse!(
    tag_s!("[") >>
    coded: alt!(
        resp_text_code_permanent_flags |
        resp_text_code_uid_validity |
        resp_text_code_uid_next |
        resp_text_code_read_only |
        resp_text_code_read_write |
        resp_text_code_try_create |
        resp_text_code_highest_mod_seq
    ) >>
    // Per the spec, the closing tag should be "] ".
    // See `resp_text` for more on why this is done differently.
    tag_s!("]") >>
    (coded)
));

named!(capability<&str>, do_parse!(
    tag_s!(" ") >>
    atom: take_till1_s!(atom_specials) >>
    (str::from_utf8(atom).unwrap())
));

named!(capability_data<Response>, do_parse!(
    tag_s!("CAPABILITY") >>
    capabilities: many1!(capability) >>
    (Response::Capabilities(capabilities))
));

named!(mailbox_data_flags<Response>, do_parse!(
    tag_s!("FLAGS ") >>
    flags: flag_list >>
    (Response::MailboxData(MailboxDatum::Flags(flags)))
));

named!(mailbox_data_exists<Response>, do_parse!(
    num: number >>
    tag_s!(" EXISTS") >>
    (Response::MailboxData(MailboxDatum::Exists(num)))
));

named!(mailbox_data_recent<Response>, do_parse!(
    num: number >>
    tag_s!(" RECENT") >>
    (Response::MailboxData(MailboxDatum::Recent(num)))
));

named!(mailbox_data<Response>, alt!(
    mailbox_data_flags |
    mailbox_data_exists |
    mailbox_data_recent
));

named!(nstring<Option<&str>>, map!(
    alt!(
        map!(tag_s!("NIL"), |s| str::from_utf8(s).unwrap()) |
        quoted
    ),
    |s| if s == "NIL" { None } else { Some(s) }
));

named!(address<Address>, do_parse!(
    tag_s!("(") >>
    name: nstring >>
    tag_s!(" ") >>
    adl: nstring >>
    tag_s!(" ") >>
    mailbox: nstring >>
    tag_s!(" ") >>
    host: nstring >>
    tag_s!(")") >>
    (Address { name, adl, mailbox, host })
));

named!(opt_addresses<Option<Vec<Address>>>, alt!(
    map!(tag_s!("NIL"), |s| None) |
    do_parse!(
        tag_s!("(") >>
        addrs: many1!(address) >>
        tag_s!(")") >>
        (Some(addrs))
    )
));

named!(msg_att_envelope<Attribute>, do_parse!(
    tag_s!("ENVELOPE (") >>
    date: nstring >>
    tag_s!(" ") >>
    subject: nstring >>
    tag_s!(" ") >>
    from: opt_addresses >>
    tag_s!(" ") >>
    sender: opt_addresses >>
    tag_s!(" ") >>
    reply_to: opt_addresses >>
    tag_s!(" ") >>
    to: opt_addresses >>
    tag_s!(" ") >>
    cc: opt_addresses >>
    tag_s!(" ") >>
    bcc: opt_addresses >>
    tag_s!(" ") >>
    in_reply_to: nstring >>
    tag_s!(" ") >>
    message_id: nstring >>
    tag_s!(")") >> ({
        Attribute::Envelope(Envelope {
            date, subject, from, sender, reply_to, to, cc, bcc, in_reply_to, message_id
        })
    })
));

named!(msg_att_internal_date<Attribute>, do_parse!(
    tag_s!("INTERNALDATE ") >>
    date: nstring >>
    (Attribute::InternalDate(date.unwrap()))
));

named!(msg_att_flags<Attribute>, do_parse!(
    tag_s!("FLAGS ") >>
    flags: flag_list >>
    (Attribute::Flags(flags))
));

named!(msg_att_rfc822_size<Attribute>, do_parse!(
    tag_s!("RFC822.SIZE ") >>
    num: number >>
    (Attribute::Rfc822Size(num))
));

named!(msg_att_mod_seq<Attribute>, do_parse!(
    tag_s!("MODSEQ (") >>
    num: number_64 >>
    tag_s!(")") >>
    (Attribute::ModSeq(num))
));

named!(msg_att<Attribute>, alt!(
    msg_att_envelope |
    msg_att_internal_date |
    msg_att_flags |
    msg_att_mod_seq |
    msg_att_rfc822_size
));

named!(msg_att_list<Vec<Attribute>>, do_parse!(
    tag_s!("(") >>
    elements: do_parse!(
        attr0: msg_att >>
        attrs: many0!(do_parse!(
            tag_s!(" ") >>
            attr: msg_att >>
            (attr)
        )) >> ({
            let mut res = vec![attr0];
            res.extend(attrs);
            res
        })
    ) >>
    tag_s!(")") >>
    (elements)
));

named!(message_data_fetch<Response>, do_parse!(
    num: number >>
    tag_s!(" FETCH ") >>
    attrs: msg_att_list >>
    (Response::Fetch(num, attrs))
));

named!(message_data_expunge<Response>, do_parse!(
    num: number >>
    tag_s!(" EXPUNGE") >>
    (Response::Expunge(num))
));

named!(tag<RequestId>, map!(take_while1_s!(tag_char),
    |s| RequestId(str::from_utf8(s).unwrap().to_string())
));

// This is not quite according to spec, which mandates the following:
//     ["[" resp-text-code "]" SP] text
// However, examples in RFC 4551 (Conditional STORE) counteract this by giving
// examples of `resp-text` that do not include the trailing space and text.
named!(resp_text<(Option<ResponseCode>, Option<&str>)>, do_parse!(
    code: opt!(resp_text_code) >>
    text: text >>
    ({
        let res = if text.len() < 1 {
            None
        } else if code.is_some() {
            Some(&text[1..])
        } else {
            Some(text)
        };
        (code, res)
    })
));

named!(response_tagged<Response>, do_parse!(
    tag: tag >>
    tag_s!(" ") >>
    status: status >>
    tag_s!(" ") >>
    text: resp_text >>
    (Response::Done(tag, status, text.0, text.1))
));

named!(resp_cond<Response>, do_parse!(
    status: status >>
    tag_s!(" ") >>
    text: resp_text >>
    (Response::Data(status, text.0, text.1))
));

named!(response_data<Response>, do_parse!(
    tag_s!("* ") >>
    contents: alt!(
        resp_cond |
        mailbox_data |
        message_data_expunge |
        message_data_fetch |
        capability_data
    ) >>
    (contents)
));

named!(response<Response>, alt!(
    response_data |
    response_tagged
));

pub fn parse(msg: &str) -> Response {
    match response(msg.as_bytes()) {
        IResult::Done(_, res) => res,
        IResult::Error(err) => panic!("error {} during parsing of {:?}", err, msg),
        IResult::Incomplete(_) => panic!("parsing incomplete: {:?}", msg),
    }
}
<|endoftext|>


<commit_before>use yaml_rust;
use slog::{Logger};
use yaml_rust::Yaml;
use model::*;

pub fn parse_dot_files(log: &Logger, dot_files: &Yaml) -> Vec<DotFile> {
  let mut parsed_dot_files = Vec::new();
  info!(log, "Processing dotfiles");
  if let Yaml::Hash(entries) = dot_files.clone() {
    for (key, value) in entries {
      match (key, value) {
        (Yaml::String(target), Yaml::String(source)) =>
          parsed_dot_files.push(DotFile{ source: source.to_string(), target: target.to_string() , dot_file_type: DotFileType::LINK }),
        (Yaml::String(target), Yaml::Hash(settings)) =>
          parsed_dot_files.push(dot_file_from_settings(target, &settings)),
        _ => {}
      }
    }
  } else {
    warn!(log, "Found no entries to process");
  }
  parsed_dot_files
}

fn dot_file_from_settings(target: String, settings: &yaml_rust::yaml::Hash) -> DotFile {
  let mut dot_file = DotFile{ source: "<todo>".to_string(), target: target.to_string(), dot_file_type: DotFileType::LINK };
  for (key, value) in settings.clone() {
    match (key, value) {
      (Yaml::String(setting_key), Yaml::String(setting_value)) => {
        match setting_key.as_ref() {
          "src" => dot_file.source = setting_value.to_string(),
          _ => {}
        }
      },
      _ => {}
    }
  }
  dot_file
}

#[cfg(test)]
mod tests {
  use slog;
  use slog_stdlog;
  use slog::{DrainExt};
  use yaml_rust::{YamlLoader};
  use yaml_rust::Yaml;
  use super::*;
  use spectral::prelude::*;

  #[test]
  fn parse_config() {
    let s =
      "
files:
    ~/.tmux/plugins/tpm: tpm
    ~/.tmux.conf:
        src: tmux.conf
        type: copy
    ~/.vimrc:
        src: vimrc
        type: link
";
    let yaml_documents = YamlLoader::load_from_str(s).unwrap();
    let yaml_config = &yaml_documents[0];
    let dot_files: &Yaml = &yaml_config["files"];
    let logger = slog::Logger::root(slog_stdlog::StdLog.fuse(), o!());
    let parsed_dot_files: Vec<DotFile> = parse_dot_files(&logger, dot_files);

    assert_that(&parsed_dot_files).has_length(3);
    assert_that(&parsed_dot_files).contains(&DotFile{ source: "tpm".to_string(), target: "~/.tmux/plugins/tpm".to_string() , dot_file_type: DotFileType::LINK });
    assert_that(&parsed_dot_files).contains(&DotFile{ source: "tmux.conf".to_string(), target: "~/.tmux.conf".to_string() , dot_file_type: DotFileType::LINK });
    assert_that(&parsed_dot_files).contains(&DotFile{ source: "vimrc".to_string(), target: "~/.vimrc".to_string() , dot_file_type: DotFileType::LINK });
  }
}
<commit_msg>parse copy<commit_after>use yaml_rust;
use slog::{Logger};
use yaml_rust::Yaml;
use model::*;

pub fn parse_dot_files(log: &Logger, dot_files: &Yaml) -> Vec<DotFile> {
  let mut parsed_dot_files = Vec::new();
  info!(log, "Processing dotfiles");
  if let Yaml::Hash(entries) = dot_files.clone() {
    for (key, value) in entries {
      match (key, value) {
        (Yaml::String(target), Yaml::String(source)) =>
          parsed_dot_files.push(DotFile{ source: source.to_string(), target: target.to_string() , dot_file_type: DotFileType::LINK }),
        (Yaml::String(target), Yaml::Hash(settings)) =>
          parsed_dot_files.push(dot_file_from_settings(target, &settings)),
        _ => {}
      }
    }
  } else {
    warn!(log, "Found no entries to process");
  }
  parsed_dot_files
}

fn dot_file_from_settings(target: String, settings: &yaml_rust::yaml::Hash) -> DotFile {
  let mut dot_file = DotFile{ source: "<todo>".to_string(), target: target.to_string(), dot_file_type: DotFileType::LINK };
  for (key, value) in settings.clone() {
    match (key, value) {
      (Yaml::String(setting_key), Yaml::String(setting_value)) => {
        match setting_key.as_ref() {
          "src" => dot_file.source = setting_value.to_string(),
          "type" => dot_file.dot_file_type = dot_file_type_from_string(&setting_value),
          _ => {}
        }
      },
      _ => {}
    }
  }
  dot_file
}

fn dot_file_type_from_string(s: &str) -> DotFileType {
  match s.to_lowercase().as_ref() {
    "copy" => DotFileType::COPY,
    "link" => DotFileType::LINK,
    _ => DotFileType::LINK
  }
}

#[cfg(test)]
mod tests {
  use slog;
  use slog_stdlog;
  use slog::{DrainExt};
  use yaml_rust::{YamlLoader};
  use yaml_rust::Yaml;
  use super::*;
  use spectral::prelude::*;

  #[test]
  fn parse_config() {
    let s =
      "
files:
    ~/.tmux/plugins/tpm: tpm
    ~/.tmux.conf:
        src: tmux.conf
        type: copy
    ~/.vimrc:
        src: vimrc
        type: link
";
    let yaml_documents = YamlLoader::load_from_str(s).unwrap();
    let yaml_config = &yaml_documents[0];
    let dot_files: &Yaml = &yaml_config["files"];
    let logger = slog::Logger::root(slog_stdlog::StdLog.fuse(), o!());
    let parsed_dot_files: Vec<DotFile> = parse_dot_files(&logger, dot_files);

    assert_that(&parsed_dot_files).has_length(3);
    assert_that(&parsed_dot_files).contains(&DotFile{ source: "tpm".to_string(), target: "~/.tmux/plugins/tpm".to_string() , dot_file_type: DotFileType::LINK });
    assert_that(&parsed_dot_files).contains(&DotFile{ source: "tmux.conf".to_string(), target: "~/.tmux.conf".to_string() , dot_file_type: DotFileType::COPY });
    assert_that(&parsed_dot_files).contains(&DotFile{ source: "vimrc".to_string(), target: "~/.vimrc".to_string() , dot_file_type: DotFileType::LINK });
  }
}
<|endoftext|>