<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/.

use std::io;
use std::process::Command;
use std::fs;
use std::path::PathBuf;

use devicemapper::{DM, Device, DmFlags, DevId, DM_SUSPEND};
use uuid::Uuid;
use nix::sys::stat::{mknod, umask, Mode, S_IFBLK, S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP};
use nix::errno::EEXIST;

use types::{Sectors, DataBlocks, FroyoError, FroyoResult, InternalError};
use raid::{RaidSegment, RaidLinearDev, RaidLinearDevSave};
use util::{clear_dev, setup_dm_dev, teardown_dm_dev};
use consts::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinPoolDevSave {
    pub data_block_size: Sectors,
    pub low_water_blocks: DataBlocks,
    pub meta_dev: RaidLinearDevSave,
    pub data_dev: RaidLinearDevSave,
}

#[derive(Debug, Clone)]
pub struct ThinPoolDev {
    dm_name: String,
    dev: Device,
    data_block_size: Sectors,
    pub low_water_blocks: DataBlocks,
    params: String,
    pub meta_dev: RaidLinearDev,
    pub data_dev: RaidLinearDev,
}

#[derive(Debug, Clone, Copy)]
pub struct ThinPoolBlockUsage {
    pub used_meta: u64,
    pub total_meta: u64,
    pub used_data: DataBlocks,
    pub total_data: DataBlocks,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolStatus {
    Good((ThinPoolWorkingStatus, ThinPoolBlockUsage)),
    Fail,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolWorkingStatus {
    Good,
    ReadOnly,
    OutOfSpace,
    NeedsCheck,
}

impl ThinPoolDev {
    pub fn new(dm: &DM,
               id: &str,
               meta_segs: Vec<RaidSegment>,
               data_segs: Vec<RaidSegment>)
               -> FroyoResult<ThinPoolDev> {
        // meta
        let meta_name = format!("thin-meta-{}", id);
        let meta_raid_dev = try!(RaidLinearDev::new(
            dm,
            &meta_name,
            &Uuid::new_v4().to_simple_string(),
            meta_segs));

        try!(clear_dev(meta_raid_dev.dev));

        // data
        let data_name = format!("thin-data-{}", id);
        let data_raid_dev = try!(RaidLinearDev::new(
            dm,
            &data_name,
            &Uuid::new_v4().to_simple_string(),
            data_segs));

        ThinPoolDev::setup(
            dm,
            id,
            DATA_BLOCK_SIZE,
            DataBlocks(TPOOL_LOW_WATER_BLOCKS),
            meta_raid_dev,
            data_raid_dev)
    }

    pub fn setup(
        dm: &DM,
        id: &str,
        data_block_size: Sectors,
        low_water_blocks: DataBlocks,
        meta_raid_dev: RaidLinearDev,
        data_raid_dev: RaidLinearDev)
        -> FroyoResult<ThinPoolDev> {

        let params = format!("{}:{} {}:{} {} {}",
                             meta_raid_dev.dev.major,
                             meta_raid_dev.dev.minor,
                             data_raid_dev.dev.major,
                             data_raid_dev.dev.minor,
                             *data_block_size,
                             *low_water_blocks);
        let table = [(0u64, *data_raid_dev.length(), "thin-pool", &*params)];

        let dm_name = format!("froyo-thin-pool-{}", id);
        let pool_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        Ok(ThinPoolDev {
            dm_name: dm_name,
            dev: pool_dev,
            data_block_size: data_block_size,
            low_water_blocks: low_water_blocks,
            params: params.clone(),
            meta_dev: meta_raid_dev,
            data_dev: data_raid_dev,
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(teardown_dm_dev(dm, &self.dm_name));
        try!(self.meta_dev.teardown(dm));
        try!(self.data_dev.teardown(dm));

        Ok(())
    }

    pub fn to_save(&self) -> ThinPoolDevSave {
        ThinPoolDevSave {
            data_block_size: self.data_block_size,
            low_water_blocks: self.low_water_blocks,
            meta_dev: self.meta_dev.to_save(),
            data_dev: self.data_dev.to_save(),
        }
    }

    pub fn status(&self) -> FroyoResult<ThinPoolStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin pool status")))
        }

        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinPoolStatus::Fail)
        }

        let status_vals = status_line.split(' ').collect::<Vec<_>>();
        if status_vals.len() < 8 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned too few values from thin pool status")))
        }

        let usage = {
            let meta_vals = status_vals[1].split('/').collect::<Vec<_>>();
            let data_vals = status_vals[2].split('/').collect::<Vec<_>>();
            ThinPoolBlockUsage {
                used_meta: meta_vals[0].parse::<u64>().unwrap(),
                total_meta: meta_vals[1].parse::<u64>().unwrap(),
                used_data: DataBlocks(data_vals[0].parse::<u64>().unwrap()),
                total_data: DataBlocks(data_vals[1].parse::<u64>().unwrap()),
            }
        };

        match status_vals[7] {
            "-" => {},
            "needs_check" => return Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::NeedsCheck, usage))),
            _ => return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }

        match status_vals[4] {
            "rw" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::Good, usage))),
            "ro" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::ReadOnly, usage))),
            "out_of_data_space" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::OutOfSpace, usage))),
            _ => Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }
    }

    // return size of a data block in bytes
    pub fn data_block_size(&self) -> u64 {
        *self.data_block_size * SECTOR_SIZE
    }

    pub fn sectors_to_blocks(&self, sectors: Sectors) -> DataBlocks {
        DataBlocks(*sectors / *self.data_block_size)
    }

    pub fn blocks_to_sectors(&self, blocks: DataBlocks) -> Sectors {
        Sectors(*blocks * *self.data_block_size)
    }

    pub fn extend_data_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.data_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    pub fn extend_meta_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.meta_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    fn dm_reload(&self) -> FroyoResult<()> {
        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.data_dev.length(), "thin-pool", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));
        Ok(())
    }

    pub fn used_sectors(&self) -> Sectors {
        self.meta_dev.length() + self.data_dev.length()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinDevSave {
    pub name: String,
    pub thin_number: u32,
    pub size: Sectors,
}

#[derive(Debug, Clone)]
pub struct ThinDev {
    dev: Device,
    name: String,
    pub thin_number: u32,
    pub size: Sectors,
    dm_name: String,
    params: String,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinStatus {
    Good(Sectors),
    Fail,
}

impl ThinDev {
    pub fn new(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        try!(dm.target_msg(&DevId::Name(&pool_dev.dm_name),
                           0, &format!("create_thin {}", thin_number)));

        let mut td = try!(ThinDev::setup(
            dm,
            froyo_id,
            name,
            thin_number,
            size,
            pool_dev));

        try!(td.create_fs(name));

        Ok(td)
    }

    pub fn setup(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        let params = format!("{}:{} {}", pool_dev.dev.major,
                             pool_dev.dev.minor, thin_number);
        let table = [(0u64, *size, "thin", &*params)];

        let dm_name = format!("froyo-thin-{}-{}", froyo_id, thin_number);
        let thin_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        try!(ThinDev::create_devnode(name, thin_dev));

        Ok(ThinDev {
            dev: thin_dev,
            name: name.to_owned(),
            thin_number: thin_number,
            size: size,
            dm_name: dm_name,
            params: params.clone(),
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        // Do this first so if devnode is in use this fails before we
        // remove the devnode
        try!(teardown_dm_dev(dm, &self.dm_name));
        try!(self.remove_devnode());

        Ok(())
    }

    pub fn extend(&mut self, sectors: Sectors) -> FroyoResult<()> {

        self.size = self.size + sectors;

        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.size, "thin", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));

        // TODO: we need to know where it's mounted in order to call
        // this
        // let output = try!(Command::new("xfs_growfs")
        //                   .arg(&mount_point)
        //                   .output());

        Ok(())
    }

    pub fn to_save(&self) -> ThinDevSave {
        ThinDevSave {
            name: self.name.clone(),
            thin_number: self.thin_number,
            size: self.size,
        }
    }

    pub fn status(&self) -> FroyoResult<ThinStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin status")))
        }

        // We should either get 1 line or the kernel is broken
        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinStatus::Fail)
        }
        let status_vals = status_line.split(' ').collect::<Vec<_>>();

        Ok(ThinStatus::Good(Sectors(
            status_vals[0].parse::<u64>().unwrap())))
    }

    fn create_devnode(name: &str, dev: Device) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");

        if let Err(e) = fs::create_dir(&pathbuf) {
            if e.kind() != io::ErrorKind::AlreadyExists {
                return Err(FroyoError::Io(e))
            }
        }

        pathbuf.push(name);

        let old_umask = umask(Mode::empty());
        let res = mknod(&pathbuf,
                    S_IFBLK,
                    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP,
                    dev.into());
        umask(old_umask);
        if let Err(e) = res {
            if e.errno() != EEXIST {
                return Err(FroyoError::Nix(e))
            }
        }

        Ok(())
    }

    fn remove_devnode(&mut self) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");
        pathbuf.push(&self.name);
        try!(fs::remove_file(&pathbuf));

        Ok(())
    }

    fn create_fs(&mut self, name: &str) -> FroyoResult<()> {
        let dev_name = format!("/dev/froyo/{}", name);
        let output = try!(Command::new("mkfs.xfs")
                          .arg("-f")
                          .arg(&dev_name)
                          .output());

        if output.status.success(){
            dbgp!("Created xfs filesystem on {}", dev_name)
        } else {
            return Err(FroyoError::Froyo(InternalError(
                format!("XFS mkfs error: {}",
                        String::from_utf8_lossy(&output.stderr)).into())))
        }
        Ok(())
    }
}
<commit_msg>thin: skip block zeroing<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/.

use std::io;
use std::process::Command;
use std::fs;
use std::path::PathBuf;

use devicemapper::{DM, Device, DmFlags, DevId, DM_SUSPEND};
use uuid::Uuid;
use nix::sys::stat::{mknod, umask, Mode, S_IFBLK, S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP};
use nix::errno::EEXIST;

use types::{Sectors, DataBlocks, FroyoError, FroyoResult, InternalError};
use raid::{RaidSegment, RaidLinearDev, RaidLinearDevSave};
use util::{clear_dev, setup_dm_dev, teardown_dm_dev};
use consts::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinPoolDevSave {
    pub data_block_size: Sectors,
    pub low_water_blocks: DataBlocks,
    pub meta_dev: RaidLinearDevSave,
    pub data_dev: RaidLinearDevSave,
}

#[derive(Debug, Clone)]
pub struct ThinPoolDev {
    dm_name: String,
    dev: Device,
    data_block_size: Sectors,
    pub low_water_blocks: DataBlocks,
    params: String,
    pub meta_dev: RaidLinearDev,
    pub data_dev: RaidLinearDev,
}

#[derive(Debug, Clone, Copy)]
pub struct ThinPoolBlockUsage {
    pub used_meta: u64,
    pub total_meta: u64,
    pub used_data: DataBlocks,
    pub total_data: DataBlocks,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolStatus {
    Good((ThinPoolWorkingStatus, ThinPoolBlockUsage)),
    Fail,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolWorkingStatus {
    Good,
    ReadOnly,
    OutOfSpace,
    NeedsCheck,
}

impl ThinPoolDev {
    pub fn new(dm: &DM,
               id: &str,
               meta_segs: Vec<RaidSegment>,
               data_segs: Vec<RaidSegment>)
               -> FroyoResult<ThinPoolDev> {
        // meta
        let meta_name = format!("thin-meta-{}", id);
        let meta_raid_dev = try!(RaidLinearDev::new(
            dm,
            &meta_name,
            &Uuid::new_v4().to_simple_string(),
            meta_segs));

        try!(clear_dev(meta_raid_dev.dev));

        // data
        let data_name = format!("thin-data-{}", id);
        let data_raid_dev = try!(RaidLinearDev::new(
            dm,
            &data_name,
            &Uuid::new_v4().to_simple_string(),
            data_segs));

        ThinPoolDev::setup(
            dm,
            id,
            DATA_BLOCK_SIZE,
            DataBlocks(TPOOL_LOW_WATER_BLOCKS),
            meta_raid_dev,
            data_raid_dev)
    }

    pub fn setup(
        dm: &DM,
        id: &str,
        data_block_size: Sectors,
        low_water_blocks: DataBlocks,
        meta_raid_dev: RaidLinearDev,
        data_raid_dev: RaidLinearDev)
        -> FroyoResult<ThinPoolDev> {

        let params = format!("{}:{} {}:{} {} {} 1 skip_block_zeroing",
                             meta_raid_dev.dev.major,
                             meta_raid_dev.dev.minor,
                             data_raid_dev.dev.major,
                             data_raid_dev.dev.minor,
                             *data_block_size,
                             *low_water_blocks);
        let table = [(0u64, *data_raid_dev.length(), "thin-pool", &*params)];

        let dm_name = format!("froyo-thin-pool-{}", id);
        let pool_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        Ok(ThinPoolDev {
            dm_name: dm_name,
            dev: pool_dev,
            data_block_size: data_block_size,
            low_water_blocks: low_water_blocks,
            params: params.clone(),
            meta_dev: meta_raid_dev,
            data_dev: data_raid_dev,
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(teardown_dm_dev(dm, &self.dm_name));
        try!(self.meta_dev.teardown(dm));
        try!(self.data_dev.teardown(dm));

        Ok(())
    }

    pub fn to_save(&self) -> ThinPoolDevSave {
        ThinPoolDevSave {
            data_block_size: self.data_block_size,
            low_water_blocks: self.low_water_blocks,
            meta_dev: self.meta_dev.to_save(),
            data_dev: self.data_dev.to_save(),
        }
    }

    pub fn status(&self) -> FroyoResult<ThinPoolStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin pool status")))
        }

        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinPoolStatus::Fail)
        }

        let status_vals = status_line.split(' ').collect::<Vec<_>>();
        if status_vals.len() < 8 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned too few values from thin pool status")))
        }

        let usage = {
            let meta_vals = status_vals[1].split('/').collect::<Vec<_>>();
            let data_vals = status_vals[2].split('/').collect::<Vec<_>>();
            ThinPoolBlockUsage {
                used_meta: meta_vals[0].parse::<u64>().unwrap(),
                total_meta: meta_vals[1].parse::<u64>().unwrap(),
                used_data: DataBlocks(data_vals[0].parse::<u64>().unwrap()),
                total_data: DataBlocks(data_vals[1].parse::<u64>().unwrap()),
            }
        };

        match status_vals[7] {
            "-" => {},
            "needs_check" => return Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::NeedsCheck, usage))),
            _ => return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }

        match status_vals[4] {
            "rw" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::Good, usage))),
            "ro" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::ReadOnly, usage))),
            "out_of_data_space" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::OutOfSpace, usage))),
            _ => Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }
    }

    // return size of a data block in bytes
    pub fn data_block_size(&self) -> u64 {
        *self.data_block_size * SECTOR_SIZE
    }

    pub fn sectors_to_blocks(&self, sectors: Sectors) -> DataBlocks {
        DataBlocks(*sectors / *self.data_block_size)
    }

    pub fn blocks_to_sectors(&self, blocks: DataBlocks) -> Sectors {
        Sectors(*blocks * *self.data_block_size)
    }

    pub fn extend_data_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.data_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    pub fn extend_meta_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.meta_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    fn dm_reload(&self) -> FroyoResult<()> {
        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.data_dev.length(), "thin-pool", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));
        Ok(())
    }

    pub fn used_sectors(&self) -> Sectors {
        self.meta_dev.length() + self.data_dev.length()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinDevSave {
    pub name: String,
    pub thin_number: u32,
    pub size: Sectors,
}

#[derive(Debug, Clone)]
pub struct ThinDev {
    dev: Device,
    name: String,
    pub thin_number: u32,
    pub size: Sectors,
    dm_name: String,
    params: String,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinStatus {
    Good(Sectors),
    Fail,
}

impl ThinDev {
    pub fn new(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        try!(dm.target_msg(&DevId::Name(&pool_dev.dm_name),
                           0, &format!("create_thin {}", thin_number)));

        let mut td = try!(ThinDev::setup(
            dm,
            froyo_id,
            name,
            thin_number,
            size,
            pool_dev));

        try!(td.create_fs(name));

        Ok(td)
    }

    pub fn setup(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        let params = format!("{}:{} {}", pool_dev.dev.major,
                             pool_dev.dev.minor, thin_number);
        let table = [(0u64, *size, "thin", &*params)];

        let dm_name = format!("froyo-thin-{}-{}", froyo_id, thin_number);
        let thin_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        try!(ThinDev::create_devnode(name, thin_dev));

        Ok(ThinDev {
            dev: thin_dev,
            name: name.to_owned(),
            thin_number: thin_number,
            size: size,
            dm_name: dm_name,
            params: params.clone(),
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        // Do this first so if devnode is in use this fails before we
        // remove the devnode
        try!(teardown_dm_dev(dm, &self.dm_name));
        try!(self.remove_devnode());

        Ok(())
    }

    pub fn extend(&mut self, sectors: Sectors) -> FroyoResult<()> {

        self.size = self.size + sectors;

        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.size, "thin", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));

        // TODO: we need to know where it's mounted in order to call
        // this
        // let output = try!(Command::new("xfs_growfs")
        //                   .arg(&mount_point)
        //                   .output());

        Ok(())
    }

    pub fn to_save(&self) -> ThinDevSave {
        ThinDevSave {
            name: self.name.clone(),
            thin_number: self.thin_number,
            size: self.size,
        }
    }

    pub fn status(&self) -> FroyoResult<ThinStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin status")))
        }

        // We should either get 1 line or the kernel is broken
        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinStatus::Fail)
        }
        let status_vals = status_line.split(' ').collect::<Vec<_>>();

        Ok(ThinStatus::Good(Sectors(
            status_vals[0].parse::<u64>().unwrap())))
    }

    fn create_devnode(name: &str, dev: Device) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");

        if let Err(e) = fs::create_dir(&pathbuf) {
            if e.kind() != io::ErrorKind::AlreadyExists {
                return Err(FroyoError::Io(e))
            }
        }

        pathbuf.push(name);

        let old_umask = umask(Mode::empty());
        let res = mknod(&pathbuf,
                    S_IFBLK,
                    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP,
                    dev.into());
        umask(old_umask);
        if let Err(e) = res {
            if e.errno() != EEXIST {
                return Err(FroyoError::Nix(e))
            }
        }

        Ok(())
    }

    fn remove_devnode(&mut self) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");
        pathbuf.push(&self.name);
        try!(fs::remove_file(&pathbuf));

        Ok(())
    }

    fn create_fs(&mut self, name: &str) -> FroyoResult<()> {
        let dev_name = format!("/dev/froyo/{}", name);
        let output = try!(Command::new("mkfs.xfs")
                          .arg("-f")
                          .arg(&dev_name)
                          .output());

        if output.status.success(){
            dbgp!("Created xfs filesystem on {}", dev_name)
        } else {
            return Err(FroyoError::Froyo(InternalError(
                format!("XFS mkfs error: {}",
                        String::from_utf8_lossy(&output.stderr)).into())))
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::{
    cmp::min,
    pin::Pin,
    task::{Context, Poll},
};

use futures_util::{ready, stream::Stream};
use partial_sort::partial_sort;
use pin_project_lite::pin_project;
use serde::{Deserialize, Serialize};
use shakmaty::ByColor;

#[derive(Serialize, Deserialize)]
#[serde(remote = "ByColor")]
pub struct ByColorDef<T> {
    white: T,
    black: T,
}

pub fn sort_by_key_and_truncate<T, K, F>(vec: &mut Vec<T>, num: usize, mut f: F)
where
    F: FnMut(&T) -> K,
    K: Ord,
{
    let num = min(num, vec.len());
    partial_sort(vec, num, |a, b| f(a).lt(&f(b)));
    vec.truncate(num);
}

pub trait DedupStreamExt: Stream {
    fn dedup_by_key<F, T>(self, f: F) -> Dedup<Self, F, T>
    where
        Self: Sized,
        F: FnMut(&Self::Item) -> T,
        T: PartialEq,
    {
        Dedup {
            stream: self,
            f,
            latest: None,
        }
    }
}

impl<S> DedupStreamExt for S where S: Stream {}

pin_project! {
    pub struct Dedup<S, F, T> where S: Stream {
        #[pin]
        stream: S,
        latest: Option<T>,
        f: F,
    }
}

impl<S, F, T> Stream for Dedup<S, F, T>
where
    S: Stream,
    F: FnMut(&S::Item) -> T,
    T: PartialEq,
{
    type Item = S::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
        let mut this = self.project();

        Poll::Ready(loop {
            if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) {
                let latest = this.latest.replace((this.f)(&item));
                if latest != *this.latest {
                    break Some(item);
                }
            } else {
                break None;
            }
        })
    }
}
<commit_msg>tweak ByColorDef order<commit_after>use std::{
    cmp::min,
    pin::Pin,
    task::{Context, Poll},
};

use futures_util::{ready, stream::Stream};
use partial_sort::partial_sort;
use pin_project_lite::pin_project;
use serde::{Deserialize, Serialize};
use shakmaty::ByColor;

#[derive(Serialize, Deserialize)]
#[serde(remote = "ByColor")]
pub struct ByColorDef<T> {
    black: T,
    white: T,
}

pub fn sort_by_key_and_truncate<T, K, F>(vec: &mut Vec<T>, num: usize, mut f: F)
where
    F: FnMut(&T) -> K,
    K: Ord,
{
    let num = min(num, vec.len());
    partial_sort(vec, num, |a, b| f(a).lt(&f(b)));
    vec.truncate(num);
}

pub trait DedupStreamExt: Stream {
    fn dedup_by_key<F, T>(self, f: F) -> Dedup<Self, F, T>
    where
        Self: Sized,
        F: FnMut(&Self::Item) -> T,
        T: PartialEq,
    {
        Dedup {
            stream: self,
            f,
            latest: None,
        }
    }
}

impl<S> DedupStreamExt for S where S: Stream {}

pin_project! {
    pub struct Dedup<S, F, T> where S: Stream {
        #[pin]
        stream: S,
        latest: Option<T>,
        f: F,
    }
}

impl<S, F, T> Stream for Dedup<S, F, T>
where
    S: Stream,
    F: FnMut(&S::Item) -> T,
    T: PartialEq,
{
    type Item = S::Item;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
        let mut this = self.project();

        Poll::Ready(loop {
            if let Some(item) = ready!(this.stream.as_mut().poll_next(cx)) {
                let latest = this.latest.replace((this.f)(&item));
                if latest != *this.latest {
                    break Some(item);
                }
            } else {
                break None;
            }
        })
    }
}
<|endoftext|>


<commit_before>#![feature(test, plugin, decl_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
#[macro_use]
extern crate rust_embed;

use std::path::PathBuf;
use std::ffi::OsStr;
use std::io::Cursor;
use rocket::response;
use rocket::http::{ContentType, Status};

#[derive(RustEmbed)]
#[folder = "examples/public/"]
struct Asset;

#[get("/")]
fn index<'r>() -> response::Result<'r> {
  Asset::get("index.html").map_or_else(
    || Err(Status::NotFound),
    |d| {
      response::Response::build()
        .header(ContentType::HTML)
        .sized_body(Cursor::new(d))
        .ok()
    },
  )
}

#[get("/dist/<file..>")]
fn dist<'r>(file: PathBuf) -> response::Result<'r> {
  let filename = file.display().to_string();
  let ext = file
    .as_path()
    .extension()
    .and_then(OsStr::to_str)
    .expect("Could not get file extension");
  let content_type = ContentType::from_extension(ext).expect("Could not get file content type");
  Asset::get(&filename.clone()).map_or_else(
    || Err(Status::NotFound),
    |d| {
      response::Response::build()
        .header(content_type)
        .sized_body(Cursor::new(d))
        .ok()
    },
  )
}

fn main() {
  rocket::ignite().mount("/", routes![index, dist]).launch();
}
<commit_msg>format project using cargo fmt<commit_after>#![feature(test, plugin, decl_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
#[macro_use]
extern crate rust_embed;

use rocket::http::{ContentType, Status};
use rocket::response;
use std::ffi::OsStr;
use std::io::Cursor;
use std::path::PathBuf;

#[derive(RustEmbed)]
#[folder = "examples/public/"]
struct Asset;

#[get("/")]
fn index<'r>() -> response::Result<'r> {
  Asset::get("index.html").map_or_else(
    || Err(Status::NotFound),
    |d| response::Response::build().header(ContentType::HTML).sized_body(Cursor::new(d)).ok(),
  )
}

#[get("/dist/<file..>")]
fn dist<'r>(file: PathBuf) -> response::Result<'r> {
  let filename = file.display().to_string();
  let ext = file.as_path().extension().and_then(OsStr::to_str).expect("Could not get file extension");
  let content_type = ContentType::from_extension(ext).expect("Could not get file content type");
  Asset::get(&filename.clone()).map_or_else(
    || Err(Status::NotFound),
    |d| response::Response::build().header(content_type).sized_body(Cursor::new(d)).ok(),
  )
}

fn main() {
  rocket::ignite().mount("/", routes![index, dist]).launch();
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate iron_mountrouter;

// To run, $ cargo run --example router
// To use, go to http://127.0.0.1:3000/
use std::fs::File;
use std::io::Read;

use iron::{Iron, Request, Response, IronResult};
use iron::headers::ContentType;
use iron::status;
use iron_mountrouter::{Router, OriginalUrl};

fn get_output(content: &str) -> String {
	let mut res = String::new();
	File::open("examples/router.html").unwrap().read_to_string(&mut res).unwrap();
	res.replace("", content)
}

fn main() {
    let mut router = Router::new();
    router.add_route("/", handler, false);
    router.add_route("/:query/:sub-query/", handler, false);

    let mut book_router = Router::new();
    book_router.add_route("/page/:key/", handler, false);
    book_router.add_route("/contents/", handler, false);
    router.add_route("/book/:book-name/", book_router, true);

    Iron::new(router).http("localhost:3000").unwrap();

    fn handler(req: &mut Request) -> IronResult<Response> {
        let ref query = req.extensions.get::<Router>()
            .unwrap();
        let mut res = Response::with((
			status::Ok,
			get_output(
				&format!(
					"Url: {:?}\nQuery params: {:?}\nOriginal url: {:?}",
					req.url.path,
					*query,
					req.extensions.get::<OriginalUrl>()
				)
			)
		));
    	res.headers.set(ContentType::html());
    	Ok(res)
    }
}<commit_msg>Updated examples to match latest change<commit_after>extern crate iron;
extern crate iron_mountrouter;

// To run, $ cargo run --example router
// To use, go to http://127.0.0.1:3000/
use std::fs::File;
use std::io::Read;

use iron::{Iron, Request, Response, IronResult};
use iron::headers::ContentType;
use iron::status;
use iron_mountrouter::{Router, StrippedUrl};

fn get_output(content: &str) -> String {
	let mut res = String::new();
	File::open("examples/router.html").unwrap().read_to_string(&mut res).unwrap();
	res.replace("", content)
}

fn main() {
    let mut router = Router::new();
    router.add_route("/", handler, false);
    router.add_route("/:query/:sub-query/", handler, false);

    let mut book_router = Router::new();
    book_router.add_route("/page/:key/", handler, false);
    book_router.add_route("/contents/", handler, false);
    router.add_route("/book/:book-name/", book_router, true);

    Iron::new(router).http("localhost:3000").unwrap();

    fn handler(req: &mut Request) -> IronResult<Response> {
        let ref query = req.extensions.get::<Router>()
            .unwrap();
        let mut res = Response::with((
			status::Ok,
			get_output(
				&format!(
					"
Url: {:?}
Query parts: {:?}
Stripped url: {:?}",
					req.url.path,
					*query,
					req.extensions.get::<StrippedUrl>()
				)
			)
		));
    	res.headers.set(ContentType::html());
    	Ok(res)
    }
}<|endoftext|>


<commit_before>use story_builder::article_provider::*;
use std::rc::Rc;
use std::borrow::Borrow;
use std::ops::Deref;
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use std::collections::HashSet;

pub struct StoryBuilder {
    article_provider: Arc<(ArticleProvider + Send + Sync)>,
    max_depth: u8,
    visited_nodes: HashSet<String>,
}

impl StoryBuilder {
    pub fn new(article_provider: Arc<(ArticleProvider + Send + Sync)>) -> StoryBuilder {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
            visited_nodes: HashSet::new(),
        }
    }

    pub fn build_story(&mut self, start_topic: &str, end_topic: &str) -> Result<String, String> {
        let start_topic = start_topic.to_lowercase();
        let end_topic = end_topic.to_lowercase();
        // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

        // If both topics are the same,
        // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

        // Load the first article
        let start_article = match self.article_provider.get(&start_topic) {
            Some(start_article) => start_article,
            None => {
                return Err(self.build_suggestions_msg(&start_topic));
            }
        };
        self.visited_nodes.insert(start_topic);
        // Load the end article
        match self.article_provider.get(&end_topic) {
            Some(end_topic) => end_topic,
            None => {
                return Err(self.build_suggestions_msg(&end_topic));
            }
        };

        /* To build a story, we need to build a tree starting at the start_article
           node and going down in a "breadth-first" way; that way, once we find
           the end note, we know it is the shortest path to it. Also, going depth-first
           will be impossibly long to complete sincethe depth of the wikipedia
           article tree is almost infinite. */
        /* We look for a paragraph that holds a reference to our end topic
           somewhere in the last level we fetched: */
        let mut last_level: Vec<Rc<ArticleNode>> = vec![Rc::new(ArticleNode::new(start_article))]; // starts with start article
        for i in 0..self.max_depth {
            // To prevent overloading the system, stop after X level deep
            // Start by loading the next level of articles:
            if i > 0 {
                // Any other iteration: go one level deeper:
                let mut current_level = vec![];
                for rc_article_node in last_level.iter() {
                    /* Iterate on each paragraph of this article, then map on it to
                       get the article for each related topic in it. */
                    // First: spawn all threads first
                    let mut threads: Vec<
                        JoinHandle<(String, Option<Box<Article + Send + Sync>>)>,
                    > = Vec::new();
                    for paragraph in rc_article_node.deref().get_paragraphs().iter() {
                        for topic in paragraph.topics.iter() {
                            let topic = topic.to_lowercase();
                            // Do not access the same article more than once!!
                            if !self.visited_nodes.contains(&topic) {
                                let article_provider = self.article_provider.clone();
                                let topic_for_thread = topic.clone();
                                let par_text = paragraph.text.clone();
                                threads.push(thread::spawn(
                                    move || (par_text, article_provider.get(&topic_for_thread)),
                                ));
                                self.visited_nodes.insert(topic);
                            }
                        }
                    }
                    // Then, join them up one at a time.
                    for t in threads.into_iter() {
                        match t.join().unwrap() {
                            (source_paragraph, Some(a)) => {
                                let mut new_node = ArticleNode::new(a);
                                new_node.attach_to(rc_article_node.clone(), source_paragraph);
                                current_level.push(Rc::new(new_node));
                            }
                            (_, None) => (),
                        }
                    }
                    println!("Done.");
                }
                last_level = current_level;
            }
            // Replace the previous level with this level.

            for article_node in last_level.iter() {
                if let Some(text) = StoryBuilder::find_text_for_topic_in_article(
                    article_node.deref().deref().borrow(),
                    &end_topic,
                ) {
                    // Found the topic. Format and return.
                    return Ok(StoryBuilder::build_final_text(
                        article_node.clone(),
                        text,
                        &end_topic,
                    ));
                }
            }
        }

        Err(
            format!(
                "Reached depth of <{}> without finding <{}>. Stopping search.",
                self.max_depth,
                end_topic
            ).to_owned(),
        )
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!(
            "Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n",
            topic
        ));
        for sugg in self.article_provider.search(topic) {
            msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }

    fn find_text_for_topic_in_article<'b>(
        article: &'b (Article + Send + Sync),
        topic: &str,
    ) -> Option<&'b str> {
        if let Some(paragraph) = article.get_paragraphs().iter().find(|par| {
            // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|t| &t.to_lowercase() == topic)
        }) {
            // We found the paragraph; return it directly.
            return Some(&paragraph.text);
        } else {
            None
        }
    }

    fn build_final_text(
        article_node: Rc<ArticleNode>,
        final_text: &str,
        final_topic: &str,
    ) -> String {
        let mut texts: Vec<String> = Vec::new();
        let mut last_topic = final_topic.to_owned();
        texts.push(format!("{}\r\n", final_text));
        {
            let n: &ArticleNode = article_node.borrow();
            if let Some(n_text) = n.text() {
                let new_topic = n.get_topic().to_owned();
                texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                last_topic = new_topic;
                texts.push(format!("{}\r\n", n_text));
            }
        }

        let mut node = article_node;
        loop {
            node = match node.parent() {
                Some(rc) => {
                    {
                        let n: &ArticleNode = rc.borrow();
                        if let Some(n_text) = n.text() {
                            let new_topic = n.get_topic().to_owned();
                            texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                            last_topic = new_topic;
                            texts.push(format!("{}\r\n", n_text));
                        }
                    }
                    rc
                }
                _ => {
                    let new_topic = node.get_topic().to_owned();
                    texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                    return texts.into_iter().rev().collect();
                }
            }
        }
    }
}

struct ArticleNode {
    data: Box<Article + Send + Sync>,
    parent: Option<Rc<ArticleNode>>,
    text: Option<String>,
}

impl ArticleNode {
    fn new(data: Box<Article + Send + Sync>) -> ArticleNode {
        ArticleNode {
            data,
            parent: None,
            text: None,
        }
    }
    fn attach_to(&mut self, parent: Rc<ArticleNode>, paragraph_text: String) {
        self.parent = Some(parent);
        self.text = Some(paragraph_text);
    }
    fn parent(&self) -> Option<Rc<ArticleNode>> {
        self.parent.clone()
    }
    fn text(&self) -> Option<String> {
        self.text.clone()
    }
}

impl<'n> Deref for ArticleNode {
    type Target = Box<Article + Send + Sync>;

    fn deref(&self) -> &Box<Article + Send + Sync> {
        &self.data
    }
}

#[cfg(test)]
mod tests;
<commit_msg>Stop saying "Done." after each article peocessed.<commit_after>use story_builder::article_provider::*;
use std::rc::Rc;
use std::borrow::Borrow;
use std::ops::Deref;
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use std::collections::HashSet;

pub struct StoryBuilder {
    article_provider: Arc<(ArticleProvider + Send + Sync)>,
    max_depth: u8,
    visited_nodes: HashSet<String>,
}

impl StoryBuilder {
    pub fn new(article_provider: Arc<(ArticleProvider + Send + Sync)>) -> StoryBuilder {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
            visited_nodes: HashSet::new(),
        }
    }

    pub fn build_story(&mut self, start_topic: &str, end_topic: &str) -> Result<String, String> {
        let start_topic = start_topic.to_lowercase();
        let end_topic = end_topic.to_lowercase();
        // If one of the topics is an empty string, do not try to make a story out of it.
        if start_topic == "" {
            return Err("Missing start topic.".to_owned());
        }
        if end_topic == "" {
            return Err("Missing end topic.".to_owned());
        }

        // If both topics are the same,
        // there is no point in trying to figure out the story.
        if start_topic == end_topic {
            return Err("No story to build; same start and end topics.".to_owned());
        }

        // Load the first article
        let start_article = match self.article_provider.get(&start_topic) {
            Some(start_article) => start_article,
            None => {
                return Err(self.build_suggestions_msg(&start_topic));
            }
        };
        self.visited_nodes.insert(start_topic);
        // Load the end article
        match self.article_provider.get(&end_topic) {
            Some(end_topic) => end_topic,
            None => {
                return Err(self.build_suggestions_msg(&end_topic));
            }
        };

        /* To build a story, we need to build a tree starting at the start_article
           node and going down in a "breadth-first" way; that way, once we find
           the end note, we know it is the shortest path to it. Also, going depth-first
           will be impossibly long to complete sincethe depth of the wikipedia
           article tree is almost infinite. */
        /* We look for a paragraph that holds a reference to our end topic
           somewhere in the last level we fetched: */
        let mut last_level: Vec<Rc<ArticleNode>> = vec![Rc::new(ArticleNode::new(start_article))]; // starts with start article
        for i in 0..self.max_depth {
            // To prevent overloading the system, stop after X level deep
            // Start by loading the next level of articles:
            if i > 0 {
                // Any other iteration: go one level deeper:
                let mut current_level = vec![];
                for rc_article_node in last_level.iter() {
                    /* Iterate on each paragraph of this article, then map on it to
                       get the article for each related topic in it. */
                    // First: spawn all threads first
                    let mut threads: Vec<
                        JoinHandle<(String, Option<Box<Article + Send + Sync>>)>,
                    > = Vec::new();
                    for paragraph in rc_article_node.deref().get_paragraphs().iter() {
                        for topic in paragraph.topics.iter() {
                            let topic = topic.to_lowercase();
                            // Do not access the same article more than once!!
                            if !self.visited_nodes.contains(&topic) {
                                let article_provider = self.article_provider.clone();
                                let topic_for_thread = topic.clone();
                                let par_text = paragraph.text.clone();
                                threads.push(thread::spawn(
                                    move || (par_text, article_provider.get(&topic_for_thread)),
                                ));
                                self.visited_nodes.insert(topic);
                            }
                        }
                    }
                    // Then, join them up one at a time.
                    for t in threads.into_iter() {
                        match t.join().unwrap() {
                            (source_paragraph, Some(a)) => {
                                let mut new_node = ArticleNode::new(a);
                                new_node.attach_to(rc_article_node.clone(), source_paragraph);
                                current_level.push(Rc::new(new_node));
                            }
                            (_, None) => (),
                        }
                    }
                }
                last_level = current_level;
            }
            // Replace the previous level with this level.

            for article_node in last_level.iter() {
                if let Some(text) = StoryBuilder::find_text_for_topic_in_article(
                    article_node.deref().deref().borrow(),
                    &end_topic,
                ) {
                    // Found the topic. Format and return.
                    return Ok(StoryBuilder::build_final_text(
                        article_node.clone(),
                        text,
                        &end_topic,
                    ));
                }
            }
        }

        Err(
            format!(
                "Reached depth of <{}> without finding <{}>. Stopping search.",
                self.max_depth,
                end_topic
            ).to_owned(),
        )
    }

    fn build_suggestions_msg(&self, topic: &str) -> String {
        let mut msg = String::from(format!(
            "Cannot find wikipedia article for <{}>, try one of the following suggestions:\r\n",
            topic
        ));
        for sugg in self.article_provider.search(topic) {
            msg.push_str(&format!("- {}\r\n", &sugg));
        }

        msg
    }

    fn find_text_for_topic_in_article<'b>(
        article: &'b (Article + Send + Sync),
        topic: &str,
    ) -> Option<&'b str> {
        if let Some(paragraph) = article.get_paragraphs().iter().find(|par| {
            // if any of the topics in the paragraph is <end>, return it.
            par.topics.iter().any(|t| &t.to_lowercase() == topic)
        }) {
            // We found the paragraph; return it directly.
            return Some(&paragraph.text);
        } else {
            None
        }
    }

    fn build_final_text(
        article_node: Rc<ArticleNode>,
        final_text: &str,
        final_topic: &str,
    ) -> String {
        let mut texts: Vec<String> = Vec::new();
        let mut last_topic = final_topic.to_owned();
        texts.push(format!("{}\r\n", final_text));
        {
            let n: &ArticleNode = article_node.borrow();
            if let Some(n_text) = n.text() {
                let new_topic = n.get_topic().to_owned();
                texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                last_topic = new_topic;
                texts.push(format!("{}\r\n", n_text));
            }
        }

        let mut node = article_node;
        loop {
            node = match node.parent() {
                Some(rc) => {
                    {
                        let n: &ArticleNode = rc.borrow();
                        if let Some(n_text) = n.text() {
                            let new_topic = n.get_topic().to_owned();
                            texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                            last_topic = new_topic;
                            texts.push(format!("{}\r\n", n_text));
                        }
                    }
                    rc
                }
                _ => {
                    let new_topic = node.get_topic().to_owned();
                    texts.push(format!("-> ({} to {})\r\n", &new_topic, last_topic));
                    return texts.into_iter().rev().collect();
                }
            }
        }
    }
}

struct ArticleNode {
    data: Box<Article + Send + Sync>,
    parent: Option<Rc<ArticleNode>>,
    text: Option<String>,
}

impl ArticleNode {
    fn new(data: Box<Article + Send + Sync>) -> ArticleNode {
        ArticleNode {
            data,
            parent: None,
            text: None,
        }
    }
    fn attach_to(&mut self, parent: Rc<ArticleNode>, paragraph_text: String) {
        self.parent = Some(parent);
        self.text = Some(paragraph_text);
    }
    fn parent(&self) -> Option<Rc<ArticleNode>> {
        self.parent.clone()
    }
    fn text(&self) -> Option<String> {
        self.text.clone()
    }
}

impl<'n> Deref for ArticleNode {
    type Target = Box<Article + Send + Sync>;

    fn deref(&self) -> &Box<Article + Send + Sync> {
        &self.data
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>use fall_tree::{NodeType, Text, TextRange, TextUnit, tu, TreeBuilder};
use lex_engine::Token;
use syn_engine::Event;

pub(crate) fn convert(
    text: Text,
    tokens: &[Token],
    events: &[Event],
    is_whitespace: &Fn(NodeType) -> bool,
    whitespace_binder: &Fn(NodeType, &[(NodeType, Text)], bool) -> usize,
    builder: &mut TreeBuilder,
) {
    let events = reshuffle_events(events);
    let (first, rest) = (events[0], &events[1..]);

    let mut len = tu(0);
    let tokens = tokens.iter().map(|&t| {
        let token_text = text.slice(TextRange::from_len(len, t.len));
        len += t.len;
        (t, token_text)
    }).collect::<Vec<_>>();

    let conv = Convertor { is_whitespace, whitespace_binder };
    match first {
        Event::Start { ty, forward_parent: _ } => {
            conv.go(ty, &tokens, rest, builder);
        }
        _ => unreachable!()
    }
}

fn reshuffle_events(events: &[Event]) -> Vec<Event> {
    let mut result = Vec::with_capacity(events.len());
    let mut holes = Vec::new();
    let mut forward_parents = Vec::new();

    for (i, &e) in events.iter().enumerate() {
        if holes.last() == Some(&i) {
            holes.pop();
            continue
        }
        match e {
            Event::Start { forward_parent: Some(_), .. } => {
                forward_parents.clear();
                let mut idx = i;
                loop {
                    let (ty, fwd) = match events[idx] {
                        Event::Start { ty, forward_parent } => (ty, forward_parent),
                        _ => unreachable!(),
                    };
                    forward_parents.push((idx, ty));
                    if let Some(fwd) = fwd {
                        idx += fwd as usize;
                    } else {
                        break;
                    }
                }
                for &(idx, ty) in forward_parents.iter().into_iter().rev() {
                    result.push(Event::Start { ty, forward_parent: None });
                    holes.push(idx);
                }
                holes.pop();
            }
            _ => result.push(e)
        }
    }
    result
}

struct Convertor<'a> {
    is_whitespace: &'a Fn(NodeType) -> bool,
    whitespace_binder: &'a Fn(NodeType, &[(NodeType, Text)], bool) -> usize,
}

#[derive(Debug)]
struct Conversion {
    right_edge: usize,
    n_events: usize,
}

impl<'a> Convertor<'a> {
    fn go(
        &self,
        ty: NodeType,
        tokens: &[(Token, Text)],
        events: &[Event],
        builder: &mut TreeBuilder,
    ) -> Conversion {
        builder.start_internal(ty);
        let (n_tokens, n_events) = self.fill(tokens, events, builder);
        let tokens = &tokens[n_tokens..];

        let trailing_ws = self.collect_tokens_for_binder(tokens);
        let right_ws = (self.whitespace_binder)(ty, &trailing_ws, false);
        for &(t, _) in &tokens[..right_ws] {
            builder.leaf(t.ty, t.len);
        }
        builder.finish_internal();

        let right_edge = n_tokens + right_ws;
        return Conversion {
            right_edge,
            n_events,
        };
    }

    fn collect_tokens_for_binder<'t>(&self, tokens: &[(Token, Text<'t>)]) -> Vec<(NodeType, Text<'t>)> {
        tokens.iter()
            .take_while(|&&(t, _)| (self.is_whitespace)(t.ty))
            .map(|&(t, text)| (t.ty, text))
            .collect()
    }

    fn fill(
        &self,
        tokens: &[(Token, Text)],
        events: &[Event],
        builder: &mut TreeBuilder,
    ) -> (usize, usize) {
        let mut tokens = tokens;
        let mut n_tokens = 0;
        let mut events = events;
        let mut n_events = 0;
        loop {
            let (first, rest) = (events[0], &events[1..]);
            n_events += 1;
            events = rest;
            match first {
                Event::Start { ty, forward_parent: _ } => {
                    let leading_ws = self.collect_tokens_for_binder(tokens);
                    let left_wd = leading_ws.len() - (self.whitespace_binder)(ty, &leading_ws, true);
                    for i in 0..left_wd {
                        let t = tokens[i].0;
                        builder.leaf(t.ty, t.len);
                    }
                    tokens = &tokens[left_wd..];
                    n_tokens += left_wd;

                    let Conversion { right_edge, n_events: child_events } =
                        self.go(ty, tokens, events, builder);
                    tokens = &tokens[right_edge..];
                    n_tokens += right_edge;
                    events = &events[child_events..];
                    n_events += child_events;
                }

                Event::End => return (n_tokens, n_events),

                Event::Token { ty, n_raw_tokens } => {
                    let non_white = tokens.iter().take_while(|&&(t, _)| (self.is_whitespace)(t.ty)).count();
                    for i in 0..non_white {
                        let t = tokens[i].0;
                        builder.leaf(t.ty, t.len);
                    }
                    tokens = &tokens[non_white..];
                    n_tokens += non_white;

                    let n_raw_tokens = n_raw_tokens as usize;
                    let mut len = tu(0);
                    for &(t, _) in &tokens[..n_raw_tokens] {
                        n_tokens += 1;
                        len += t.len;
                    }
                    builder.leaf(ty, len);
                    tokens = &tokens[n_raw_tokens..];
                }

                Event::Cached { .. } => {}
            }
        }
    }
}
<commit_msg>Unused import<commit_after>use fall_tree::{NodeType, Text, TextRange, TreeBuilder, tu};
use lex_engine::Token;
use syn_engine::Event;

pub(crate) fn convert(
    text: Text,
    tokens: &[Token],
    events: &[Event],
    is_whitespace: &Fn(NodeType) -> bool,
    whitespace_binder: &Fn(NodeType, &[(NodeType, Text)], bool) -> usize,
    builder: &mut TreeBuilder,
) {
    let events = reshuffle_events(events);
    let (first, rest) = (events[0], &events[1..]);

    let mut len = tu(0);
    let tokens = tokens.iter().map(|&t| {
        let token_text = text.slice(TextRange::from_len(len, t.len));
        len += t.len;
        (t, token_text)
    }).collect::<Vec<_>>();

    let conv = Convertor { is_whitespace, whitespace_binder };
    match first {
        Event::Start { ty, forward_parent: _ } => {
            conv.go(ty, &tokens, rest, builder);
        }
        _ => unreachable!()
    }
}

fn reshuffle_events(events: &[Event]) -> Vec<Event> {
    let mut result = Vec::with_capacity(events.len());
    let mut holes = Vec::new();
    let mut forward_parents = Vec::new();

    for (i, &e) in events.iter().enumerate() {
        if holes.last() == Some(&i) {
            holes.pop();
            continue
        }
        match e {
            Event::Start { forward_parent: Some(_), .. } => {
                forward_parents.clear();
                let mut idx = i;
                loop {
                    let (ty, fwd) = match events[idx] {
                        Event::Start { ty, forward_parent } => (ty, forward_parent),
                        _ => unreachable!(),
                    };
                    forward_parents.push((idx, ty));
                    if let Some(fwd) = fwd {
                        idx += fwd as usize;
                    } else {
                        break;
                    }
                }
                for &(idx, ty) in forward_parents.iter().into_iter().rev() {
                    result.push(Event::Start { ty, forward_parent: None });
                    holes.push(idx);
                }
                holes.pop();
            }
            _ => result.push(e)
        }
    }
    result
}

struct Convertor<'a> {
    is_whitespace: &'a Fn(NodeType) -> bool,
    whitespace_binder: &'a Fn(NodeType, &[(NodeType, Text)], bool) -> usize,
}

#[derive(Debug)]
struct Conversion {
    right_edge: usize,
    n_events: usize,
}

impl<'a> Convertor<'a> {
    fn go(
        &self,
        ty: NodeType,
        tokens: &[(Token, Text)],
        events: &[Event],
        builder: &mut TreeBuilder,
    ) -> Conversion {
        builder.start_internal(ty);
        let (n_tokens, n_events) = self.fill(tokens, events, builder);
        let tokens = &tokens[n_tokens..];

        let trailing_ws = self.collect_tokens_for_binder(tokens);
        let right_ws = (self.whitespace_binder)(ty, &trailing_ws, false);
        for &(t, _) in &tokens[..right_ws] {
            builder.leaf(t.ty, t.len);
        }
        builder.finish_internal();

        let right_edge = n_tokens + right_ws;
        return Conversion {
            right_edge,
            n_events,
        };
    }

    fn collect_tokens_for_binder<'t>(&self, tokens: &[(Token, Text<'t>)]) -> Vec<(NodeType, Text<'t>)> {
        tokens.iter()
            .take_while(|&&(t, _)| (self.is_whitespace)(t.ty))
            .map(|&(t, text)| (t.ty, text))
            .collect()
    }

    fn fill(
        &self,
        tokens: &[(Token, Text)],
        events: &[Event],
        builder: &mut TreeBuilder,
    ) -> (usize, usize) {
        let mut tokens = tokens;
        let mut n_tokens = 0;
        let mut events = events;
        let mut n_events = 0;
        loop {
            let (first, rest) = (events[0], &events[1..]);
            n_events += 1;
            events = rest;
            match first {
                Event::Start { ty, forward_parent: _ } => {
                    let leading_ws = self.collect_tokens_for_binder(tokens);
                    let left_wd = leading_ws.len() - (self.whitespace_binder)(ty, &leading_ws, true);
                    for i in 0..left_wd {
                        let t = tokens[i].0;
                        builder.leaf(t.ty, t.len);
                    }
                    tokens = &tokens[left_wd..];
                    n_tokens += left_wd;

                    let Conversion { right_edge, n_events: child_events } =
                        self.go(ty, tokens, events, builder);
                    tokens = &tokens[right_edge..];
                    n_tokens += right_edge;
                    events = &events[child_events..];
                    n_events += child_events;
                }

                Event::End => return (n_tokens, n_events),

                Event::Token { ty, n_raw_tokens } => {
                    let non_white = tokens.iter().take_while(|&&(t, _)| (self.is_whitespace)(t.ty)).count();
                    for i in 0..non_white {
                        let t = tokens[i].0;
                        builder.leaf(t.ty, t.len);
                    }
                    tokens = &tokens[non_white..];
                    n_tokens += non_white;

                    let n_raw_tokens = n_raw_tokens as usize;
                    let mut len = tu(0);
                    for &(t, _) in &tokens[..n_raw_tokens] {
                        n_tokens += 1;
                        len += t.len;
                    }
                    builder.leaf(ty, len);
                    tokens = &tokens[n_raw_tokens..];
                }

                Event::Cached { .. } => {}
            }
        }
    }
}
<|endoftext|>


<commit_before>/// Serial port management module supporting one
/// writer and multiple readers
///
/// Clients can lock a port for writing, but
/// subscribe to data from multiple ports for reads

use std::collections::HashSet;
use std::iter::FromIterator;
use std::sync::mpsc::{Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, SystemTime};

use base64;

use serial_support::errors::*;
use serial_support::messages::*;
use serial_support::sub_manager::*;
use serial_support::port_manager::*;
use serial_support::writelock_manager::*;
use serial_support::common::*;

/// Manager manages connection state.
/// TODO Needs a better name
pub struct Manager {
  /// Manage write lock status
  writelock_manager: WriteLockManager,
  /// Manage ports
  port_manager: PortManager,
  /// Manage subscriptions
  sub_manager: SubscriptionManager,
  /// Receiver for serial requests
  receiver: Receiver<SerialRequest>,
  /// Receiver for response subscription requests
  subsc_receiver: SubscReceiver,
}

impl Manager {
  ///Constructor
  pub fn new(receiver: Receiver<SerialRequest>, subsc_receiver: SubscReceiver) -> Manager {
    Manager {
      writelock_manager: WriteLockManager::new(),
      port_manager: PortManager::new(),
      sub_manager: SubscriptionManager::new(),
      receiver: receiver,
      subsc_receiver: subsc_receiver,
    }
  }

  ///Spawn an instance in a new thread.
  pub fn spawn(receiver: Receiver<SerialRequest>,
               subsc_receiver: SubscReceiver)
               -> thread::JoinHandle<()> {
    thread::spawn(move || { Manager::new(receiver, subsc_receiver).run(); })
  }

  /// Main loop
  fn run(&mut self) {

    // Bad ports we couldn't read from
    // A set of SerialResponse::Errors built from
    // from the serial port read/write error responses
    let mut bad_ports = HashSet::<String>::new();
    // Shutdown flag
    let mut shutdown = false;
    // Check about 30 times a second
    let sleep_dur = Duration::from_millis(33);

    while !shutdown {
      // Sleep for a little bit to avoid pegging cpu
      // TODO Dynamic timing loop
      thread::sleep(sleep_dur);

      // Handle serial operation requests
      match self.receiver.try_recv() {
        Err(e) => {
          match e {
            TryRecvError::Empty => {
              // nothing to do
            }
            TryRecvError::Disconnected => {
              // Remote end hung up, time to shutdown
              info!("Shutting down SerialPortManager");
              shutdown = true;
            }
          }
        }
        Ok(req) => self.handle_serial_request(req),
      }

      // Check for new data on each port
      for (port_name, result) in self.port_manager.read_all_ports() {
        match result {
          Ok(data) => {
            let response = match String::from_utf8(data) {
              // We need to send as binary
              Err(e) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: base64::encode(&e.into_bytes()),
                  base64: Some(true),
                }
              }
              Ok(s) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: s,
                  base64: Some(false),
                }
              }
            };
            self.broadcast_message_for_port(&port_name, response);
          }
          // Send data reads
          Err(e) => {
            warn!("Error reading port!");
            warn!("{}",e);
            bad_ports.insert(port_name);
          }
        }
      }

      //Handle write requests
      let mut recv_count = 0;
      let mut do_recv = true;
      while do_recv {
        recv_count += 1;
        match self.subsc_receiver.try_recv() {
          Ok(sub_request) => self.sub_manager.add_subscription(sub_request),
          Err(e) => {
            match e {
              TryRecvError::Disconnected => {
                // Does this mean all senders have disconnected?
                // Or just one?
                debug!("Got disconnected when trying to get serial request");
              }
              TryRecvError::Empty => do_recv = false,
            }
          }
        }

        // Don't let this loop get stuck here forever
        // starving the outer loop
        if recv_count == 50 {
          do_recv = false;
        }
      }

      // Cleanup bad serial ports that failed read or write
      // We remove them from everything before
      self.cleanup_bad_ports(&bad_ports);
      bad_ports.clear();
    }
  }

  /// Handles and dispatches SerialRequest sent by
  /// the channel
  fn handle_serial_request(&mut self, msg: SerialRequest) {
    let response = match msg {
      SerialRequest::Open { sub_id, port } => self.handle_open_port(sub_id, port),
      SerialRequest::WriteLock { sub_id, port } => self.handle_write_lock(sub_id, port),
      SerialRequest::ReleaseWriteLock { sub_id, port } => {
        self.handle_release_write_lock(sub_id, port)
      }
      SerialRequest::Write {
        sub_id,
        port,
        data,
        base64,
      } => self.handle_write_port(sub_id, port, data, base64.unwrap_or(false)),
      SerialRequest::Close { sub_id, port } => self.handle_close_port(sub_id, port),
    };
    match response {
      Ok(response) => {}
      Err(e) => {}
    }
  }

  /// Handle write port requests
  fn handle_write_port(&self,
                       sub_id: String,
                       port_name: String,
                       data: String,
                       base_64: bool)
                       -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self.check_owns_writelock(&port_name, &sub_id)?;
    match base_64 {
      true => {
        base64::decode(&data)
          .map_err(|e| ErrorKind::Base64(e).into())
          .and_then(|d| self.port_manager.write_port(&port_name, &d))
          .and(Ok(SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
      false => {
        self
          .port_manager
          .write_port(&port_name, data.as_bytes())
          .and(Ok(SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
    }
  }

  /// Handle write lock requests
  fn handle_write_lock(&mut self, sub_id: String, port_name: String) -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self
      .writelock_manager
      .lock_port(&port_name, &sub_id)
      .and(Ok(SerialResponse::Ok {
                msg: format!("Locking port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle write requests
  fn handle_release_write_lock(&mut self,
                               sub_id: String,
                               port_name: Option<String>)
                               -> Result<SerialResponse> {
    match port_name {
      None => {
        self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        Ok(SerialResponse::Ok { msg: "Unlocking all ports locked by client succeeded".to_string() })
      }
      Some(port_name) => {
        self
          .writelock_manager
          .unlock_port(&port_name, &sub_id)
          .and(Ok(SerialResponse::Ok {
                    msg: format!("Locking port {} succeeded", port_name).to_string(),
                  }))
      }
    }
  }

  /// Handle open port requests
  fn handle_open_port(&mut self, sub_id: String, port_name: String) -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self.port_manager.open_port(&port_name)?;
    self
      .sub_manager
      .add_port(&sub_id, &port_name)
      .and(Ok(SerialResponse::Ok {
                msg: format!("Opening port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle close port requests
  fn handle_close_port(&mut self,
                       sub_id: String,
                       port_name: Option<String>)
                       -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    // Unsubscribe from ports
    match port_name {
      None => self.sub_manager.clear_ports(Some(&sub_id)),
      Some(pn) => self.sub_manager.remove_port(&sub_id, &pn),
    }?;
    // Close ports with no subscribers
    let open_ports = HashSet::<String>::from_iter(self.port_manager.open_ports());
    let subscribed_ports = HashSet::<String>::from_iter(self.sub_manager.subscribed_ports());
    let ports_with_no_subs = open_ports.difference(&subscribed_ports);
    // For each open port that isn't subscribed,
    for port_to_close in ports_with_no_subs {
      // close it
      self.port_manager.close_port(&port_to_close);
      // remove the write lock
      self.writelock_manager.clear_lock(&port_to_close);
      // Let them know its closed
      let close_resp = SerialResponse::Closed { port: port_to_close.clone() };
      self.send_message(&sub_id, close_resp);
    }
    Ok(SerialResponse::Ok { msg: "Derp".to_string() })
  }

  /// Cleanup any bad ports
  /// Clears out the passed in hash set
  fn cleanup_bad_ports(&mut self, bad_ports: &HashSet<String>) {
    for port_name in bad_ports.iter() {
      // Tell everyone port is sick
      let err_resp = SerialResponse::Error {
        display: format!("Error reading from port '{}', closing!", port_name),
        description: format!("An error occured while trying to read data from '{}'",
                             port_name),
      };
      self.broadcast_message_for_port(port_name, err_resp);
      // Tesll everyone the sick ports were closed
      let close_resp = SerialResponse::Closed { port: port_name.clone() };
      self.broadcast_message_for_port(port_name, close_resp);
      // Close bad ports
      self.port_manager.close_port(port_name);
      // Remove write locks on bad ports
      self.writelock_manager.clear_lock(port_name);
      // Remove bad ports from subscriptions
      self.sub_manager.remove_port_from_all(port_name);
    }
    // Clear set of bad_ports
  }

  pub fn send_message(&mut self, sub_id: &String, msg: SerialResponse) {
    match self.sub_manager.send_message(sub_id, msg) {
      Err(e) => {
        let mut bad_subs = Vec::new();
        bad_subs.push(e);
        self.cleanup_bad_subs(bad_subs);
      }
      _ => {}
    }
  }

  /// Broadcast a message to all subscribers and
  /// then cleanup any subs that errored
  pub fn broadcast_message(&mut self, msg: SerialResponse) {
    let bad_subs = self.sub_manager.broadcast_message(msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Broadcast a message to all subscribers interested in the
  /// given port and then cleanup any subs that errored
  pub fn broadcast_message_for_port(&mut self, port_name: &String, msg: SerialResponse) {
    let bad_subs = self
      .sub_manager
      .broadcast_message_for_port(port_name, msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Cleanup any bad subs where a message send failed
  fn cleanup_bad_subs(&mut self, bad_subs: Vec<Error>) {
    for e in bad_subs {
      match e {
        Error(ErrorKind::SubscriberSendError(sub_id), _) => {
          self.sub_manager.end_subscription(&sub_id);
          self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        }
        _ => { /*nop*/ }
      }
    }
  }

  fn check_sub_id(&self, sub_id: &String) -> Result<()> {
    self.sub_manager.check_subscription_exists(sub_id)
  }

  fn check_owns_writelock(&self, port_name: &String, sub_id: &String) -> Result<()> {
    self
      .writelock_manager
      .check_owns_write_lock(port_name, sub_id)
  }
}
<commit_msg>Slight format tweak<commit_after>/// Serial port management module supporting one
/// writer and multiple readers
///
/// Clients can lock a port for writing, but
/// subscribe to data from multiple ports for reads

use std::collections::HashSet;
use std::iter::FromIterator;
use std::sync::mpsc::{Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, SystemTime};

use base64;

use serial_support::errors::*;
use serial_support::messages::*;
use serial_support::sub_manager::*;
use serial_support::port_manager::*;
use serial_support::writelock_manager::*;
use serial_support::common::*;

/// Manager manages connection state.
/// TODO Needs a better name
pub struct Manager {
  /// Manage write lock status
  writelock_manager: WriteLockManager,
  /// Manage ports
  port_manager: PortManager,
  /// Manage subscriptions
  sub_manager: SubscriptionManager,
  /// Receiver for serial requests
  receiver: Receiver<SerialRequest>,
  /// Receiver for response subscription requests
  subsc_receiver: SubscReceiver,
}

impl Manager {
  ///Constructor
  pub fn new(receiver: Receiver<SerialRequest>, subsc_receiver: SubscReceiver) -> Manager {
    Manager {
      writelock_manager: WriteLockManager::new(),
      port_manager: PortManager::new(),
      sub_manager: SubscriptionManager::new(),
      receiver: receiver,
      subsc_receiver: subsc_receiver,
    }
  }

  ///Spawn an instance in a new thread.
  pub fn spawn(receiver: Receiver<SerialRequest>,
               subsc_receiver: SubscReceiver)
               -> thread::JoinHandle<()> {
    thread::spawn(move || { Manager::new(receiver, subsc_receiver).run(); })
  }

  /// Main loop
  fn run(&mut self) {

    // Bad ports we couldn't read from
    // A set of SerialResponse::Errors built from
    // from the serial port read/write error responses
    let mut bad_ports = HashSet::<String>::new();
    // Shutdown flag
    let mut shutdown = false;
    // Check about 30 times a second
    let sleep_dur = Duration::from_millis(33);

    while !shutdown {
      // Sleep for a little bit to avoid pegging cpu
      // TODO Dynamic timing loop
      thread::sleep(sleep_dur);

      // Handle serial operation requests
      match self.receiver.try_recv() {
        Err(e) => {
          match e {
            TryRecvError::Empty => {
              // nothing to do
            }
            TryRecvError::Disconnected => {
              // Remote end hung up, time to shutdown
              info!("Shutting down SerialPortManager");
              shutdown = true;
            }
          }
        }
        Ok(req) => self.handle_serial_request(req),
      }

      // Check for new data on each port
      for (port_name, result) in self.port_manager.read_all_ports() {
        match result {
          Ok(data) => {
            let response = match String::from_utf8(data) {
              // We need to send as binary
              Err(e) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: base64::encode(&e.into_bytes()),
                  base64: Some(true),
                }
              }
              Ok(s) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: s,
                  base64: Some(false),
                }
              }
            };
            self.broadcast_message_for_port(&port_name, response);
          }
          // Send data reads
          Err(e) => {
            warn!("Error reading port!");
            warn!("{}", e);
            bad_ports.insert(port_name);
          }
        }
      }

      //Handle write requests
      let mut recv_count = 0;
      let mut do_recv = true;
      while do_recv {
        recv_count += 1;
        match self.subsc_receiver.try_recv() {
          Ok(sub_request) => self.sub_manager.add_subscription(sub_request),
          Err(e) => {
            match e {
              TryRecvError::Disconnected => {
                // Does this mean all senders have disconnected?
                // Or just one?
                debug!("Got disconnected when trying to get serial request");
              }
              TryRecvError::Empty => do_recv = false,
            }
          }
        }

        // Don't let this loop get stuck here forever
        // starving the outer loop
        if recv_count == 50 {
          do_recv = false;
        }
      }

      // Cleanup bad serial ports that failed read or write
      // We remove them from everything before
      self.cleanup_bad_ports(&bad_ports);
      bad_ports.clear();
    }
  }

  /// Handles and dispatches SerialRequest sent by
  /// the channel
  fn handle_serial_request(&mut self, msg: SerialRequest) {
    let response = match msg {
      SerialRequest::Open { sub_id, port } => self.handle_open_port(sub_id, port),
      SerialRequest::WriteLock { sub_id, port } => self.handle_write_lock(sub_id, port),
      SerialRequest::ReleaseWriteLock { sub_id, port } => {
        self.handle_release_write_lock(sub_id, port)
      }
      SerialRequest::Write {
        sub_id,
        port,
        data,
        base64,
      } => self.handle_write_port(sub_id, port, data, base64.unwrap_or(false)),
      SerialRequest::Close { sub_id, port } => self.handle_close_port(sub_id, port),
    };
    match response {
      Ok(response) => {}
      Err(e) => {}
    }
  }

  /// Handle write port requests
  fn handle_write_port(&self,
                       sub_id: String,
                       port_name: String,
                       data: String,
                       base_64: bool)
                       -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self.check_owns_writelock(&port_name, &sub_id)?;
    match base_64 {
      true => {
        base64::decode(&data)
          .map_err(|e| ErrorKind::Base64(e).into())
          .and_then(|d| self.port_manager.write_port(&port_name, &d))
          .and(Ok(SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
      false => {
        self
          .port_manager
          .write_port(&port_name, data.as_bytes())
          .and(Ok(SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
    }
  }

  /// Handle write lock requests
  fn handle_write_lock(&mut self, sub_id: String, port_name: String) -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self
      .writelock_manager
      .lock_port(&port_name, &sub_id)
      .and(Ok(SerialResponse::Ok {
                msg: format!("Locking port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle write requests
  fn handle_release_write_lock(&mut self,
                               sub_id: String,
                               port_name: Option<String>)
                               -> Result<SerialResponse> {
    match port_name {
      None => {
        self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        Ok(SerialResponse::Ok { msg: "Unlocking all ports locked by client succeeded".to_string() })
      }
      Some(port_name) => {
        self
          .writelock_manager
          .unlock_port(&port_name, &sub_id)
          .and(Ok(SerialResponse::Ok {
                    msg: format!("Locking port {} succeeded", port_name).to_string(),
                  }))
      }
    }
  }

  /// Handle open port requests
  fn handle_open_port(&mut self, sub_id: String, port_name: String) -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self.port_manager.open_port(&port_name)?;
    self
      .sub_manager
      .add_port(&sub_id, &port_name)
      .and(Ok(SerialResponse::Ok {
                msg: format!("Opening port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle close port requests
  fn handle_close_port(&mut self,
                       sub_id: String,
                       port_name: Option<String>)
                       -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    // Unsubscribe from ports
    match port_name {
      None => self.sub_manager.clear_ports(Some(&sub_id)),
      Some(pn) => self.sub_manager.remove_port(&sub_id, &pn),
    }?;
    // Close ports with no subscribers
    let open_ports = HashSet::<String>::from_iter(self.port_manager.open_ports());
    let subscribed_ports = HashSet::<String>::from_iter(self.sub_manager.subscribed_ports());
    let ports_with_no_subs = open_ports.difference(&subscribed_ports);
    // For each open port that isn't subscribed,
    for port_to_close in ports_with_no_subs {
      // close it
      self.port_manager.close_port(&port_to_close);
      // remove the write lock
      self.writelock_manager.clear_lock(&port_to_close);
      // Let them know its closed
      let close_resp = SerialResponse::Closed { port: port_to_close.clone() };
      self.send_message(&sub_id, close_resp);
    }
    Ok(SerialResponse::Ok { msg: "Derp".to_string() })
  }

  /// Cleanup any bad ports
  /// Clears out the passed in hash set
  fn cleanup_bad_ports(&mut self, bad_ports: &HashSet<String>) {
    for port_name in bad_ports.iter() {
      // Tell everyone port is sick
      let err_resp = SerialResponse::Error {
        display: format!("Error reading from port '{}', closing!", port_name),
        description: format!("An error occured while trying to read data from '{}'",
                             port_name),
      };
      self.broadcast_message_for_port(port_name, err_resp);
      // Tesll everyone the sick ports were closed
      let close_resp = SerialResponse::Closed { port: port_name.clone() };
      self.broadcast_message_for_port(port_name, close_resp);
      // Close bad ports
      self.port_manager.close_port(port_name);
      // Remove write locks on bad ports
      self.writelock_manager.clear_lock(port_name);
      // Remove bad ports from subscriptions
      self.sub_manager.remove_port_from_all(port_name);
    }
    // Clear set of bad_ports
  }

  pub fn send_message(&mut self, sub_id: &String, msg: SerialResponse) {
    match self.sub_manager.send_message(sub_id, msg) {
      Err(e) => {
        let mut bad_subs = Vec::new();
        bad_subs.push(e);
        self.cleanup_bad_subs(bad_subs);
      }
      _ => {}
    }
  }

  /// Broadcast a message to all subscribers and
  /// then cleanup any subs that errored
  pub fn broadcast_message(&mut self, msg: SerialResponse) {
    let bad_subs = self.sub_manager.broadcast_message(msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Broadcast a message to all subscribers interested in the
  /// given port and then cleanup any subs that errored
  pub fn broadcast_message_for_port(&mut self, port_name: &String, msg: SerialResponse) {
    let bad_subs = self
      .sub_manager
      .broadcast_message_for_port(port_name, msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Cleanup any bad subs where a message send failed
  fn cleanup_bad_subs(&mut self, bad_subs: Vec<Error>) {
    for e in bad_subs {
      match e {
        Error(ErrorKind::SubscriberSendError(sub_id), _) => {
          self.sub_manager.end_subscription(&sub_id);
          self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        }
        _ => { /*nop*/ }
      }
    }
  }

  fn check_sub_id(&self, sub_id: &String) -> Result<()> {
    self.sub_manager.check_subscription_exists(sub_id)
  }

  fn check_owns_writelock(&self, port_name: &String, sub_id: &String) -> Result<()> {
    self
      .writelock_manager
      .check_owns_write_lock(port_name, sub_id)
  }
}
<|endoftext|>