<commit_before>use crate::{Location, World};
use serde_derive::{Deserialize, Serialize};
use std::slice;
use vitral::SRgba;

impl World {
    /// Return whether location is contained in the current play area.
    pub fn is_valid_location(&self, _loc: Location) -> bool {
        //Location::origin().v2_at(loc).map_or(false, ::on_screen)
        true
    }

    /// Return terrain at location.
    pub fn terrain(&self, loc: Location) -> Terrain {
        let mut t = self.world_cache.get_terrain(loc);

        if t == Terrain::Door && self.has_mobs(loc) {
            // Standing in the doorway opens the door.
            t = Terrain::OpenDoor;
        }

        t
    }

    /// If location contains a portal, return the destination of the portal.
    pub fn portal(&self, loc: Location) -> Option<Location> { self.world_cache.get_portal(loc) }

    /// Return whether location has a border portals.
    ///
    /// Portals are divided into border and hole portals. A hole portal is usually surrounded by
    /// local scenery at all sides, while border portals are assumed to be at the edges of a convex
    /// patch of local terrain with nothing local past them.
    ///
    /// The difference between the two is important in how map memory works, map memory display
    /// goes through border portals normally, but ignores hole portals.
    pub fn is_border_portal(&self, _loc: Location) -> bool {
        // TODO: Implement internal data for border portals
        // Turn type Portal in the internal portal data into enum { Border(Portal), Edge(Portal) }
        false
    }

    /// Return a portal if it can be seen through.
    pub fn visible_portal(&self, loc: Location) -> Option<Location> {
        // Only void-form is transparent to portals.
        if self.terrain(loc).form() == Form::Void {
            self.portal(loc)
        } else {
            None
        }
    }
}

/// Movement effect of a terrain tile.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Kind {
    /// Regular flat solid ground, can walk across easily.
    Ground,
    /// An obstacle that fills the entire cell, blocks field of view.
    Block,
    /// An obstacle that can be seen through.
    ///
    /// Not necessarily a literal window, thinner blocks like pillars and statues might also be
    /// see-through.
    Window,
    /// A tile that blocks sight but can be walked through.
    Door,
    /// Bodies of water, regular units can't walk into them.
    ///
    /// Flying units (if we have any) can cross. Falling into water is going to involve tricky
    /// logic since our maps aren't 3D.
    Water,
    /// Like water, but much more fun.
    Magma,
}

/// Visual form of a terrain tile.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Form {
    /// Nothingness, lets sight pass through portals
    Void,
    /// Marker pointing at adjacent nothingness
    Gate,
    /// Single frame on floor layer
    Floor,
    /// Single frame on object layer
    Prop,
    /// Blobbing form on object layer
    Blob,
    /// Wall-form on object layer
    Wall,
}

struct TerrainData {
    name: &'static str,
    kind: Kind,
    form: Form,
    map_chars: &'static str,
    /// For variants that should not show up in main terrain sets.
    is_regular: bool,
    /// 4-bit components, R << 8 + G << 4 + B.
    color: u16,
}

macro_rules! count_tts {
    () => {0usize};
    ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)};
}

macro_rules! terrain_enum {
    {
        $($sym:ident: $data:expr,)+
    } => {
        #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
        pub enum Terrain {
            $($sym,)+
        }

        const N_ENUM: usize = count_tts!($($sym)+);

        static TERRAIN_DATA: [TerrainData; N_ENUM] = [
            $($data,)+
        ];

        static TERRAIN_ENUM: [Terrain; N_ENUM] = [
            $(Terrain::$sym,)+
        ];
    }
}

terrain_enum! {
    Empty:       TerrainData { name: "n/a",       kind: Kind::Block,  form: Form::Void,  map_chars: "",    is_regular: true,  color: 0xF0F },
    Void:        TerrainData { name: "void",      kind: Kind::Block,  form: Form::Void,  map_chars: "",    is_regular: true,  color: 0x011 },
    Downstairs:  TerrainData { name: "exit down", kind: Kind::Ground, form: Form::Gate,  map_chars: ">",   is_regular: true,  color: 0x0EE },
    Upstairs:    TerrainData { name: "exit up",   kind: Kind::Ground, form: Form::Gate,  map_chars: "<",   is_regular: true,  color: 0x0FF },
    Ground:      TerrainData { name: "ground",    kind: Kind::Ground, form: Form::Floor, map_chars: ".,_", is_regular: true,  color: 0x111 },
    Grass:       TerrainData { name: "grass",     kind: Kind::Ground, form: Form::Floor, map_chars: ",._", is_regular: true,  color: 0x231 },
    Sand:        TerrainData { name: "sand",      kind: Kind::Ground, form: Form::Floor, map_chars: ",._", is_regular: true,  color: 0x650 },
    Snow:        TerrainData { name: "snow",      kind: Kind::Ground, form: Form::Floor, map_chars: ",._", is_regular: true,  color: 0x788 },
    Water:       TerrainData { name: "water",     kind: Kind::Water,  form: Form::Floor, map_chars: "~=",  is_regular: true,  color: 0x058 },
    Shallows:    TerrainData { name: "shallows",  kind: Kind::Ground, form: Form::Floor, map_chars: "~=",  is_regular: true,  color: 0x08B },
    Magma:       TerrainData { name: "magma",     kind: Kind::Magma,  form: Form::Floor, map_chars: "=~",  is_regular: true,  color: 0xF22 },
    Tree:        TerrainData { name: "tree",      kind: Kind::Block,  form: Form::Prop,  map_chars: "",    is_regular: true,  color: 0x8B1 },
    DeadTree:    TerrainData { name: "dead tree", kind: Kind::Block,  form: Form::Prop,  map_chars: "",    is_regular: true,  color: 0x690 },
    Wall:        TerrainData { name: "wall",      kind: Kind::Block,  form: Form::Wall,  map_chars: "#*",  is_regular: true,  color: 0xBBB },
    Rock:        TerrainData { name: "rock",      kind: Kind::Block,  form: Form::Blob,  map_chars: "*#",  is_regular: true,  color: 0xB84 },
    Door:        TerrainData { name: "door",      kind: Kind::Door,   form: Form::Wall,  map_chars: "|",   is_regular: true,  color: 0x842 },
    OpenDoor:    TerrainData { name: "open door", kind: Kind::Ground, form: Form::Wall,  map_chars: "",    is_regular: false, color: 0xFAF },
    Window:      TerrainData { name: "window",    kind: Kind::Window, form: Form::Wall,  map_chars: "+",   is_regular: true,  color: 0xBFF },
    Pillar:      TerrainData { name: "pillar",    kind: Kind::Window, form: Form::Prop,  map_chars: "I",   is_regular: true,  color: 0xCCD },
    // TODO: Get rid of grass2, give render a coherent noise source for tiles and make it do the
    // variation locally.
    Grass2:      TerrainData { name: "grass",     kind: Kind::Ground, form: Form::Floor, map_chars: "",    is_regular: false, color: 0x230 },
}

impl Terrain {
    pub fn iter() -> slice::Iter<'static, Terrain> { TERRAIN_ENUM.iter() }

    pub fn from_color(color: SRgba) -> Option<Terrain> {
        let key =
            (((color.r >> 4) as u16) << 8) + (((color.g >> 4) as u16) << 4) + (color.b >> 4) as u16;
        Self::iter()
            .filter(|t| t.is_regular())
            .find(|t| TERRAIN_DATA[**t as usize].color == key)
            .cloned()
    }

    #[inline(always)]
    pub fn kind(self) -> Kind { TERRAIN_DATA[self as usize].kind }

    #[inline(always)]
    pub fn form(self) -> Form { TERRAIN_DATA[self as usize].form }

    pub fn blocks_sight(self) -> bool {
        match self.kind() {
            Kind::Block | Kind::Door => true,
            _ => false,
        }
    }

    pub fn blocks_shot(self) -> bool {
        match self.kind() {
            Kind::Block | Kind::Window | Kind::Door => true,
            _ => false,
        }
    }

    pub fn blocks_walk(self) -> bool {
        match self.kind() {
            Kind::Ground | Kind::Door => false,
            _ => true,
        }
    }

    pub fn name(self) -> &'static str { TERRAIN_DATA[self as usize].name }

    pub fn is_open(self) -> bool { self.kind() == Kind::Ground }

    pub fn is_door(self) -> bool { self.kind() == Kind::Door }

    pub fn is_luminous(self) -> bool { self.kind() == Kind::Magma }

    pub fn is_wall(self) -> bool { self.form() == Form::Wall }

    pub fn is_hull(self) -> bool { self.form() == Form::Wall || self.form() == Form::Blob }

    pub fn is_blob(self) -> bool { self.form() == Form::Blob }

    pub fn is_block(self) -> bool { self.is_hull() || self.form() == Form::Prop }

    pub fn is_regular(self) -> bool { TERRAIN_DATA[self as usize].is_regular }

    /// For constructing text maps.
    pub fn preferred_map_chars(self) -> &'static str { TERRAIN_DATA[self as usize].map_chars }

    /// Terrain is a narrow object that blocks movement.
    ///
    /// Prop obstacles might not be distinguishable from floors if you only see a corner of the
    /// terrain tile. Use this if there's need to highlight partially visible terrain as obstacles.
    pub fn is_narrow_obstacle(self) -> bool { self.blocks_walk() && self.form() == Form::Prop }

    pub fn color(self) -> SRgba {
        let col = TERRAIN_DATA[self as usize].color;
        let r = ((col >> 8) & 0xf) as u8;
        let g = ((col >> 4) & 0xf) as u8;
        let b = (col & 0xf) as u8;

        SRgba::new(r << 4, g << 4, b << 4, 0xff)
    }

    /// Vertical delta (up a level / down a level / flat) for this terrain.
    pub fn dz(self) -> i32 {
        use Terrain::*;
        match self {
            Upstairs => -1,
            Downstairs => 1,
            _ => 0,
        }
    }
}

impl Default for Terrain {
    fn default() -> Self { Terrain::Empty }
}

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

    #[test]
    fn test_colors_are_unique() {
        use std::collections::HashSet;

        let terrains: HashSet<Terrain> = Terrain::iter()
            .filter(|t| t.is_regular())
            .cloned()
            .collect();
        let colors: HashSet<SRgba> = terrains.iter().map(|t| t.color()).collect();

        assert_eq!(colors.len(), terrains.len());
    }

    #[test]
    fn test_from_color() {
        assert_eq!(
            Terrain::from_color(SRgba::new(0xff, 0x88, 0xff, 0xff)),
            None
        );
        assert_eq!(
            Terrain::from_color(SRgba::new(0x22, 0x30, 0x1f, 0xff)),
            Some(Terrain::Grass)
        );
    }
}
<commit_msg>Terrain FromStr<commit_after>use crate::{Location, World};
use serde_derive::{Deserialize, Serialize};
use std::slice;
use vitral::SRgba;

impl World {
    /// Return whether location is contained in the current play area.
    pub fn is_valid_location(&self, _loc: Location) -> bool {
        //Location::origin().v2_at(loc).map_or(false, ::on_screen)
        true
    }

    /// Return terrain at location.
    pub fn terrain(&self, loc: Location) -> Terrain {
        let mut t = self.world_cache.get_terrain(loc);

        if t == Terrain::Door && self.has_mobs(loc) {
            // Standing in the doorway opens the door.
            t = Terrain::OpenDoor;
        }

        t
    }

    /// If location contains a portal, return the destination of the portal.
    pub fn portal(&self, loc: Location) -> Option<Location> { self.world_cache.get_portal(loc) }

    /// Return whether location has a border portals.
    ///
    /// Portals are divided into border and hole portals. A hole portal is usually surrounded by
    /// local scenery at all sides, while border portals are assumed to be at the edges of a convex
    /// patch of local terrain with nothing local past them.
    ///
    /// The difference between the two is important in how map memory works, map memory display
    /// goes through border portals normally, but ignores hole portals.
    pub fn is_border_portal(&self, _loc: Location) -> bool {
        // TODO: Implement internal data for border portals
        // Turn type Portal in the internal portal data into enum { Border(Portal), Edge(Portal) }
        false
    }

    /// Return a portal if it can be seen through.
    pub fn visible_portal(&self, loc: Location) -> Option<Location> {
        // Only void-form is transparent to portals.
        if self.terrain(loc).form() == Form::Void {
            self.portal(loc)
        } else {
            None
        }
    }
}

/// Movement effect of a terrain tile.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Kind {
    /// Regular flat solid ground, can walk across easily.
    Ground,
    /// An obstacle that fills the entire cell, blocks field of view.
    Block,
    /// An obstacle that can be seen through.
    ///
    /// Not necessarily a literal window, thinner blocks like pillars and statues might also be
    /// see-through.
    Window,
    /// A tile that blocks sight but can be walked through.
    Door,
    /// Bodies of water, regular units can't walk into them.
    ///
    /// Flying units (if we have any) can cross. Falling into water is going to involve tricky
    /// logic since our maps aren't 3D.
    Water,
    /// Like water, but much more fun.
    Magma,
}

/// Visual form of a terrain tile.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Form {
    /// Nothingness, lets sight pass through portals
    Void,
    /// Marker pointing at adjacent nothingness
    Gate,
    /// Single frame on floor layer
    Floor,
    /// Single frame on object layer
    Prop,
    /// Blobbing form on object layer
    Blob,
    /// Wall-form on object layer
    Wall,
}

struct TerrainData {
    name: &'static str,
    kind: Kind,
    form: Form,
    map_chars: &'static str,
    /// For variants that should not show up in main terrain sets.
    is_regular: bool,
    /// 4-bit components, R << 8 + G << 4 + B.
    color: u16,
}

macro_rules! count_tts {
    () => {0usize};
    ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)};
}

macro_rules! terrain_enum {
    {
        $($sym:ident: $data:expr,)+
    } => {
        #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
        pub enum Terrain {
            $($sym,)+
        }

        const N_ENUM: usize = count_tts!($($sym)+);

        static TERRAIN_DATA: [TerrainData; N_ENUM] = [
            $($data,)+
        ];

        static TERRAIN_ENUM: [Terrain; N_ENUM] = [
            $(Terrain::$sym,)+
        ];
    }
}

terrain_enum! {
    Empty:       TerrainData { name: "n/a",       kind: Kind::Block,  form: Form::Void,  map_chars: "",    is_regular: true,  color: 0xF0F },
    Void:        TerrainData { name: "void",      kind: Kind::Block,  form: Form::Void,  map_chars: "",    is_regular: true,  color: 0x011 },
    Downstairs:  TerrainData { name: "exit down", kind: Kind::Ground, form: Form::Gate,  map_chars: ">",   is_regular: true,  color: 0x0EE },
    Upstairs:    TerrainData { name: "exit up",   kind: Kind::Ground, form: Form::Gate,  map_chars: "<",   is_regular: true,  color: 0x0FF },
    Ground:      TerrainData { name: "ground",    kind: Kind::Ground, form: Form::Floor, map_chars: ".,_", is_regular: true,  color: 0x111 },
    Grass:       TerrainData { name: "grass",     kind: Kind::Ground, form: Form::Floor, map_chars: ",._", is_regular: true,  color: 0x231 },
    Sand:        TerrainData { name: "sand",      kind: Kind::Ground, form: Form::Floor, map_chars: ",._", is_regular: true,  color: 0x650 },
    Snow:        TerrainData { name: "snow",      kind: Kind::Ground, form: Form::Floor, map_chars: ",._", is_regular: true,  color: 0x788 },
    Water:       TerrainData { name: "water",     kind: Kind::Water,  form: Form::Floor, map_chars: "~=",  is_regular: true,  color: 0x058 },
    Shallows:    TerrainData { name: "shallows",  kind: Kind::Ground, form: Form::Floor, map_chars: "~=",  is_regular: true,  color: 0x08B },
    Magma:       TerrainData { name: "magma",     kind: Kind::Magma,  form: Form::Floor, map_chars: "=~",  is_regular: true,  color: 0xF22 },
    Tree:        TerrainData { name: "tree",      kind: Kind::Block,  form: Form::Prop,  map_chars: "",    is_regular: true,  color: 0x8B1 },
    DeadTree:    TerrainData { name: "dead tree", kind: Kind::Block,  form: Form::Prop,  map_chars: "",    is_regular: true,  color: 0x690 },
    Wall:        TerrainData { name: "wall",      kind: Kind::Block,  form: Form::Wall,  map_chars: "#*",  is_regular: true,  color: 0xBBB },
    Rock:        TerrainData { name: "rock",      kind: Kind::Block,  form: Form::Blob,  map_chars: "*#",  is_regular: true,  color: 0xB84 },
    Door:        TerrainData { name: "door",      kind: Kind::Door,   form: Form::Wall,  map_chars: "|",   is_regular: true,  color: 0x842 },
    OpenDoor:    TerrainData { name: "open door", kind: Kind::Ground, form: Form::Wall,  map_chars: "",    is_regular: false, color: 0xFAF },
    Window:      TerrainData { name: "window",    kind: Kind::Window, form: Form::Wall,  map_chars: "+",   is_regular: true,  color: 0xBFF },
    Pillar:      TerrainData { name: "pillar",    kind: Kind::Window, form: Form::Prop,  map_chars: "I",   is_regular: true,  color: 0xCCD },
    // TODO: Get rid of grass2, give render a coherent noise source for tiles and make it do the
    // variation locally.
    Grass2:      TerrainData { name: "grass",     kind: Kind::Ground, form: Form::Floor, map_chars: "",    is_regular: false, color: 0x230 },
}

impl Terrain {
    pub fn iter() -> slice::Iter<'static, Terrain> { TERRAIN_ENUM.iter() }

    pub fn from_color(color: SRgba) -> Option<Terrain> {
        let key =
            (((color.r >> 4) as u16) << 8) + (((color.g >> 4) as u16) << 4) + (color.b >> 4) as u16;
        Self::iter()
            .filter(|t| t.is_regular())
            .find(|t| TERRAIN_DATA[**t as usize].color == key)
            .cloned()
    }

    #[inline(always)]
    pub fn kind(self) -> Kind { TERRAIN_DATA[self as usize].kind }

    #[inline(always)]
    pub fn form(self) -> Form { TERRAIN_DATA[self as usize].form }

    pub fn blocks_sight(self) -> bool {
        match self.kind() {
            Kind::Block | Kind::Door => true,
            _ => false,
        }
    }

    pub fn blocks_shot(self) -> bool {
        match self.kind() {
            Kind::Block | Kind::Window | Kind::Door => true,
            _ => false,
        }
    }

    pub fn blocks_walk(self) -> bool {
        match self.kind() {
            Kind::Ground | Kind::Door => false,
            _ => true,
        }
    }

    pub fn name(self) -> &'static str { TERRAIN_DATA[self as usize].name }

    pub fn is_open(self) -> bool { self.kind() == Kind::Ground }

    pub fn is_door(self) -> bool { self.kind() == Kind::Door }

    pub fn is_luminous(self) -> bool { self.kind() == Kind::Magma }

    pub fn is_wall(self) -> bool { self.form() == Form::Wall }

    pub fn is_hull(self) -> bool { self.form() == Form::Wall || self.form() == Form::Blob }

    pub fn is_blob(self) -> bool { self.form() == Form::Blob }

    pub fn is_block(self) -> bool { self.is_hull() || self.form() == Form::Prop }

    pub fn is_regular(self) -> bool { TERRAIN_DATA[self as usize].is_regular }

    /// For constructing text maps.
    pub fn preferred_map_chars(self) -> &'static str { TERRAIN_DATA[self as usize].map_chars }

    /// Terrain is a narrow object that blocks movement.
    ///
    /// Prop obstacles might not be distinguishable from floors if you only see a corner of the
    /// terrain tile. Use this if there's need to highlight partially visible terrain as obstacles.
    pub fn is_narrow_obstacle(self) -> bool { self.blocks_walk() && self.form() == Form::Prop }

    pub fn color(self) -> SRgba {
        let col = TERRAIN_DATA[self as usize].color;
        let r = ((col >> 8) & 0xf) as u8;
        let g = ((col >> 4) & 0xf) as u8;
        let b = (col & 0xf) as u8;

        SRgba::new(r << 4, g << 4, b << 4, 0xff)
    }

    /// Vertical delta (up a level / down a level / flat) for this terrain.
    pub fn dz(self) -> i32 {
        use Terrain::*;
        match self {
            Upstairs => -1,
            Downstairs => 1,
            _ => 0,
        }
    }
}

impl Default for Terrain {
    fn default() -> Self { Terrain::Empty }
}

impl std::str::FromStr for Terrain {
    type Err = Box<dyn std::error::Error>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(t) = Terrain::iter()
            .find(|t| t.name() == s)
            .cloned() {
                Ok(t)
        } else {
            Err(format!("Unknown terrain '{}'", s))?
        }
    }
}

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

    #[test]
    fn test_colors_are_unique() {
        use std::collections::HashSet;

        let terrains: HashSet<Terrain> = Terrain::iter()
            .filter(|t| t.is_regular())
            .cloned()
            .collect();
        let colors: HashSet<SRgba> = terrains.iter().map(|t| t.color()).collect();

        assert_eq!(colors.len(), terrains.len());
    }

    #[test]
    fn test_from_color() {
        assert_eq!(
            Terrain::from_color(SRgba::new(0xff, 0x88, 0xff, 0xff)),
            None
        );
        assert_eq!(
            Terrain::from_color(SRgba::new(0x22, 0x30, 0x1f, 0xff)),
            Some(Terrain::Grass)
        );
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_must_use, unused_imports, unstable)]

extern crate "rustc-serialize" as rustc_serialize;
extern crate log;

use std::fmt;
use std::str;
use std::string;
use std::old_io::{File, Open, Append, Read, ReadWrite};
use std::old_io::TempDir;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{BufferedReader, BufferedWriter};
use std::old_path::BytesContainer;
use self::rustc_serialize::base64::{STANDARD, FromBase64, ToBase64};

/// A result type that's specfici to the Reader module.
/// TODO Decide if this is necessary
pub type ReaderResult<T, E> = Result<T, E>;

/// Reader struct of its basic properties.
pub struct Reader {
    /// Path to file where the Reader is created.
    path: Path,
    /// BufferedReader for reading the file. Initialized with the Path.
    read_buffer: BufferedReader<File>,
    /// BufferedWriter for writing to the file. Initialized with the Path.
    write_buffer: BufferedWriter<File>,
}

/// ReaderFile traits
pub trait ReaderFile {
    // Opens a new File to the Path provided.
    // Returns a boxed File.
    fn open(&self) -> File;
    // Inserts a string to the database.
    fn insert_string(&mut self, String);
}

impl fmt::Debug for Reader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Reader: ( path: {} )", self.path.display())
    }
}

impl ToString for Reader {
    fn to_string(&self) -> String {
        format!("{:?}", self)
    }
}

impl Reader {
    /// Creates a new Reader from the Path provided.
    /// Opens a new BufferedReader and BufferedWriter (with Append mode) to the file.
    /// If the file doesn't exist, it is created.
    // TODO, create a .lock file to let other readers know the database is in use (see: #2).
    pub fn new(apath: &Path) -> Reader {
        Reader::file_lock_create(apath);
        Reader {
            path: {
                if !apath.exists() { File::create(&apath.clone()); }
                apath.clone()
            },
            read_buffer: {
                match File::open_mode(&apath.clone(), Open, Read) {
                    Ok(file)    => { BufferedReader::new(file) },
                    Err(..)     => {
                        panic!("Failed to create a read buffer to file path: {}", apath.display())
                    }
                }
            },
            write_buffer: {
                match File::open_mode(&apath.clone(), Append, ReadWrite) {
                    Ok(file)    => { BufferedWriter::new(file) },
                    Err(..)     => {
                        panic!("Failed to create a write buffer to file path: {}", apath.display())
                    }
                }
            }
        }
    }

    /// This is a helder function that realistically shouldn't exist in production.
    /// Used primarily for "spilling" the entire database file into a Vec<String>
    fn spill(&mut self) -> Vec<String> {
        let mut result: Vec<String> = Vec::new();
        for line_iter in self.read_buffer.lines() {
            result.push(line_iter.unwrap().trim().to_string());
        }
        return result;
    }

    /// Inserts a &str into the database.
    fn insert_str(&mut self, item: &str) {
        self.write_buffer.write_line(item);
        self.write_buffer.flush();
    }

    /// Inserts a byte array into the database.
    fn insert(&mut self, item: &[u8]) {
        self.write_buffer.write(item);
        self.write_buffer.flush();
    }

    /// Read a &str from the database
    fn read_line(&mut self) -> String {
        match self.read_buffer.read_line() {
            Ok(string)  => { string.to_string() },
            Err(..)     => {
                error!("Unable to read next line. BufferedReader error.");
                "".to_string()
            },
        }
    }

    fn file_lock_create(lockpath: &Path) -> (bool, Path) {

        if lockpath.exists() {
            return (true, lockpath.clone())
        }

        let mut filelock_path = lockpath.clone();

        // Remove the old name
        filelock_path.pop();
        // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
        let mut filename_lock: String = str::from_utf8(lockpath.filename().unwrap()).unwrap().to_string();
        filename_lock.push_str(".lock");
        // Join the new filename with the path
        filelock_path = filelock_path.join(filename_lock);
        println!("{}", filelock_path.display());
        match File::create(&filelock_path) {
            Ok(..)  => (true, filelock_path),
            Err(..) => (false, filelock_path),
        }
    }

    fn file_lock_remove(&self, filelock: &Path) -> bool {
        fs::unlink(&filelock.clone());
        filelock.exists()
    }

}

impl ReaderFile for Reader {

    fn open(&self) -> File {
        match File::open_mode(&self.path, Open, ReadWrite) {
            Ok(file)    => file,
            Err(..)     => { panic!("File {} couldn't be opened!", &self.path.display()); },
        }
    }

    fn insert_string(&mut self, item: String) {
        self.insert_str(&*item);
    }

}

#[test]
fn test_open_file() {
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
}

#[test]
fn test_create_file() {
    use std::rand;
    let mut path_str = String::from_str("tests/");
    path_str.push_str(&*rand::random::<usize>().to_string());
    path_str.push_str(".txt");

    let (tempdir, apath) = setup();
    let path = tempdir.path().join(rand::random::<usize>().to_string());

    //let path = Path::new(path_str);
    assert!(!path.exists());
    let reader = Reader::new(&path.clone());
    assert!(path.exists());
    fs::unlink(&path);
}

#[test]
fn test_read_file() {
    // We should output the entire contents of the database file we open
    // into standard output.
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["10 11".to_string(), "20 21".to_string()];
    assert_eq!(expected, reader.spill());
}

#[test]
fn test_write_string_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_string("30 31".to_string());
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_write_str_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_str("30 31");
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_file_path_lock() {

    let (tempdir, path) = setup();

    let mut expected = path.clone();
    expected.pop();

    // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
    let mut filename_lock: String = str::from_utf8(path.filename().unwrap()).unwrap().to_string();
    filename_lock.push_str(".lock");
    expected = expected.join(filename_lock);

    let reader = Reader::new(&expected.clone());
    assert!(expected.exists() && expected.is_file());
}

#[test]
fn test_reader_show() {
    let reader: Reader = Reader::new(&Path::new("./tests/file.txt"));
    assert_eq!("Reader: ( path: tests/file.txt )", reader.to_string());
}

/// Test setup code. Current functions:
/// - Create a new file with `TempDir` and a random name.
/// - Write a 2x2 matrix of records into the base-test.txt file
/// - Returns a tuple of `TempDir` and `Path` to the file.
/// - The path is for r/w access and `TempDir` is so that the directory
///   isn't deleted before the test is completed.
#[allow(dead_code, unused_must_use)]
fn setup() -> (TempDir, Path) {
    use std::rand;

    let tmpdir = match TempDir::new("txtdb-tests") {
        Ok(dir) => dir,
        Err(..) => panic!("Cannot create test directory. Tests will fail."),
    };

    let final_dir = tmpdir.path().join(rand::random::<usize>().to_string());

    let mut file = File::create(&final_dir.clone());
    file.write_str("10 11\n20 21\n");

    (tmpdir, final_dir)
}
<commit_msg>Added a id counter to Reader<commit_after>#![allow(dead_code, unused_must_use, unused_imports, unstable)]

extern crate "rustc-serialize" as rustc_serialize;
extern crate log;

use utils;
use std::fmt;
use std::str;
use std::string;
use std::old_io::{File, Open, Append, Read, ReadWrite};
use std::old_io::TempDir;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;
use std::old_io::{BufferedReader, BufferedWriter};
use std::old_path::BytesContainer;
use self::rustc_serialize::base64::{STANDARD, FromBase64, ToBase64};

/// A result type that's specfici to the Reader module.
/// TODO Decide if this is necessary
pub type ReaderResult<T, E> = Result<T, E>;

/// Reader struct of its basic properties.
pub struct Reader {
    /// Path to file where the Reader is created.
    path: Path,
    /// BufferedReader for reading the file. Initialized with the Path.
    read_buffer: BufferedReader<File>,
    /// BufferedWriter for writing to the file. Initialized with the Path.
    write_buffer: BufferedWriter<File>,
    /// Index counter to know how many records exist.
    id_count: u64,
}

/// ReaderFile traits
pub trait ReaderFile {
    // Opens a new File to the Path provided.
    // Returns a boxed File.
    fn open(&self) -> File;
    // Inserts a string to the database.
    fn insert_string(&mut self, String);
}

impl fmt::Debug for Reader {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Reader: ( path: {} )", self.path.display())
    }
}

impl ToString for Reader {
    fn to_string(&self) -> String {
        format!("{:?}", self)
    }
}

impl Reader {
    /// Creates a new Reader from the Path provided.
    /// Opens a new BufferedReader and BufferedWriter (with Append mode) to the file.
    /// If the file doesn't exist, it is created.
    // TODO, create a .lock file to let other readers know the database is in use (see: #2).
    pub fn new(apath: &Path) -> Reader {
        Reader::file_lock_create(apath);
        // if file_lock exists, panic and crash with appropriate error.
        // Error: A .lock file already exists, if this is from a previous session, consider
        // deleting the .lock file.

        if !apath.exists() { File::create(&apath.clone()); }

        let mut buffer_reader = match File::open_mode(&apath.clone(), Open, Read) {
                                    Ok(file)    => BufferedReader::new(file),
                                    Err(..)     => panic!("Failed to create a read buffer to file path: {}",
                                                            apath.display()),
                                };

        let current_record_count = match buffer_reader.read_line() {
                                                Ok(line)    => {
                                                    let first_line = utils::string_slice(line);
                                                    let input = first_line[0].parse::<u64>();
                                                    match input {
                                                        Ok(num) => num,
                                                        Err(..) => 0,
                                                    }
                                                },
                                                Err(..) => 0,
                                    };

        Reader {
            path: apath.clone(),
            read_buffer: buffer_reader,
            write_buffer: {
                match File::open_mode(&apath.clone(), Append, ReadWrite) {
                    Ok(file)    => { BufferedWriter::new(file) },
                    Err(..)     => { panic!("Failed to create a write buffer to file path: {}",
                                            apath.display()) },
                }
            },
            id_count: current_record_count,
        }
    }

    /// This is a helder function that realistically shouldn't exist in production.
    /// Used primarily for "spilling" the entire database file into a Vec<String>
    fn spill(&mut self) -> Vec<String> {
        let mut result: Vec<String> = Vec::new();
        for line_iter in self.read_buffer.lines() {
            result.push(line_iter.unwrap().trim().to_string());
        }
        return result;
    }

    /// Inserts a &str into the database.
    fn insert_str(&mut self, item: &str) {
        self.write_buffer.write_line(item);
        self.write_buffer.flush();
    }

    /// Inserts a byte array into the database.
    fn insert(&mut self, item: &[u8]) {
        self.write_buffer.write(item);
        self.write_buffer.flush();
    }

    /// Read a &str from the database
    fn read_line(&mut self) -> String {
        match self.read_buffer.read_line() {
            Ok(string)  => { string.to_string() },
            Err(..)     => {
                error!("Unable to read next line. BufferedReader error.");
                "".to_string()
            },
        }
    }

    fn file_lock_create(lockpath: &Path) -> (bool, Path) {

        if lockpath.exists() {
            return (true, lockpath.clone())
        }

        let mut filelock_path = lockpath.clone();

        // Remove the old name
        filelock_path.pop();
        // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
        let mut filename_lock: String = str::from_utf8(lockpath.filename().unwrap()).unwrap().to_string();
        filename_lock.push_str(".lock");
        // Join the new filename with the path
        filelock_path = filelock_path.join(filename_lock);
        println!("{}", filelock_path.display());
        match File::create(&filelock_path) {
            Ok(..)  => (true, filelock_path),
            Err(..) => (false, filelock_path),
        }
    }

    fn file_lock_remove(&self, filelock: &Path) -> bool {
        fs::unlink(&filelock.clone());
        filelock.exists()
    }

}

impl ReaderFile for Reader {

    fn open(&self) -> File {
        match File::open_mode(&self.path, Open, ReadWrite) {
            Ok(file)    => file,
            Err(..)     => { panic!("File {} couldn't be opened!", &self.path.display()); },
        }
    }

    fn insert_string(&mut self, item: String) {
        self.insert_str(&*item);
    }

}

#[test]
fn test_open_file() {
    let reader = Reader::new(&Path::new("tests/base-test.txt"));
}

#[test]
fn test_create_file() {
    use std::rand;
    let mut path_str = String::from_str("tests/");
    path_str.push_str(&*rand::random::<usize>().to_string());
    path_str.push_str(".txt");

    let (tempdir, apath) = setup();
    let path = tempdir.path().join(rand::random::<usize>().to_string());

    //let path = Path::new(path_str);
    assert!(!path.exists());
    let reader = Reader::new(&path.clone());
    assert!(path.exists());
    fs::unlink(&path);
}

#[test]
fn test_read_file() {
    // We should output the entire contents of the database file we open
    // into standard output.
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["10 11".to_string(), "20 21".to_string()];
    assert_eq!(expected, reader.spill());
}

#[test]
fn test_write_string_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_string("30 31".to_string());
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_write_str_to_file() {
    let (tempdir, path) = setup();
    let mut reader = Reader::new(&path);
    let expected = vec!["10 11".to_string(), "20 21".to_string(), "30 31".to_string()];
    reader.insert_str("30 31");
    assert_eq![expected, reader.spill()];
}

#[test]
fn test_file_path_lock() {

    let (tempdir, path) = setup();

    let mut expected = path.clone();
    expected.pop();

    // Surely, there's a less ugly way to take the filename of a Path and convert it to a string?!
    let mut filename_lock: String = str::from_utf8(path.filename().unwrap()).unwrap().to_string();
    filename_lock.push_str(".lock");
    expected = expected.join(filename_lock);

    let reader = Reader::new(&expected.clone());
    assert!(expected.exists() && expected.is_file());
}

#[test]
fn test_reader_show() {
    let reader: Reader = Reader::new(&Path::new("./tests/file.txt"));
    assert_eq!("Reader: ( path: tests/file.txt )", reader.to_string());
}

/// Test setup code. Current functions:
/// - Create a new file with `TempDir` and a random name.
/// - Write a 2x2 matrix of records into the base-test.txt file
/// - Returns a tuple of `TempDir` and `Path` to the file.
/// - The path is for r/w access and `TempDir` is so that the directory
///   isn't deleted before the test is completed.
#[allow(dead_code, unused_must_use)]
fn setup() -> (TempDir, Path) {
    use std::rand;

    let tmpdir = match TempDir::new("txtdb-tests") {
        Ok(dir) => dir,
        Err(..) => panic!("Cannot create test directory. Tests will fail."),
    };

    let final_dir = tmpdir.path().join(rand::random::<usize>().to_string());

    let mut file = File::create(&final_dir.clone());
    file.write_str("10 11\n20 21\n");

    (tmpdir, final_dir)
}
<|endoftext|>


<commit_before>use ffmpeg;
use progress::{Status, status_sum};
use source::{Sources, Source};
use std::error::Error as StdError;
use std::ffi::OsStr;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use target::Target;
use utils::common_prefix;
use utils::{erase_up};

#[derive(Debug, Clone)]
pub struct Conversion {
    id: u64,
    pub source: Source,
    pub target: Target,
    pub status: Status
}

#[derive(Debug)]
pub enum Error {
    TargetDirExists { target_dir: PathBuf }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::TargetDirExists { .. } => "Target directory exists"
        }
    }
    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::TargetDirExists { .. } => None
        }
    }
}
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::TargetDirExists {ref target_dir} => write!(f, "{}: {}", self.description(), target_dir.to_string_lossy())
        }

    }
}

impl Conversion {
    pub fn new(id: u64, path: PathBuf, source: Source) -> Self {
        let status = Status::new(source.ffprobe.mpixel());
        let target = Target { path: path };
        Conversion { id: id, target: target, source: source, status: status }
    }
}

#[derive(Debug)]
pub struct Conversions(Vec<Conversion>);

impl Conversions {
    pub fn from_sources(s: Sources) -> Result<Conversions, Error> {
        let paths: Vec<_> = s.iter().map(|s| s.path.clone()).collect();

        use std::ffi::OsString;
        let (base_path_len, target_dir): (usize, PathBuf) = {
            let mut base_path: Vec<OsString> = get_longest_prefix(&paths).into_iter().map(|x| x.to_os_string()).collect();
            let base_path_len = base_path.len();
            let mut folder_name = base_path.pop().unwrap().to_os_string();
            folder_name.push(" - Converted");
            base_path.push(folder_name);
            (base_path_len, base_path.into_iter().collect())
        };

        if target_dir.exists() {
            return Err(Error::TargetDirExists {target_dir: target_dir})
        }

        let conversions: Vec<_> = (paths).into_iter().zip(s).zip(0..).map(
            |((path, source), id)| Conversion::new(id, reprefix(&path, &target_dir, base_path_len), source)
        ).collect();

        Ok(Conversions(conversions))
    }
    pub fn print_table(&self) -> usize {
        use table::print_table;
        use table::Cell::{self, Text, Empty};
        use table::Alignment::{Left, Right};
        use time::pretty_centiseconds;
        use std::borrow::Cow;
        use strings::truncate_left;
        use std::iter::once;
        fn seconds_to_cell<'a>(n: f64) -> Cell<'a> {
            Text(Right(pretty_centiseconds((n * 100.).round() as i64).into()))
        }
        fn eta<'a>(s: &Status) -> Cell<'a> {
            match *s {
                Status::Pending(_) => Empty,
                Status::Progress(ref p) => { p.eta().map_or(Empty, seconds_to_cell) },
                Status::Done(ref p) => seconds_to_cell(p.duration)
            }
        }

        fn row<'a>(c: &'a Conversion) -> Vec<Cell<'a>> {
            let paths: Cow<'a, str> = match ::path::find_relative_cwd(c.target.path.as_path()) {
                Ok(p) => Cow::Owned(p.to_string_lossy().into_owned()),
                Err(_) => { c.target.path.to_string_lossy() }
            };

            vec![
                Text(Left(truncate_left(paths, "...", 60))),
                Text(Left((&c.status).into())),
                eta(&c.status),
            ]
        }

        let conversions = self.into_iter().map(row).chain(once(vec![]));

        let global_status: Option<Status> = status_sum(self.into_iter().map(|&Conversion { ref status, ..}| status));

        let sums = match global_status {
            Some(ref global_status) => vec![vec![], vec![
                Text(Left("Total".into())),
                Text(Left(global_status.into())),
                eta(global_status)
            ]],
            None => vec![]
        };

        let data = conversions.chain(sums);

        print_table(Some(vec!["Path", "Status", "Eta", ""]), data)
    }

    pub fn convert(mut self) -> Result<(), (ffmpeg::Error)> {
        let mut lines = 0;
        for n in 0..self.len() {
            // Okay, hope this scope thing is going to be better in the future :)
            let (local_mpixel, ffmpeg_con): (f64, Conversion) = {
                let ref mut c = self[n];
                (c.source.ffprobe.mpixel(), c.clone())
            };
            for time in try!(ffmpeg::FFmpegIterator::new(ffmpeg_con)) {
                {
                    let time = try!(time);
                    let ref mut c = self[n];
                    let local_progress = time / c.source.ffprobe.duration * local_mpixel;
                    c.status.update(local_progress);
                }
                erase_up(lines);
                lines = self.print_table();
            }
            {
                let ref mut c = self[n];
                c.status.end();
            };
            erase_up(lines);
            lines = self.print_table();
        }
        erase_up(lines);
        self.print_table();
        print!("\n");
        Ok(())
    }
}

impl Deref for Conversions {
    type Target = [Conversion];
    fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for Conversions {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

fn reprefix(p: &PathBuf, new_prefix: &PathBuf, count: usize) -> PathBuf {
    let unprefixed: PathBuf = p.into_iter().skip(count).collect();
    new_prefix.join(unprefixed)
}

fn get_longest_prefix<'a>(paths: &'a [PathBuf]) -> Vec<&'a OsStr> {
    let components: Vec<_> = paths.into_iter().map(|p| {
        let mut p: Vec<_> = p.into_iter().collect();
        p.pop();
        p
    }).collect();

    let mut iter = components.iter();
    let longest = match iter.next() {
        Some(s) => s.as_slice(),
        None => &[]
    };

    let longest = iter.fold(longest, |longest, new| common_prefix(longest, new));

    (longest).into_iter().map(|&x| x).collect()
}
<commit_msg>Fix #1<commit_after>use ffmpeg;
use progress::{Status, status_sum};
use source::{Sources, Source};
use std::error::Error as StdError;
use std::ffi::OsStr;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use target::Target;
use utils::common_prefix;
use utils::{erase_up};

#[derive(Debug, Clone)]
pub struct Conversion {
    id: u64,
    pub source: Source,
    pub target: Target,
    pub status: Status
}

#[derive(Debug)]
pub enum Error {
    TargetDirExists { target_dir: PathBuf }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::TargetDirExists { .. } => "Target directory exists"
        }
    }
    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::TargetDirExists { .. } => None
        }
    }
}
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::TargetDirExists {ref target_dir} => write!(f, "{}: {}", self.description(), target_dir.to_string_lossy())
        }

    }
}

impl Conversion {
    pub fn new(id: u64, path: PathBuf, source: Source) -> Self {
        let status = Status::new(source.ffprobe.mpixel());
        let target = Target { path: path };
        Conversion { id: id, target: target, source: source, status: status }
    }
}

#[derive(Debug)]
pub struct Conversions(Vec<Conversion>);

impl Conversions {
    pub fn from_sources(s: Sources) -> Result<Conversions, Error> {
        let paths: Vec<_> = s.iter().map(|s| s.path.clone()).collect();

        use std::ffi::OsString;
        let (base_path_len, target_dir): (usize, PathBuf) = {
            let mut base_path: Vec<OsString> = get_longest_prefix(&paths).into_iter().map(|x| x.to_os_string()).collect();
            let base_path_len = base_path.len();
            let mut folder_name = base_path.pop().unwrap().to_os_string();
            folder_name.push(" - Converted");
            base_path.push(folder_name);
            (base_path_len, base_path.into_iter().collect())
        };

        if target_dir.exists() {
            return Err(Error::TargetDirExists {target_dir: target_dir})
        }

        let target_paths = paths.into_iter().map(
            |path| convert_path(&path, &target_dir, base_path_len, "mkv".as_ref())
        );

        let conversions: Vec<_> = (target_paths).into_iter().zip(s).zip(0..).map(
            |((target_path, source), id)| Conversion::new(
                id,
                target_path,
                source
            )
        ).collect();

        Ok(Conversions(conversions))
    }
    pub fn print_table(&self) -> usize {
        use table::print_table;
        use table::Cell::{self, Text, Empty};
        use table::Alignment::{Left, Right};
        use time::pretty_centiseconds;
        use std::borrow::Cow;
        use strings::truncate_left;
        use std::iter::once;
        fn seconds_to_cell<'a>(n: f64) -> Cell<'a> {
            Text(Right(pretty_centiseconds((n * 100.).round() as i64).into()))
        }
        fn eta<'a>(s: &Status) -> Cell<'a> {
            match *s {
                Status::Pending(_) => Empty,
                Status::Progress(ref p) => { p.eta().map_or(Empty, seconds_to_cell) },
                Status::Done(ref p) => seconds_to_cell(p.duration)
            }
        }

        fn row<'a>(c: &'a Conversion) -> Vec<Cell<'a>> {
            let paths: Cow<'a, str> = match ::path::find_relative_cwd(c.target.path.as_path()) {
                Ok(p) => Cow::Owned(p.to_string_lossy().into_owned()),
                Err(_) => { c.target.path.to_string_lossy() }
            };

            vec![
                Text(Left(truncate_left(paths, "...", 60))),
                Text(Left((&c.status).into())),
                eta(&c.status),
            ]
        }

        let conversions = self.into_iter().map(row).chain(once(vec![]));

        let global_status: Option<Status> = status_sum(self.into_iter().map(|&Conversion { ref status, ..}| status));

        let sums = match global_status {
            Some(ref global_status) => vec![vec![], vec![
                Text(Left("Total".into())),
                Text(Left(global_status.into())),
                eta(global_status)
            ]],
            None => vec![]
        };

        let data = conversions.chain(sums);

        print_table(Some(vec!["Path", "Status", "Eta", ""]), data)
    }

    pub fn convert(mut self) -> Result<(), (ffmpeg::Error)> {
        let mut lines = 0;
        for n in 0..self.len() {
            // Okay, hope this scope thing is going to be better in the future :)
            let (local_mpixel, ffmpeg_con): (f64, Conversion) = {
                let ref mut c = self[n];
                (c.source.ffprobe.mpixel(), c.clone())
            };
            for time in try!(ffmpeg::FFmpegIterator::new(ffmpeg_con)) {
                {
                    let time = try!(time);
                    let ref mut c = self[n];
                    let local_progress = time / c.source.ffprobe.duration * local_mpixel;
                    c.status.update(local_progress);
                }
                erase_up(lines);
                lines = self.print_table();
            }
            {
                let ref mut c = self[n];
                c.status.end();
            };
            erase_up(lines);
            lines = self.print_table();
        }
        erase_up(lines);
        self.print_table();
        print!("\n");
        Ok(())
    }
}

impl Deref for Conversions {
    type Target = [Conversion];
    fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for Conversions {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

fn convert_path(p: &PathBuf, new_prefix: &PathBuf, count: usize, extension: &OsStr) -> PathBuf {
    let mut unprefixed: PathBuf = p.into_iter().skip(count).collect();
    unprefixed.set_extension(extension);
    new_prefix.join(unprefixed)
}

fn get_longest_prefix<'a>(paths: &'a [PathBuf]) -> Vec<&'a OsStr> {
    let components: Vec<_> = paths.into_iter().map(|p| {
        let mut p: Vec<_> = p.into_iter().collect();
        p.pop();
        p
    }).collect();

    let mut iter = components.iter();
    let longest = match iter.next() {
        Some(s) => s.as_slice(),
        None => &[]
    };

    let longest = iter.fold(longest, |longest, new| common_prefix(longest, new));

    (longest).into_iter().map(|&x| x).collect()
}
<|endoftext|>


<commit_before>// Copyright 2020 The Tink-Rust Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#![feature(test)]
extern crate test;
use test::Bencher;

const MSG: &[u8] = b"this data needs to be signed";

fn setup(
    kt: tink_proto::KeyTemplate,
) -> (
    Box<dyn tink_core::Signer>,
    Box<dyn tink_core::Verifier>,
    Vec<u8>,
) {
    tink_signature::init();
    let kh = tink_core::keyset::Handle::new(&kt).unwrap();
    let s = tink_signature::new_signer(&kh).unwrap();
    let pubkh = kh.public().unwrap();
    let v = tink_signature::new_verifier(&pubkh).unwrap();
    let sig = s.sign(MSG).unwrap();
    (s, v, sig)
}

#[bench]
fn bench_ecdsa_p256_sign(b: &mut Bencher) {
    let (s, _v, _sig) = setup(tink_signature::ecdsa_p256_key_template());
    b.iter(|| s.sign(MSG).unwrap());
}

#[bench]
fn bench_ecdsa_p256_verify(b: &mut Bencher) {
    let (_s, v, sig) = setup(tink_signature::ecdsa_p256_key_template());
    b.iter(|| v.verify(&sig, MSG).unwrap());
}

#[bench]
fn bench_ed25519_sign(b: &mut Bencher) {
    let (s, _v, _sig) = setup(tink_signature::ed25519_key_template());
    b.iter(|| s.sign(MSG).unwrap());
}

#[bench]
fn bench_ed25519_verify(b: &mut Bencher) {
    let (_s, v, sig) = setup(tink_signature::ed25519_key_template());
    b.iter(|| v.verify(&sig, MSG).unwrap());
}
<commit_msg>tink-signature: add benchmark for verify failure<commit_after>// Copyright 2020 The Tink-Rust Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#![feature(test)]
extern crate test;
use test::Bencher;

const MSG: &[u8] = b"this data needs to be signed";

fn setup(
    kt: tink_proto::KeyTemplate,
) -> (
    Box<dyn tink_core::Signer>,
    Box<dyn tink_core::Verifier>,
    Vec<u8>,
) {
    tink_signature::init();
    let kh = tink_core::keyset::Handle::new(&kt).unwrap();
    let s = tink_signature::new_signer(&kh).unwrap();
    let pubkh = kh.public().unwrap();
    let v = tink_signature::new_verifier(&pubkh).unwrap();
    let sig = s.sign(MSG).unwrap();
    (s, v, sig)
}

/// Size of the prefix information in the signature. If this is corrupted, the tag will be
/// rejected immediately without performing any cryptographic operations.
const PREFIX_SIZE: usize = tink_core::cryptofmt::NON_RAW_PREFIX_SIZE;

fn setup_failure(kt: tink_proto::KeyTemplate) -> (Box<dyn tink_core::Verifier>, Vec<u8>) {
    let (_s, v, sig) = setup(kt);
    (
        v,
        sig.iter()
            .enumerate()
            .map(|(i, b)| if i < PREFIX_SIZE { *b } else { b ^ 0b10101010 })
            .collect(),
    )
}

#[bench]
fn bench_ecdsa_p256_sign(b: &mut Bencher) {
    let (s, _v, _sig) = setup(tink_signature::ecdsa_p256_key_template());
    b.iter(|| s.sign(MSG).unwrap());
}

#[bench]
fn bench_ecdsa_p256_verify(b: &mut Bencher) {
    let (_s, v, sig) = setup(tink_signature::ecdsa_p256_key_template());
    b.iter(|| v.verify(&sig, MSG).unwrap());
}

#[bench]
fn bench_ecdsa_p256_verify_fail(b: &mut Bencher) {
    let (v, sig) = setup_failure(tink_signature::ecdsa_p256_key_template());
    b.iter(|| v.verify(&sig, MSG).unwrap_err());
}

#[bench]
fn bench_ed25519_sign(b: &mut Bencher) {
    let (s, _v, _sig) = setup(tink_signature::ed25519_key_template());
    b.iter(|| s.sign(MSG).unwrap());
}

#[bench]
fn bench_ed25519_verify(b: &mut Bencher) {
    let (_s, v, sig) = setup(tink_signature::ed25519_key_template());
    b.iter(|| v.verify(&sig, MSG).unwrap());
}

#[bench]
fn bench_ed25519_verify_fail(b: &mut Bencher) {
    let (v, sig) = setup_failure(tink_signature::ed25519_key_template());
    b.iter(|| v.verify(&sig, MSG).unwrap_err());
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::sync::mpsc::TryRecvError;
use std::sync::{Arc, Mutex};
use std::old_io::timer;
use std::time::Duration;

use messages::{ActorMessages, TransactionRequest, MarketMessages, MarketHistory};
use messages::ActorMessages::{StockRequest, MoneyRequest, CommitTransaction, AbortTransaction, History, Time, ReceiveActivityCount};
use messages::MarketMessages::{BuyRequest, SellRequest, Commit, Cancel, RegisterActor, MatchRequest};
use actor::Actor;

// Smarter actor
// (monitors price last sold at and put a sell request if any stocks are above their purchase price)

pub fn start_smarter_actor(actor_id: usize, existing_markets: HashMap<usize, Sender<MarketMessages>>, actor_tx: Sender<ActorMessages>, actor_rx: Receiver<ActorMessages>) {
  println!("Starting Smarter_Actor {}", actor_id);
  let mut init_history = false;
  let mut actor = Actor { id: actor_id,
                          money: 100,
                          stocks: HashMap::new(),
                          pending_money: 0,
                          pending_stock: (0, 0),
                          markets: existing_markets,
                          history: Arc::new(Mutex::new(MarketHistory {history: HashMap::new(), stocks: vec![]}))
                          };
  // Stocks = HashMap<market_id, HashMap<stock_id, (price,quantity)>>
  let mut buy_requests : HashMap<usize, HashMap<usize,(usize,usize)>> = HashMap::new();
  let mut unique_id : usize = 0;

  for (_, market_tx) in actor.markets.iter() {
    market_tx.send(RegisterActor(actor.id, actor_tx.clone())).unwrap();
  }

  loop {
    //Logic
    let mark_clone = actor.markets.clone();
    let stock_clone = actor.stocks.clone();

    if(init_history){
      let hist = actor.history.lock().unwrap();

      // For each stock in history
      for stock in hist.stocks.iter(){
        match(hist.history.get(stock)){
          Some(mut transactions) => {
            println!("Length of transactions is {} for {}.",transactions.len(),stock);
            if(transactions.len() > 0){
              let (buyer,seller) = transactions[transactions.len()-1].clone();
              match buy_requests.get(&0){
                Some(stock_requests) => {
                  match stock_requests.get(stock){
                    Some(&(request_price,request_quantity)) => {
                      // If price is above out request a sell
                      if(buyer.price > request_price){
                        send_message(0,&mark_clone,BuyRequest(TransactionRequest {transaction_id: unique_id, actor_id:actor_id, stock_id: stock.clone(), price:buyer.price, quantity:buyer.quantity }));
                        unique_id = unique_id + 1;
                      }
                    },
                    None => {
                      // Submit new buy request
                    }
                  }
                },
                None =>{} // Market didn't exist?

              }
            }

            },
          None => {}
        }

      }

      println!("History has length {} for smarter_actor.",hist.stocks.len());
    }

    match actor_rx.try_recv() {
      Ok(message) => {
          match message {
            StockRequest(stock_request) => {
              println!("Started Stock Request in smarter_actor {}", actor.id);
              let market_tx;
              let tx_text = mark_clone.get(&stock_request.market_id);
              match tx_text {
                Some(tx) => {
                  market_tx = tx;
                },
                None => {
                  return; //HOW DID WE GET A MISSING MARKET?
                }
              }
              if has_pending_transaction(&actor) {
                market_tx.send(Cancel(actor.id)).unwrap();
              }
              else {
                //if we have the stock. set it aside.
                let stock_id = stock_request.stock_id;
                let quantity = stock_request.quantity;
                let stock = stock_clone.get(&stock_id);
                match stock {
                  Some(owned_quantity) => {
                    if *owned_quantity >= quantity {
                      remove_stock(&mut actor, (stock_id, quantity));
                      actor.pending_stock = (stock_id, quantity);
                      market_tx.send(Commit(actor.id)).unwrap();
                    }
                    else {
                      market_tx.send(Cancel(actor.id)).unwrap();
                    }
                  },
                  None => {
                    market_tx.send(Cancel(actor.id)).unwrap();
                  }
                }
              }
              print_status(&actor);
              },
            MoneyRequest(money_request) => {
              let market_tx;
              let tx_text = actor.markets.get(&money_request.market_id);
              match tx_text {
                Some(tx) => {
                  market_tx = tx;
                },
                None => {
                  return; //HOW DID WE GET A MISSING MARKET?
                }
              }
              if has_pending_transaction(&actor) {
                market_tx.send(Cancel(actor.id)).unwrap();
              }
              else {
                //if we have the money. set it aside.
                if actor.money >= money_request.amount {
                  actor.money = actor.money - money_request.amount;
                  actor.pending_money = money_request.amount;
                  market_tx.send(Commit(actor.id)).unwrap();
                }
                else {
                  market_tx.send(Cancel(actor.id)).unwrap();
                }
              }
              print_status(&actor);
              },
            CommitTransaction(commit_transaction_request) => {
              //if we have money pending, then look up the stock id and add that quantity purchased.
              //remove the pending money
              if actor.pending_money > 0 {
                let price_per_unit = commit_transaction_request.price;
                let units = commit_transaction_request.quantity;
                let leftover_money = actor.pending_money - price_per_unit * units;

                //make a function for adding stock.
                add_stock(&mut actor, (commit_transaction_request.stock_id, units));
                actor.money = actor.money + leftover_money;

                actor.pending_money = 0;
              }

              //if we have stock pending, look up the quantity purchased and add the money.
              //remove the pending stock
              if actor.pending_stock.1 > 0 {
                let money = commit_transaction_request.price * commit_transaction_request.quantity;
                let restore_stock = (commit_transaction_request.stock_id, actor.pending_stock.1 - commit_transaction_request.quantity);
                if restore_stock.1 > 0 {
                  add_stock(&mut actor, restore_stock);
                }
                actor.money = actor.money + money;
                actor.pending_stock = (0,0);
              }
              println!("After a committed transaction in smarter_actor {} ", actor.id);
              print_status(&actor);
              },
            AbortTransaction => {
              //move pending stock back into stocks.
              if actor.pending_stock.1 != 0 {
                let pending_stock_clone = actor.pending_stock.clone();
                add_stock(&mut actor, pending_stock_clone);
                //now that we have moved it. Clear out the pending stock.
                actor.pending_stock = (0,0); //setting the quantity to zero clears it.
              }
              for (stock_id, quantity) in actor.stocks.iter() {
                println!("After aborting the transaction, smarter_actor {} now has StockId: {} Quantity: {}", actor.id, *stock_id, *quantity);
              }

              //move pending money back into money.
              if actor.pending_money > 0 {
                actor.money = actor.money + actor.pending_money;
                actor.pending_money = 0;
              }
              println!("After aborting the transaction, smarter_actor {} now has {} money.", actor.id, actor.money);
            },
            History(history) => {
              println!("Smarter_actor {} received history {}", actor.id, *(history.lock().unwrap()));
              actor.history = history;
              init_history = true;
              },
            Time(current, max) => {
              println!("Smarter_actor {} received time {}", actor.id, current);
            },
            ReceiveActivityCount(_,_,_) => {},
          }
        },
      Err(TryRecvError::Empty) => {timer::sleep(Duration::milliseconds(10));},
      Err(TryRecvError::Disconnected) => {println!("ERROR: Smarter_actor {} disconnected", actor.id);}
    }
  }
}

fn add_stock(actor: &mut Actor, stock_to_add: (usize, usize)) {
  let stock_clone = actor.stocks.clone();
  let held_stock = stock_clone.get(&stock_to_add.0);
  match held_stock {
    Some(stock_count) => {
        //we have some stock. We need to add to our reserve.
        actor.stocks.insert(stock_to_add.0, stock_to_add.1 + *stock_count);
      },
    None => {
      //we don't have any stock left. Just add it back.
      actor.stocks.insert(stock_to_add.0, stock_to_add.1);
    }
  }
}

// send_message(0,actor.markets,BuyRequest(TransactionRequest {transaction_id: unique_id, actor_id:actor_id, stock_id: stock, price:buyer.price, quantity:buyer.quantity }));

fn send_message(market_id : usize, markets: &HashMap<usize, Sender<MarketMessages>>, message: MarketMessages){
  match markets.get(&market_id){
    Some(market) => {
      market.send(message);
      },
    None => {}
  }
}

fn remove_stock(actor: &mut Actor, stock_to_remove: (usize, usize)) {
  let stock_clone = actor.stocks.clone();
  let held_stock = stock_clone.get(&stock_to_remove.0);
  match held_stock {
    Some(stock_count) => {
        //we have some stock. We need to add to our reserve.
        actor.stocks.insert(stock_to_remove.0, *stock_count - stock_to_remove.1);
      },
    None => {} //TODO Should we error handle here?
  }
}

fn print_status(actor: &Actor) {
  for (stock_id, quantity) in (*actor).stocks.iter() {
    println!("Actor {} now has StockId: {} Quantity: {}", (*actor).id, *stock_id, *quantity);
  }
  println!("Actor {} has {} money.", (*actor).id, (*actor).money);
}

fn has_pending_transaction(actor: &Actor) -> bool {
  (*actor).pending_money > 0 || (*actor).pending_stock.1 > 0
}
<commit_msg>Smarter actor submits buys and sells<commit_after>use std::collections::HashMap;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::sync::mpsc::TryRecvError;
use std::sync::{Arc, Mutex};
use std::old_io::timer;
use std::time::Duration;

use messages::{ActorMessages, TransactionRequest, MarketMessages, MarketHistory};
use messages::ActorMessages::{StockRequest, MoneyRequest, CommitTransaction, AbortTransaction, History, Time, ReceiveActivityCount};
use messages::MarketMessages::{BuyRequest, SellRequest, Commit, Cancel, RegisterActor, MatchRequest};
use actor::Actor;

// Smarter actor
// (monitors price last sold at and put a sell request if any stocks are above their purchase price)

pub fn start_smarter_actor(actor_id: usize, existing_markets: HashMap<usize, Sender<MarketMessages>>, actor_tx: Sender<ActorMessages>, actor_rx: Receiver<ActorMessages>) {
  println!("Starting Smarter_Actor {}", actor_id);
  let mut init_history = false;
  let mut actor = Actor { id: actor_id,
                          money: 100,
                          stocks: HashMap::new(),
                          pending_money: 0,
                          pending_stock: (0, 0),
                          markets: existing_markets,
                          history: Arc::new(Mutex::new(MarketHistory {history: HashMap::new(), stocks: vec![]}))
                          };
  // Stocks = HashMap<market_id, HashMap<stock_id, (price,quantity)>>
  let mut buy_requests : HashMap<usize, HashMap<usize,(usize,usize)>> = HashMap::new();
  let mut unique_id : usize = 0;

  for (_, market_tx) in actor.markets.iter() {
    market_tx.send(RegisterActor(actor.id, actor_tx.clone())).unwrap();
  }

  loop {
    //Logic
    let mark_clone = actor.markets.clone();
    let stock_clone = actor.stocks.clone();

    if(init_history){
      let hist = actor.history.lock().unwrap();

      // For each stock in history
      for stock in hist.stocks.iter(){
        match(hist.history.get(stock)){
          Some(mut transactions) => {
            // Check that stock's last transaction
            if(transactions.len() > 0){
              let (buyer,seller) = transactions[transactions.len()-1].clone();
              match buy_requests.clone().get(&0){
                Some(stock_requests) => {
                  match stock_requests.get(stock){
                    Some(&(request_price,request_quantity)) => {
                      // If it's price is above ours request a sell
                      if(buyer.price > request_price){
                        let trans : TransactionRequest  = TransactionRequest   {  transaction_id: unique_id
                                                                                , actor_id:actor_id
                                                                                , stock_id: stock.clone()
                                                                                , price:buyer.price
                                                                                , quantity:request_quantity
                                                                                };
                        send_message(0,&mark_clone,SellRequest(trans.clone()));
                        println!("smarter_actor : {} submitted a SellRequest : {}",actor_id,trans);
                        unique_id = unique_id + 1;
                      }
                    },
                    None => {
                      // Otherwise lets try to buy some stock to sell later
                      let request_quantity = (actor.money/10)/seller.price;
                      let trans : TransactionRequest  = TransactionRequest{   transaction_id: unique_id
                                                                            , actor_id:actor_id
                                                                            , stock_id: stock.clone()
                                                                            , price:seller.price
                                                                            , quantity:request_quantity
                                                                          };
                      send_message(0,&mark_clone,BuyRequest(trans.clone()));
                      unique_id = unique_id + 1;
                      println!("smarter_actor : {} submitted a BuyRequest : {}",actor_id,trans);
                    }
                  }
                },
                None =>{
                  buy_requests.insert(0,HashMap::new());
                  } // Market didn't exist?

              }
            }

            },
          None => {}
        }

      }
    }

    match actor_rx.try_recv() {
      Ok(message) => {
          match message {
            StockRequest(stock_request) => {
              println!("Started Stock Request in smarter_actor {}", actor.id);
              let market_tx;
              let tx_text = mark_clone.get(&stock_request.market_id);
              match tx_text {
                Some(tx) => {
                  market_tx = tx;
                },
                None => {
                  return; //HOW DID WE GET A MISSING MARKET?
                }
              }
              if has_pending_transaction(&actor) {
                market_tx.send(Cancel(actor.id)).unwrap();
              }
              else {
                //if we have the stock. set it aside.
                let stock_id = stock_request.stock_id;
                let quantity = stock_request.quantity;
                let stock = stock_clone.get(&stock_id);
                match stock {
                  Some(owned_quantity) => {
                    if *owned_quantity >= quantity {
                      remove_stock(&mut actor, (stock_id, quantity));
                      actor.pending_stock = (stock_id, quantity);
                      market_tx.send(Commit(actor.id)).unwrap();
                    }
                    else {
                      market_tx.send(Cancel(actor.id)).unwrap();
                    }
                  },
                  None => {
                    market_tx.send(Cancel(actor.id)).unwrap();
                  }
                }
              }
              print_status(&actor);
              },
            MoneyRequest(money_request) => {
              let market_tx;
              let tx_text = actor.markets.get(&money_request.market_id);
              match tx_text {
                Some(tx) => {
                  market_tx = tx;
                },
                None => {
                  return; //HOW DID WE GET A MISSING MARKET?
                }
              }
              if has_pending_transaction(&actor) {
                market_tx.send(Cancel(actor.id)).unwrap();
              }
              else {
                //if we have the money. set it aside.
                if actor.money >= money_request.amount {
                  actor.money = actor.money - money_request.amount;
                  actor.pending_money = money_request.amount;
                  market_tx.send(Commit(actor.id)).unwrap();
                }
                else {
                  market_tx.send(Cancel(actor.id)).unwrap();
                }
              }
              print_status(&actor);
              },
            CommitTransaction(commit_transaction_request) => {
              //if we have money pending, then look up the stock id and add that quantity purchased.
              //remove the pending money
              if actor.pending_money > 0 {
                let price_per_unit = commit_transaction_request.price;
                let units = commit_transaction_request.quantity;
                let leftover_money = actor.pending_money - price_per_unit * units;

                //make a function for adding stock.
                add_stock(&mut actor, (commit_transaction_request.stock_id, units));
                actor.money = actor.money + leftover_money;

                actor.pending_money = 0;
              }

              //if we have stock pending, look up the quantity purchased and add the money.
              //remove the pending stock
              if actor.pending_stock.1 > 0 {
                let money = commit_transaction_request.price * commit_transaction_request.quantity;
                let restore_stock = (commit_transaction_request.stock_id, actor.pending_stock.1 - commit_transaction_request.quantity);
                if restore_stock.1 > 0 {
                  add_stock(&mut actor, restore_stock);
                }
                actor.money = actor.money + money;
                actor.pending_stock = (0,0);
              }
              println!("After a committed transaction in smarter_actor {} ", actor.id);
              print_status(&actor);
              },
            AbortTransaction => {
              //move pending stock back into stocks.
              if actor.pending_stock.1 != 0 {
                let pending_stock_clone = actor.pending_stock.clone();
                add_stock(&mut actor, pending_stock_clone);
                //now that we have moved it. Clear out the pending stock.
                actor.pending_stock = (0,0); //setting the quantity to zero clears it.
              }
              for (stock_id, quantity) in actor.stocks.iter() {
                println!("After aborting the transaction, smarter_actor {} now has StockId: {} Quantity: {}", actor.id, *stock_id, *quantity);
              }

              //move pending money back into money.
              if actor.pending_money > 0 {
                actor.money = actor.money + actor.pending_money;
                actor.pending_money = 0;
              }
              println!("After aborting the transaction, smarter_actor {} now has {} money.", actor.id, actor.money);
            },
            History(history) => {
              println!("Smarter_actor {} received history {}", actor.id, *(history.lock().unwrap()));
              actor.history = history;
              init_history = true;
              },
            Time(current, max) => {
              println!("Smarter_actor {} received time {}", actor.id, current);
            },
            ReceiveActivityCount(_,_,_) => {},
          }
        },
      Err(TryRecvError::Empty) => {timer::sleep(Duration::milliseconds(10));},
      Err(TryRecvError::Disconnected) => {println!("ERROR: Smarter_actor {} disconnected", actor.id);}
    }
  }
}

fn add_stock(actor: &mut Actor, stock_to_add: (usize, usize)) {
  let stock_clone = actor.stocks.clone();
  let held_stock = stock_clone.get(&stock_to_add.0);
  match held_stock {
    Some(stock_count) => {
        //we have some stock. We need to add to our reserve.
        actor.stocks.insert(stock_to_add.0, stock_to_add.1 + *stock_count);
      },
    None => {
      //we don't have any stock left. Just add it back.
      actor.stocks.insert(stock_to_add.0, stock_to_add.1);
    }
  }
}

// send_message(0,actor.markets,BuyRequest(TransactionRequest {transaction_id: unique_id, actor_id:actor_id, stock_id: stock, price:buyer.price, quantity:buyer.quantity }));

fn send_message(market_id : usize, markets: &HashMap<usize, Sender<MarketMessages>>, message: MarketMessages){
  match markets.get(&market_id){
    Some(market) => {
      market.send(message);
      },
    None => {}
  }
}

fn remove_stock(actor: &mut Actor, stock_to_remove: (usize, usize)) {
  let stock_clone = actor.stocks.clone();
  let held_stock = stock_clone.get(&stock_to_remove.0);
  match held_stock {
    Some(stock_count) => {
        //we have some stock. We need to add to our reserve.
        actor.stocks.insert(stock_to_remove.0, *stock_count - stock_to_remove.1);
      },
    None => {} //TODO Should we error handle here?
  }
}

fn print_status(actor: &Actor) {
  for (stock_id, quantity) in (*actor).stocks.iter() {
    println!("Actor {} now has StockId: {} Quantity: {}", (*actor).id, *stock_id, *quantity);
  }
  println!("Actor {} has {} money.", (*actor).id, (*actor).money);
}

fn has_pending_transaction(actor: &Actor) -> bool {
  (*actor).pending_money > 0 || (*actor).pending_stock.1 > 0
}
<|endoftext|>