<commit_before>mod key;
mod rangeset;

use key::Key;
use rangeset::{Range, RangeSet};
use std::collections::HashMap;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct VKeyMarker;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct EKeyMarker;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct FKeyMarker;
pub type VKey = Key<VKeyMarker>;
pub type EKey = Key<EKeyMarker>;
pub type FKey = Key<FKeyMarker>;
pub type FaceLen = u32;

/// Editable mesh with persistent adjacency data. Modeled loosely
/// after Blender's BMesh:
///
/// http://wiki.blender.org/index.php/Dev:2.6/Source/Modeling/BMesh/Design
pub struct Mesh<VData, EData, FData> {
    verts: HashMap<VKey, Vert<VData>>,
    edges: HashMap<EKey, Edge<EData>>,
    faces: HashMap<FKey, Face<FData>>,

    vert_range_set: RangeSet,
    edge_range_set: RangeSet,
    face_range_set: RangeSet
}

// TODO
fn new_key_range_set() -> RangeSet {
    RangeSet::new(Range::new(0, 0xffffffff - 1))
}

impl<VData: Clone, EData: Clone, FData: Clone> Mesh<VData, EData, FData> {
    pub fn new() -> Mesh<VData, EData, FData> {
        Mesh {
            verts: HashMap::new(),
            edges: HashMap::new(),
            faces: HashMap::new(),

            vert_range_set: new_key_range_set(),
            edge_range_set: new_key_range_set(),
            face_range_set: new_key_range_set()
        }
    }

    pub fn vert_data(&self, vk: VKey) -> Option<&VData> {
        self.verts.get(&vk).map(|v| &v.vdata)
    }

    pub fn edge_data(&self, ek: EKey) -> Option<&EData> {
        self.edges.get(&ek).map(|e| &e.edata)
    }

    pub fn face_data(&self, fk: FKey) -> Option<&FData> {
        self.faces.get(&fk).map(|f| &f.fdata)
    }

    pub fn vert_data_mut(&mut self, vk: VKey) -> Option<&mut VData> {
        self.verts.get_mut(&vk).map(|v| &mut v.vdata)
    }

    pub fn edge_data_mut(&mut self, ek: EKey) -> Option<&mut EData> {
        self.edges.get_mut(&ek).map(|e| &mut e.edata)
    }

    pub fn face_data_mut(&mut self, fk: FKey) -> Option<&mut FData> {
        self.faces.get_mut(&fk).map(|f| &mut f.fdata)
    }

    /// Return the edge between two verts, or None if no such edge
    /// exists.
    pub fn find_edge(&self, vk0: VKey, vk1: VKey) -> Option<EKey> {
        for ek in self.verts[&vk0].edges.iter() {
            if self.edges[ek].is_vert_adjacent(vk1) {
                return Some(*ek)
            }
        }
        None
    }

    /// Get all faces adjacent to the specified vertex. The order is
    /// arbitrary.
    pub fn vert_adjacent_faces(&self, vk: VKey) -> Vec<FKey> {
        let mut adj = Vec::new();
        for ek in self.verts[&vk].edges.iter() {
            for fk in self.edges[ek].faces.iter() {
                if !adj.contains(fk) {
                    adj.push(*fk);
                }
            }
        }
        adj
    }

    /// Add a new isolated vertex and return its key. Fail and return
    /// None if there are no more vertex keys available.
    pub fn add_vert(&mut self, vdata: VData) -> Option<VKey> {
        if let Some(val) = self.vert_range_set.take_any_one() {
            let vkey = VKey::new(val);
            self.verts.insert(vkey, Vert::new(vdata));
            Some(vkey)
        } else {
            None
        }
    }

    pub fn add_edge(&mut self, vk0: VKey, vk1: VKey, edata: EData) -> Option<EKey> {
        // TODO(nicholasbishop): check that the verts are in the mesh,
        if vk0 == vk1 {
            None
        } else if let Some(ek) = self.find_edge(vk0, vk1) {
            Some(ek)
        } else {
            if let Some(val) = self.edge_range_set.take_any_one() {
                let ekey = EKey::new(val);
                self.verts.get_mut(&vk0).unwrap().push_edge(ekey);
                self.verts.get_mut(&vk1).unwrap().push_edge(ekey);
                self.edges.insert(ekey, Edge::new(vk0, vk1, edata));
                Some(ekey)
            } else {
                None
            }
        }
    }

    pub fn add_face(&mut self, vk: &[VKey], edata: EData, fdata: FData) -> Option<FKey> {
        let mut loops = Vec::with_capacity(vk.len());
        for i in 0..vk.len() {
            let vk0 = vk[i];
            let vk1 = vk[if i < (vk.len() - 1) { i + 1 } else { 0 }];
            // TODO(nicholasbishop): do more checking up front so that
            // edges don't get created on failure, or delete them after.
            if let Some(ek) = self.add_edge(vk0, vk1, edata.clone()) {
                loops.push(Loop { vert: vk0, edge: ek });
            } else {
                // Error: failed to create edge
                return None;
            }
        }
        if let Some(val) = self.face_range_set.take_any_one() {
            let fk = FKey::new(val);
            for lp in loops.iter() {
                self.edges.get_mut(&lp.edge).unwrap().push_face(fk);
            }
            self.faces.insert(fk, Face::new(loops, fdata));
            Some(fk)
        } else {
            None
        }
    }
}

pub struct Vert<VData> {
    pub vdata: VData,
    edges: Vec<EKey>
}

impl<VData> Vert<VData> {
    fn new(vdata: VData) -> Vert<VData> {
        Vert { vdata: vdata, edges: Vec::new() }
    }

    /// Check if the edge is in the set of edges adjacent to this vert.
    pub fn is_edge_adjacent(&self, ek: EKey) -> bool {
        self.edges.contains(&ek)
    }

    /// Add edge to set of edges adjacent to the vert. Does nothing if
    /// the edge is already in the set.
    fn push_edge(&mut self, ek: EKey) {
        if !self.is_edge_adjacent(ek) {
            self.edges.push(ek);
        }
    }
}

pub struct Edge<EData> {
    pub edata: EData,
    verts: [VKey; 2],
    faces: Vec<FKey>
}

impl<EData> Edge<EData> {
    fn new(vk0: VKey, vk1: VKey, edata: EData) -> Edge<EData> {
        Edge { edata: edata, verts: [vk0, vk1], faces: Vec::new() }
    }

    /// Check if the vert is one of the two verts adjacent to this edge.
    fn is_vert_adjacent(&self, vk: VKey) -> bool {
        self.verts.contains(&vk)
    }

    /// Check if the face is in the set of edges adjacent to this edge.
    pub fn is_face_adjacent(&self, fk: FKey) -> bool {
        self.faces.contains(&fk)
    }

    /// Add face to set of faces adjacent to the edge. Does nothing if
    /// the face is already in the set.
    fn push_face(&mut self, fk: FKey) {
        if !self.is_face_adjacent(fk) {
            self.faces.push(fk);
        }
    }
}

pub struct Loop {
    vert: VKey,
    edge: EKey,
}

pub struct Face<FData> {
    pub fdata: FData,
    loops: Vec<Loop>
}

impl<FData> Face<FData> {
    fn new(loops: Vec<Loop>, fdata: FData) -> Face<FData> {
        Face { fdata: fdata, loops: loops }
    }
}

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

    // Placeholder for vert/edge/face data
    const DAT: () = ();

    #[test]
    fn test_add_vert() {
        let mut mesh = Mesh::<_, (), ()>::new();
        assert!(mesh.add_vert(DAT).is_some());
        assert!(!mesh.verts.is_empty());
    }

    #[test]
    fn test_add_edge() {
        let mut mesh = Mesh::<_, _, ()>::new();
        let a = mesh.add_vert(DAT).unwrap();
        let b = mesh.add_vert(DAT).unwrap();

        // Add a valid edge
        let e = mesh.add_edge(a, b, DAT).unwrap();
        assert!(mesh.verts[&a].edges.contains(&e));
        assert!(mesh.verts[&b].edges.contains(&e));

        // Edge from a vertex to the same vertex is not allowed
        assert!(mesh.add_edge(a, a, DAT).is_none());

        // Duplicate edge should return the existing edge
        assert_eq!(mesh.add_edge(a, b, DAT).unwrap(), e);
        assert_eq!(mesh.add_edge(b, a, DAT).unwrap(), e);
    }

    #[test]
    fn test_add_face() {
        let mut mesh = Mesh::new();
        let a = mesh.add_vert(DAT).unwrap();
        let b = mesh.add_vert(DAT).unwrap();
        let c = mesh.add_vert(DAT).unwrap();

        // Add a valid triangle
        let fk = mesh.add_face(&[a, b, c], DAT, DAT).unwrap();
        assert_eq!(mesh.edges.len(), 3);
        for lp in mesh.faces[&fk].loops.iter() {
            assert!(mesh.edges[&lp.edge].is_face_adjacent(fk));
        }

        // Add an invalid triangle with invalid edge
        assert!(mesh.add_face(&[a, b, b], DAT, DAT).is_none());
    }

    #[test]
    fn test_vert_adjacent_faces() {
        // Create a bow-tie mesh
        let mut mesh = Mesh::new();
        let middle = mesh.add_vert(DAT).unwrap();
        let l0 = mesh.add_vert(DAT).unwrap();
        let l1 = mesh.add_vert(DAT).unwrap();
        let r0 = mesh.add_vert(DAT).unwrap();
        let r1 = mesh.add_vert(DAT).unwrap();
        let fk0 = mesh.add_face(&[middle, l0, l1], DAT, DAT).unwrap();
        let fk1 = mesh.add_face(&[middle, r0, r1], DAT, DAT).unwrap();

        let adj = mesh.vert_adjacent_faces(middle);
        assert_eq!(adj.len(), 2);
        assert!(adj.contains(&fk0));
        assert!(adj.contains(&fk1));
    }

    #[test]
    fn test_ngon() {
        let mut mesh = Mesh::new();
        let a = mesh.add_vert(DAT).unwrap();
        let b = mesh.add_vert(DAT).unwrap();
        let c = mesh.add_vert(DAT).unwrap();
        let d = mesh.add_vert(DAT).unwrap();
        let e = mesh.add_vert(DAT).unwrap();

        let fk = mesh.add_face(&[a, b, c, d, e], DAT, DAT).unwrap();
        let face = &mesh.faces[&fk];

        assert_eq!(face.loops.len(), 5);
    }
}
<commit_msg>Improve accessors<commit_after>mod key;
mod rangeset;

use key::Key;
use rangeset::{Range, RangeSet};
use std::collections::HashMap;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct VKeyMarker;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct EKeyMarker;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
struct FKeyMarker;
pub type VKey = Key<VKeyMarker>;
pub type EKey = Key<EKeyMarker>;
pub type FKey = Key<FKeyMarker>;
pub type FaceLen = u32;

/// Editable mesh with persistent adjacency data. Modeled loosely
/// after Blender's BMesh:
///
/// http://wiki.blender.org/index.php/Dev:2.6/Source/Modeling/BMesh/Design
pub struct Mesh<VData, EData, FData> {
    verts: HashMap<VKey, Vert<VData>>,
    edges: HashMap<EKey, Edge<EData>>,
    faces: HashMap<FKey, Face<FData>>,

    vert_range_set: RangeSet,
    edge_range_set: RangeSet,
    face_range_set: RangeSet
}

// TODO
fn new_key_range_set() -> RangeSet {
    RangeSet::new(Range::new(0, 0xffffffff - 1))
}

impl<VData: Clone, EData: Clone, FData: Clone> Mesh<VData, EData, FData> {
    pub fn new() -> Mesh<VData, EData, FData> {
        Mesh {
            verts: HashMap::new(),
            edges: HashMap::new(),
            faces: HashMap::new(),

            vert_range_set: new_key_range_set(),
            edge_range_set: new_key_range_set(),
            face_range_set: new_key_range_set()
        }
    }

    pub fn get_vert(&self, vk: VKey) -> Option<&Vert<VData>> {
        self.verts.get(&vk)
    }

    pub fn get_edge(&self, ek: EKey) -> Option<&Edge<EData>> {
        self.edges.get(&ek)
    }

    pub fn get_face(&self, fk: FKey) -> Option<&Face<FData>> {
        self.faces.get(&fk)
    }

    pub fn get_vert_data(&mut self, vk: VKey) -> Option<&mut Vert<VData>> {
        self.verts.get_mut(&vk)
    }

    pub fn get_edge_data(&mut self, ek: EKey) -> Option<&mut Edge<EData>> {
        self.edges.get_mut(&ek)
    }

    pub fn get_face_data(&mut self, fk: FKey) -> Option<&mut Face<FData>> {
        self.faces.get_mut(&fk)
    }

    /// Return the edge between two verts, or None if no such edge
    /// exists.
    pub fn find_edge(&self, vk0: VKey, vk1: VKey) -> Option<EKey> {
        for ek in self.verts[&vk0].edges.iter() {
            if self.edges[ek].is_vert_adjacent(vk1) {
                return Some(*ek)
            }
        }
        None
    }

    /// Get all faces adjacent to the specified vertex. The order is
    /// arbitrary.
    pub fn vert_adjacent_faces(&self, vk: VKey) -> Vec<FKey> {
        let mut adj = Vec::new();
        for ek in self.verts[&vk].edges.iter() {
            for fk in self.edges[ek].faces.iter() {
                if !adj.contains(fk) {
                    adj.push(*fk);
                }
            }
        }
        adj
    }

    /// Add a new isolated vertex and return its key. Fail and return
    /// None if there are no more vertex keys available.
    pub fn add_vert(&mut self, vdata: VData) -> Option<VKey> {
        if let Some(val) = self.vert_range_set.take_any_one() {
            let vkey = VKey::new(val);
            self.verts.insert(vkey, Vert::new(vdata));
            Some(vkey)
        } else {
            None
        }
    }

    pub fn add_edge(&mut self, vk0: VKey, vk1: VKey, edata: EData) -> Option<EKey> {
        // TODO(nicholasbishop): check that the verts are in the mesh,
        if vk0 == vk1 {
            None
        } else if let Some(ek) = self.find_edge(vk0, vk1) {
            Some(ek)
        } else {
            if let Some(val) = self.edge_range_set.take_any_one() {
                let ekey = EKey::new(val);
                self.verts.get_mut(&vk0).unwrap().push_edge(ekey);
                self.verts.get_mut(&vk1).unwrap().push_edge(ekey);
                self.edges.insert(ekey, Edge::new(vk0, vk1, edata));
                Some(ekey)
            } else {
                None
            }
        }
    }

    pub fn add_face(&mut self, vk: &[VKey], edata: EData, fdata: FData) -> Option<FKey> {
        let mut loops = Vec::with_capacity(vk.len());
        for i in 0..vk.len() {
            let vk0 = vk[i];
            let vk1 = vk[if i < (vk.len() - 1) { i + 1 } else { 0 }];
            // TODO(nicholasbishop): do more checking up front so that
            // edges don't get created on failure, or delete them after.
            if let Some(ek) = self.add_edge(vk0, vk1, edata.clone()) {
                loops.push(Loop { vert: vk0, edge: ek });
            } else {
                // Error: failed to create edge
                return None;
            }
        }
        if let Some(val) = self.face_range_set.take_any_one() {
            let fk = FKey::new(val);
            for lp in loops.iter() {
                self.edges.get_mut(&lp.edge).unwrap().push_face(fk);
            }
            self.faces.insert(fk, Face::new(loops, fdata));
            Some(fk)
        } else {
            None
        }
    }
}

pub struct Vert<VData> {
    pub vdata: VData,
    edges: Vec<EKey>
}

impl<VData> Vert<VData> {
    fn new(vdata: VData) -> Vert<VData> {
        Vert { vdata: vdata, edges: Vec::new() }
    }

    /// Check if the edge is in the set of edges adjacent to this vert.
    pub fn is_edge_adjacent(&self, ek: EKey) -> bool {
        self.edges.contains(&ek)
    }

    /// Add edge to set of edges adjacent to the vert. Does nothing if
    /// the edge is already in the set.
    fn push_edge(&mut self, ek: EKey) {
        if !self.is_edge_adjacent(ek) {
            self.edges.push(ek);
        }
    }
}

pub struct Edge<EData> {
    pub edata: EData,
    verts: [VKey; 2],
    faces: Vec<FKey>
}

impl<EData> Edge<EData> {
    fn new(vk0: VKey, vk1: VKey, edata: EData) -> Edge<EData> {
        Edge { edata: edata, verts: [vk0, vk1], faces: Vec::new() }
    }

    /// Check if the vert is one of the two verts adjacent to this edge.
    fn is_vert_adjacent(&self, vk: VKey) -> bool {
        self.verts.contains(&vk)
    }

    /// Check if the face is in the set of edges adjacent to this edge.
    pub fn is_face_adjacent(&self, fk: FKey) -> bool {
        self.faces.contains(&fk)
    }

    /// Add face to set of faces adjacent to the edge. Does nothing if
    /// the face is already in the set.
    fn push_face(&mut self, fk: FKey) {
        if !self.is_face_adjacent(fk) {
            self.faces.push(fk);
        }
    }
}

pub struct Loop {
    pub vert: VKey,
    pub edge: EKey
}

pub struct Face<FData> {
    pub fdata: FData,
    loops: Vec<Loop>
}

impl<FData> Face<FData> {
    fn new(loops: Vec<Loop>, fdata: FData) -> Face<FData> {
        Face { fdata: fdata, loops: loops }
    }

    pub fn get_loops(&self) -> &Vec<Loop> {
        &self.loops
    }
}

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

    // Placeholder for vert/edge/face data
    const DAT: () = ();

    #[test]
    fn test_add_vert() {
        let mut mesh = Mesh::<_, (), ()>::new();
        assert!(mesh.add_vert(DAT).is_some());
        assert!(!mesh.verts.is_empty());
    }

    #[test]
    fn test_add_edge() {
        let mut mesh = Mesh::<_, _, ()>::new();
        let a = mesh.add_vert(DAT).unwrap();
        let b = mesh.add_vert(DAT).unwrap();

        // Add a valid edge
        let e = mesh.add_edge(a, b, DAT).unwrap();
        assert!(mesh.verts[&a].edges.contains(&e));
        assert!(mesh.verts[&b].edges.contains(&e));

        // Edge from a vertex to the same vertex is not allowed
        assert!(mesh.add_edge(a, a, DAT).is_none());

        // Duplicate edge should return the existing edge
        assert_eq!(mesh.add_edge(a, b, DAT).unwrap(), e);
        assert_eq!(mesh.add_edge(b, a, DAT).unwrap(), e);
    }

    #[test]
    fn test_add_face() {
        let mut mesh = Mesh::new();
        let a = mesh.add_vert(DAT).unwrap();
        let b = mesh.add_vert(DAT).unwrap();
        let c = mesh.add_vert(DAT).unwrap();

        // Add a valid triangle
        let fk = mesh.add_face(&[a, b, c], DAT, DAT).unwrap();
        assert_eq!(mesh.edges.len(), 3);
        for lp in mesh.faces[&fk].loops.iter() {
            assert!(mesh.edges[&lp.edge].is_face_adjacent(fk));
        }

        // Add an invalid triangle with invalid edge
        assert!(mesh.add_face(&[a, b, b], DAT, DAT).is_none());
    }

    #[test]
    fn test_vert_adjacent_faces() {
        // Create a bow-tie mesh
        let mut mesh = Mesh::new();
        let middle = mesh.add_vert(DAT).unwrap();
        let l0 = mesh.add_vert(DAT).unwrap();
        let l1 = mesh.add_vert(DAT).unwrap();
        let r0 = mesh.add_vert(DAT).unwrap();
        let r1 = mesh.add_vert(DAT).unwrap();
        let fk0 = mesh.add_face(&[middle, l0, l1], DAT, DAT).unwrap();
        let fk1 = mesh.add_face(&[middle, r0, r1], DAT, DAT).unwrap();

        let adj = mesh.vert_adjacent_faces(middle);
        assert_eq!(adj.len(), 2);
        assert!(adj.contains(&fk0));
        assert!(adj.contains(&fk1));
    }

    #[test]
    fn test_ngon() {
        let mut mesh = Mesh::new();
        let a = mesh.add_vert(DAT).unwrap();
        let b = mesh.add_vert(DAT).unwrap();
        let c = mesh.add_vert(DAT).unwrap();
        let d = mesh.add_vert(DAT).unwrap();
        let e = mesh.add_vert(DAT).unwrap();

        let fk = mesh.add_face(&[a, b, c, d, e], DAT, DAT).unwrap();
        let face = &mesh.faces[&fk];

        assert_eq!(face.loops.len(), 5);
    }
}
<|endoftext|>


<commit_before>use super::*;

pub struct NROM {
    prg_rom: Vec<u8>,
    chr_rom: Vec<u8>,
    prg_ram: Vec<u8>,
}

impl NROM {
    pub fn new(prg_rom: Vec<u8>, chr_rom: Vec<u8>, prg_ram: Vec<u8>) -> NROM {
        NROM {
            prg_rom: prg_rom,
            chr_rom: chr_rom,
            prg_ram: prg_ram,
        }
    }
}

impl Mapper for NROM {
    fn prg_read(&self, idx: u16) -> u8 {
        match idx {
            0x6000...0x7FFF => self.prg_ram[((idx - 0x6000) as usize % self.prg_ram.len())],
            0x8000...0xFFFF => self.prg_rom[((idx - 0x8000) as usize % self.prg_ram.len())],
            x => invalid_address!(x),
        }
    }

    fn prg_write(&mut self, idx: u16, val: u8) {
        match idx {
            0x6000...0x7FFF => {
                let idx = (idx - 0x6000) as usize % self.prg_ram.len();
                self.prg_ram[idx] = val;
            }
            0x8000...0xFFFF => (),//Do nothing
            x => invalid_address!(x),
        }
    }

    fn chr_read(&self, idx: u16) -> u8 {
        self.chr_rom[idx as usize % self.chr_rom.len()]
    }

    #[allow(unused_variables)]
    fn chr_write(&mut self, idx: u16, val: u8) {
        // Do Nothing
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use mappers::Mapper;

    #[test]
    fn test_can_create_mapper_0() {
        NROM::new(vec![], vec![], vec![]);
    }

    fn create_test_mapper() -> NROM {
        NROM::new(vec!(0u8; 0x4000), vec!(0u8; 0x4000), vec!(0u8; 0x1000))
    }

    #[test]
    fn test_prg_ram_read_write() {
        let mut nrom = create_test_mapper();
        println!("{}", nrom.prg_ram.len());

        nrom.prg_write(0x6111, 15);
        assert_eq!(nrom.prg_read(0x6111), 15);

        nrom.prg_write(0x6112, 16);
        assert_eq!(nrom.prg_read(0x7112), 16);
    }

    #[test]
    fn test_prg_rom_read() {
        let prg_rom: Vec<_> = (0..0x4000)
                                  .map(|val| (val % 0xFF) as u8)
                                  .collect();
        let mapper = NROM::new(prg_rom, vec!(0u8; 0x4000), vec!(0u8; 0x1000));

        assert_eq!(mapper.prg_read(0x8111), mapper.prg_read(0xC111));
    }

    #[test]
    fn test_prg_rom_write() {
        let mut mapper = create_test_mapper();

        mapper.prg_write(0x8612, 15);
        assert_eq!(mapper.prg_read(0x8612), 0);
    }

    #[test]
    fn test_chr_rom_read() {
        let chr_rom: Vec<_> = (0..0x2000)
                                  .map(|val| (val % 0xFF) as u8)
                                  .collect();
        let mapper = NROM::new(vec!(0u8; 0x4000), chr_rom, vec!(0u8; 0x1000));

        assert_eq!(mapper.prg_read(0x8111), mapper.prg_read(0xC111));
    }

    #[test]
    fn test_chr_rom_write() {
        let mut mapper = create_test_mapper();

        mapper.chr_write(0x1612, 15);
        assert_eq!(mapper.chr_read(0x1612), 0);
    }
}
<commit_msg>Fixed bug where emulator was reading wrong data from PRG ROM.<commit_after>use super::*;

pub struct NROM {
    prg_rom: Vec<u8>,
    chr_rom: Vec<u8>,
    prg_ram: Vec<u8>,
}

impl NROM {
    pub fn new(prg_rom: Vec<u8>, chr_rom: Vec<u8>, prg_ram: Vec<u8>) -> NROM {
        NROM {
            prg_rom: prg_rom,
            chr_rom: chr_rom,
            prg_ram: prg_ram,
        }
    }
}

impl Mapper for NROM {
    fn prg_read(&self, idx: u16) -> u8 {
        match idx {
            0x6000...0x7FFF => self.prg_ram[((idx - 0x6000) as usize % self.prg_ram.len())],
            0x8000...0xFFFF => self.prg_rom[((idx - 0x8000) as usize % self.prg_rom.len())],
            x => invalid_address!(x),
        }
    }

    fn prg_write(&mut self, idx: u16, val: u8) {
        match idx {
            0x6000...0x7FFF => {
                let idx = (idx - 0x6000) as usize % self.prg_ram.len();
                self.prg_ram[idx] = val;
            }
            0x8000...0xFFFF => (),//Do nothing
            x => invalid_address!(x),
        }
    }

    fn chr_read(&self, idx: u16) -> u8 {
        self.chr_rom[idx as usize % self.chr_rom.len()]
    }

    #[allow(unused_variables)]
    fn chr_write(&mut self, idx: u16, val: u8) {
        // Do Nothing
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use mappers::Mapper;

    #[test]
    fn test_can_create_mapper_0() {
        NROM::new(vec![], vec![], vec![]);
    }

    fn create_test_mapper() -> NROM {
        NROM::new(vec!(0u8; 0x4000), vec!(0u8; 0x4000), vec!(0u8; 0x1000))
    }

    #[test]
    fn test_prg_ram_read_write() {
        let mut nrom = create_test_mapper();
        println!("{}", nrom.prg_ram.len());

        nrom.prg_write(0x6111, 15);
        assert_eq!(nrom.prg_read(0x6111), 15);

        nrom.prg_write(0x6112, 16);
        assert_eq!(nrom.prg_read(0x7112), 16);
    }

    #[test]
    fn test_prg_rom_read() {
        let prg_rom: Vec<_> = (0..0x4000)
                                  .map(|val| (val % 0xFF) as u8)
                                  .collect();
        let mapper = NROM::new(prg_rom, vec!(0u8; 0x4000), vec!(0u8; 0x1000));

        assert_eq!(mapper.prg_read(0x8111), mapper.prg_read(0xC111));
    }

    #[test]
    fn test_prg_rom_mirroring() {
    	let mut prg_rom: Vec<_> = vec!(0u8; 0x4000);
    	prg_rom[0x2612] = 0x15;
        let mapper = NROM::new(prg_rom, vec!(0u8; 0x1000), vec!(0u8; 0x1000));
        assert_eq!(mapper.prg_read(0xA612), 0x15);
    }

    #[test]
    fn test_prg_rom_write() {
        let mut mapper = create_test_mapper();

        mapper.prg_write(0x8612, 15);
        assert_eq!(mapper.prg_read(0x8612), 0);
    }

    #[test]
    fn test_chr_rom_read() {
        let chr_rom: Vec<_> = (0..0x2000)
                                  .map(|val| (val % 0xFF) as u8)
                                  .collect();
        let mapper = NROM::new(vec!(0u8; 0x4000), chr_rom, vec!(0u8; 0x1000));

        assert_eq!(mapper.prg_read(0x8111), mapper.prg_read(0xC111));
    }

    #[test]
    fn test_chr_rom_write() {
        let mut mapper = create_test_mapper();

        mapper.chr_write(0x1612, 15);
        assert_eq!(mapper.chr_read(0x1612), 0);
    }
}
<|endoftext|>


<commit_before>// This file is released under the same terms as Rust itself.

use ci;
use db::{Db, PendingEntry, QueueEntry, RunningEntry};
use std::marker::PhantomData;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use ui::{self, Pr};
use vcs::{self, Commit};

pub trait Worker<E: Send + Clone, M: Send + Clone> {
    fn run(&mut self, recv_msg: Receiver<M>, send_event: Sender<E>);
}

pub struct WorkerThread<E: Send + Clone + 'static, M: Send + Clone + 'static> {
    pub recv_event: Receiver<E>,
    pub send_msg: Sender<M>,
}

impl<E: Send + Clone + 'static, M: Send + Clone + 'static> WorkerThread<E, M> {
    pub fn start<T: Worker<E, M> + Send + 'static>(mut worker: T) -> Self {
        let (send_msg, recv_msg) = channel();
        let (send_event, recv_event) = channel();
        thread::spawn(move || {
            worker.run(recv_msg, send_event);
        });
        WorkerThread {
            recv_event: recv_event,
            send_msg: send_msg,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PipelineId(pub i32);

pub trait Ci<C: Commit> {
    fn start_build(&self, pipeline_id: PipelineId, commit: C);
}

impl<C: Commit> Ci<C> for WorkerThread<ci::Event<C>, ci::Message<C>> {
    fn start_build(&self, pipeline_id: PipelineId, commit: C) {
        self.send_msg.send(ci::Message::StartBuild(pipeline_id, commit))
            .unwrap();
    }
}

pub trait Ui<P: Pr> {
    fn send_result(&self, PipelineId, P, ui::Status<P>);
}

impl<P> Ui<P> for WorkerThread<ui::Event<P>, ui::Message<P>>
where P: Pr
{
    fn send_result(
        &self,
        pipeline_id: PipelineId,
        pr: P,
        status: ui::Status<P>,
    ) {
        self.send_msg.send(ui::Message::SendResult(pipeline_id, pr, status))
            .unwrap();
    }
}

pub trait Vcs<C: Commit> {
    fn merge_to_staging(&self, PipelineId, C, String, C::Remote);
    fn move_staging_to_master(&self, PipelineId, C);
}

impl<C: Commit> Vcs<C> for WorkerThread<vcs::Event<C>, vcs::Message<C>> {
    fn merge_to_staging(
        &self,
        pipeline_id: PipelineId,
        pull_commit: C,
        message: String,
        remote: C::Remote
    ) {
        self.send_msg.send(vcs::Message::MergeToStaging(
            pipeline_id, pull_commit, message, remote
        )).unwrap();
    }
    fn move_staging_to_master(
        &self,
        pipeline_id: PipelineId,
        merge_commit: C
    ) {
        self.send_msg.send(vcs::Message::MoveStagingToMaster(
            pipeline_id, merge_commit
        )).unwrap();
    }
}

// TODO: When Rust starts enforcing lifetimes on type aliases,
// use a type alias with something like:
//
//     pub type WorkerPipeline<'cntx, P: Pr + 'static> =
//         Pipeline<
//             'cntx,
//             P,
//             WorkerThread<ci::Event<P::C>, ci::Message<P::C>>,
//             WorkerThread<ui::Event<P::C, P>, ui::Message<P>>,
//             WorkerThread<vcs::Event<P::C>, vcs::Message<P>>,
//         >;
//
// That way, we can avoid all these ackward generics in main.
pub struct Pipeline<'cntx, P, B, U, V>
where P: Pr + 'static,
      B: Ci<P::C> + 'cntx,
      U: Ui<P> + 'cntx,
      V: Vcs<P::C> + 'cntx
{
    pub _pr: PhantomData<P>,
    pub id: PipelineId,
    pub ci: &'cntx B,
    pub ui: &'cntx U,
    pub vcs: &'cntx V,
}

#[derive(Clone)]
pub enum Event<P: Pr + 'static> {
    UiEvent(ui::Event<P>),
    VcsEvent(vcs::Event<P::C>),
    CiEvent(ci::Event<P::C>),
}

pub trait GetPipelineId {
    fn pipeline_id(&self) -> PipelineId;
}

impl<P: Pr + 'static> GetPipelineId for Event<P> {
    fn pipeline_id(&self) -> PipelineId {
        match *self {
            Event::UiEvent(ref e) => e.pipeline_id(),
            Event::CiEvent(ref e) => e.pipeline_id(),
            Event::VcsEvent(ref e) => e.pipeline_id(),
        }
    }
}

impl<'cntx, P, B, U, V> Pipeline<'cntx, P, B, U, V>
where P: Pr + 'static,
      B: Ci<P::C> + 'cntx,
      U: Ui<P> + 'cntx,
      V: Vcs<P::C> + 'cntx
{
    pub fn new(
        id: PipelineId,
        ci: &'cntx B,
        ui: &'cntx U,
        vcs: &'cntx V,
    ) -> Self {
        Pipeline {
            _pr: PhantomData,
            id: id,
            ci: ci,
            ui: ui,
            vcs: vcs,
        }
    }
    pub fn handle_event<D: Db<P>>(
        &mut self,
        db: &mut D,
        event: Event<P>,
    ) {
        match event {
            Event::UiEvent(ui::Event::Approved(
                pipeline_id,
                pr,
                commit,
                message,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                let commit = match (
                    commit,
                    db.peek_pending_by_pr(self.id, &pr).map(|p| p.commit),
                ) {
                    (Some(reviewed_pr), Some(current_pr)) => {
                        if reviewed_pr != current_pr {
                            self.ui.send_result(
                                self.id,
                                pr.clone(),
                                ui::Status::Invalidated,
                            );
                            None
                        } else {
                            Some(reviewed_pr)
                        }
                    }
                    (Some(reviewed_pr), None) => {
                        Some(reviewed_pr)
                    }
                    (None, Some(current_pr)) => {
                        Some(current_pr)
                    }
                    (None, None) => {
                        self.ui.send_result(
                            self.id,
                            pr.clone(),
                            ui::Status::NoCommit,
                        );
                        None
                    }
                };
                if let Some(commit) = commit {
                    db.cancel_by_pr(self.id, &pr);
                    db.push_queue(self.id, QueueEntry{
                        commit: commit,
                        pr: pr,
                        message: message,
                    });
                }
            },
            Event::UiEvent(ui::Event::Opened(
                pipeline_id, pr, commit, title, url
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                db.add_pending(self.id, PendingEntry{
                    commit: commit,
                    pr: pr,
                    title: title,
                    url: url,
                });
            },
            Event::UiEvent(ui::Event::Changed(
                pipeline_id, pr, commit, title, url
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if db.cancel_by_pr_different_commit(self.id, &pr, &commit) {
                    self.ui.send_result(
                        self.id,
                        pr.clone(),
                        ui::Status::Invalidated,
                    );
                }
                db.add_pending(self.id, PendingEntry{
                    commit: commit,
                    pr: pr,
                    title: title,
                    url: url,
                });
            },
            Event::UiEvent(ui::Event::Closed(pipeline_id, pr)) => {
                assert_eq!(&pipeline_id, &self.id);
                db.take_pending_by_pr(self.id, &pr);
                db.cancel_by_pr(self.id, &pr);
            },
            Event::UiEvent(ui::Event::Canceled(pipeline_id, pr)) => {
                assert_eq!(&pipeline_id, &self.id);
                db.cancel_by_pr(self.id, &pr);
            },
            Event::VcsEvent(vcs::Event::MergedToStaging(
                pipeline_id,
                pull_commit,
                merge_commit
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(mut running) = db.take_running(self.id) {
                    if running.pull_commit != pull_commit {
                        warn!("VCS merged event with wrong commit");
                    } else if running.merge_commit.is_some() {
                        warn!("VCS merged event with running commit");
                    } else if running.canceled {
                        // Drop it on the floor. It's canceled.
                    } else if running.built {
                        warn!("Got merge finished after finished building!");
                    } else {
                        running.merge_commit = Some(merge_commit.clone());
                        self.ci.start_build(
                            pipeline_id,
                            merge_commit.clone(),
                        );
                        self.ui.send_result(
                            self.id,
                            running.pr.clone(),
                            ui::Status::StartingBuild(
                                pull_commit,
                                merge_commit,
                            ),
                        );
                        db.put_running(self.id, running);
                    }
                } else {
                    warn!("VCS merged event with no queued PR");
                }
            },
            Event::VcsEvent(vcs::Event::FailedMergeToStaging(
                pipeline_id,
                pull_commit,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if running.pull_commit != pull_commit {
                        warn!("VCS merged event with wrong commit");
                    } else if running.merge_commit.is_some() {
                        warn!("VCS merged event with running commit");
                    } else if running.built {
                        warn!("Got merge failed after finished building!");
                    } else if running.canceled {
                        // Drop it on the floor. It's canceled.
                    } else {
                        self.ui.send_result(
                            self.id,
                            running.pr.clone(),
                            ui::Status::Unmergeable(pull_commit),
                        );
                    }
                } else {
                    warn!("VCS merged event with no queued PR");
                }
            },
            Event::CiEvent(ci::Event::BuildStarted(
                pipeline_id,
                building_commit,
                url,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.peek_running(self.id) {
                    if let Some(merged_commit) = running.merge_commit {
                        if merged_commit != building_commit {
                            warn!("Building a different commit");
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if running.built {
                            warn!("Got CI build started after done building!");
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr.clone(),
                                ui::Status::Testing(
                                    running.pull_commit.clone(),
                                    building_commit,
                                    url,
                                ),
                            );
                        }
                    } else {
                        warn!("Building a commit that never merged");
                    }
                } else {
                    warn!("CI build started event with no queued PR");
                }
            },
            Event::CiEvent(ci::Event::BuildFailed(
                pipeline_id,
                built_commit,
                url,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if let Some(ref merged_commit) = running.merge_commit {
                        if merged_commit != &built_commit {
                            warn!("Finished building a different commit");
                            db.put_running(self.id, running.clone());
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if running.built {
                            warn!("Got duplicate BuildFailed event");
                            // Put it back
                            db.put_running(self.id, running.clone());
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr.clone(),
                                ui::Status::Failure(
                                    running.pull_commit.clone(),
                                    merged_commit.clone(),
                                    url,
                                ),
                            );
                        }
                    } else {
                        warn!("Finished building a commit that never merged");
                    }
                } else {
                    warn!("CI build failed event with no queued PR");
                }
            },
            Event::CiEvent(ci::Event::BuildSucceeded(
                pipeline_id,
                built_commit,
                url,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(mut running) = db.take_running(self.id) {
                    if let Some(ref merged_commit) = running.merge_commit {
                        if merged_commit != &built_commit {
                            warn!("Finished building a different commit")
                        } else if running.canceled {
                            // Canceled; drop on the floor.
                        } else if running.built {
                            warn!("Got duplicate BuildSucceeded event");
                            // Put it back.
                            db.put_running(self.id, running.clone());
                        } else {
                            self.vcs.move_staging_to_master(
                                self.id,
                                merged_commit.clone(),
                            );
                            self.ui.send_result(
                                self.id,
                                running.pr.clone(),
                                ui::Status::Success(
                                    running.pull_commit.clone(),
                                    merged_commit.clone(),
                                    url,
                                ),
                            );
                            // Put it back with it marked as built.
                            running.built = true;
                            db.put_running(self.id, running.clone());
                        }
                    } else {
                        warn!("Finished building a commit that never merged");
                    }
                } else {
                    warn!("CI build succeeded event with no queued PR");
                }
            },
            Event::VcsEvent(vcs::Event::FailedMoveToMaster(
                pipeline_id,
                merge_commit,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if let Some(running_merge_commit) = running.merge_commit {
                        if running_merge_commit != merge_commit {
                            warn!("VCS move event with wrong commit");
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if !running.built {
                            warn!("Failed move to master before built!");
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr,
                                ui::Status::Unmoveable(
                                    running.pull_commit,
                                    running_merge_commit,
                                ),
                            );
                        }
                    } else {
                        warn!("VCS move event with commit that never ran");
                    }
                } else {
                    warn!("VCS move event with no queued PR");
                }
            },
            Event::VcsEvent(vcs::Event::MovedToMaster(
                pipeline_id,
                merge_commit,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if let Some(running_merge_commit) = running.merge_commit {
                        if running_merge_commit != merge_commit {
                            warn!("VCS move event with wrong commit");
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if !running.built {
                            warn!("Moved to master before done building!");
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr,
                                ui::Status::Completed(
                                    running.pull_commit,
                                    running_merge_commit,
                                ),
                            );
                        }
                    } else {
                        warn!("VCS move event with commit that never ran");
                    }
                } else {
                    warn!("VCS move event with no queued PR");
                }
            }
        }
        if db.peek_running(self.id).is_none() {
            if let Some(next) = db.pop_queue(self.id) {
                self.vcs.merge_to_staging(
                    self.id,
                    next.commit.clone(),
                    next.message.clone(),
                    next.pr.remote(),
                );
                db.put_running(self.id, RunningEntry{
                    pr: next.pr,
                    message: next.message,
                    pull_commit: next.commit,
                    merge_commit: None,
                    canceled: false,
                    built: false,
                });
            }
        }
    }
}

#[cfg(test)] mod test;
<commit_msg>Fill in the rest of the hardening<commit_after>// This file is released under the same terms as Rust itself.

use ci;
use db::{Db, PendingEntry, QueueEntry, RunningEntry};
use std::marker::PhantomData;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use ui::{self, Pr};
use vcs::{self, Commit};

pub trait Worker<E: Send + Clone, M: Send + Clone> {
    fn run(&mut self, recv_msg: Receiver<M>, send_event: Sender<E>);
}

pub struct WorkerThread<E: Send + Clone + 'static, M: Send + Clone + 'static> {
    pub recv_event: Receiver<E>,
    pub send_msg: Sender<M>,
}

impl<E: Send + Clone + 'static, M: Send + Clone + 'static> WorkerThread<E, M> {
    pub fn start<T: Worker<E, M> + Send + 'static>(mut worker: T) -> Self {
        let (send_msg, recv_msg) = channel();
        let (send_event, recv_event) = channel();
        thread::spawn(move || {
            worker.run(recv_msg, send_event);
        });
        WorkerThread {
            recv_event: recv_event,
            send_msg: send_msg,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct PipelineId(pub i32);

pub trait Ci<C: Commit> {
    fn start_build(&self, pipeline_id: PipelineId, commit: C);
}

impl<C: Commit> Ci<C> for WorkerThread<ci::Event<C>, ci::Message<C>> {
    fn start_build(&self, pipeline_id: PipelineId, commit: C) {
        self.send_msg.send(ci::Message::StartBuild(pipeline_id, commit))
            .unwrap();
    }
}

pub trait Ui<P: Pr> {
    fn send_result(&self, PipelineId, P, ui::Status<P>);
}

impl<P> Ui<P> for WorkerThread<ui::Event<P>, ui::Message<P>>
where P: Pr
{
    fn send_result(
        &self,
        pipeline_id: PipelineId,
        pr: P,
        status: ui::Status<P>,
    ) {
        self.send_msg.send(ui::Message::SendResult(pipeline_id, pr, status))
            .unwrap();
    }
}

pub trait Vcs<C: Commit> {
    fn merge_to_staging(&self, PipelineId, C, String, C::Remote);
    fn move_staging_to_master(&self, PipelineId, C);
}

impl<C: Commit> Vcs<C> for WorkerThread<vcs::Event<C>, vcs::Message<C>> {
    fn merge_to_staging(
        &self,
        pipeline_id: PipelineId,
        pull_commit: C,
        message: String,
        remote: C::Remote
    ) {
        self.send_msg.send(vcs::Message::MergeToStaging(
            pipeline_id, pull_commit, message, remote
        )).unwrap();
    }
    fn move_staging_to_master(
        &self,
        pipeline_id: PipelineId,
        merge_commit: C
    ) {
        self.send_msg.send(vcs::Message::MoveStagingToMaster(
            pipeline_id, merge_commit
        )).unwrap();
    }
}

// TODO: When Rust starts enforcing lifetimes on type aliases,
// use a type alias with something like:
//
//     pub type WorkerPipeline<'cntx, P: Pr + 'static> =
//         Pipeline<
//             'cntx,
//             P,
//             WorkerThread<ci::Event<P::C>, ci::Message<P::C>>,
//             WorkerThread<ui::Event<P::C, P>, ui::Message<P>>,
//             WorkerThread<vcs::Event<P::C>, vcs::Message<P>>,
//         >;
//
// That way, we can avoid all these ackward generics in main.
pub struct Pipeline<'cntx, P, B, U, V>
where P: Pr + 'static,
      B: Ci<P::C> + 'cntx,
      U: Ui<P> + 'cntx,
      V: Vcs<P::C> + 'cntx
{
    pub _pr: PhantomData<P>,
    pub id: PipelineId,
    pub ci: &'cntx B,
    pub ui: &'cntx U,
    pub vcs: &'cntx V,
}

#[derive(Clone)]
pub enum Event<P: Pr + 'static> {
    UiEvent(ui::Event<P>),
    VcsEvent(vcs::Event<P::C>),
    CiEvent(ci::Event<P::C>),
}

pub trait GetPipelineId {
    fn pipeline_id(&self) -> PipelineId;
}

impl<P: Pr + 'static> GetPipelineId for Event<P> {
    fn pipeline_id(&self) -> PipelineId {
        match *self {
            Event::UiEvent(ref e) => e.pipeline_id(),
            Event::CiEvent(ref e) => e.pipeline_id(),
            Event::VcsEvent(ref e) => e.pipeline_id(),
        }
    }
}

impl<'cntx, P, B, U, V> Pipeline<'cntx, P, B, U, V>
where P: Pr + 'static,
      B: Ci<P::C> + 'cntx,
      U: Ui<P> + 'cntx,
      V: Vcs<P::C> + 'cntx
{
    pub fn new(
        id: PipelineId,
        ci: &'cntx B,
        ui: &'cntx U,
        vcs: &'cntx V,
    ) -> Self {
        Pipeline {
            _pr: PhantomData,
            id: id,
            ci: ci,
            ui: ui,
            vcs: vcs,
        }
    }
    pub fn handle_event<D: Db<P>>(
        &mut self,
        db: &mut D,
        event: Event<P>,
    ) {
        match event {
            Event::UiEvent(ui::Event::Approved(
                pipeline_id,
                pr,
                commit,
                message,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                let commit = match (
                    commit,
                    db.peek_pending_by_pr(self.id, &pr).map(|p| p.commit),
                ) {
                    (Some(reviewed_pr), Some(current_pr)) => {
                        if reviewed_pr != current_pr {
                            self.ui.send_result(
                                self.id,
                                pr.clone(),
                                ui::Status::Invalidated,
                            );
                            None
                        } else {
                            Some(reviewed_pr)
                        }
                    }
                    (Some(reviewed_pr), None) => {
                        Some(reviewed_pr)
                    }
                    (None, Some(current_pr)) => {
                        Some(current_pr)
                    }
                    (None, None) => {
                        self.ui.send_result(
                            self.id,
                            pr.clone(),
                            ui::Status::NoCommit,
                        );
                        None
                    }
                };
                if let Some(commit) = commit {
                    db.cancel_by_pr(self.id, &pr);
                    db.push_queue(self.id, QueueEntry{
                        commit: commit,
                        pr: pr,
                        message: message,
                    });
                }
            },
            Event::UiEvent(ui::Event::Opened(
                pipeline_id, pr, commit, title, url
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                db.add_pending(self.id, PendingEntry{
                    commit: commit,
                    pr: pr,
                    title: title,
                    url: url,
                });
            },
            Event::UiEvent(ui::Event::Changed(
                pipeline_id, pr, commit, title, url
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if db.cancel_by_pr_different_commit(self.id, &pr, &commit) {
                    self.ui.send_result(
                        self.id,
                        pr.clone(),
                        ui::Status::Invalidated,
                    );
                }
                db.add_pending(self.id, PendingEntry{
                    commit: commit,
                    pr: pr,
                    title: title,
                    url: url,
                });
            },
            Event::UiEvent(ui::Event::Closed(pipeline_id, pr)) => {
                assert_eq!(&pipeline_id, &self.id);
                db.take_pending_by_pr(self.id, &pr);
                db.cancel_by_pr(self.id, &pr);
            },
            Event::UiEvent(ui::Event::Canceled(pipeline_id, pr)) => {
                assert_eq!(&pipeline_id, &self.id);
                db.cancel_by_pr(self.id, &pr);
            },
            Event::VcsEvent(vcs::Event::MergedToStaging(
                pipeline_id,
                pull_commit,
                merge_commit
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(mut running) = db.take_running(self.id) {
                    if running.pull_commit != pull_commit {
                        warn!("VCS merged event with wrong commit");
                    } else if running.merge_commit.is_some() {
                        warn!("VCS merged event with running commit");
                    } else if running.canceled {
                        // Drop it on the floor. It's canceled.
                    } else if running.built {
                        warn!("Got merge finished after finished building!");
                    } else {
                        running.merge_commit = Some(merge_commit.clone());
                        self.ci.start_build(
                            pipeline_id,
                            merge_commit.clone(),
                        );
                        self.ui.send_result(
                            self.id,
                            running.pr.clone(),
                            ui::Status::StartingBuild(
                                pull_commit,
                                merge_commit,
                            ),
                        );
                        db.put_running(self.id, running);
                    }
                } else {
                    warn!("VCS merged event with no queued PR");
                }
            },
            Event::VcsEvent(vcs::Event::FailedMergeToStaging(
                pipeline_id,
                pull_commit,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if running.pull_commit != pull_commit {
                        warn!("VCS merged event with wrong commit");
                    } else if running.merge_commit.is_some() {
                        warn!("VCS merged event with running commit");
                    } else if running.built {
                        warn!("Got merge failed after finished building!");
                    } else if running.canceled {
                        // Drop it on the floor. It's canceled.
                    } else {
                        self.ui.send_result(
                            self.id,
                            running.pr.clone(),
                            ui::Status::Unmergeable(pull_commit),
                        );
                    }
                } else {
                    warn!("VCS merged event with no queued PR");
                }
            },
            Event::CiEvent(ci::Event::BuildStarted(
                pipeline_id,
                building_commit,
                url,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.peek_running(self.id) {
                    if let Some(merged_commit) = running.merge_commit {
                        if merged_commit != building_commit {
                            warn!("Building a different commit");
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if running.built {
                            warn!("Got CI build started after done building!");
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr.clone(),
                                ui::Status::Testing(
                                    running.pull_commit.clone(),
                                    building_commit,
                                    url,
                                ),
                            );
                        }
                    } else {
                        warn!("Building a commit that never merged");
                    }
                } else {
                    warn!("CI build started event with no queued PR");
                }
            },
            Event::CiEvent(ci::Event::BuildFailed(
                pipeline_id,
                built_commit,
                url,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if let Some(ref merged_commit) = running.merge_commit {
                        if merged_commit != &built_commit {
                            warn!("Finished building a different commit");
                            db.put_running(self.id, running.clone());
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if running.built {
                            warn!("Got duplicate BuildFailed event");
                            // Put it back
                            db.put_running(self.id, running.clone());
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr.clone(),
                                ui::Status::Failure(
                                    running.pull_commit.clone(),
                                    merged_commit.clone(),
                                    url,
                                ),
                            );
                        }
                    } else {
                        warn!("Finished building a commit that never merged");
                    }
                } else {
                    warn!("CI build failed event with no queued PR");
                }
            },
            Event::CiEvent(ci::Event::BuildSucceeded(
                pipeline_id,
                built_commit,
                url,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(mut running) = db.take_running(self.id) {
                    if let Some(ref merged_commit) = running.merge_commit {
                        if merged_commit != &built_commit {
                            warn!("Finished building a different commit");
                            db.put_running(self.id, running.clone());
                        } else if running.canceled {
                            // Canceled; drop on the floor.
                        } else if running.built {
                            warn!("Got duplicate BuildSucceeded event");
                            // Put it back.
                            db.put_running(self.id, running.clone());
                        } else {
                            self.vcs.move_staging_to_master(
                                self.id,
                                merged_commit.clone(),
                            );
                            self.ui.send_result(
                                self.id,
                                running.pr.clone(),
                                ui::Status::Success(
                                    running.pull_commit.clone(),
                                    merged_commit.clone(),
                                    url,
                                ),
                            );
                            // Put it back with it marked as built.
                            running.built = true;
                            db.put_running(self.id, running.clone());
                        }
                    } else {
                        warn!("Finished building a commit that never merged");
                    }
                } else {
                    warn!("CI build succeeded event with no queued PR");
                }
            },
            Event::VcsEvent(vcs::Event::FailedMoveToMaster(
                pipeline_id,
                merge_commit,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if let Some(running_merge_commit) = running.merge_commit {
                        if running_merge_commit != merge_commit {
                            warn!("VCS move event with wrong commit");
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if !running.built {
                            warn!("Failed move to master before built!");
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr,
                                ui::Status::Unmoveable(
                                    running.pull_commit,
                                    running_merge_commit,
                                ),
                            );
                        }
                    } else {
                        warn!("VCS move event with commit that never ran");
                    }
                } else {
                    warn!("VCS move event with no queued PR");
                }
            },
            Event::VcsEvent(vcs::Event::MovedToMaster(
                pipeline_id,
                merge_commit,
            )) => {
                assert_eq!(&pipeline_id, &self.id);
                if let Some(running) = db.take_running(self.id) {
                    if let Some(running_merge_commit) = running.merge_commit {
                        if running_merge_commit != merge_commit {
                            warn!("VCS move event with wrong commit");
                        } else if running.canceled {
                            // Drop it on the floor. It's canceled.
                        } else if !running.built {
                            warn!("Moved to master before done building!");
                        } else {
                            self.ui.send_result(
                                self.id,
                                running.pr,
                                ui::Status::Completed(
                                    running.pull_commit,
                                    running_merge_commit,
                                ),
                            );
                        }
                    } else {
                        warn!("VCS move event with commit that never ran");
                    }
                } else {
                    warn!("VCS move event with no queued PR");
                }
            }
        }
        if db.peek_running(self.id).is_none() {
            if let Some(next) = db.pop_queue(self.id) {
                self.vcs.merge_to_staging(
                    self.id,
                    next.commit.clone(),
                    next.message.clone(),
                    next.pr.remote(),
                );
                db.put_running(self.id, RunningEntry{
                    pr: next.pr,
                    message: next.message,
                    pull_commit: next.commit,
                    merge_commit: None,
                    canceled: false,
                    built: false,
                });
            }
        }
    }
}

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


<commit_before>#![feature(collections)]

//! A TIS-100 emulator.
//!
//! # Example
//!
//! ```
//! use tis_100::save::parse_save;
//! use tis_100::machine::Sandbox;
//!
//! // This program reads the value from the console and simply passes it to the console output.
//! let src = "@1\nMOV UP DOWN\n@5\nMOV UP DOWN\n@9\nMOV UP RIGHT\n@10\nMOV LEFT DOWN\n";
//!
//! let save = parse_save(src).unwrap();
//! let mut sandbox = Sandbox::from_save(&save);
//!
//! sandbox.write_console(42);
//!
//! for _ in 0..5 {
//!     sandbox.step();
//! }
//!
//! assert_eq!(sandbox.read_console(), Some(42));
//! ```

extern crate hlua;

pub mod core;
pub mod lex;
pub mod parse;
pub mod io;
pub mod node;
pub mod image;
pub mod save;
pub mod spec;
pub mod machine;
<commit_msg>use feature(vecmap) instead of feature(collections)<commit_after>#![feature(vecmap)]

//! A TIS-100 emulator.
//!
//! # Example
//!
//! ```
//! use tis_100::save::parse_save;
//! use tis_100::machine::Sandbox;
//!
//! // This program reads the value from the console and simply passes it to the console output.
//! let src = "@1\nMOV UP DOWN\n@5\nMOV UP DOWN\n@9\nMOV UP RIGHT\n@10\nMOV LEFT DOWN\n";
//!
//! let save = parse_save(src).unwrap();
//! let mut sandbox = Sandbox::from_save(&save);
//!
//! sandbox.write_console(42);
//!
//! for _ in 0..5 {
//!     sandbox.step();
//! }
//!
//! assert_eq!(sandbox.read_console(), Some(42));
//! ```

extern crate hlua;

pub mod core;
pub mod lex;
pub mod parse;
pub mod io;
pub mod node;
pub mod image;
pub mod save;
pub mod spec;
pub mod machine;
<|endoftext|>


<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A library that provides ASCII-only string and character types, equivalent to the `char`, `str`
//! and `String` types in the standard library.
//!
#![cfg_attr(feature = "std", doc="[The documentation for the `core` mode is here](https://tomprogrammer.github.io/rust-ascii/core/ascii/index.html).")]
#![cfg_attr(not(feature = "std"), doc="This is the documentation for `core` mode.")]
//! Please refer to the readme file to learn about the different feature modes of this crate.
//!
//! # Requirements
//!
//! The `ascii` library requires rustc 1.9.0 or greater, due to the [stabilization of
//! `AsciiExt`](https://github.com/rust-lang/rust/pull/32804). Using only `core` instead of `std` in
//! your project lowers this requirement to rustc 1.6.0 or greater.
//!
//! # History
//!
//! This package included the Ascii types that were removed from the Rust standard library by the
//! 2014-12 [reform of the `std::ascii` module](https://github.com/rust-lang/rfcs/pull/486). The
//! API changed significantly since then.

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
extern crate core;

#[cfg(feature = "quickcheck")]
extern crate quickcheck;

mod free_functions;
mod ascii_char;
mod ascii_str;
#[cfg(feature = "std")]
mod ascii_string;

pub use free_functions::{caret_encode, caret_decode};
pub use ascii_char::{AsciiChar, ToAsciiChar, ToAsciiCharError};
pub use ascii_str::{AsciiStr, AsAsciiStr, AsMutAsciiStr, AsAsciiStrError, Chars, CharsMut, Lines};
#[cfg(feature = "std")]
pub use ascii_string::{AsciiString, IntoAsciiString, FromAsciiError};
<commit_msg>Update minimum Rust version in lib.rs docs too.<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A library that provides ASCII-only string and character types, equivalent to the `char`, `str`
//! and `String` types in the standard library.
//!
#![cfg_attr(feature = "std", doc="[The documentation for the `core` mode is here](https://tomprogrammer.github.io/rust-ascii/core/ascii/index.html).")]
#![cfg_attr(not(feature = "std"), doc="This is the documentation for `core` mode.")]
//! Please refer to the readme file to learn about the different feature modes of this crate.
//!
//! # Requirements
//!
//! The minimum supported Rust version is 1.9.0.
//! Enabling the quickcheck integration requires Rust 1.12.0.
//!
//! # History
//!
//! This package included the Ascii types that were removed from the Rust standard library by the
//! 2014-12 [reform of the `std::ascii` module](https://github.com/rust-lang/rfcs/pull/486). The
//! API changed significantly since then.

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
extern crate core;

#[cfg(feature = "quickcheck")]
extern crate quickcheck;

mod free_functions;
mod ascii_char;
mod ascii_str;
#[cfg(feature = "std")]
mod ascii_string;

pub use free_functions::{caret_encode, caret_decode};
pub use ascii_char::{AsciiChar, ToAsciiChar, ToAsciiCharError};
pub use ascii_str::{AsciiStr, AsAsciiStr, AsMutAsciiStr, AsAsciiStrError, Chars, CharsMut, Lines};
#[cfg(feature = "std")]
pub use ascii_string::{AsciiString, IntoAsciiString, FromAsciiError};
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate xswag_base as base;
extern crate lalrpop_util;

pub mod lex;
pub mod ast;
pub mod grammar;

use base::diag::Report;
use base::code::{BytePos, FileMap, Span};
use lalrpop_util::ParseError;
use lex::{Token, TokenSpan};
use std::cell::RefCell;

const MAX_PREVIEW_LEN: usize = 15;

pub fn parse_compilation_unit(file: &FileMap)
    -> (Option<ast::CompilationUnit>, Vec<Report>)
{
    // Save all tokens that have been read already
    let tokens = RefCell::new(Vec::new());
    let mut lexing_err = RefCell::new(None);

    // Stop at the first lexing error and remove all non real token for parsing
    let lexer = lex::Tokenizer::new(file)
        .take_while(|res| match res {
            &Ok(_) => true,
            &Err(ref e) => {
                *lexing_err.borrow_mut() = Some(e.clone());
                false
            }
        })
        .map(Result::unwrap)
        .inspect(|t| tokens.borrow_mut().push(t.clone()))
        .map(|ts| { (ts.span.lo, ts.tok, ts.span.hi) })
        .filter(|t| t.1.is_real());

    let mut errors = Vec::new();

    let res = grammar::java8::parse_CompilationUnit(&mut errors, lexer);
    let res = res.map_err(|e| match e {
        ParseError::User { error: e } => e,
        ParseError::InvalidToken { location: loc } => {
            Report::simple_error("lalrpop InvalidToken", Span::single(loc))
        },
        ParseError::UnrecognizedToken { token, expected } => {
            match token {
                None => {
                    Report::simple_error(
                        "lalrpop UnrecognizedToken ???",
                        Span::dummy()
                    )
                },
                Some((lo, tok, hi)) => {
                    handle_unexpected_token(
                        tok,
                        Span::new(lo, hi),
                        file,
                        &expected,
                        &tokens.borrow(),
                    )
                }
            }
        },
        ParseError::ExtraToken  { token: (lo, tok, hi) } => {
            Report::simple_error(
                format!("lalrpop ExtraToken {:?}", tok),
                Span::new(lo, hi),
            )
        },
    });

    let mut reps = Vec::new();
    let ast = if let Some(ref lexing_err) = *lexing_err.borrow() {
        reps.push(lexing_err.report.clone());
        None
    } else {
        res.map(|ast| Some(ast)).unwrap_or_else(|e| { reps.push(e); None })
    };

    reps.extend_from_slice(&errors);
    (ast, reps)
}

fn handle_unexpected_token(
    token: Token,
    span: Span,
    file: &FileMap,
    expected: &[String],
    tokens: &[TokenSpan]
) -> Report {
    // ------ Prepare a nice error message -------
    let tok_name = token.as_java_string();

    // Prepare token preview
    let mut chs = file.src()[span.into_range()].chars();
    let mut tok_str: String = chs.by_ref().take(MAX_PREVIEW_LEN).collect();
    if chs.next().is_some() {
        // if the original span was longer than 15 chars, we append '...'
        tok_str.push_str("...");
    }

    // pack as `Report`
    let msg = format!(
        "unexpected token '{}' (`{}`). Expected one of {:?}",
        tok_name,
        tok_str,
        expected,
    );
    let rep = Report::simple_error(msg, span);

    // ------ Check if we can give the user some more useful information ------
    // Check if the user has forgotten a semicolon. We trigger this note if
    //   1. the unexpected token is the first token in the line
    //   2. the last line does not end with a semicolon or a opening brace '{'
    //

    // The `loc_lo` was returned from the map: unwrap OK
    let loc_lo = file.get_loc(span.lo);
    let line = file.get_line(loc_lo.line).unwrap();

    // check condition [1]
    let is_line_start = line
        .char_indices()
        .take_while(|&(i, _)| i < loc_lo.col.0 as usize)
        .all(|(_, c)| c.is_whitespace());

    // check condition [2]
    let prev = tokens
        .iter()
        .rev()
        // get the previous token (ignoring whitespace)
        .skip_while(|ts| ts.span.lo >= span.lo)
        .find(|ts| ts.tok.is_real());
    let is_good = prev.map(|ts| ts.tok == Token::Semi || ts.tok == Token::BraceOp);

    if is_line_start && is_good == Some(false) {
        // we can safely unwrap here: `is_good.is_some()` => `prev.is_some()`
        rep.with_span_note(
            "maybe you forgot a semicolon (`;`) at the end of this line?",
            Span::single(prev.unwrap().span.hi - BytePos(1)),
        )
    } else {
        rep.with_note("this is a syntax error. Make sure all \
            parentheses are balanced and nothing is missing.")
    }
}
<commit_msg>Improve "unexpected token" message a bit<commit_after>#[macro_use]
extern crate log;
extern crate xswag_base as base;
extern crate lalrpop_util;

pub mod lex;
pub mod ast;
pub mod grammar;

use base::diag::Report;
use base::code::{BytePos, FileMap, Span};
use lalrpop_util::ParseError;
use lex::{Token, TokenSpan};
use std::cell::RefCell;

const MAX_PREVIEW_LEN: usize = 15;

pub fn parse_compilation_unit(file: &FileMap)
    -> (Option<ast::CompilationUnit>, Vec<Report>)
{
    // Save all tokens that have been read already
    let tokens = RefCell::new(Vec::new());
    let lexing_err = RefCell::new(None);

    // Stop at the first lexing error and remove all non real token for parsing
    let lexer = lex::Tokenizer::new(file)
        .take_while(|res| match res {
            &Ok(_) => true,
            &Err(ref e) => {
                *lexing_err.borrow_mut() = Some(e.clone());
                false
            }
        })
        .map(Result::unwrap)
        .inspect(|t| tokens.borrow_mut().push(t.clone()))
        .map(|ts| { (ts.span.lo, ts.tok, ts.span.hi) })
        .filter(|t| t.1.is_real());

    let mut errors = Vec::new();

    let res = grammar::java8::parse_CompilationUnit(&mut errors, lexer);
    let res = res.map_err(|e| match e {
        ParseError::User { error: e } => e,
        ParseError::InvalidToken { location: loc } => {
            Report::simple_error("lalrpop InvalidToken", Span::single(loc))
        },
        ParseError::UnrecognizedToken { token, expected } => {
            match token {
                None => {
                    Report::simple_error(
                        "lalrpop UnrecognizedToken ???",
                        Span::dummy()
                    )
                },
                Some((lo, tok, hi)) => {
                    handle_unexpected_token(
                        tok,
                        Span::new(lo, hi),
                        file,
                        &expected,
                        &tokens.borrow(),
                    )
                }
            }
        },
        ParseError::ExtraToken  { token: (lo, tok, hi) } => {
            Report::simple_error(
                format!("lalrpop ExtraToken {:?}", tok),
                Span::new(lo, hi),
            )
        },
    });

    let mut reps = Vec::new();
    let ast = if let Some(ref lexing_err) = *lexing_err.borrow() {
        reps.push(lexing_err.report.clone());
        None
    } else {
        res.map(|ast| Some(ast)).unwrap_or_else(|e| { reps.push(e); None })
    };

    reps.extend_from_slice(&errors);
    (ast, reps)
}

fn handle_unexpected_token(
    token: Token,
    span: Span,
    file: &FileMap,
    expected: &[String],
    tokens: &[TokenSpan]
) -> Report {
    // ------ Prepare a nice error message -------
    let tok_name = token.as_java_string();

    // Prepare token preview
    let mut chs = file.src()[span.into_range()].chars();
    let mut tok_str: String = chs.by_ref().take(MAX_PREVIEW_LEN).collect();
    if chs.next().is_some() {
        // if the original span was longer than 15 chars, we append '...'
        tok_str.push_str("...");
    }

    // pack as `Report`
    let msg = if tok_name == tok_str {
        format!(
            "unexpected '{}'. Expected one of {:?}",
            tok_name,
            expected,
        )
    } else {
        format!(
            "unexpected token '{}' (`{}`). Expected one of {:?}",
            tok_name,
            tok_str,
            expected,
        )
    };
    let rep = Report::simple_error(msg, span);

    // ------ Check if we can give the user some more useful information ------
    // Check if the user has forgotten a semicolon. We trigger this note if
    //   1. the unexpected token is the first token in the line
    //   2. the last line does not end with a semicolon or a opening brace '{'
    //

    // The `loc_lo` was returned from the map: unwrap OK
    let loc_lo = file.get_loc(span.lo);
    let line = file.get_line(loc_lo.line).unwrap();

    // check condition [1]
    let is_line_start = line
        .char_indices()
        .take_while(|&(i, _)| i < loc_lo.col.0 as usize)
        .all(|(_, c)| c.is_whitespace());

    // check condition [2]
    let prev = tokens
        .iter()
        .rev()
        // get the previous token (ignoring whitespace)
        .skip_while(|ts| ts.span.lo >= span.lo)
        .find(|ts| ts.tok.is_real());
    let is_good = prev.map(|ts| ts.tok == Token::Semi || ts.tok == Token::BraceOp);

    if is_line_start && is_good == Some(false) {
        // we can safely unwrap here: `is_good.is_some()` => `prev.is_some()`
        rep.with_span_note(
            "maybe you forgot a semicolon (`;`) at the end of this line?",
            Span::single(prev.unwrap().span.hi - BytePos(1)),
        )
    } else {
        rep.with_note("this is a syntax error. Make sure all \
            parentheses are balanced and nothing is missing.")
    }
}
<|endoftext|>


<commit_before>pub mod system;
pub mod node;
pub mod cpu;
mod util;
<commit_msg>make util public<commit_after>pub mod system;
pub mod node;
pub mod cpu;
pub mod util;
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]
#[macro_use]
extern crate log;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

extern crate walkdir;

use proc_macro::TokenStream;
use syn::*;
use quote::Tokens;

#[cfg(debug_assertions)]
fn generate_assets(ident: &syn::Ident, folder_path: String) -> quote::Tokens {
  quote!{
      use std::fs::File;
      use std::io::Read;
      use std::path::Path;

      impl #ident {
          pub fn get(file_path: &str) -> Option<Vec<u8>> {
              let folder_path = #folder_path;
              let name = &format!("{}{}", folder_path, file_path);
              let path = &Path::new(name);
              let key = String::from(path.to_str().expect("Path does not have a string representation"));
              info!("file: {}", key);
              let mut file = match File::open(path) {
                  Ok(mut file) => file,
                  Err(e) => {
                      error!("file: {} {}", key, e);
                      return None
                  }
              };
              let mut data: Vec<u8> = Vec::new();
              match file.read_to_end(&mut data) {
                  Ok(_) => Some(data),
                  Err(e) =>  {
                      error!("file: {} {}", key, e);
                      return None
                  }
              }
          }
      }
  }
}

#[cfg(not(debug_assertions))]
fn generate_assets(ident: &syn::Ident, folder_path: String) -> quote::Tokens {
  use std::fs::File;
  use std::io::Read;
  use std::path::Path;
  use walkdir::WalkDir;
  let mut values = Vec::<Tokens>::new();
  for entry in WalkDir::new(folder_path.clone())
    .into_iter()
    .filter_map(|e| e.ok())
    .filter(|e| e.file_type().is_file())
  {
    info!("file: {}", entry.path().display());
    let base = &folder_path.clone();
    let key = String::from(
      entry
        .path()
        .to_str()
        .expect("Path does not have a string representation"),
    ).replace(base, "");
    let mut file = File::open(&Path::new(&entry.path())).unwrap_or_else(|e| {
      panic!("could not open file -> {} {}", key, e);
    });
    let mut data: Vec<u8> = Vec::new();
    file.read_to_end(&mut data).unwrap_or_else(|e| {
      panic!("could not read file -> {} {}", key, e);
    });
    let value = quote!{
      #key => Some(vec!#data),
    };
    values.push(value);
  }
  quote!{
      impl #ident {
          pub fn get(file_path: &str) -> Option<Vec<u8>> {
              match file_path {
                  #(#values)*
                  _ => None,
              }
          }
      }
  }
}

fn help() {
  panic!("#[derive(RustEmbed)] should contain one attribute like this #[golem(\"examples/public/\")]");
}

fn impl_rust_embed(ast: &syn::DeriveInput) -> Tokens {
  match ast.body {
    Body::Enum(_) => help(),
    Body::Struct(ref data) => match data {
      &VariantData::Struct(_) => help(),
      _ => {}
    },
  };
  let ident = &ast.ident;
  if ast.attrs.len() == 0 || ast.attrs.len() > 1 {
    help();
  }
  let value = &ast.attrs[0].value;
  let items = match value {
    &MetaItem::List(ref attr_name, ref items) => {
      if attr_name == "folder" {
        items
      } else {
        panic!("#[derive(RustEmbed)] attribute name must be folder");
      }
    }
    _ => {
      panic!("#[derive(RustEmbed)] attribute name must be folder");
    }
  };
  let item = &items[0];
  let lit = match item {
    &NestedMetaItem::Literal(ref l) => l,
    _ => {
      panic!("Hello");
    }
  };
  let folder_path = match lit {
    &Lit::Str(ref val, _) => val.clone(),
    _ => {
      panic!("#[derive(RustEmbed)] attribute value must be a string literal");
    }
  };
  generate_assets(ident, folder_path)
}

#[proc_macro_derive(RustEmbed, attributes(folder))]
pub fn derive_input_object(input: TokenStream) -> TokenStream {
  let s = input.to_string();
  let ast = syn::parse_derive_input(&s).unwrap();
  let gen = impl_rust_embed(&ast);
  gen.parse().unwrap()
}
<commit_msg>fix help message<commit_after>#![recursion_limit = "1024"]
#[macro_use]
extern crate log;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate syn;

extern crate walkdir;

use proc_macro::TokenStream;
use syn::*;
use quote::Tokens;

#[cfg(debug_assertions)]
fn generate_assets(ident: &syn::Ident, folder_path: String) -> quote::Tokens {
  quote!{
      use std::fs::File;
      use std::io::Read;
      use std::path::Path;

      impl #ident {
          pub fn get(file_path: &str) -> Option<Vec<u8>> {
              let folder_path = #folder_path;
              let name = &format!("{}{}", folder_path, file_path);
              let path = &Path::new(name);
              let key = String::from(path.to_str().expect("Path does not have a string representation"));
              info!("file: {}", key);
              let mut file = match File::open(path) {
                  Ok(mut file) => file,
                  Err(e) => {
                      error!("file: {} {}", key, e);
                      return None
                  }
              };
              let mut data: Vec<u8> = Vec::new();
              match file.read_to_end(&mut data) {
                  Ok(_) => Some(data),
                  Err(e) =>  {
                      error!("file: {} {}", key, e);
                      return None
                  }
              }
          }
      }
  }
}

#[cfg(not(debug_assertions))]
fn generate_assets(ident: &syn::Ident, folder_path: String) -> quote::Tokens {
  use std::fs::File;
  use std::io::Read;
  use std::path::Path;
  use walkdir::WalkDir;
  let mut values = Vec::<Tokens>::new();
  for entry in WalkDir::new(folder_path.clone())
    .into_iter()
    .filter_map(|e| e.ok())
    .filter(|e| e.file_type().is_file())
  {
    info!("file: {}", entry.path().display());
    let base = &folder_path.clone();
    let key = String::from(
      entry
        .path()
        .to_str()
        .expect("Path does not have a string representation"),
    ).replace(base, "");
    let mut file = File::open(&Path::new(&entry.path())).unwrap_or_else(|e| {
      panic!("could not open file -> {} {}", key, e);
    });
    let mut data: Vec<u8> = Vec::new();
    file.read_to_end(&mut data).unwrap_or_else(|e| {
      panic!("could not read file -> {} {}", key, e);
    });
    let value = quote!{
      #key => Some(vec!#data),
    };
    values.push(value);
  }
  quote!{
      impl #ident {
          pub fn get(file_path: &str) -> Option<Vec<u8>> {
              match file_path {
                  #(#values)*
                  _ => None,
              }
          }
      }
  }
}

fn help() {
  panic!("#[derive(RustEmbed)] should contain one attribute like this #[folder(\"examples/public/\")]");
}

fn impl_rust_embed(ast: &syn::DeriveInput) -> Tokens {
  match ast.body {
    Body::Enum(_) => help(),
    Body::Struct(ref data) => match data {
      &VariantData::Struct(_) => help(),
      _ => {}
    },
  };
  let ident = &ast.ident;
  if ast.attrs.len() == 0 || ast.attrs.len() > 1 {
    help();
  }
  let value = &ast.attrs[0].value;
  let items = match value {
    &MetaItem::List(ref attr_name, ref items) => {
      if attr_name == "folder" {
        items
      } else {
        panic!("#[derive(RustEmbed)] attribute name must be folder");
      }
    }
    _ => {
      panic!("#[derive(RustEmbed)] attribute name must be folder");
    }
  };
  let item = &items[0];
  let lit = match item {
    &NestedMetaItem::Literal(ref l) => l,
    _ => {
      panic!("Hello");
    }
  };
  let folder_path = match lit {
    &Lit::Str(ref val, _) => val.clone(),
    _ => {
      panic!("#[derive(RustEmbed)] attribute value must be a string literal");
    }
  };
  info!("folder: {}", folder_path);
  generate_assets(ident, folder_path)
}

#[proc_macro_derive(RustEmbed, attributes(folder))]
pub fn derive_input_object(input: TokenStream) -> TokenStream {
  let s = input.to_string();
  let ast = syn::parse_derive_input(&s).unwrap();
  let gen = impl_rust_embed(&ast);
  gen.parse().unwrap()
}
<|endoftext|>


<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 https://mozilla.org/MPL/2.0/.

//! A library to localize strings, translating them according to runtime options.
//!
//! Basically, this library allows your project to generate a `lformat!` macro, that behaves
//! similarly to `format!`, except the message string (the first argument) might get translated
//! (if you can find the appropriate string for the language).
//!
//! # Usage
//!
//! First, you'll need to add the following to your `Cargo.toml` file:
//!
//! ```toml
//! build = "build.rs"
//!
//! [build-dependencies]
//! crowbook-localize = "0.0.8"
//!
//! [dependencies]
//! lazy_static = "0.2" # the generated file needs `lazy_static!`
//! ```
//!
//! You'll then need to create the `build.rs` file, which can look like this:
//!
//! ```rust,ignore
//! extern crate crowbook_localize;
//! use crowbook_localize::{Localizer, Extractor};
//!
//! fn main() {
//!     // Generate a `lang/default.pot` containing strings used to call `lformat!`
//!     let mut extractor = Extractor::new();
//!     extractor.add_messages_from_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/src")).unwrap();
//!     extractor.write_pot_file(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/default.pot")).unwrap();
//!
//!     // Generate the `localize_macros.rs` file
//!     let mut localizer = Localizer::new(&extractor);
//!     localizer.write_macro_file(concat!(env!("CARGO_MANIFEST_DIR"), "/src/lib/localize_macros.rs")).unwrap();
//! }
//! ```
//!
//! This way, a `localize_macros.rs` file will be created at build time in `src/lib`.
//! To use it, the last step is to modify your `src/lib/lib.rs` file:
//!
//! ```rust,ignore
//! #[macro_use] extern crate lazy_static;
//! #[macro_use] mod localize_macros;
//! ```
//!
//! Once this is done, you can start replacing your calls to `format!` with calls to `lformat!`.
//!
//! In order to get translation, you'll need to actually translate the strings in separate
//! files, and set your `build.rs` to load them.
//!
//! E.g., if you have the following code:
//!
//! ```rust,ignore
//! println!("{}", lformat!("Hello, world!"));
//! ```
//! and you want it translated in french, you'll have to create a `lang/fr.po` file
//! from the `lang/default.pot` file containing:
//!
//! ```text
//! msgid "Hello, world!";
//! msgstr "Bonjour le monde !";
//! ```
//!
//! And load it in your `build.rs` file:
//!
//! ```rust,ignore
//! let mut localizer = Localizer::new();
//! localizer.add_lang("fr", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/fr.mp"))).unwrap();
//! localizer.write_macro_file("...");
//! ```
//!
//! Once *this* is done, you can use the `localize_macros::set_lang` function
//! to switch the language at runtime:
//!
//! ```rust,ignore
//! use localize_macros::set_lang;
//! set_lang("en");
//! println!("{}", lformat!("Hello, world!")); // prints "Hello, world!"
//! set_lang("fr");
//! println!("{}", lformat!("Hello, world!")); // prints "Bonjour le monde !"
//! ```
//!
//! # Updating your translation
//!
//! When you add new strings that need to be translated (by more calls to `lformat!`),
//! or when you change the content of existing strings, you can use [Gettext's `msgmerge`](https://www.gnu.org/software/gettext/manual/html_node/msgmerge-Invocation.html)
//! command to update your translation. While it is not guaranteed that the formats are
//! strictly identicals, it should work. (That is, it is a bug if it doesn't; but at this
//! stage, this library is absolutely not guaranteed to be bug-free.)
//!
//! # Warning
//!
//! In case the complexity of the operation didn't discourage you, I should warn you
//! that this library is highly experimental at this time.

extern crate regex;
#[macro_use] extern crate lazy_static;
extern crate walkdir;

mod common;
mod macrogen;
mod lang;
mod error;
mod localizer;
mod message;
mod extractor;

pub use error::{Result, Error};
pub use localizer::Localizer;
pub use extractor::Extractor;
<commit_msg>Update documentation to advocate using OUT_DIR<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 https://mozilla.org/MPL/2.0/.

//! A library to localize strings, translating them according to runtime options.
//!
//! Basically, this library allows your project to generate a `lformat!` macro, that behaves
//! similarly to `format!`, except the message string (the first argument) might get translated
//! (if you can find the appropriate string for the language).
//!
//! # Usage
//!
//! First, you'll need to add the following to your `Cargo.toml` file:
//!
//! ```toml
//! build = "build.rs"
//!
//! [build-dependencies]
//! crowbook-localize = "0.0.9"
//!
//! [dependencies]
//! lazy_static = "0.2" # the generated file needs `lazy_static!`
//! ```
//!
//! You'll then need to create the `build.rs` file, which can look like this:
//!
//! ```rust,ignore
//! extern crate crowbook_localize;
//! use crowbook_localize::{Localizer, Extractor};
//!
//! fn main() {
//!     // Generate a `lang/default.pot` containing strings used to call `lformat!`
//!     let mut extractor = Extractor::new();
//!     extractor.add_messages_from_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/src")).unwrap();
//!     extractor.write_pot_file(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/default.pot")).unwrap();
//!
//!     // Generate the `localize_macros.rs` file
//!     let mut localizer = Localizer::new(&extractor);
//!     localizer.write_macro_file(concat!(env!("CARGO_MANIFEST_DIR"), "/src/lib/localize_macros.rs")).unwrap();
//! }
//! ```
//!
//! This way, a `localize_macros.rs` file will be created at build time in `src/lib`.
//! To use it, the last step is to modify your `src/lib/lib.rs` file:
//!
//! ```rust,ignore
//! #[macro_use] extern crate lazy_static;
//! #[macro_use] mod localize_macros;
//! ```
//!
//! Once this is done, you can start replacing your calls to `format!` with calls to `lformat!`.
//!
//! In order to get translation, you'll need to actually translate the strings in separate
//! files, and set your `build.rs` to load them.
//!
//! E.g., if you have the following code:
//!
//! ```rust,ignore
//! println!("{}", lformat!("Hello, world!"));
//! ```
//! and you want it translated in french, you'll have to create a `lang/fr.po` file
//! from the `lang/default.pot` file containing:
//!
//! ```text
//! msgid "Hello, world!";
//! msgstr "Bonjour le monde !";
//! ```
//!
//! And load it in your `build.rs` file:
//!
//! ```rust,ignore
//! let mut localizer = Localizer::new();
//! localizer.add_lang("fr", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/lang/fr.mp"))).unwrap();
//! localizer.write_macro_file(concat!(env!("OUT_DIR"), "/localize_macros.rs")).unwrap();
//! ```
//!
//! You'll also need to `include!` the result of this build script into a `localize_macros.rs` file:
//!
//! ```rust,ignore
//! include!(concat!(env!("OUT_DIR"), "/localize_macros.rs"));
//! ```
//!
//! Once *this* is done, you can use the `localize_macros::set_lang` function
//! to switch the language at runtime:
//!
//! ```rust,ignore
//! use localize_macros::set_lang;
//! set_lang("en");
//! println!("{}", lformat!("Hello, world!")); // prints "Hello, world!"
//! set_lang("fr");
//! println!("{}", lformat!("Hello, world!")); // prints "Bonjour le monde !"
//! ```
//!
//! # Updating your translation
//!
//! When you add new strings that need to be translated (by more calls to `lformat!`),
//! or when you change the content of existing strings, you can use [Gettext's `msgmerge` and `msgcmp`](https://www.gnu.org/software/gettext/manual/html_node/msgmerge-Invocation.html)
//! commands to update your translation. While it is not guaranteed that the formats are
//! strictly identical, it should work. (That is, it is a bug if it doesn't; but at this
//! stage, this library is absolutely not guaranteed to be bug-free.)
//!
//! # Warning
//!
//! In case the complexity of the operation didn't discourage you, I should warn you
//! that this library is highly experimental at this time.

extern crate regex;
#[macro_use] extern crate lazy_static;
extern crate walkdir;

mod common;
mod macrogen;
mod lang;
mod error;
mod localizer;
mod message;
mod extractor;

pub use error::{Result, Error};
pub use localizer::Localizer;
pub use extractor::Extractor;
<|endoftext|>