<commit_before>use memory::MemSegment;
use cart::{Cart, ScreenMode};
use std::rc::Rc;
use std::cell::RefCell;
use super::Color;
use super::PaletteIndex;
use super::TilePattern;

/// Represents the PPU's memory map.
pub struct PPUMemory {
    cart: Rc<RefCell<Cart>>,
    vram: [u8; 0x0F00],
    palette: [Color; 0x20],
}

fn get_nametable_addrs(mode: ScreenMode) -> [u16; 4] {
    match mode {
        ScreenMode::Vertical => [0x2000, 0x2400, 0x2000, 0x2400],
        ScreenMode::Horizontal => [0x2000, 0x2000, 0x2400, 0x2400],
        ScreenMode::OneScreenLow => [0x2000, 0x2000, 0x2000, 0x2000],
        ScreenMode::OneScreenHigh => [0x2400, 0x2400, 0x2400, 0x2400],
        ScreenMode::FourScreen => [0x2000, 0x2400, 0x2800, 0x2C00],
    }
}

impl PPUMemory {
    pub fn new(cart: Rc<RefCell<Cart>>) -> PPUMemory {
        PPUMemory {
            cart: cart,
            vram: [0u8; 0x0F00],
            palette: [Color::from_bits_truncate(0); 0x20],
        }
    }
}

fn get_tile_addr(tile_id: u8, plane: u8, fine_y_scroll: u16, tile_table: u16) -> u16 {
    let mut tile_addr = 0u16;
    tile_addr |= fine_y_scroll;
    tile_addr |= plane as u16; //Plane must be 0 for low or 8 for high
    tile_addr |= (tile_id as u16) << 4;
    tile_addr |= tile_table; //Table must be 0x0000 or 0x1000
    tile_addr
}

impl PPUMemory {
    pub fn read_bypass_palette(&mut self, idx: u16) -> u8 {
        let idx = self.translate_vram_address(idx);
        self.vram[idx]
    }

    fn translate_vram_address(&self, idx: u16) -> usize {
        let idx = idx & 0x0FFF;
        let nametable_num = (idx / 0x0400) as usize;
        let idx_in_nametable = idx % 0x400;
        let mode = self.cart.borrow().get_mirroring_mode();
        let translated = get_nametable_addrs(mode)[nametable_num] + idx_in_nametable;
        translated as usize % self.vram.len()
    }

    pub fn read_palette(&mut self, idx: PaletteIndex) -> Color {
        let bits = self.read(idx.to_addr());
        Color::from_bits_truncate(bits)
    }

    pub fn read_tile_pattern(&mut self,
                             tile_id: u8,
                             fine_y_scroll: u16,
                             tile_table: u16)
                             -> TilePattern {
        let lo_addr = get_tile_addr(tile_id, 0, fine_y_scroll, tile_table);
        let hi_addr = get_tile_addr(tile_id, 8, fine_y_scroll, tile_table);
        TilePattern {
            lo: self.read(lo_addr),
            hi: self.read(hi_addr),
        }
    }

    #[allow(dead_code)]
    pub fn dump_nametable(&mut self, idx: u16) {
        let start_idx = 0x2000 + (idx * 0x400);
        println!("Nametable {}:", idx);
        self.print_columns(start_idx..(start_idx + 0x3C0), 32)
    }

    #[allow(dead_code)]
    pub fn dump_attribute_table(&mut self, idx: u16) {
        let start_idx = 0x2000 + (idx * 0x400);
        println!("Attribute table {}:", idx);
        self.print_columns((start_idx + 0x3C0)..(start_idx + 0x400), 32);
    }
}

impl MemSegment for PPUMemory {
    fn read(&mut self, idx: u16) -> u8 {
        match idx {
            0x0000...0x1FFF => {
                let mut cart = self.cart.borrow_mut();
                cart.chr_read(idx)
            }
            0x2000...0x3EFF => self.read_bypass_palette(idx),
            0x3F00...0x3FFF => {
                match (idx & 0x001F) as usize {
                        0x10 => self.palette[0x00],
                        0x14 => self.palette[0x04],
                        0x18 => self.palette[0x08],
                        0x1C => self.palette[0x0C],
                        x => self.palette[x],
                    }
                    .bits()
            }
            x => invalid_address!(x),
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        match idx {
            0x0000...0x1FFF => {
                let mut cart = self.cart.borrow_mut();
                cart.chr_write(idx, val)
            }
            0x2000...0x3EFF => {
                let idx = self.translate_vram_address(idx);
                self.vram[idx] = val;
            }
            0x3F00...0x3FFF => {
                let val = Color::from_bits_truncate(val);
                match (idx & 0x001F) as usize {
                    0x10 => self.palette[0x00] = val,
                    0x14 => self.palette[0x04] = val,
                    0x18 => self.palette[0x08] = val,
                    0x1C => self.palette[0x0C] = val,
                    x => self.palette[x] = val,
                }
            }
            x => invalid_address!(x),
        }
    }
}

#[cfg(test)]
mod tests {
    use memory::MemSegment;
    use ppu::tests::*;
    use ppu::{Color, PPU};
    use cart::ScreenMode;

    #[test]
    fn ppu_can_read_write_palette() {
        let mut ppu = create_test_ppu();

        ppu.reg.v = 0x3F00;
        ppu.write(0x2007, 12);
        ppu.reg.v = 0x3F00;
        assert_eq!(ppu.ppu_mem.palette[0], Color::from_bits_truncate(12));

        ppu.reg.v = 0x3F01;
        ppu.write(0x2007, 212);
        ppu.reg.v = 0x3F01;
        assert_eq!(ppu.read(0x2007), 212 & 0x3F);
    }

    #[test]
    fn test_palette_mirroring() {
        let mut ppu = create_test_ppu();

        let mirrors = [0x3F10, 0x3F14, 0x3F18, 0x3F1C];
        let targets = [0x3F00, 0x3F04, 0x3F08, 0x3F0C];
        for x in 0..4 {

            ppu.reg.v = targets[x];
            ppu.write(0x2007, 12);
            ppu.reg.v = mirrors[x];
            assert_eq!(ppu.read(0x2007), 12);

            ppu.reg.v = mirrors[x];
            ppu.write(0x2007, 12);
            ppu.reg.v = targets[x];
            assert_eq!(ppu.read(0x2007), 12);
        }
    }

    fn to_nametable_idx(idx: u16, tbl: u16) -> u16 {
        0x2000 + (0x0400 * tbl) + idx
    }

    fn assert_mirrored(ppu: &mut PPU, tbl1: u16, tbl2: u16) {
        for idx in 0x0000..0x0400 {
            let tbl1_idx = to_nametable_idx(idx, tbl1);
            let tbl2_idx = to_nametable_idx(idx, tbl2);

            println!("Translated: tbl1: {:04X}, tbl2: {:04X}",
                ppu.ppu_mem.translate_vram_address(tbl1_idx),
                ppu.ppu_mem.translate_vram_address(tbl2_idx),
            );

            ppu.ppu_mem.write(tbl1_idx, 0xFF);
            assert_eq!(0xFF, ppu.ppu_mem.read(tbl2_idx));

            ppu.ppu_mem.write(tbl2_idx, 0x61);
            assert_eq!(0x61, ppu.ppu_mem.read(tbl1_idx));
        }
    }

    fn assert_not_mirrored(ppu: &mut PPU, tbl1: u16, tbl2: u16) {
        for idx in 0x0000..0x0400 {
            let tbl1_idx = to_nametable_idx(idx, tbl1);
            let tbl2_idx = to_nametable_idx(idx, tbl2);

            println!("Translated: tbl1: {:04X}, tbl2: {:04X}",
                ppu.ppu_mem.translate_vram_address(tbl1_idx),
                ppu.ppu_mem.translate_vram_address(tbl2_idx),
            );

            ppu.ppu_mem.write(tbl1_idx, 0x00);
            ppu.ppu_mem.write(tbl2_idx, 0x00);

            ppu.ppu_mem.write(tbl1_idx, 0xFF);
            assert_eq!(0x00, ppu.ppu_mem.read(tbl2_idx));

            ppu.ppu_mem.write(tbl2_idx, 0x61);
            assert_eq!(0xFF, ppu.ppu_mem.read(tbl1_idx));
        }
    }

    #[test]
    fn single_screen_mirroring_mirrors_both_ways() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::OneScreenLow);

        assert_mirrored(&mut ppu, 0, 1);
        assert_mirrored(&mut ppu, 1, 2);
        assert_mirrored(&mut ppu, 2, 3);
    }

    #[test]
    fn four_screen_mirroring_mirrors_both_ways() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::FourScreen);

        assert_not_mirrored(&mut ppu, 0, 1);
        assert_not_mirrored(&mut ppu, 1, 2);
        assert_not_mirrored(&mut ppu, 2, 3);
    }

    #[test]
    fn horizontal_mirroring_mirrors_horizontally() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::Horizontal);

        assert_mirrored(&mut ppu, 0, 1);
        assert_mirrored(&mut ppu, 2, 3);
        assert_not_mirrored(&mut ppu, 0, 2);
        assert_not_mirrored(&mut ppu, 1, 3);
    }

    #[test]
    fn vertical_mirroring_mirrors_vertically() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::Vertical);

        assert_not_mirrored(&mut ppu, 0, 1);
        assert_not_mirrored(&mut ppu, 2, 3);
        assert_mirrored(&mut ppu, 0, 2);
        assert_mirrored(&mut ppu, 1, 3);
    }
}
<commit_msg>Optimized the read_palette function.<commit_after>use memory::MemSegment;
use cart::{Cart, ScreenMode};
use std::rc::Rc;
use std::cell::RefCell;
use super::Color;
use super::PaletteIndex;
use super::TilePattern;

/// Represents the PPU's memory map.
pub struct PPUMemory {
    cart: Rc<RefCell<Cart>>,
    vram: [u8; 0x0F00],
    palette: [Color; 0x20],
}

fn get_nametable_addrs(mode: ScreenMode) -> [u16; 4] {
    match mode {
        ScreenMode::Vertical => [0x2000, 0x2400, 0x2000, 0x2400],
        ScreenMode::Horizontal => [0x2000, 0x2000, 0x2400, 0x2400],
        ScreenMode::OneScreenLow => [0x2000, 0x2000, 0x2000, 0x2000],
        ScreenMode::OneScreenHigh => [0x2400, 0x2400, 0x2400, 0x2400],
        ScreenMode::FourScreen => [0x2000, 0x2400, 0x2800, 0x2C00],
    }
}

impl PPUMemory {
    pub fn new(cart: Rc<RefCell<Cart>>) -> PPUMemory {
        PPUMemory {
            cart: cart,
            vram: [0u8; 0x0F00],
            palette: [Color::from_bits_truncate(0); 0x20],
        }
    }
}

fn get_tile_addr(tile_id: u8, plane: u8, fine_y_scroll: u16, tile_table: u16) -> u16 {
    let mut tile_addr = 0u16;
    tile_addr |= fine_y_scroll;
    tile_addr |= plane as u16; //Plane must be 0 for low or 8 for high
    tile_addr |= (tile_id as u16) << 4;
    tile_addr |= tile_table; //Table must be 0x0000 or 0x1000
    tile_addr
}

impl PPUMemory {
    pub fn read_bypass_palette(&mut self, idx: u16) -> u8 {
        let idx = self.translate_vram_address(idx);
        self.vram[idx]
    }

    fn translate_vram_address(&self, idx: u16) -> usize {
        let idx = idx & 0x0FFF;
        let nametable_num = (idx / 0x0400) as usize;
        let idx_in_nametable = idx % 0x400;
        let mode = self.cart.borrow().get_mirroring_mode();
        let translated = get_nametable_addrs(mode)[nametable_num] + idx_in_nametable;
        translated as usize % self.vram.len()
    }

    pub fn read_palette(&mut self, idx: PaletteIndex) -> Color {
        self.read_palette_mem(idx.to_addr() as usize)
    }

    fn read_palette_mem(&self, idx: usize) -> Color {
        match (idx % 0x1F) as usize {
            0x10 => self.palette[0x00],
            0x14 => self.palette[0x04],
            0x18 => self.palette[0x08],
            0x1C => self.palette[0x0C],
            x => self.palette[x],
        }
    }

    pub fn read_tile_pattern(&mut self,
                             tile_id: u8,
                             fine_y_scroll: u16,
                             tile_table: u16)
                             -> TilePattern {
        let lo_addr = get_tile_addr(tile_id, 0, fine_y_scroll, tile_table);
        let hi_addr = get_tile_addr(tile_id, 8, fine_y_scroll, tile_table);
        TilePattern {
            lo: self.read(lo_addr),
            hi: self.read(hi_addr),
        }
    }

    #[allow(dead_code)]
    pub fn dump_nametable(&mut self, idx: u16) {
        let start_idx = 0x2000 + (idx * 0x400);
        println!("Nametable {}:", idx);
        self.print_columns(start_idx..(start_idx + 0x3C0), 32)
    }

    #[allow(dead_code)]
    pub fn dump_attribute_table(&mut self, idx: u16) {
        let start_idx = 0x2000 + (idx * 0x400);
        println!("Attribute table {}:", idx);
        self.print_columns((start_idx + 0x3C0)..(start_idx + 0x400), 32);
    }
}

impl MemSegment for PPUMemory {
    fn read(&mut self, idx: u16) -> u8 {
        match idx {
            0x0000...0x1FFF => {
                let mut cart = self.cart.borrow_mut();
                cart.chr_read(idx)
            }
            0x2000...0x3EFF => self.read_bypass_palette(idx),
            0x3F00...0x3FFF => self.read_palette_mem(idx as usize).bits(),
            x => invalid_address!(x),
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        match idx {
            0x0000...0x1FFF => {
                let mut cart = self.cart.borrow_mut();
                cart.chr_write(idx, val)
            }
            0x2000...0x3EFF => {
                let idx = self.translate_vram_address(idx);
                self.vram[idx] = val;
            }
            0x3F00...0x3FFF => {
                let val = Color::from_bits_truncate(val);
                match (idx & 0x001F) as usize {
                    0x10 => self.palette[0x00] = val,
                    0x14 => self.palette[0x04] = val,
                    0x18 => self.palette[0x08] = val,
                    0x1C => self.palette[0x0C] = val,
                    x => self.palette[x] = val,
                }
            }
            x => invalid_address!(x),
        }
    }
}

#[cfg(test)]
mod tests {
    use memory::MemSegment;
    use ppu::tests::*;
    use ppu::{Color, PPU};
    use cart::ScreenMode;

    #[test]
    fn ppu_can_read_write_palette() {
        let mut ppu = create_test_ppu();

        ppu.reg.v = 0x3F00;
        ppu.write(0x2007, 12);
        ppu.reg.v = 0x3F00;
        assert_eq!(ppu.ppu_mem.palette[0], Color::from_bits_truncate(12));

        ppu.reg.v = 0x3F01;
        ppu.write(0x2007, 212);
        ppu.reg.v = 0x3F01;
        assert_eq!(ppu.read(0x2007), 212 & 0x3F);
    }

    #[test]
    fn test_palette_mirroring() {
        let mut ppu = create_test_ppu();

        let mirrors = [0x3F10, 0x3F14, 0x3F18, 0x3F1C];
        let targets = [0x3F00, 0x3F04, 0x3F08, 0x3F0C];
        for x in 0..4 {

            ppu.reg.v = targets[x];
            ppu.write(0x2007, 12);
            ppu.reg.v = mirrors[x];
            assert_eq!(ppu.read(0x2007), 12);

            ppu.reg.v = mirrors[x];
            ppu.write(0x2007, 12);
            ppu.reg.v = targets[x];
            assert_eq!(ppu.read(0x2007), 12);
        }
    }

    fn to_nametable_idx(idx: u16, tbl: u16) -> u16 {
        0x2000 + (0x0400 * tbl) + idx
    }

    fn assert_mirrored(ppu: &mut PPU, tbl1: u16, tbl2: u16) {
        for idx in 0x0000..0x0400 {
            let tbl1_idx = to_nametable_idx(idx, tbl1);
            let tbl2_idx = to_nametable_idx(idx, tbl2);

            println!("Translated: tbl1: {:04X}, tbl2: {:04X}",
                ppu.ppu_mem.translate_vram_address(tbl1_idx),
                ppu.ppu_mem.translate_vram_address(tbl2_idx),
            );

            ppu.ppu_mem.write(tbl1_idx, 0xFF);
            assert_eq!(0xFF, ppu.ppu_mem.read(tbl2_idx));

            ppu.ppu_mem.write(tbl2_idx, 0x61);
            assert_eq!(0x61, ppu.ppu_mem.read(tbl1_idx));
        }
    }

    fn assert_not_mirrored(ppu: &mut PPU, tbl1: u16, tbl2: u16) {
        for idx in 0x0000..0x0400 {
            let tbl1_idx = to_nametable_idx(idx, tbl1);
            let tbl2_idx = to_nametable_idx(idx, tbl2);

            println!("Translated: tbl1: {:04X}, tbl2: {:04X}",
                ppu.ppu_mem.translate_vram_address(tbl1_idx),
                ppu.ppu_mem.translate_vram_address(tbl2_idx),
            );

            ppu.ppu_mem.write(tbl1_idx, 0x00);
            ppu.ppu_mem.write(tbl2_idx, 0x00);

            ppu.ppu_mem.write(tbl1_idx, 0xFF);
            assert_eq!(0x00, ppu.ppu_mem.read(tbl2_idx));

            ppu.ppu_mem.write(tbl2_idx, 0x61);
            assert_eq!(0xFF, ppu.ppu_mem.read(tbl1_idx));
        }
    }

    #[test]
    fn single_screen_mirroring_mirrors_both_ways() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::OneScreenLow);

        assert_mirrored(&mut ppu, 0, 1);
        assert_mirrored(&mut ppu, 1, 2);
        assert_mirrored(&mut ppu, 2, 3);
    }

    #[test]
    fn four_screen_mirroring_mirrors_both_ways() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::FourScreen);

        assert_not_mirrored(&mut ppu, 0, 1);
        assert_not_mirrored(&mut ppu, 1, 2);
        assert_not_mirrored(&mut ppu, 2, 3);
    }

    #[test]
    fn horizontal_mirroring_mirrors_horizontally() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::Horizontal);

        assert_mirrored(&mut ppu, 0, 1);
        assert_mirrored(&mut ppu, 2, 3);
        assert_not_mirrored(&mut ppu, 0, 2);
        assert_not_mirrored(&mut ppu, 1, 3);
    }

    #[test]
    fn vertical_mirroring_mirrors_vertically() {
        let mut ppu = create_test_ppu_with_mirroring(ScreenMode::Vertical);

        assert_not_mirrored(&mut ppu, 0, 1);
        assert_not_mirrored(&mut ppu, 2, 3);
        assert_mirrored(&mut ppu, 0, 2);
        assert_mirrored(&mut ppu, 1, 3);
    }
}
<|endoftext|>


<commit_before>use yaml_rust::Yaml;
use command::{Command, Session, SendKeys, Split, Layout, Window, Window2, Attach};

#[cfg(test)] use yaml_rust::{YamlLoader};

pub fn main(yaml_string: &Vec<Yaml>, project_name: String) -> Vec<Command> {
    let mut commands: Vec<Command> = vec!();

    for doc in yaml_string {
        let root = match doc["root"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        let (first_window, windows) = doc["windows"].as_vec().expect("No Windows have been defined.").split_at(1);

        match &first_window[0] {
             &Yaml::Hash(ref h)  => {
                 for (k, v) in h {
                     if v.as_hash().is_some() {
                         commands.push(Command::Session(Session{name: project_name.clone(), window_name: k.as_str().unwrap().to_string(), root: root.clone()}));
                         commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                     } else {
                         commands.push(Command::Session(Session{name: project_name.clone(), window_name: k.as_str().unwrap().to_string(), root: root.clone()}));
                         commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}", project_name, k.as_str().unwrap().to_string()).to_string(), exec: v.as_str().expect("Bad exec command").to_string()}));
                     }
                 }
             },
             &Yaml::String(ref s) => {
                 commands.push(Command::Session(Session{name: project_name.clone(), window_name: s.clone(), root: root.clone()}))
             },
             &Yaml::Integer(ref s) => {
                 commands.push(Command::Session(Session{name: project_name.clone(), window_name: s.to_string(), root: root.clone()}))
             },
             _ => panic!("Muxed config file formatting isn't recognized.")
        };

        for (i,window) in windows.iter().enumerate() {
            match window {
                &Yaml::Hash(ref h)  => {
                    for (k, v) in h {
                        if v.as_hash().is_some() {
                            commands.push(Command::Window(Window{value: k.as_str().unwrap().to_string(), root: root.clone(), exec: "".to_string()}));
                            commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                        } else {
                            commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: k.as_str().unwrap().to_string(), root: root.clone()}));
                            commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}", project_name, k.as_str().unwrap().to_string()).to_string(), exec: v.as_str().expect("Bad exec command").to_string()}));
                        }
                    }
                },
                &Yaml::String(ref s) => {
                    commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: s.clone(), root: root.clone()}))
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: s.to_string(), root: root.clone()}))
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };
    commands.push(Command::Attach(Attach{name: project_name.clone()}));
    commands
}

fn pane_matcher(session: String, panes: &Yaml, root: Option<String>, window: String) -> Vec<Command> {
    let mut commands = vec!();
    let panes2 = panes["panes"].as_vec().expect("Something is wrong with panes.");

    for (i, pane) in panes2.iter().enumerate() {
        if i < (panes2.len()-1) {
            commands.push(Command::Split(Split{target: format!("{}:{}.{}", session, window, i.to_string()).to_string(), root: root.clone()}));
        };
        commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}.{}", session, window, i).to_string(), exec: pane.as_str().expect("Bad exec command").to_string()}));
    };

    if panes["layout"].as_str().is_some() {
        let layout = panes["layout"].as_str().unwrap().to_string();
        commands.push(Command::Layout(Layout{target: format!("{}:{}", session, window).to_string(), layout: panes["layout"].as_str().expect("Bad layout").to_string()}));
    };

    commands
}

#[test]
pub fn windows_defined_as_array_has_4_commands() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, "muxed".to_string()).len(), 4)
}

#[test]
pub fn windows_defined_as_array_has_1_session() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Session(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_defined_as_array_has_2_windows() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Window2(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 2)
}

#[test]
pub fn windows_defined_as_array_has_1_attach() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Attach(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_with_integer_names() {
    let s = "---
windows: [1, 'vim', 3]
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, "muxed".to_string()).len(), 4)
}

#[test]
pub fn windows_as_list() {
    let s = "---
windows:
  - cargo: ''
  - vim: ''
  - git: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());
    assert_eq!(commands.len(), 7)
}

#[test]
pub fn root_command() {
    let s = "---
root: '~/.muxed'
windows:
  - cargo: ''
  - vim: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());

    let first_window: Option<Session> = match commands[0].clone() {
        Command::Session(w) => Some(w),
        _                   => None
    };

    assert_eq!(first_window.unwrap().root.unwrap(), "~/.muxed".to_string())
}

#[test]
pub fn panes_array_has_7_commands() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());
    println!("{:?}", commands);
    assert_eq!(commands.len(), 6)
}

#[test]
pub fn panes_array_has_1_split() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Split(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_layout() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Layout(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_no_window() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Window2(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn panes_array_has_1_session() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Session(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

//#[test]
//pub fn panes_command_execs_array() {
//    let s = "---
//windows:
//  - editor:
//      layout: 'main-vertical'
//      panes: ['vim', 'guard']
//";
//    let yaml = YamlLoader::load_from_str(s).unwrap();
//    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
//      &Command::SendKeys(_) => true,
//      _ => false
//    }).collect();
//
//    assert_eq!(remains.len(), 2);
//    assert_eq!(remains[0].exec, "vim");
//    assert_eq!(remains[1].exec, "guard")
//}
<commit_msg>Minor refactoring<commit_after>use yaml_rust::Yaml;
use command::{Command, Session, SendKeys, Split, Layout, Window, Window2, Attach};

#[cfg(test)] use yaml_rust::{YamlLoader};

pub fn main(yaml_string: &Vec<Yaml>, project_name: String) -> Vec<Command> {
    let mut commands: Vec<Command> = vec!();

    for doc in yaml_string {
        let root = match doc["root"].as_str() {
            Some(x) => Some(x.to_string()),
            None    => None
        };

        let (first_window, windows) = doc["windows"].as_vec().expect("No Windows have been defined.").split_at(1);

        match &first_window[0] {
             &Yaml::Hash(ref h)  => {
                 for (k, v) in h {
                     if v.as_hash().is_some() {
                         commands.push(Command::Session(Session{name: project_name.clone(), window_name: k.as_str().unwrap().to_string(), root: root.clone()}));
                         commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                     } else {
                         commands.push(Command::Session(Session{name: project_name.clone(), window_name: k.as_str().unwrap().to_string(), root: root.clone()}));
                         commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}", project_name, k.as_str().unwrap().to_string()).to_string(), exec: v.as_str().expect("Bad exec command").to_string()}));
                     }
                 }
             },
             &Yaml::String(ref s) => {
                 commands.push(Command::Session(Session{name: project_name.clone(), window_name: s.clone(), root: root.clone()}))
             },
             &Yaml::Integer(ref s) => {
                 commands.push(Command::Session(Session{name: project_name.clone(), window_name: s.to_string(), root: root.clone()}))
             },
             _ => panic!("Muxed config file formatting isn't recognized.")
        };

        for window in windows.iter() {
            match window {
                &Yaml::Hash(ref h)  => {
                    for (k, v) in h {
                        if v.as_hash().is_some() {
                            commands.push(Command::Window(Window{value: k.as_str().unwrap().to_string(), root: root.clone(), exec: "".to_string()}));
                            commands.append(&mut pane_matcher(project_name.clone(), v, root.clone(), k.as_str().unwrap().to_string()));
                        } else {
                            commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: k.as_str().unwrap().to_string(), root: root.clone()}));
                            commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}", project_name, k.as_str().unwrap().to_string()).to_string(), exec: v.as_str().expect("Bad exec command").to_string()}));
                        }
                    }
                },
                &Yaml::String(ref s) => {
                    commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: s.clone(), root: root.clone()}))
                },
                &Yaml::Integer(ref s) => {
                    commands.push(Command::Window2(Window2{session_name: project_name.clone(), name: s.to_string(), root: root.clone()}))
                },
                _ => panic!("Muxed config file formatting isn't recognized.")
            };
        };
    };
    commands.push(Command::Attach(Attach{name: project_name.clone()}));
    commands
}

fn pane_matcher(session: String, panes: &Yaml, root: Option<String>, window: String) -> Vec<Command> {
    let mut commands = vec!();
    let panes2 = panes["panes"].as_vec().expect("Something is wrong with panes.");

    for (i, pane) in panes2.iter().enumerate() {
        if i < (panes2.len()-1) {
            commands.push(Command::Split(Split{target: format!("{}:{}.{}", session, window, i.to_string()).to_string(), root: root.clone()}));
        };
        commands.push(Command::SendKeys(SendKeys{target: format!("{}:{}.{}", session, window, i).to_string(), exec: pane.as_str().expect("Bad exec command").to_string()}));
    };

    if panes["layout"].as_str().is_some() {
        let layout = panes["layout"].as_str().expect("Bad layout").to_string();
        commands.push(Command::Layout(Layout{target: format!("{}:{}", session, window).to_string(), layout: layout}));
    };

    commands
}

#[test]
pub fn windows_defined_as_array_has_4_commands() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, "muxed".to_string()).len(), 4)
}

#[test]
pub fn windows_defined_as_array_has_1_session() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Session(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_defined_as_array_has_2_windows() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Window2(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 2)
}

#[test]
pub fn windows_defined_as_array_has_1_attach() {
    let s = "---
windows: ['cargo', 'vim', 'git']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Attach(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn windows_with_integer_names() {
    let s = "---
windows: [1, 'vim', 3]
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    assert_eq!(main(&yaml, "muxed".to_string()).len(), 4)
}

#[test]
pub fn windows_as_list() {
    let s = "---
windows:
  - cargo: ''
  - vim: ''
  - git: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());
    assert_eq!(commands.len(), 7)
}

#[test]
pub fn root_command() {
    let s = "---
root: '~/.muxed'
windows:
  - cargo: ''
  - vim: ''
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());

    let first_window: Option<Session> = match commands[0].clone() {
        Command::Session(w) => Some(w),
        _                   => None
    };

    assert_eq!(first_window.unwrap().root.unwrap(), "~/.muxed".to_string())
}

#[test]
pub fn panes_array_has_7_commands() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";
    let yaml = YamlLoader::load_from_str(s).unwrap();
    let commands = main(&yaml, "muxed".to_string());
    println!("{:?}", commands);
    assert_eq!(commands.len(), 6)
}

#[test]
pub fn panes_array_has_1_split() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Split(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_1_layout() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Layout(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

#[test]
pub fn panes_array_has_no_window() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Window2(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 0)
}

#[test]
pub fn panes_array_has_1_session() {
    let s = "---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['vim', 'guard']
";

    let yaml = YamlLoader::load_from_str(s).unwrap();
    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
      &Command::Session(_) => true,
      _ => false
    }).collect();

    assert_eq!(remains.len(), 1)
}

//#[test]
//pub fn panes_command_execs_array() {
//    let s = "---
//windows:
//  - editor:
//      layout: 'main-vertical'
//      panes: ['vim', 'guard']
//";
//    let yaml = YamlLoader::load_from_str(s).unwrap();
//    let remains: Vec<Command> = main(&yaml, "muxed".to_string()).into_iter().filter(|x| match x {
//      &Command::SendKeys(_) => true,
//      _ => false
//    }).collect();
//
//    assert_eq!(remains.len(), 2);
//    assert_eq!(remains[0].exec, "vim");
//    assert_eq!(remains[1].exec, "guard")
//}
<|endoftext|>


<commit_before>use calx::color::*;
use calx::{Rgba, Kernel, KernelTerrain};
use calx::backend::{Image};
use content::{TerrainType, Brush};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    Northeast,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    Northwest,
}

#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Purpose {
    Element,
    Filler,
}

impl Angle {
    /// Return the angle of the vertical surface, if any, in degrees.
    pub fn degree(&self) -> Option<f32> {
        match *self {
            Angle::Up => None,
            Angle::North => Some(0.0),
            Angle::Northeast => Some(60.0),
            Angle::Southeast => Some(120.0),
            Angle::YWall => Some(150.0),
            Angle::South => Some(180.0),
            Angle::XWall => Some(210.0),
            Angle::Southwest => Some(240.0),
            Angle::Northwest => Some(300.0),
        }
    }
}

/// Generate draw instructions for a terrain cell.
///
/// Params to the draw function: Draw layer, brush, brush frame, main
/// color, border color.
pub fn render<F>(k: &Kernel<TerrainType>, mut draw: F)
    where F: FnMut(Image, Angle, Purpose, Rgba, Rgba)
{
    use content::Brush::*;
    use self::Angle::*;
    use self::Purpose::*;

    enum T {
        Floor(Brush, Rgba),
        Floor2(Brush, Rgba, Rgba),
        Prop(Brush, Rgba),
        Prop2(Brush, Rgba, Rgba),
        Wall(Brush, Rgba),
        Wall2(Brush, Rgba, Rgba),
        Block(Brush, Rgba),
        Block2(Brush, Rgba, Rgba),
        Filler(Brush),
    }

    fn process<C: KernelTerrain, F>(
        k: &Kernel<C>, draw: &mut F, kind: T)
        where F: FnMut(Image, Angle, Purpose, Rgba, Rgba)
        {
            match kind {
                T::Floor(brush, color) => process(k, draw, T::Floor2(brush, color, BLACK)),
                T::Prop(brush, color) => process(k, draw, T::Prop2(brush, color, BLACK)),
                T::Wall(brush, color) => process(k, draw, T::Wall2(brush, color, BLACK)),
                T::Block(brush, color) => process(k, draw, T::Block2(brush, color, BLACK)),

                T::Floor2(brush, color, back) => {
                    draw(brush.get(0), Up, Element, color, back);
                }
                T::Prop2(brush, color, back) => {
                    draw(brush.get(0), South, Element, color, back);
                }
                T::Wall2(brush, color, back) => {
                    let extends = k.wall_extends();
                    if extends[0] {
                        draw(brush.get(2), XWall, Element, color, back);
                    } else {
                        draw(brush.get(0), XWall, Element, color, back);
                    }
                    if extends[1] {
                        draw(brush.get(3), YWall, Element, color, back);
                    } else {
                        draw(brush.get(1), YWall, Element, color, back);
                    }
                }
                T::Block2(brush, color, back) => {
                    let faces = k.block_faces();

                    if faces[5] { draw(BlockRear.get(0), Northwest, Element, color, BLACK); }
                    if faces[0] {
                        draw(BlockRear.get(1), North, Element, color, BLACK);
                        draw(BlockRear.get(2), North, Element, color, BLACK);
                    }
                    if faces[1] { draw(BlockRear.get(3), Northeast, Element, color, BLACK); }
                    if faces[4] { draw(brush.get(0), Southwest, Element, color, back); }
                    if faces[3] {
                        draw(brush.get(1), South, Element, color, back);
                        draw(brush.get(2), South, Element, color, back);
                    }
                    if faces[2] { draw(brush.get(3), Southeast, Element, color, back); }
                }
                T::Filler(brush) => {
                    draw(brush.get(0), Up, Filler, BLACK, BLACK);
                }
            }
        }

    for i in match k.center {
        TerrainType::Void => vec![T::Floor(BlankFloor, MAGENTA)],
        TerrainType::Floor => vec![T::Floor(Floor, SLATEGRAY)],
        TerrainType::Water => vec![T::Floor(Water, ROYALBLUE)],
        TerrainType::Shallows => vec![T::Floor(Shallows, CORNFLOWERBLUE)],
        TerrainType::Magma => vec![T::Floor2(Water, YELLOW, DARKRED)],
        TerrainType::Downstairs => vec![T::Floor(StairsDown, SLATEGRAY)],
        TerrainType::Wall => vec![
            T::Filler(BlankFloor),
            T::Wall(BrickWall, LIGHTSLATEGRAY),
        ],
        TerrainType::RockWall => vec![
            T::Filler(BlankFloor),
            T::Wall(RockWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Rock => vec![
            T::Filler(BlankFloor),
            T::Block(BlockRock, DARKGOLDENROD),
        ],
        TerrainType::Tree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
            T::Prop(TreeFoliage, GREEN),
        ],
        TerrainType::Grass => vec![T::Floor(Floor, DARKGREEN)],
        TerrainType::Grass2 => vec![ T::Floor(Grass, DARKGREEN)],
        TerrainType::Stalagmite => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stalagmite, DARKGOLDENROD),
        ],
        TerrainType::Door => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
            T::Wall(DoorWall, SADDLEBROWN),
        ],
        TerrainType::OpenDoor => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Window => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickWindowWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Table => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Fence => vec![
            // The floor type beneath the fence tile is visible, make it grass
            // if there's grass behind the fence. Otherwise make it regular
            // floor.
            if k.n == TerrainType::Grass || k.ne == TerrainType::Grass || k.nw == TerrainType::Grass {
                T::Floor(Grass, GREEN)
            } else {
                T::Floor(Floor, SLATEGRAY)
            },
            T::Wall(FenceWall, DARKGOLDENROD),
        ],
        TerrainType::Bars => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BarsWall, GAINSBORO),
        ],
        TerrainType::Fountain => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Altar => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Fountain, GAINSBORO),
        ],
        TerrainType::Barrel => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Barrell, DARKGOLDENROD),
        ],
        TerrainType::Grave => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Grave, SLATEGRAY),
        ],
        TerrainType::Stone => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stone, SLATEGRAY),
        ],
        TerrainType::Menhir => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Menhir, SLATEGRAY),
        ],
        TerrainType::DeadTree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
        ],
    }.into_iter() {
        process(k, &mut draw, i);
    }
}
<commit_msg>New block formatter<commit_after>use calx::color::*;
use calx::{Rgba, Kernel, KernelTerrain};
use calx::backend::{Image};
use content::{TerrainType, Brush};

/// Surface angle for a visible sprite, used for dynamic lighting.
///
/// ```notrust
///
///      # north #
///     n         n
///    w           e
///
///  # x_         _y #
///   s  -x_   _y-  s
///    w    -*-    e
///       y-   -x
///      # south #
/// ```
#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Angle {
    Up,
    North,
    XWallBack,
    Northeast,
    East,
    Southeast,
    YWall,
    South,
    XWall,
    Southwest,
    West,
    Northwest,
    YWallBack,
}

#[derive(Copy, Eq, PartialEq, Clone, Debug)]
pub enum Purpose {
    Element,
    Filler,
}

impl Angle {
    /// Return the angle of the vertical surface, if any, in degrees.
    pub fn degree(&self) -> Option<f32> {
        match *self {
            Angle::Up => None,
            Angle::North => Some(0.0),
            Angle::XWallBack => Some(30.0),
            Angle::Northeast => Some(60.0),
            Angle::East => Some(90.0),
            Angle::Southeast => Some(120.0),
            Angle::YWall => Some(150.0),
            Angle::South => Some(180.0),
            Angle::XWall => Some(210.0),
            Angle::Southwest => Some(240.0),
            Angle::West => Some(270.0),
            Angle::Northwest => Some(300.0),
            Angle::YWallBack => Some(330.0),
        }
    }
}

/// Generate draw instructions for a terrain cell.
///
/// Params to the draw function: Draw layer, brush, brush frame, main
/// color, border color.
pub fn render<F>(k: &Kernel<TerrainType>, mut draw: F)
    where F: FnMut(Image, Angle, Purpose, Rgba, Rgba)
{
    use content::Brush::*;
    use self::Angle::*;
    use self::Purpose::*;

    enum T {
        Floor(Brush, Rgba),
        Floor2(Brush, Rgba, Rgba),
        Prop(Brush, Rgba),
        Prop2(Brush, Rgba, Rgba),
        Wall(Brush, Rgba),
        Wall2(Brush, Rgba, Rgba),
        Block(Brush, Rgba),
        Block2(Brush, Rgba, Rgba),
        Filler(Brush),
    }

    fn process<C: KernelTerrain, F>(
        k: &Kernel<C>, draw: &mut F, kind: T)
        where F: FnMut(Image, Angle, Purpose, Rgba, Rgba)
        {
            match kind {
                T::Floor(brush, color) => process(k, draw, T::Floor2(brush, color, BLACK)),
                T::Prop(brush, color) => process(k, draw, T::Prop2(brush, color, BLACK)),
                T::Wall(brush, color) => process(k, draw, T::Wall2(brush, color, BLACK)),
                T::Block(brush, color) => process(k, draw, T::Block2(brush, color, BLACK)),

                T::Floor2(brush, color, back) => {
                    draw(brush.get(0), Up, Element, color, back);
                }
                T::Prop2(brush, color, back) => {
                    draw(brush.get(0), South, Element, color, back);
                }
                T::Wall2(brush, color, back) => {
                    let extends = k.wall_extends();
                    if extends[0] {
                        draw(brush.get(2), XWall, Element, color, back);
                    } else {
                        draw(brush.get(0), XWall, Element, color, back);
                    }
                    if extends[1] {
                        draw(brush.get(3), YWall, Element, color, back);
                    } else {
                        draw(brush.get(1), YWall, Element, color, back);
                    }
                }
                T::Block2(brush, color, back) => {

                    // This part gets a little tricky. Basic idea is that
                    // there's an inner pointy-top hex core and the block hull
                    // will snap to that instead of the outer flat-top hex
                    // edge if neither adjacent face to the outer hex vertex
                    // is connected to another block.
                    //
                    // Based on how the sprites split up, the processing is
                    // done in four vertical segments.

                    let faces = k.block_faces();

                    // Do we snap to the outer vertices?
                    let ne_vertex = !faces[0] || !faces[1];
                    let e_vertex  = !faces[1] || !faces[2];
                    let se_vertex = !faces[2] || !faces[3];
                    let sw_vertex = !faces[3] || !faces[4];
                    let w_vertex  = !faces[4] || !faces[5];
                    let nw_vertex = !faces[5] || !faces[0];

                    // Segment 2, middle left
                    {
                        if faces[0] {
                            if nw_vertex && ne_vertex {
                                draw(BlockRear.get(1), North, Element, color, BLACK);
                            } else if nw_vertex {
                                draw(BlockRear.get(9), XWallBack, Element, color, BLACK);
                            } else {
                                draw(BlockRear.get(5), YWallBack, Element, color, BLACK);
                            }
                        }
                        if faces[3] {
                            if sw_vertex && se_vertex {
                                draw(brush.get(1), South, Element, color, back);
                            } else if sw_vertex {
                                draw(brush.get(5), YWall, Element, color, back);
                            } else {
                                draw(brush.get(9), XWall, Element, color, back);
                            }
                        }
                    }

                    // Segment 3, middle right
                    {
                        if faces[0] {
                            if ne_vertex && nw_vertex {
                                draw(BlockRear.get(2), North, Element, color, BLACK);
                            } else if ne_vertex {
                                draw(BlockRear.get(6), YWallBack, Element, color, BLACK);
                            } else {
                                draw(BlockRear.get(10), XWallBack, Element, color, BLACK);
                            }
                        }
                        if faces[3] {
                            if se_vertex && sw_vertex {
                                draw(brush.get(2), South, Element, color, back);
                            } else if se_vertex {
                                draw(brush.get(10), XWall, Element, color, back);
                            } else {
                                draw(brush.get(6), YWall, Element, color, back);
                            }
                        }
                    }

                    // The side segments need to come after the middle
                    // segments so that the vertical edges can overwrite the
                    // middle segment pixels.

                    // Segment 1, left edge
                    {
                        if w_vertex {
                            if faces[5] {
                                if nw_vertex {
                                    draw(BlockRear.get(0), Northwest, Element, color, BLACK);
                                } else {
                                    draw(BlockRear.get(4), YWallBack, Element, color, BLACK);
                                }
                            }

                            if faces[4] {
                                if sw_vertex {
                                    draw(brush.get(0), Southwest, Element, color, back);
                                } else {
                                    draw(brush.get(8), XWall, Element, color, back);
                                }
                            }
                        } else {
                            // Draw the left vertical line.
                            draw(BlockVertical.get(2), West, Element, color, BLACK);
                            if !faces[0] {
                                draw(BlockVertical.get(0), West, Element, color, BLACK);
                            }
                            if !faces[3] {
                                draw(BlockVertical.get(4), West, Element, color, BLACK);
                            }
                        }
                    }

                    // Segment 4, right edge
                    {
                        if e_vertex {
                            if faces[1] {
                                if ne_vertex {
                                    draw(BlockRear.get(3), Northeast, Element, color, BLACK);
                                } else {
                                    draw(BlockRear.get(11), XWallBack, Element, color, BLACK);
                                }
                            }

                            if faces[2] {
                                if se_vertex {
                                    draw(brush.get(3), Southeast, Element, color, back);
                                } else {
                                    draw(brush.get(7), YWall, Element, color, back);
                                }
                            }
                        } else {
                            // Draw the right vertical line.
                            draw(BlockVertical.get(3), East, Element, color, BLACK);
                            if !faces[0] {
                                draw(BlockVertical.get(1), East, Element, color, BLACK);
                            }
                            if !faces[3] {
                                draw(BlockVertical.get(5), East, Element, color, BLACK);
                            }
                        }
                    }
                }
                T::Filler(brush) => {
                    draw(brush.get(0), Up, Filler, BLACK, BLACK);
                }
            }
        }

    for i in match k.center {
        TerrainType::Void => vec![T::Floor(BlankFloor, MAGENTA)],
        TerrainType::Floor => vec![T::Floor(Floor, SLATEGRAY)],
        TerrainType::Water => vec![T::Floor(Water, ROYALBLUE)],
        TerrainType::Shallows => vec![T::Floor(Shallows, CORNFLOWERBLUE)],
        TerrainType::Magma => vec![T::Floor2(Water, YELLOW, DARKRED)],
        TerrainType::Downstairs => vec![T::Floor(StairsDown, SLATEGRAY)],
        TerrainType::Wall => vec![
            T::Filler(BlankFloor),
            T::Wall(BrickWall, LIGHTSLATEGRAY),
        ],
        TerrainType::RockWall => vec![
            T::Filler(BlankFloor),
            T::Wall(RockWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Rock => vec![
            T::Filler(BlankFloor),
            T::Block(BlockRock, DARKGOLDENROD),
        ],
        TerrainType::Tree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
            T::Prop(TreeFoliage, GREEN),
        ],
        TerrainType::Grass => vec![T::Floor(Floor, DARKGREEN)],
        TerrainType::Grass2 => vec![ T::Floor(Grass, DARKGREEN)],
        TerrainType::Stalagmite => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stalagmite, DARKGOLDENROD),
        ],
        TerrainType::Door => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
            T::Wall(DoorWall, SADDLEBROWN),
        ],
        TerrainType::OpenDoor => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickOpenWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Window => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BrickWindowWall, LIGHTSLATEGRAY),
        ],
        TerrainType::Table => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Fence => vec![
            // The floor type beneath the fence tile is visible, make it grass
            // if there's grass behind the fence. Otherwise make it regular
            // floor.
            if k.n == TerrainType::Grass || k.ne == TerrainType::Grass || k.nw == TerrainType::Grass {
                T::Floor(Grass, GREEN)
            } else {
                T::Floor(Floor, SLATEGRAY)
            },
            T::Wall(FenceWall, DARKGOLDENROD),
        ],
        TerrainType::Bars => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Wall(BarsWall, GAINSBORO),
        ],
        TerrainType::Fountain => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Table, DARKGOLDENROD),
        ],
        TerrainType::Altar => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Fountain, GAINSBORO),
        ],
        TerrainType::Barrel => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Barrell, DARKGOLDENROD),
        ],
        TerrainType::Grave => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Grave, SLATEGRAY),
        ],
        TerrainType::Stone => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Stone, SLATEGRAY),
        ],
        TerrainType::Menhir => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(Menhir, SLATEGRAY),
        ],
        TerrainType::DeadTree => vec![
            T::Floor(BlankFloor, SLATEGRAY),
            T::Prop(TreeTrunk, SADDLEBROWN),
        ],
    }.into_iter() {
        process(k, &mut draw, i);
    }
}
<|endoftext|>


<commit_before>use token;

// Calculate accepts input tokens that are
// ordered according to Reverse Polish Notation
// and returns a result
pub fn calculate(input: &Vec<token::Token>) -> Option<f64> {
    let mut input = input.clone();
    let mut stack = Vec::new();
    let mut len = input.len();

    // Iterate over the tokens and calculate a result
    while len > 0 {
        let tok = input.remove(0);
        match tok {
            token::Token::DecimalNumber(n) => stack.push(token::Token::DecimalNumber(n)),
            token::Token::Operator(o, _, _) => {
                let right = stack.pop();
                let left = stack.pop();

                match (left, right) {
                    (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) => stack.push(token::Token::DecimalNumber(operate(o, n1, n2))),
                    _ => break
                }
            },
            token::Token::WholeNumber(n) => stack.push(token::Token::DecimalNumber(n as f64)),
            token::Token::FunctionCall(function_name) => {
                match &function_name as &str {
                    "cos" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.cos()));
                        }
                    },
                    "sin" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.sin()));
                        }
                    },
                    "tan" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.tan()));
                        }
                    },
                    "floor" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.floor()));
                        }
                    },
                    "ceil" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.ceil()));
                        }
                    },
                    "round" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.round()));
                        }
                    },
                    "trunc" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.trunc()));
                        }
                    },
                    "fract" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.fract()));
                        }
                    },
                    "pow" => {
                        let right = stack.pop();
                        let left = stack.pop();

                        if let (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) = (left, right) {
                            stack.push(token::Token::DecimalNumber(n1.powf(n2)));
                        }
                    },
                    "sqrt" => {
                        let arg = stack.pop();
                        match arg {
                            Some(token::Token::DecimalNumber(n)) => stack.push(token::Token::DecimalNumber((n as f64).sqrt())),
                            Some(token::Token::WholeNumber(n)) => stack.push(token::Token::DecimalNumber((n as f64).sqrt())),
                            _ => ()
                        }
                    },
                    "max" => {
                        let right = stack.pop();
                        let left = stack.pop();

                        if let (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) = (left, right) {
                            stack.push(token::Token::DecimalNumber(n1.max(n2)));
                        }
                    },
                    "min" => {
                        let right = stack.pop();
                        let left = stack.pop();

                        if let (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) = (left, right) {
                            stack.push(token::Token::DecimalNumber(n1.min(n2)));
                        }
                    },
                    "sum" => {
                        let values = Vec::new();

                        loop {
                            match stack.last() {
                                Some(&token::Token::DecimalNumber(n)) => {
                                    values.push(n);
                                },
                                Some(&token::Token::LeftParenthesis) => break
                                _ => ()
                            }
                        }

                        stack.push(token::Token::DecimalNumber(values.fold(0, |acc, val| acc + val).collect()));
                    },
                    _ => break
                }
            },
            _ => ()
        }
        len = input.len();
    }

    let result = stack.pop();

    match result {
        Some(token::Token::DecimalNumber(n)) => Some(n),
        _ => None
    }
}

fn operate(operator: char, left: f64, right: f64) -> f64 {
    match operator {
        '+' => left + right,
        '-' => left - right,
        '*' => left * right,
        '/' => left / right,
        '^' => left.powf(right),
        _ => 0f64
    }
}
<commit_msg>Added a sum() function<commit_after>use token;

// Calculate accepts input tokens that are
// ordered according to Reverse Polish Notation
// and returns a result
pub fn calculate(input: &Vec<token::Token>) -> Option<f64> {
    let mut input = input.clone();
    let mut stack = Vec::new();
    let mut len = input.len();

    // Iterate over the tokens and calculate a result
    while len > 0 {
        let tok = input.remove(0);
        match tok {
            token::Token::DecimalNumber(n) => stack.push(token::Token::DecimalNumber(n)),
            token::Token::Operator(o, _, _) => {
                let right = stack.pop();
                let left = stack.pop();

                match (left, right) {
                    (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) => stack.push(token::Token::DecimalNumber(operate(o, n1, n2))),
                    _ => break
                }
            },
            token::Token::WholeNumber(n) => stack.push(token::Token::DecimalNumber(n as f64)),
            token::Token::FunctionCall(function_name) => {
                match &function_name as &str {
                    "cos" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.cos()));
                        }
                    },
                    "sin" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.sin()));
                        }
                    },
                    "tan" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.tan()));
                        }
                    },
                    "floor" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.floor()));
                        }
                    },
                    "ceil" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.ceil()));
                        }
                    },
                    "round" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.round()));
                        }
                    },
                    "trunc" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.trunc()));
                        }
                    },
                    "fract" => {
                        let arg = stack.pop();

                        if let Some(token::Token::DecimalNumber(n1)) = arg {
                            stack.push(token::Token::DecimalNumber(n1.fract()));
                        }
                    },
                    "pow" => {
                        let right = stack.pop();
                        let left = stack.pop();

                        if let (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) = (left, right) {
                            stack.push(token::Token::DecimalNumber(n1.powf(n2)));
                        }
                    },
                    "sqrt" => {
                        let arg = stack.pop();
                        match arg {
                            Some(token::Token::DecimalNumber(n)) => stack.push(token::Token::DecimalNumber((n as f64).sqrt())),
                            Some(token::Token::WholeNumber(n)) => stack.push(token::Token::DecimalNumber((n as f64).sqrt())),
                            _ => ()
                        }
                    },
                    "max" => {
                        let right = stack.pop();
                        let left = stack.pop();

                        if let (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) = (left, right) {
                            stack.push(token::Token::DecimalNumber(n1.max(n2)));
                        }
                    },
                    "min" => {
                        let right = stack.pop();
                        let left = stack.pop();

                        if let (Some(token::Token::DecimalNumber(n1)), Some(token::Token::DecimalNumber(n2))) = (left, right) {
                            stack.push(token::Token::DecimalNumber(n1.min(n2)));
                        }
                    },
                    "sum" => {
                        let mut values = Vec::new();

                        loop {
                            match stack.last() {
                                Some(&token::Token::DecimalNumber(n)) => {
                                    values.push(n);
                                    stack.pop();
                                },
                                _ => break
                            }
                        }

                        stack.push(token::Token::DecimalNumber(values.iter().fold(0.0, |acc, val| acc + val)));
                    },
                    _ => break
                }
            },
            _ => ()
        }
        len = input.len();
    }

    let result = stack.pop();

    match result {
        Some(token::Token::DecimalNumber(n)) => Some(n),
        _ => None
    }
}

fn operate(operator: char, left: f64, right: f64) -> f64 {
    match operator {
        '+' => left + right,
        '-' => left - right,
        '*' => left * right,
        '/' => left / right,
        '^' => left.powf(right),
        _ => 0f64
    }
}
<|endoftext|>


<commit_before>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use futures_channel::{mpsc, oneshot};
use futures_core::future::Future;
use futures_core::stream::Stream;
use futures_core::task;
use futures_core::task::Poll;
use futures_util::future::FutureExt;
use futures_util::stream::StreamExt;
use std::marker::Unpin;
use std::pin;

use Continue;
use MainContext;
use Priority;
use Source;

/// Represents a `Future` around a `glib::Source`. The future will
/// be resolved once the source has provided a value
pub struct SourceFuture<F, T> {
    create_source: Option<F>,
    source: Option<(Source, oneshot::Receiver<T>)>,
}

impl<F, T: 'static> SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceFuture`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the value provided by the source to the oneshot sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> SourceFuture<F, T> {
        SourceFuture {
            create_source: Some(create_source),
            source: None,
        }
    }
}

impl<F, T> Unpin for SourceFuture<F, T> {}

impl<F, T> Future for SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    type Output = T;

    fn poll(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<T> {
        let SourceFuture {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            assert!(
                main_context.is_owner(),
                "Spawning futures only allowed if the thread is owning the MainContext"
            );

            // Channel for sending back the Source result to our future here.
            //
            // In theory we could directly continue polling the
            // corresponding task from the Source callback,
            // however this would break at the very least
            // the g_main_current_source() API.
            let (send, recv) = oneshot::channel();

            let s = create_source(send);

            s.attach(Some(&main_context));
            *source = Some((s, recv));
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll_unpin(ctx)
        };
        #[allow(clippy::match_wild_err_arm)]
        match res {
            Poll::Ready(Err(_)) => panic!("Source sender was unexpectedly closed"),
            Poll::Ready(Ok(v)) => {
                // Get rid of the reference to the source, it triggered
                let _ = source.take();
                Poll::Ready(v)
            }
            Poll::Pending => Poll::Pending,
        }
    }
}

impl<T, F> Drop for SourceFuture<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future(value: u32) -> Box<dyn Future<Output = ()> + std::marker::Unpin + Send> {
    timeout_future_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_with_priority(
    priority: Priority,
    value: u32,
) -> Box<dyn Future<Output = ()> + std::marker::Unpin + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds(
    value: u32,
) -> Box<dyn Future<Output = ()> + std::marker::Unpin + Send> {
    timeout_future_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds_with_priority(
    priority: Priority,
    value: u32,
) -> Box<dyn Future<Output = ()> + std::marker::Unpin + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new_seconds(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future(
    pid: ::Pid,
) -> Box<dyn Future<Output = (::Pid, i32)> + std::marker::Unpin + Send> {
    child_watch_future_with_priority(::PRIORITY_DEFAULT, pid)
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future_with_priority(
    priority: Priority,
    pid: ::Pid,
) -> Box<dyn Future<Output = (::Pid, i32)> + std::marker::Unpin + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::child_watch_source_new(pid, None, priority, move |pid, code| {
            let _ = send.take().unwrap().send((pid, code));
        })
    }))
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future(signum: i32) -> Box<dyn Future<Output = ()> + std::marker::Unpin + Send> {
    unix_signal_future_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future_with_priority(
    priority: Priority,
    signum: i32,
) -> Box<dyn Future<Output = ()> + std::marker::Unpin + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::unix_signal_source_new(signum, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Represents a `Stream` around a `glib::Source`. The stream will
/// be provide all values that are provided by the source
pub struct SourceStream<F, T> {
    create_source: Option<F>,
    source: Option<(Source, mpsc::UnboundedReceiver<T>)>,
}

impl<F, T> Unpin for SourceStream<F, T> {}

impl<F, T: 'static> SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceStream`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the values provided by the source to the sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> SourceStream<F, T> {
        SourceStream {
            create_source: Some(create_source),
            source: None,
        }
    }
}

impl<F, T> Stream for SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    type Item = T;

    fn poll_next(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<Option<T>> {
        let SourceStream {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            assert!(
                main_context.is_owner(),
                "Spawning futures only allowed if the thread is owning the MainContext"
            );

            // Channel for sending back the Source result to our future here.
            //
            // In theory we could directly continue polling the
            // corresponding task from the Source callback,
            // however this would break at the very least
            // the g_main_current_source() API.
            let (send, recv) = mpsc::unbounded();

            let s = create_source(send);

            s.attach(Some(&main_context));
            *source = Some((s, recv));
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll_next_unpin(ctx)
        };
        #[allow(clippy::match_wild_err_arm)]
        match res {
            Poll::Ready(v) => {
                if v.is_none() {
                    // Get rid of the reference to the source, it triggered
                    let _ = source.take();
                }
                Poll::Ready(v)
            }
            Poll::Pending => Poll::Pending,
        }
    }
}

impl<T, F> Drop for SourceStream<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream(value: u32) -> Box<dyn Stream<Item = ()> + std::marker::Unpin + Send> {
    interval_stream_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_with_priority(
    priority: Priority,
    value: u32,
) -> Box<dyn Stream<Item = ()> + std::marker::Unpin + Send> {
    Box::new(SourceStream::new(move |send| {
        ::timeout_source_new(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds(
    value: u32,
) -> Box<dyn Stream<Item = ()> + std::marker::Unpin + Send> {
    interval_stream_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds_with_priority(
    priority: Priority,
    value: u32,
) -> Box<dyn Stream<Item = ()> + std::marker::Unpin + Send> {
    Box::new(SourceStream::new(move |send| {
        ::timeout_source_new_seconds(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream(signum: i32) -> Box<dyn Stream<Item = ()> + std::marker::Unpin + Send> {
    unix_signal_stream_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream_with_priority(
    priority: Priority,
    signum: i32,
) -> Box<dyn Stream<Item = ()> + std::marker::Unpin + Send> {
    Box::new(SourceStream::new(move |send| {
        ::unix_signal_source_new(signum, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

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

    #[test]
    fn test_timeout() {
        let c = MainContext::new();

        let res = c.block_on(timeout_future(20));

        assert_eq!(res, ());
    }

    #[test]
    fn test_timeout_send() {
        let c = MainContext::new();
        let l = ::MainLoop::new(Some(&c), false);

        let l_clone = l.clone();
        c.spawn(timeout_future(20).then(move |()| {
            l_clone.quit();
            futures_util::future::ready(())
        }));

        l.run();
    }

    #[test]
    fn test_interval() {
        let c = MainContext::new();

        let mut count = 0;

        {
            let count = &mut count;
            let res = c.block_on(
                interval_stream(20)
                    .take(2)
                    .for_each(|()| {
                        *count = *count + 1;

                        futures_util::future::ready(())
                    })
                    .map(|_| ()),
            );

            assert_eq!(res, ());
        }

        assert_eq!(count, 2);
    }

    #[test]
    fn test_timeout_and_channel() {
        let c = MainContext::default();

        let res = c.block_on(timeout_future(20).then(|()| {
            let (sender, receiver) = oneshot::channel();

            thread::spawn(move || {
                sender.send(1).unwrap();
            });

            receiver.then(|i| futures_util::future::ready(i.unwrap()))
        }));

        assert_eq!(res, 1);
    }
}
<commit_msg>Return Pin<Box<_>> instead of Box<_> for our futures/streams<commit_after>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use futures_channel::{mpsc, oneshot};
use futures_core::future::Future;
use futures_core::stream::Stream;
use futures_core::task;
use futures_core::task::Poll;
use futures_util::future::FutureExt;
use futures_util::stream::StreamExt;
use std::marker::Unpin;
use std::pin;
use std::pin::Pin;

use Continue;
use MainContext;
use Priority;
use Source;

/// Represents a `Future` around a `glib::Source`. The future will
/// be resolved once the source has provided a value
pub struct SourceFuture<F, T> {
    create_source: Option<F>,
    source: Option<(Source, oneshot::Receiver<T>)>,
}

impl<F, T: 'static> SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceFuture`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the value provided by the source to the oneshot sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> SourceFuture<F, T> {
        SourceFuture {
            create_source: Some(create_source),
            source: None,
        }
    }
}

impl<F, T> Unpin for SourceFuture<F, T> {}

impl<F, T> Future for SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    type Output = T;

    fn poll(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<T> {
        let SourceFuture {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            assert!(
                main_context.is_owner(),
                "Spawning futures only allowed if the thread is owning the MainContext"
            );

            // Channel for sending back the Source result to our future here.
            //
            // In theory we could directly continue polling the
            // corresponding task from the Source callback,
            // however this would break at the very least
            // the g_main_current_source() API.
            let (send, recv) = oneshot::channel();

            let s = create_source(send);

            s.attach(Some(&main_context));
            *source = Some((s, recv));
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll_unpin(ctx)
        };
        #[allow(clippy::match_wild_err_arm)]
        match res {
            Poll::Ready(Err(_)) => panic!("Source sender was unexpectedly closed"),
            Poll::Ready(Ok(v)) => {
                // Get rid of the reference to the source, it triggered
                let _ = source.take();
                Poll::Ready(v)
            }
            Poll::Pending => Poll::Pending,
        }
    }
}

impl<T, F> Drop for SourceFuture<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future(value: u32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
    timeout_future_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_with_priority(
    priority: Priority,
    value: u32,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
    Box::pin(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds(value: u32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
    timeout_future_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds_with_priority(
    priority: Priority,
    value: u32,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
    Box::pin(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new_seconds(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future(
    pid: ::Pid,
) -> Pin<Box<dyn Future<Output = (::Pid, i32)> + Send + 'static>> {
    child_watch_future_with_priority(::PRIORITY_DEFAULT, pid)
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future_with_priority(
    priority: Priority,
    pid: ::Pid,
) -> Pin<Box<dyn Future<Output = (::Pid, i32)> + Send + 'static>> {
    Box::pin(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::child_watch_source_new(pid, None, priority, move |pid, code| {
            let _ = send.take().unwrap().send((pid, code));
        })
    }))
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future(signum: i32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
    unix_signal_future_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future_with_priority(
    priority: Priority,
    signum: i32,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
    Box::pin(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::unix_signal_source_new(signum, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Represents a `Stream` around a `glib::Source`. The stream will
/// be provide all values that are provided by the source
pub struct SourceStream<F, T> {
    create_source: Option<F>,
    source: Option<(Source, mpsc::UnboundedReceiver<T>)>,
}

impl<F, T> Unpin for SourceStream<F, T> {}

impl<F, T: 'static> SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceStream`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the values provided by the source to the sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> SourceStream<F, T> {
        SourceStream {
            create_source: Some(create_source),
            source: None,
        }
    }
}

impl<F, T> Stream for SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    type Item = T;

    fn poll_next(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<Option<T>> {
        let SourceStream {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            assert!(
                main_context.is_owner(),
                "Spawning futures only allowed if the thread is owning the MainContext"
            );

            // Channel for sending back the Source result to our future here.
            //
            // In theory we could directly continue polling the
            // corresponding task from the Source callback,
            // however this would break at the very least
            // the g_main_current_source() API.
            let (send, recv) = mpsc::unbounded();

            let s = create_source(send);

            s.attach(Some(&main_context));
            *source = Some((s, recv));
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll_next_unpin(ctx)
        };
        #[allow(clippy::match_wild_err_arm)]
        match res {
            Poll::Ready(v) => {
                if v.is_none() {
                    // Get rid of the reference to the source, it triggered
                    let _ = source.take();
                }
                Poll::Ready(v)
            }
            Poll::Pending => Poll::Pending,
        }
    }
}

impl<T, F> Drop for SourceStream<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream(value: u32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
    interval_stream_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_with_priority(
    priority: Priority,
    value: u32,
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
    Box::pin(SourceStream::new(move |send| {
        ::timeout_source_new(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds(value: u32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
    interval_stream_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds_with_priority(
    priority: Priority,
    value: u32,
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
    Box::pin(SourceStream::new(move |send| {
        ::timeout_source_new_seconds(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream(signum: i32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
    unix_signal_stream_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream_with_priority(
    priority: Priority,
    signum: i32,
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
    Box::pin(SourceStream::new(move |send| {
        ::unix_signal_source_new(signum, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

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

    #[test]
    fn test_timeout() {
        let c = MainContext::new();

        let res = c.block_on(timeout_future(20));

        assert_eq!(res, ());
    }

    #[test]
    fn test_timeout_send() {
        let c = MainContext::new();
        let l = ::MainLoop::new(Some(&c), false);

        let l_clone = l.clone();
        c.spawn(timeout_future(20).then(move |()| {
            l_clone.quit();
            futures_util::future::ready(())
        }));

        l.run();
    }

    #[test]
    fn test_interval() {
        let c = MainContext::new();

        let mut count = 0;

        {
            let count = &mut count;
            let res = c.block_on(
                interval_stream(20)
                    .take(2)
                    .for_each(|()| {
                        *count = *count + 1;

                        futures_util::future::ready(())
                    })
                    .map(|_| ()),
            );

            assert_eq!(res, ());
        }

        assert_eq!(count, 2);
    }

    #[test]
    fn test_timeout_and_channel() {
        let c = MainContext::default();

        let res = c.block_on(timeout_future(20).then(|()| {
            let (sender, receiver) = oneshot::channel();

            thread::spawn(move || {
                sender.send(1).unwrap();
            });

            receiver.then(|i| futures_util::future::ready(i.unwrap()))
        }));

        assert_eq!(res, 1);
    }
}
<|endoftext|>