<commit_before>extern crate orbclient;
extern crate orbfont;
extern crate orbimage;
extern crate starship;

use orbclient::{Color, EventOption, Window, K_UP, K_DOWN, K_DEL, K_ESC, K_F1, K_F2, K_F5, K_F6};
use orbfont::Font;
use orbimage::Image;

use starship::block::{Block, BlockResource};

use std::collections::BTreeMap;
use std::cmp::max;
use std::fs;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

fn main(){
    let mut window = Window::new_flags(100, 100, 1024, 768, "Frontier", true).unwrap();
    let font = Font::from_path("res/FiraMono-Regular.ttf").unwrap();

    let ship_lock = Arc::new(Mutex::new(starship::load("res/ship.json").unwrap()));

    let mut block_kinds: BTreeMap<String, Image> = BTreeMap::new();
    for entry_result in fs::read_dir("res/blocks/").unwrap() {
        let entry = entry_result.unwrap();
        let path = entry.path();
        if path.is_dir() {
            let mut image_path = path.clone();
            image_path.push("image.png");
            if image_path.is_file() {
                block_kinds.insert(entry.file_name().into_string().unwrap(), Image::from_path(&image_path).unwrap());
            }
        }
    }

    assert!(ship_lock.lock().unwrap().decks.len() > 0);

    let running = Arc::new(AtomicBool::new(true));
    let redraw = Arc::new(AtomicBool::new(true));

    let running_update = running.clone();
    let redraw_update = redraw.clone();
    let ship_update = ship_lock.clone();
    let handle = thread::spawn(move || {
        while running_update.load(Ordering::SeqCst) {
            {
                let mut ship = ship_update.lock().unwrap();
                if ship.update() {
                    redraw_update.store(true, Ordering::SeqCst);
                }
            }
            thread::sleep(Duration::from_millis(10));
        }
    });

    let mut dragging = None;
    let mut editing = None;
    let mut show_info = false;

    while running.load(Ordering::SeqCst) {
        {
            let mut ship = ship_lock.lock().unwrap();

            if redraw.load(Ordering::SeqCst) {
                redraw.store(false, Ordering::SeqCst);

                let window_w = window.width();
                //let window_h = window.height();

                let deck = &ship.decks[ship.current_deck];

                window.set(Color::rgb(255, 255, 255));

                let title = font.render(&format!("{} - {} - {}", ship.name, ship.current_deck, deck.name), 24.0);
                let title_x = (window_w - title.width()) as i32/2;
                title.draw(&mut window, title_x, 0, Color::rgb(0, 0, 0));

                window.rect(0, 26, window_w, 2, Color::rgb(0, 0, 0));

                for block in deck.blocks.iter() {
                    let x = block.x as i32 * 32;
                    let y = block.y as i32 * 32 + 32;
                    if let Some(image) = block_kinds.get(&block.kind) {
                        image.draw(&mut window, x, y);
                    } else {
                        window.rect(x, y, 32, 32, Color::rgb(128, 128, 128));
                        font.render(&block.kind, 16.0).draw(&mut window, x, y, Color::rgb(0, 0, 0));
                    }

                    if show_info {
                        if block.kind == "Tank" {
                            if let Some((name, resource)) = block.resources.iter().next() {
                                let text = font.render(name, 16.0);
                                text.draw(&mut window, x, y, Color::rgb(0, 0, 0));

                                let text = font.render(&format!("{}", resource.amount as u32), 16.0);
                                text.draw(&mut window, x, y + 16, Color::rgb(0, 0, 0));
                            }
                        } else if block.kind == "Vent" {
                            if let Some(resource) = block.resources.get("air") {
                                let text = font.render(&format!("{}", resource.amount as u32), 16.0);
                                text.draw(&mut window, x, y, Color::rgb(0, 0, 0));
                            }
                            if let Some(resource) = block.resources.get("free_air") {
                                let text = font.render(&format!("{}", resource.amount as u32), 16.0);
                                text.draw(&mut window, x, y + 16, Color::rgb(0, 0, 0));
                            }
                        } else if block.kind == "Deck" || block.kind == "Man" {
                            if let Some(resource) = block.resources.get("free_air") {
                                let text = font.render(&format!("{}", resource.amount as u32), 16.0);
                                text.draw(&mut window, x, y + 16, Color::rgb(0, 0, 0));
                            }
                        }
                    }
                }

                if let Some(i) = dragging {
                    if let Some(block) = deck.blocks.get(i) {
                        let x = block.x as i32 * 32;
                        let y = block.y as i32 * 32 + 32;

                        window.rect(x, y, 32, 2, Color::rgb(255, 0, 0));
                        window.rect(x, y, 2, 32, Color::rgb(255, 0, 0));
                        window.rect(x, y + 30, 32, 2, Color::rgb(255, 0, 0));
                        window.rect(x + 30, y, 2, 32, Color::rgb(255, 0, 0));
                    }
                }

                if let Some((block_x, block_y)) = editing {
                    let x = block_x as i32 * 32;
                    let y = block_y as i32 * 32 + 32;

                    window.rect(x, y, 32, 2, Color::rgb(0, 0, 255));
                    window.rect(x, y, 2, 32, Color::rgb(0, 0, 255));
                    window.rect(x, y + 30, 32, 2, Color::rgb(0, 0, 255));
                    window.rect(x + 30, y, 2, 32, Color::rgb(0, 0, 255));
                }

                window.sync();
            }

            let mut deck_i = ship.current_deck;
            let mut reload = false;
            let mut save = false;
            {
                let deck_len = ship.decks.len();
                let deck = &mut ship.decks[deck_i];

                let mut evented = true;
                while evented {
                    evented = false;

                    for event in window.events() {
                        evented = true;

                        match event.to_option() {
                            EventOption::Key(key_event) => if key_event.pressed {
                                match key_event.scancode {
                                    K_UP => if deck_i + 1 < deck_len {
                                        deck_i += 1;
                                    },
                                    K_DOWN => if deck_i > 0 {
                                        deck_i -= 1;
                                    },
                                    K_DEL => if let Some((block_x, block_y)) = editing.take() {
                                        let mut remove = None;
                                        for (i, block) in deck.blocks.iter().enumerate() {
                                            if block_x == block.x && block_y == block.y {
                                                remove = Some(i);
                                            }
                                        }
                                        if let Some(i) = remove {
                                            deck.blocks.remove(i);
                                            redraw.store(true, Ordering::SeqCst);
                                        }
                                    },
                                    K_ESC => if editing.take().is_some() {
                                        redraw.store(true, Ordering::SeqCst);
                                    },
                                    K_F1 => if ! show_info {
                                        show_info = true;
                                        redraw.store(true, Ordering::SeqCst);
                                    },
                                    K_F2 => if show_info {
                                        show_info = false;
                                        redraw.store(true, Ordering::SeqCst);
                                    },
                                    K_F5 => reload = true,
                                    K_F6 => save = true,
                                    _ => {
                                        match key_event.character {
                                            'A' | 'a' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("air".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'E' | 'e' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("electricity".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'F' | 'f' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("fuel".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'W' | 'w' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("water".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'C' | 'c' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Conduit".to_string());
                                                block.resources.insert("air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("electricity".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("fuel".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("water".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'D' | 'd' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Deck".to_string());
                                                block.resources.insert("free_air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'H' | 'h' => if let Some((block_x, block_y)) = editing.take() {
                                                deck.blocks.push(Block::new(block_x, block_y, "Hull".to_string()));
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'M' | 'm' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Man".to_string());
                                                block.resources.insert("free_air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'V' | 'v' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Vent".to_string());
                                                block.resources.insert("air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("free_air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            _ => ()
                                        }
                                    }
                                }
                            },
                            EventOption::Mouse(mouse_event) => {
                                if mouse_event.left_button {
                                    if let Some(i) = dragging {
                                        let x = max(mouse_event.x/32, 0) as usize;
                                        let y = max((mouse_event.y - 32)/32, 0) as usize;

                                        let block = &mut deck.blocks[i];
                                        if block.x != x || block.y != y {
                                            block.x = x;
                                            block.y = y;
                                            redraw.store(true, Ordering::SeqCst);
                                        }
                                    } else {
                                        println!("Left {}, {}", mouse_event.x, mouse_event.y);

                                        for (i, block) in deck.blocks.iter().enumerate() {
                                            let x = block.x as i32 * 32;
                                            let y = block.y as i32 * 32 + 32;
                                            if mouse_event.x >= x && mouse_event.x < x + 32 && mouse_event.y >= y && mouse_event.y < y + 32 {
                                                dragging = Some(i);
                                                redraw.store(true, Ordering::SeqCst);
                                                println!("    {:?}", block);
                                            }
                                        }
                                    }
                                } else {
                                    if dragging.take().is_some() {
                                        redraw.store(true, Ordering::SeqCst);
                                    }
                                }

                                if mouse_event.right_button {
                                    let x = max(mouse_event.x/32, 0) as usize;
                                    let y = max((mouse_event.y - 32)/32, 0) as usize;

                                    if editing != Some((x, y)) {
                                        println!("Right {}, {}", mouse_event.x, mouse_event.y);
                                        editing = Some((x, y));
                                        redraw.store(true, Ordering::SeqCst);

                                        for block in deck.blocks.iter() {
                                            let x = block.x as i32 * 32;
                                            let y = block.y as i32 * 32 + 32;
                                            if mouse_event.x >= x && mouse_event.x < x + 32 && mouse_event.y >= y && mouse_event.y < y + 32 {
                                                println!("    {:?}", block);
                                            }
                                        }
                                    }
                                }
                            },
                            EventOption::Quit(_quit_event) => running.store(false, Ordering::SeqCst),
                            _ => ()
                        }
                    }
                }
            }

            if deck_i != ship.current_deck {
                ship.current_deck = deck_i;
                redraw.store(true, Ordering::SeqCst);
            }

            if save {
                println!("Save");
                starship::save("res/ship.json", &*ship).unwrap();
            }

            if reload {
                println!("Reload");
                *ship = starship::load("res/ship.json").unwrap();
                redraw.store(true, Ordering::SeqCst);
            }
        }

        thread::sleep(Duration::from_millis(10));
    }

    handle.join().unwrap();
}
<commit_msg>Better info display<commit_after>extern crate orbclient;
extern crate orbfont;
extern crate orbimage;
extern crate starship;

use orbclient::{Color, EventOption, Window, K_UP, K_DOWN, K_DEL, K_ESC, K_F1, K_F2, K_F5, K_F6};
use orbfont::Font;
use orbimage::Image;

use starship::block::{Block, BlockResource};

use std::borrow::Borrow;
use std::collections::BTreeMap;
use std::cmp::max;
use std::fs;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

fn main(){
    let mut window = Window::new_flags(100, 100, 1024, 768, "Frontier", true).unwrap();
    let font = Font::from_path("res/FiraMono-Regular.ttf").unwrap();

    let ship_lock = Arc::new(Mutex::new(starship::load("res/ship.json").unwrap()));

    let mut block_kinds: BTreeMap<String, Image> = BTreeMap::new();
    for entry_result in fs::read_dir("res/blocks/").unwrap() {
        let entry = entry_result.unwrap();
        let path = entry.path();
        if path.is_dir() {
            let mut image_path = path.clone();
            image_path.push("image.png");
            if image_path.is_file() {
                block_kinds.insert(entry.file_name().into_string().unwrap(), Image::from_path(&image_path).unwrap());
            }
        }
    }

    let mut title;
    {
        let ship = ship_lock.lock().unwrap();
        assert!(ship.decks.len() > 0);
        title = font.render(&format!("{} - {} - {}", ship.name, ship.current_deck, ship.decks[ship.current_deck].name), 24.0);
    }

    let running = Arc::new(AtomicBool::new(true));
    let redraw = Arc::new(AtomicBool::new(true));

    let running_update = running.clone();
    let redraw_update = redraw.clone();
    let ship_update = ship_lock.clone();
    let handle = thread::spawn(move || {
        while running_update.load(Ordering::SeqCst) {
            {
                let mut ship = ship_update.lock().unwrap();
                if ship.update() {
                    redraw_update.store(true, Ordering::SeqCst);
                }
            }
            thread::sleep(Duration::from_millis(10));
        }
    });

    let mut dragging = None;
    let mut editing = None;
    let mut show_info = true;

    while running.load(Ordering::SeqCst) {
        {
            let mut ship = ship_lock.lock().unwrap();

            if redraw.load(Ordering::SeqCst) {
                redraw.store(false, Ordering::SeqCst);

                let window_w = window.width();
                //let window_h = window.height();

                let deck = &ship.decks[ship.current_deck];

                window.set(Color::rgb(255, 255, 255));

                let title_x = (window_w - title.width()) as i32/2;
                title.draw(&mut window, title_x, 0, Color::rgb(0, 0, 0));

                window.rect(0, 26, window_w, 2, Color::rgb(0, 0, 0));

                for block in deck.blocks.iter() {
                    let x = block.x as i32 * 32;
                    let y = block.y as i32 * 32 + 32;
                    if let Some(image) = block_kinds.get(&block.kind) {
                        image.draw(&mut window, x, y);
                    } else {
                        window.rect(x, y, 32, 32, Color::rgb(128, 128, 128));
                        font.render(&block.kind, 16.0).draw(&mut window, x, y, Color::rgb(0, 0, 0));
                    }

                    if show_info {
                        let mut info_rect = |x: i32, y: i32, name: &str, resource: &BlockResource| {
                            let color = match name {
                                "air" => Color::rgb(0, 255, 0),
                                "electricity" => Color::rgb(255, 255, 0),
                                "free_air" => Color::rgb(0, 255, 255),
                                "fuel" => Color::rgb(255, 0, 0),
                                "water" => Color::rgb(0, 0, 255),
                                _ => Color::rgb(255, 0, 255)
                            };
                            let dy = (28.0 * resource.amount/resource.capacity) as i32;
                            window.rect(x, y + 28 - dy, 4, dy as u32, color);
                        };

                        for (i, (name, resource)) in block.resources.iter().enumerate() {
                            info_rect(x + 2 + i as i32 * 6, y + 2, name.borrow(), resource);
                        }
                    }
                }

                if let Some(i) = dragging {
                    if let Some(block) = deck.blocks.get(i) {
                        let x = block.x as i32 * 32;
                        let y = block.y as i32 * 32 + 32;

                        window.rect(x, y, 32, 2, Color::rgb(255, 0, 0));
                        window.rect(x, y, 2, 32, Color::rgb(255, 0, 0));
                        window.rect(x, y + 30, 32, 2, Color::rgb(255, 0, 0));
                        window.rect(x + 30, y, 2, 32, Color::rgb(255, 0, 0));
                    }
                }

                if let Some((block_x, block_y)) = editing {
                    let x = block_x as i32 * 32;
                    let y = block_y as i32 * 32 + 32;

                    window.rect(x, y, 32, 2, Color::rgb(0, 0, 255));
                    window.rect(x, y, 2, 32, Color::rgb(0, 0, 255));
                    window.rect(x, y + 30, 32, 2, Color::rgb(0, 0, 255));
                    window.rect(x + 30, y, 2, 32, Color::rgb(0, 0, 255));
                }

                window.sync();
            }

            let mut deck_i = ship.current_deck;
            let mut reload = false;
            let mut save = false;
            {
                let deck_len = ship.decks.len();
                let deck = &mut ship.decks[deck_i];

                let mut evented = true;
                while evented {
                    evented = false;

                    for event in window.events() {
                        evented = true;

                        match event.to_option() {
                            EventOption::Key(key_event) => if key_event.pressed {
                                match key_event.scancode {
                                    K_UP => if deck_i + 1 < deck_len {
                                        deck_i += 1;
                                    },
                                    K_DOWN => if deck_i > 0 {
                                        deck_i -= 1;
                                    },
                                    K_DEL => if let Some((block_x, block_y)) = editing.take() {
                                        let mut remove = None;
                                        for (i, block) in deck.blocks.iter().enumerate() {
                                            if block_x == block.x && block_y == block.y {
                                                remove = Some(i);
                                            }
                                        }
                                        if let Some(i) = remove {
                                            deck.blocks.remove(i);
                                            redraw.store(true, Ordering::SeqCst);
                                        }
                                    },
                                    K_ESC => if editing.take().is_some() {
                                        redraw.store(true, Ordering::SeqCst);
                                    },
                                    K_F1 => if ! show_info {
                                        show_info = true;
                                        redraw.store(true, Ordering::SeqCst);
                                    },
                                    K_F2 => if show_info {
                                        show_info = false;
                                        redraw.store(true, Ordering::SeqCst);
                                    },
                                    K_F5 => reload = true,
                                    K_F6 => save = true,
                                    _ => {
                                        match key_event.character {
                                            'A' | 'a' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("air".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'E' | 'e' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("electricity".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'F' | 'f' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("fuel".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'W' | 'w' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Tank".to_string());
                                                block.resources.insert("water".into(), BlockResource { amount: 100.0, capacity: 100.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'C' | 'c' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Conduit".to_string());
                                                block.resources.insert("air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("electricity".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("fuel".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("water".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'D' | 'd' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Deck".to_string());
                                                block.resources.insert("free_air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'H' | 'h' => if let Some((block_x, block_y)) = editing.take() {
                                                deck.blocks.push(Block::new(block_x, block_y, "Hull".to_string()));
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'M' | 'm' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Man".to_string());
                                                block.resources.insert("free_air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            'V' | 'v' => if let Some((block_x, block_y)) = editing.take() {
                                                let mut block = Block::new(block_x, block_y, "Vent".to_string());
                                                block.resources.insert("air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                block.resources.insert("free_air".into(), BlockResource { amount: 0.0, capacity: 5.0 });
                                                deck.blocks.push(block);
                                                redraw.store(true, Ordering::SeqCst);
                                            },
                                            _ => ()
                                        }
                                    }
                                }
                            },
                            EventOption::Mouse(mouse_event) => {
                                if mouse_event.left_button {
                                    if let Some(i) = dragging {
                                        let x = max(mouse_event.x/32, 0) as usize;
                                        let y = max((mouse_event.y - 32)/32, 0) as usize;

                                        let block = &mut deck.blocks[i];
                                        if block.x != x || block.y != y {
                                            block.x = x;
                                            block.y = y;
                                            redraw.store(true, Ordering::SeqCst);
                                        }
                                    } else {
                                        println!("Left {}, {}", mouse_event.x, mouse_event.y);

                                        for (i, block) in deck.blocks.iter().enumerate() {
                                            let x = block.x as i32 * 32;
                                            let y = block.y as i32 * 32 + 32;
                                            if mouse_event.x >= x && mouse_event.x < x + 32 && mouse_event.y >= y && mouse_event.y < y + 32 {
                                                dragging = Some(i);
                                                redraw.store(true, Ordering::SeqCst);
                                                println!("    {:?}", block);
                                            }
                                        }
                                    }
                                } else {
                                    if dragging.take().is_some() {
                                        redraw.store(true, Ordering::SeqCst);
                                    }
                                }

                                if mouse_event.right_button {
                                    let x = max(mouse_event.x/32, 0) as usize;
                                    let y = max((mouse_event.y - 32)/32, 0) as usize;

                                    if editing != Some((x, y)) {
                                        println!("Right {}, {}", mouse_event.x, mouse_event.y);
                                        editing = Some((x, y));
                                        redraw.store(true, Ordering::SeqCst);

                                        for block in deck.blocks.iter() {
                                            let x = block.x as i32 * 32;
                                            let y = block.y as i32 * 32 + 32;
                                            if mouse_event.x >= x && mouse_event.x < x + 32 && mouse_event.y >= y && mouse_event.y < y + 32 {
                                                println!("    {:?}", block);
                                            }
                                        }
                                    }
                                }
                            },
                            EventOption::Quit(_quit_event) => running.store(false, Ordering::SeqCst),
                            _ => ()
                        }
                    }
                }
            }

            if deck_i != ship.current_deck {
                ship.current_deck = deck_i;
                title = font.render(&format!("{} - {} - {}", ship.name, ship.current_deck, ship.decks[ship.current_deck].name), 24.0);
                redraw.store(true, Ordering::SeqCst);
            }

            if save {
                println!("Save");
                starship::save("res/ship.json", &*ship).unwrap();
            }

            if reload {
                println!("Reload");
                *ship = starship::load("res/ship.json").unwrap();
                redraw.store(true, Ordering::SeqCst);
            }
        }

        thread::sleep(Duration::from_millis(10));
    }

    handle.join().unwrap();
}
<|endoftext|>


<commit_before>extern crate ansi_term;
extern crate rustyline;
extern crate shlex;
extern crate libc;
extern crate errno;

use std::env;
use std::os::unix::process::CommandExt;
use std::process::Command;

use ansi_term::Colour::Red;
use ansi_term::Colour::Green;
use rustyline::Editor;
use rustyline::error::ReadlineError;

mod jobs;
mod tools;

fn main() {
    const VERSION: &'static str = env!("CARGO_PKG_VERSION");
    println!("##### Welcome to RUSH v{} #####", VERSION);

    let user = env::var("USER").unwrap();
    let mut proc_status_ok = true;
    let mut prompt;
    let mut painter;
    let mut rl = Editor::<()>::new();
    loop {
        if proc_status_ok {painter = Green;} else {painter = Red;}
        prompt = format!("{}@{} ",
                         painter.paint(user.to_string()),
                         painter.paint("RUSH: ~$"));

        let cmd = rl.readline(&prompt);
        match cmd {
            Ok(line) => {
                if line.trim() == "exit" {
                    println!("Bye.");
                    break;
                } else if line.trim() == "" {
                    continue;
                }
                rl.add_history_entry(&line);

                let args = shlex::split(line.trim()).unwrap();
                let mut child;
                match Command::new(&args[0]).args(&(args[1..]))
                    .before_exec(
                        || {
                            unsafe {
                                let pid = libc::getpid();
                                libc::setpgid(0, pid);
                            }
                            Ok(())
                        }
                    )
                    .spawn() {
                    Ok(x) => child = x,
                    Err(e) => {
                        proc_status_ok = false;
                        println!("{:?}", e);
                        continue
                    }
                }
                unsafe {
                    let pid = child.id() as i32;
                    tools::rlog(format!("try give term to {}\n", pid));
                    jobs::give_terminal_to(pid);
                    tools::rlog(format!("waiting pid {}\n", pid));
                }
                let ecode = child.wait().unwrap();
                proc_status_ok = ecode.success();
                tools::rlog(format!("done. ok: {}\n", proc_status_ok));
                unsafe {
                    let pid = libc::getpid();
                    tools::rlog(format!("try give term to {}\n", pid));
                    jobs::give_terminal_to(pid);
                }
            },
            Err(ReadlineError::Interrupted) => {
                // Ctrl-C
                continue;
            },
            Err(ReadlineError::Eof) => {
                // Ctrl-D
                continue;
            },
            Err(err) => {
                println!("RL Error: {:?}", err);
                continue;
            }
        }
    }
}
<commit_msg>issue fix<commit_after>extern crate ansi_term;
extern crate rustyline;
extern crate shlex;
extern crate libc;
extern crate errno;

use std::env;
use std::os::unix::process::CommandExt;
use std::process::Command;
use std::thread;
use std::time::Duration;

use ansi_term::Colour::Red;
use ansi_term::Colour::Green;
use rustyline::Editor;
use rustyline::error::ReadlineError;

mod jobs;
mod tools;

fn main() {
    const VERSION: &'static str = env!("CARGO_PKG_VERSION");
    println!("##### Welcome to RUSH v{} #####", VERSION);

    let user = env::var("USER").unwrap();
    let mut proc_status_ok = true;
    let mut prompt;
    let mut painter;
    let mut rl = Editor::<()>::new();
    loop {
        if proc_status_ok {painter = Green;} else {painter = Red;}
        prompt = format!("{}@{} ",
                         painter.paint(user.to_string()),
                         painter.paint("RUSH: ~$"));

        let cmd = rl.readline(&prompt);
        match cmd {
            Ok(line) => {
                if line.trim() == "exit" {
                    println!("Bye.");
                    break;
                } else if line.trim() == "" {
                    continue;
                }
                rl.add_history_entry(&line);

                let args = shlex::split(line.trim()).unwrap();
                tools::rlog(format!("run {:?}\n", args));
                let mut child;
                match Command::new(&args[0]).args(&(args[1..]))
                    .before_exec(
                        || {
                            unsafe {
                                let pid = libc::getpid();
                                libc::setpgid(0, pid);
                            }
                            Ok(())
                        }
                    )
                    .spawn() {
                    Ok(x) => child = x,
                    Err(e) => {
                        proc_status_ok = false;
                        println!("{:?}", e);
                        continue
                    }
                }
                unsafe {
                    let pid = child.id() as i32;
                    let gid = libc::getpgid(pid);
                    thread::sleep(Duration::from_millis(1));
                    tools::rlog(format!("try give term to {}\n", gid));
                    jobs::give_terminal_to(gid);
                    tools::rlog(format!("waiting pid {}\n", gid));
                }
                let ecode = child.wait().unwrap();
                proc_status_ok = ecode.success();
                tools::rlog(format!("done. ok: {}\n", proc_status_ok));
                unsafe {
                    let gid = libc::getpgid(0);
                    tools::rlog(format!("try give term to {}\n", gid));
                    jobs::give_terminal_to(gid);
                }
            },
            Err(ReadlineError::Interrupted) => {
                // Ctrl-C
                continue;
            },
            Err(ReadlineError::Eof) => {
                // Ctrl-D
                continue;
            },
            Err(err) => {
                println!("RL Error: {:?}", err);
                continue;
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate gfx_core;
extern crate piston;
extern crate piston_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate sdl2;
extern crate sdl2_window;

extern crate gfx_graphics;
extern crate gfx;
extern crate gfx_device_gl;
extern crate music;

use std::os;
use std::path::Path;

use gfx_device_gl::{Resources, CommandBuffer};
use gfx_graphics::GfxGraphics;
use graphics::{Image, Context, clear, DrawState, rectangle};
use opengl_graphics::GlGraphics;
use piston::*;
use piston::input::*;
use piston::input::Input::*;
use piston_window::{Window, G2dTexture, WindowSettings, Events, EventLoop, EventSettings,
                    PistonWindow, Flip, TextureSettings, OpenGL};
use sdl2::mixer;
use sdl2_window::Sdl2Window;

use lightsout::{StructLevel, Level};
use number_renderer::NumberRenderer;
use game::Game;

mod lightsout;
mod number_renderer;
mod game;

// workaround for a link failure
// mod link {
//      // #[link(name="SDL2_mixer")]
//      extern {}
// }

#[derive(Debug)]
struct Env {
    mousex: f64,
    mousey: f64,
    window_width: f64,
    window_height: f64,
}

#[derive(Copy, Clone, Hash, PartialEq, Eq)]
struct Music;

#[derive(Copy, Clone, Hash, PartialEq, Eq)]
enum Sound {
    Win,
    Tick,
    Click,
    Ai,
}

#[inline(always)]
fn pt_in_rect(px: f64, py: f64, rx: f64, ry: f64, w: f64, h: f64) -> bool {
    px >= rx && px <= rx + w && py >= ry && py <= ry + h
}

fn render_level(level: &Level,
                env: &Env,
                c: &Context,
                gl: &mut GfxGraphics<Resources, CommandBuffer>) {
    let (sx, sy) = level.size();
    let margin = 10.0;
    let w = 60.0;
    let h = 50.0;
    for y in 0..sy {
        for x in 0..sx {
            let cx = margin + (margin + w) * (x as f64);
            let cy = margin + (margin + h) * (y as f64);
            if pt_in_rect(env.mousex, env.mousey, cx, cy, w, h) {
                rectangle([1.0, 1.0, 0.0, 1.0],
                          [cx - 1.0, cy - 1.0, w + 2.0, h + 2.0],
                          c.transform,
                          gl);
            }
            rectangle(if level.get(x, y).unwrap() == 0 {
                          [0.4, 0.4, 0.4, 1.0]
                      } else {
                          [0.8, 0.8, 0.8, 1.0]
                      },
                      [cx, cy, w, h],
                      c.transform,
                      gl);
        }
    }
}

fn mouse_to_level(level: &Level, mx: f64, my: f64) -> Option<(usize, usize)> {
    let (sx, sy) = level.size();
    let margin = 10.0;
    let w = 60.0;
    let h = 50.0;
    if !pt_in_rect(mx,
                   my,
                   margin,
                   margin,
                   margin + (w + margin) * sx as f64,
                   margin + (h + margin) * sy as f64) {
        return None;
    }

    let lx = (mx - margin) / (margin + w);
    let ly = (my - margin) / (margin + h);
    let ulx = lx as usize;
    let uly = ly as usize;
    let sx = margin + (margin + w) * (ulx as f64);
    let sy = margin + (margin + h) * (uly as f64);
    if pt_in_rect(mx, my, sx, sy, w, h) {
        Some((ulx, uly))
    } else {
        None
    }
}

fn render_score(score: isize,
                bg: &G2dTexture,
                nr: &NumberRenderer,
                c: &Context,
                gl: &mut GfxGraphics<Resources, CommandBuffer>) {
    let img = Image::new().rect([800.0 - 160.0, 0.0, 160.0, 600.0]);
    img.draw(bg, &DrawState::default(), c.transform, gl);
    nr.render(score as u32, 715.0, 100.0, 170.0, [1.0, 1.0, 1.0], c, gl);
}

fn win_screen(window: &mut PistonWindow<Sdl2Window>,
              game: &mut Game,
              assets: &Path,
              gl: &mut GlGraphics) {
    let win = G2dTexture::from_path(&mut window.factory,
                                    &assets.join("win.png"),
                                    Flip::None,
                                    &TextureSettings::new())
            .unwrap();
    let imgwin = Image::new().rect([200.0, 175.0, 400.0, 250.0]);
    let bg = G2dTexture::from_path(&mut window.factory,
                                   &assets.join("bg.png"),
                                   Flip::None,
                                   &TextureSettings::new())
            .unwrap();
    let nr = NumberRenderer::new(window, assets);
    let mut t = 0.0f64;

    while let Some(event) = window.next() {
        match event {
            Render(args) => {
                window.draw_2d(&event, |c, gl| {
                    clear([0., 0., 0., 0.], gl);
                    imgwin.draw(&win, &DrawState::default(), c.transform, gl);
                    nr.render(game.score as u32,
                              400.0,
                              350.0,
                              200.0,
                              [1.0, 1.0, 1.0],
                              &c,
                              gl);
                    render_score(game.score, &bg, &nr, &c, gl);
                });
            }
            Update(args) => {
                t += args.dt;
            }
            Press(_) => {
                if t > 0.6 {
                    return;
                }
            }
            _ => {}
        }
    }
}

fn start_screen(window: &mut PistonWindow<Sdl2Window>,
                evloop: &Events,
                assets: &Path,
                gl: &mut GlGraphics) {
    let msg = G2dTexture::from_path(&mut window.factory,
                                    &assets.join("start.png"),
                                    Flip::None,
                                    &TextureSettings::new())
            .unwrap();
    let mut t = 0.0f64;
    let img = Image::new().rect([200., 175., 400., 250.]);

    while let Some(event) = window.next() {
        match event {
            Render(args) => {
                window.draw_2d(&event, |c, gl| {
                    clear([0., 0., 0., 0.], gl);
                    img.draw(&msg, &DrawState::default(), c.transform, gl);
                });
            }
            Update(args) => {
                t += args.dt;
            }
            Press(_) => {
                if t > 1.0 {
                    return;
                }
            }
            _ => {}
        }
    }
}

fn init_audio() {
    // use directsound if possible. xaudio2 doesn't work for some reason.
    // println!("audio disabled temporarily");
    // for i in 0 .. sdl2::audio::get_num_audio_drivers() {
    //      if "directsound" == sdl2::audio::get_audio_driver(i).as_slice() {
    //              sdl2::audio::audio_init("directsound").unwrap();
    //              break;
    //      }
    // }
    // println!("audio: {}", sdl2::audio::get_current_audio_driver());
    let _ = mixer::init(mixer::INIT_OGG);
    // TODO: 0x8010 is SDL_audio flag
    mixer::open_audio(mixer::DEFAULT_FREQUENCY,
                      mixer::DEFAULT_FORMAT,
                      mixer::DEFAULT_CHANNELS,
                      1024)
            .unwrap();
    mixer::allocate_channels(4);
}

fn game_screen(window: &mut PistonWindow<Sdl2Window>,
               game: &mut Game,
               assets: &Path,
               gl: &mut GlGraphics) {
    let nr = NumberRenderer::new(window, assets);
    let bg = G2dTexture::from_path(&mut window.factory,
                                   &assets.join("bg.png"),
                                   Flip::None,
                                   &TextureSettings::new())
            .unwrap();

    let mut env = Env {
        mousex: 0.,
        mousey: 0.,
        window_width: 800., // TODO fixme
        window_height: 600.,
    };

    let snd_click = mixer::Chunk::from_file(&assets.join("click.ogg")).unwrap();
    let snd_ai = mixer::Chunk::from_file(&assets.join("ai.ogg")).unwrap();
    let snd_tick = mixer::Chunk::from_file(&assets.join("tick.ogg")).unwrap();
    let channel_all = mixer::Channel::all();

    while let Some(event) = window.next() {
        match event {
            Render(args) => {
                window.draw_2d(&event, |c, gl| {
                    clear([0.0, 0.0, 0.0, 1.0], gl);
                    render_level(&game.level, &env, &c, gl);
                    render_score(game.score, &bg, &nr, &c, gl);
                });
            }
            Update(args) => {
                game.update(args.dt);
                if game.ticked() {
                    channel_all.play(&snd_tick, 0);
                    // music::play_sound(&Sound::Tick, music::Repeat::Times(1));
                }

                if game.level.is_solved() {
                    let snd_win = mixer::Chunk::from_file(&assets.join("win.ogg")).unwrap();
                    let channel_all = mixer::Channel::all();
                    channel_all.play(&snd_win, 0);

                    win_screen(window, game, &assets, gl);

                    if !game.change_level_size(1, 1, true) {
                        game.restart(2, 2, true);
                    }
                }
            }
            Move(Motion::MouseCursor(x, y)) => {
                env.mousex = x;
                env.mousey = y;
            }
            Press(Button::Mouse(args)) => {
                println!("{:?} {:?}", args, env);
                match mouse_to_level(&game.level, env.mousex, env.mousey) {
                    Some((x, y)) => {
                        game.level.make_move(x, y);
                        game.add_score(-1);
                        channel_all.play(&snd_click, 0);
                        // music::play_sound(&Sound::Click, music::Repeat::Times(1));
                    }
                    _ => {}
                }
            }
            Press(Button::Keyboard(key)) => {
                println!("{:?} {:?}", key, env);
                match key {
                    Key::Space => {
                        game.make_ai_move();
                        game.add_score(-3);
                        channel_all.play(&snd_ai, 0);
                        // music::play_sound(&Sound::Ai, music::Repeat::Times(1));
                    }
                    Key::Up => {
                        game.change_level_size(0, -1, false);
                        game.add_score(-50);
                    }
                    Key::Right => {
                        game.change_level_size(1, 0, false);
                        game.add_score(-50);
                    }
                    Key::Down => {
                        game.change_level_size(0, 1, false);
                        game.add_score(-50);
                    }
                    Key::Left => {
                        game.change_level_size(-1, 0, false);
                        game.add_score(-50);
                    }
                    Key::D1 => {
                        match mouse_to_level(&game.level, env.mousex, env.mousey) {
                            Some((x, y)) => {
                                game.level.set(x, y, 0);
                            }
                            _ => {}
                        }
                    }
                    Key::D2 => {
                        match mouse_to_level(&game.level, env.mousex, env.mousey) {
                            Some((x, y)) => {
                                game.level.set(x, y, 1);
                            }
                            _ => {}
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

fn main() {
    let opengl = OpenGL::V4_0;

    let mut window: PistonWindow<Sdl2Window> = WindowSettings::new("Rust Lights Out", (800, 600))
        .fullscreen(false)
        .exit_on_esc(true)
        .samples(4)
        .opengl(opengl)
        .build()
        .unwrap();

    let ev_settings = EventSettings::new().ups(30).max_fps(60);
    let ev_loop = Events::new(ev_settings);

    let ref mut gl = GlGraphics::new(opengl);

    let mut env = Env {
        mousex: 0f64,
        mousey: 0f64,
        window_width: window.size().width as f64,
        window_height: window.size().height as f64,
    };

    init_audio();

    let assets = Path::new("bin/assets");

    let mut game = Game::new(2usize, 2usize);

    println!("pwd: {:?}", std::env::current_dir());
    // music::bind_sound_file(Sound::Click, assets.join("click.ogg"));
    // music::bind_sound_file(Sound::Tick, assets.join("tick.ogg"));
    // music::bind_sound_file(Sound::Ai, assets.join("ai.ogg"));
    // music::bind_sound_file(Sound::Win, assets.join("win.ogg"));
    start_screen(&mut window, &ev_loop, &assets, gl);
    game_screen(&mut window, &mut game, &assets, gl);
}
<commit_msg>cleanup commented out code<commit_after>extern crate gfx_core;
extern crate piston;
extern crate piston_window;
extern crate graphics;
extern crate opengl_graphics;
extern crate sdl2;
extern crate sdl2_window;

extern crate gfx_graphics;
extern crate gfx;
extern crate gfx_device_gl;
extern crate music;

use std::os;
use std::path::Path;

use gfx_device_gl::{Resources, CommandBuffer};
use gfx_graphics::GfxGraphics;
use graphics::{Image, Context, clear, DrawState, rectangle};
use opengl_graphics::GlGraphics;
use piston::*;
use piston::input::*;
use piston::input::Input::*;
use piston_window::{Window, G2dTexture, WindowSettings, Events, EventLoop, EventSettings,
                    PistonWindow, Flip, TextureSettings, OpenGL};
use sdl2::mixer;
use sdl2_window::Sdl2Window;

use lightsout::{StructLevel, Level};
use number_renderer::NumberRenderer;
use game::Game;

mod lightsout;
mod number_renderer;
mod game;

#[derive(Debug)]
struct Env {
    mousex: f64,
    mousey: f64,
    window_width: f64,
    window_height: f64,
}

#[derive(Copy, Clone, Hash, PartialEq, Eq)]
struct Music;

#[derive(Copy, Clone, Hash, PartialEq, Eq)]
enum Sound {
    Win,
    Tick,
    Click,
    Ai,
}

#[inline(always)]
fn pt_in_rect(px: f64, py: f64, rx: f64, ry: f64, w: f64, h: f64) -> bool {
    px >= rx && px <= rx + w && py >= ry && py <= ry + h
}

fn render_level(level: &Level,
                env: &Env,
                c: &Context,
                gl: &mut GfxGraphics<Resources, CommandBuffer>) {
    let (sx, sy) = level.size();
    let margin = 10.0;
    let w = 60.0;
    let h = 50.0;
    for y in 0..sy {
        for x in 0..sx {
            let cx = margin + (margin + w) * (x as f64);
            let cy = margin + (margin + h) * (y as f64);
            if pt_in_rect(env.mousex, env.mousey, cx, cy, w, h) {
                rectangle([1.0, 1.0, 0.0, 1.0],
                          [cx - 1.0, cy - 1.0, w + 2.0, h + 2.0],
                          c.transform,
                          gl);
            }
            rectangle(if level.get(x, y).unwrap() == 0 {
                          [0.4, 0.4, 0.4, 1.0]
                      } else {
                          [0.8, 0.8, 0.8, 1.0]
                      },
                      [cx, cy, w, h],
                      c.transform,
                      gl);
        }
    }
}

fn mouse_to_level(level: &Level, mx: f64, my: f64) -> Option<(usize, usize)> {
    let (sx, sy) = level.size();
    let margin = 10.0;
    let w = 60.0;
    let h = 50.0;
    if !pt_in_rect(mx,
                   my,
                   margin,
                   margin,
                   margin + (w + margin) * sx as f64,
                   margin + (h + margin) * sy as f64) {
        return None;
    }

    let lx = (mx - margin) / (margin + w);
    let ly = (my - margin) / (margin + h);
    let ulx = lx as usize;
    let uly = ly as usize;
    let sx = margin + (margin + w) * (ulx as f64);
    let sy = margin + (margin + h) * (uly as f64);
    if pt_in_rect(mx, my, sx, sy, w, h) {
        Some((ulx, uly))
    } else {
        None
    }
}

fn render_score(score: isize,
                bg: &G2dTexture,
                nr: &NumberRenderer,
                c: &Context,
                gl: &mut GfxGraphics<Resources, CommandBuffer>) {
    let img = Image::new().rect([800.0 - 160.0, 0.0, 160.0, 600.0]);
    img.draw(bg, &DrawState::default(), c.transform, gl);
    nr.render(score as u32, 715.0, 100.0, 170.0, [1.0, 1.0, 1.0], c, gl);
}

fn win_screen(window: &mut PistonWindow<Sdl2Window>,
              game: &mut Game,
              assets: &Path,
              gl: &mut GlGraphics) {
    let win = G2dTexture::from_path(&mut window.factory,
                                    &assets.join("win.png"),
                                    Flip::None,
                                    &TextureSettings::new())
            .unwrap();
    let imgwin = Image::new().rect([200.0, 175.0, 400.0, 250.0]);
    let bg = G2dTexture::from_path(&mut window.factory,
                                   &assets.join("bg.png"),
                                   Flip::None,
                                   &TextureSettings::new())
            .unwrap();
    let nr = NumberRenderer::new(window, assets);
    let mut t = 0.0f64;

    while let Some(event) = window.next() {
        match event {
            Render(args) => {
                window.draw_2d(&event, |c, gl| {
                    clear([0., 0., 0., 0.], gl);
                    imgwin.draw(&win, &DrawState::default(), c.transform, gl);
                    nr.render(game.score as u32,
                              400.0,
                              350.0,
                              200.0,
                              [1.0, 1.0, 1.0],
                              &c,
                              gl);
                    render_score(game.score, &bg, &nr, &c, gl);
                });
            }
            Update(args) => {
                t += args.dt;
            }
            Press(_) => {
                if t > 0.6 {
                    return;
                }
            }
            _ => {}
        }
    }
}

fn start_screen(window: &mut PistonWindow<Sdl2Window>,
                evloop: &Events,
                assets: &Path,
                gl: &mut GlGraphics) {
    let msg = G2dTexture::from_path(&mut window.factory,
                                    &assets.join("start.png"),
                                    Flip::None,
                                    &TextureSettings::new())
            .unwrap();
    let mut t = 0.0f64;
    let img = Image::new().rect([200., 175., 400., 250.]);

    while let Some(event) = window.next() {
        match event {
            Render(args) => {
                window.draw_2d(&event, |c, gl| {
                    clear([0., 0., 0., 0.], gl);
                    img.draw(&msg, &DrawState::default(), c.transform, gl);
                });
            }
            Update(args) => {
                t += args.dt;
            }
            Press(_) => {
                if t > 1.0 {
                    return;
                }
            }
            _ => {}
        }
    }
}

fn init_audio() {
    let _ = mixer::init(mixer::INIT_OGG);

    mixer::open_audio(mixer::DEFAULT_FREQUENCY,
                      mixer::DEFAULT_FORMAT,
                      mixer::DEFAULT_CHANNELS,
                      1024)
            .unwrap();
    mixer::allocate_channels(4);
}

fn game_screen(window: &mut PistonWindow<Sdl2Window>,
               game: &mut Game,
               assets: &Path,
               gl: &mut GlGraphics) {
    let nr = NumberRenderer::new(window, assets);
    let bg = G2dTexture::from_path(&mut window.factory,
                                   &assets.join("bg.png"),
                                   Flip::None,
                                   &TextureSettings::new())
            .unwrap();

    let mut env = Env {
        mousex: 0.,
        mousey: 0.,
        window_width: 800., // TODO fixme
        window_height: 600.,
    };

    let snd_click = mixer::Chunk::from_file(&assets.join("click.ogg")).unwrap();
    let snd_ai = mixer::Chunk::from_file(&assets.join("ai.ogg")).unwrap();
    let snd_tick = mixer::Chunk::from_file(&assets.join("tick.ogg")).unwrap();
    let channel_all = mixer::Channel::all();

    while let Some(event) = window.next() {
        match event {
            Render(args) => {
                window.draw_2d(&event, |c, gl| {
                    clear([0.0, 0.0, 0.0, 1.0], gl);
                    render_level(&game.level, &env, &c, gl);
                    render_score(game.score, &bg, &nr, &c, gl);
                });
            }
            Update(args) => {
                game.update(args.dt);
                if game.ticked() {
                    channel_all.play(&snd_tick, 0);
                }

                if game.level.is_solved() {
                    let snd_win = mixer::Chunk::from_file(&assets.join("win.ogg")).unwrap();
                    let channel_all = mixer::Channel::all();
                    channel_all.play(&snd_win, 0);

                    win_screen(window, game, &assets, gl);

                    if !game.change_level_size(1, 1, true) {
                        game.restart(2, 2, true);
                    }
                }
            }
            Move(Motion::MouseCursor(x, y)) => {
                env.mousex = x;
                env.mousey = y;
            }
            Press(Button::Mouse(args)) => {
                println!("{:?} {:?}", args, env);
                match mouse_to_level(&game.level, env.mousex, env.mousey) {
                    Some((x, y)) => {
                        game.level.make_move(x, y);
                        game.add_score(-1);
                        channel_all.play(&snd_click, 0);
                    }
                    _ => {}
                }
            }
            Press(Button::Keyboard(key)) => {
                println!("{:?} {:?}", key, env);
                match key {
                    Key::Space => {
                        game.make_ai_move();
                        game.add_score(-3);
                        channel_all.play(&snd_ai, 0);
                    }
                    Key::Up => {
                        game.change_level_size(0, -1, false);
                        game.add_score(-50);
                    }
                    Key::Right => {
                        game.change_level_size(1, 0, false);
                        game.add_score(-50);
                    }
                    Key::Down => {
                        game.change_level_size(0, 1, false);
                        game.add_score(-50);
                    }
                    Key::Left => {
                        game.change_level_size(-1, 0, false);
                        game.add_score(-50);
                    }
                    Key::D1 => {
                        match mouse_to_level(&game.level, env.mousex, env.mousey) {
                            Some((x, y)) => {
                                game.level.set(x, y, 0);
                            }
                            _ => {}
                        }
                    }
                    Key::D2 => {
                        match mouse_to_level(&game.level, env.mousex, env.mousey) {
                            Some((x, y)) => {
                                game.level.set(x, y, 1);
                            }
                            _ => {}
                        }
                    }
                    _ => {}
                }
            }
            _ => {}
        }
    }
}

fn main() {
    let opengl = OpenGL::V4_0;

    let mut window: PistonWindow<Sdl2Window> = WindowSettings::new("Rust Lights Out", (800, 600))
        .fullscreen(false)
        .exit_on_esc(true)
        .samples(4)
        .opengl(opengl)
        .build()
        .unwrap();

    let ev_settings = EventSettings::new().ups(30).max_fps(60);
    let ev_loop = Events::new(ev_settings);

    let ref mut gl = GlGraphics::new(opengl);

    let mut env = Env {
        mousex: 0f64,
        mousey: 0f64,
        window_width: window.size().width as f64,
        window_height: window.size().height as f64,
    };

    init_audio();

    let assets = Path::new("bin/assets");

    let mut game = Game::new(2usize, 2usize);

    println!("pwd: {:?}", std::env::current_dir());
    start_screen(&mut window, &ev_loop, &assets, gl);
    game_screen(&mut window, &mut game, &assets, gl);
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Definitions and utilities for working with sinks.
//!
//! Sinks ("well-known flows" in terms of GRR nomenclature) are ever-existing
//! sessions on the GRR server that listen for various kinds of data. They are
//! a way to break away from the usual request-response workflow.
//!
//! For example, sinks can be used to notify the server about agent startup
//! (which is clearly not a response to a particular request) or to transfer
//! file blobs to a specialized storage.

use crate::action;
use crate::session;

/// Data sent to a server not belonging to a particular action call.
///
/// Sometimes the agent should send data to the server that is not connected
/// with a particular flow request. An example can be the agent startup data
/// sent to the server regardless of whether there was a request for it or not.
/// Another example can be file contents that are delivered to the server not
/// as part of the flow results but go to a separate store that has logic for
/// deduplicating data that we already collected in the past.
pub trait Parcel {
    /// Low-level Protocol Buffers type representing the parcel data.
    type Proto: protobuf::Message;

    /// A name of the corresponding RDF class in GRR.
    const RDF_NAME: &'static str;

    /// Converts the parcel to its low-level representation.
    fn into_proto(self) -> Self::Proto;
}

/// A parcel addressed to a particular server-side sink.
pub struct AddressedParcel<P: Parcel> {
    /// Destination of the parcel.
    dest: Sink,
    /// Actual parcel.
    parcel: P,
}

impl<P: Parcel> std::convert::TryInto<rrg_proto::jobs::GrrMessage> for AddressedParcel<P> {

    type Error = protobuf::ProtobufError;

    fn try_into(self) -> Result<rrg_proto::jobs::GrrMessage, protobuf::ProtobufError> {
        let mut message = rrg_proto::jobs::GrrMessage::new();
        message.set_session_id(String::from(self.dest.id));
        message.set_field_type(rrg_proto::jobs::GrrMessage_Type::MESSAGE);
        message.set_args_rdf_name(String::from(P::RDF_NAME));

        let proto = self.parcel.into_proto();
        message.set_args(protobuf::Message::write_to_bytes(&proto)?);

        Ok(message)
    }
}

/// Handle to a specific sink.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct Sink {
    /// An underlying identifier of the sink.
    id: &'static str,
}

impl Sink {

    // TODO: Refactor sinks to use custom `Message` type rather than `Response`
    // and make response always have `request_id` and `response_id` fields.

    /// Adresses a given `parcel` to this sink.
    pub fn address<P: Parcel>(&self, parcel: P) -> AddressedParcel<P> {
        AddressedParcel {
            dest: *self,
            parcel,
        }
    }

    // TODO: Remove the function below once only parcels are used.
    /// Wraps an action response to a sink-specific session response.
    pub fn wrap<R>(&self, response: R) -> session::Response<R>
    where
        R: action::Response,
    {
        session::Response {
            session_id: String::from(self.id),
            request_id: None,
            response_id: None,
            data: response,
        }
    }
}

/// A handle to the sink expecting startup information.
pub const STARTUP: Sink = Sink { id: "/flows/F:Startup" };

/// A handle to the transfer store sink.
pub const TRANSFER_STORE: Sink = Sink { id: "/flows/F:TransferStore" };
<commit_msg>Expose the destination field of addressed parcels.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Definitions and utilities for working with sinks.
//!
//! Sinks ("well-known flows" in terms of GRR nomenclature) are ever-existing
//! sessions on the GRR server that listen for various kinds of data. They are
//! a way to break away from the usual request-response workflow.
//!
//! For example, sinks can be used to notify the server about agent startup
//! (which is clearly not a response to a particular request) or to transfer
//! file blobs to a specialized storage.

use crate::action;
use crate::session;

/// Data sent to a server not belonging to a particular action call.
///
/// Sometimes the agent should send data to the server that is not connected
/// with a particular flow request. An example can be the agent startup data
/// sent to the server regardless of whether there was a request for it or not.
/// Another example can be file contents that are delivered to the server not
/// as part of the flow results but go to a separate store that has logic for
/// deduplicating data that we already collected in the past.
pub trait Parcel {
    /// Low-level Protocol Buffers type representing the parcel data.
    type Proto: protobuf::Message;

    /// A name of the corresponding RDF class in GRR.
    const RDF_NAME: &'static str;

    /// Converts the parcel to its low-level representation.
    fn into_proto(self) -> Self::Proto;
}

/// A parcel addressed to a particular server-side sink.
pub struct AddressedParcel<P: Parcel> {
    /// Destination of the parcel.
    sink: Sink,
    /// Actual parcel.
    parcel: P,
}

impl<P: Parcel> AddressedParcel<P> {

    /// Sink to which the parcel should be delivered to.
    pub fn sink(&self) -> Sink {
        self.sink
    }
}

impl<P: Parcel> std::convert::TryInto<rrg_proto::jobs::GrrMessage> for AddressedParcel<P> {

    type Error = protobuf::ProtobufError;

    fn try_into(self) -> Result<rrg_proto::jobs::GrrMessage, protobuf::ProtobufError> {
        let mut message = rrg_proto::jobs::GrrMessage::new();
        message.set_session_id(String::from(self.sink.id));
        message.set_field_type(rrg_proto::jobs::GrrMessage_Type::MESSAGE);
        message.set_args_rdf_name(String::from(P::RDF_NAME));

        let proto = self.parcel.into_proto();
        message.set_args(protobuf::Message::write_to_bytes(&proto)?);

        Ok(message)
    }
}

/// Handle to a specific sink.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct Sink {
    /// An underlying identifier of the sink.
    id: &'static str,
}

impl Sink {

    // TODO: Refactor sinks to use custom `Message` type rather than `Response`
    // and make response always have `request_id` and `response_id` fields.

    /// Adresses a given `parcel` to this sink.
    pub fn address<P: Parcel>(&self, parcel: P) -> AddressedParcel<P> {
        AddressedParcel {
            sink: *self,
            parcel,
        }
    }

    // TODO: Remove the function below once only parcels are used.
    /// Wraps an action response to a sink-specific session response.
    pub fn wrap<R>(&self, response: R) -> session::Response<R>
    where
        R: action::Response,
    {
        session::Response {
            session_id: String::from(self.id),
            request_id: None,
            response_id: None,
            data: response,
        }
    }
}

/// A handle to the sink expecting startup information.
pub const STARTUP: Sink = Sink { id: "/flows/F:Startup" };

/// A handle to the transfer store sink.
pub const TRANSFER_STORE: Sink = Sink { id: "/flows/F:TransferStore" };
<|endoftext|>


<commit_before>use cairo::{Context, Surface};
use pango::{FontDescription, Layout, LayoutExt};
use pangocairo::CairoContextExt;

#[derive(Clone, Debug)]
pub struct Color {
    red: f64,
    green: f64,
    blue: f64,
}

impl Color {
    pub fn white() -> Color {
        Color {
            red: 1.0,
            green: 1.0,
            blue: 1.0,
        }
    }

    pub fn red() -> Color {
        Color {
            red: 1.0,
            green: 0.0,
            blue: 0.0,
        }
    }

    pub fn blue() -> Color {
        Color {
            red: 0.0,
            green: 0.0,
            blue: 1.0,
        }
    }

    pub fn apply_to_context(&self, cr: &Context) {
        cr.set_source_rgb(self.red, self.green, self.blue);
    }
}

#[derive(Clone, Debug)]
pub struct Padding {
    left: f64,
    right: f64,
    top: f64,
    bottom: f64,
}

impl Padding {
    pub fn new(left: f64, right: f64, top: f64, bottom: f64) -> Padding {
        Padding {
            left,
            right,
            top,
            bottom,
        }
    }

    pub fn none() -> Padding {
        Padding {
            left: 0.0,
            right: 0.0,
            top: 0.0,
            bottom: 0.0,
        }
    }

    pub fn uniform(value: f64) -> Padding {
        Padding {
            left: value,
            right: value,
            top: value,
            bottom: value,
        }
    }
}

#[derive(Clone)]
pub struct Attributes {
    pub font: FontDescription,
    pub fg_color: Color,
    pub bg_color: Option<Color>,
    pub padding: Padding,
}

impl Attributes {
    pub fn with_font(&self, font: FontDescription) -> Attributes {
        let mut new = self.clone();
        new.font = font;
        new
    }

    pub fn with_fg_color(&self, fg_color: Color) -> Attributes {
        let mut new = self.clone();
        new.fg_color = fg_color;
        new
    }

    pub fn with_bg_color(&self, bg_color: Option<Color>) -> Attributes {
        let mut new = self.clone();
        new.bg_color = bg_color;
        new
    }

    pub fn with_padding(&self, padding: Padding) -> Attributes {
        let mut new = self.clone();
        new.padding = padding;
        new
    }
}

#[derive(Clone)]
pub struct Text {
    pub attr: Attributes,
    pub text: String,
    pub stretch: bool,
}

impl Text {
    pub fn layout(self, surface: &Surface) -> TextLayout {
        let context = Context::new(&surface);

        let layout = context.create_pango_layout();
        layout.set_text(&self.text, self.text.len() as i32);
        layout.set_font_description(Some(&self.attr.font));

        TextLayout {
            attr: self.attr.clone(),
            stretch: self.stretch,
            width: None,
            height: None,
            context: context,
            layout: layout,
        }
    }
}

pub struct TextLayout {
    attr: Attributes,
    stretch: bool,
    width: Option<f64>,
    height: Option<f64>,
    context: Context,
    layout: Layout,
}

impl TextLayout {
    pub fn width(&self) -> f64 {
        self.width
            .unwrap_or_else(|| {
                                let text_width = self.layout.get_pixel_size().0 as f64;
                                text_width + self.attr.padding.left + self.attr.padding.right
                            })
    }

    pub fn height(&self) -> f64 {
        self.height
            .unwrap_or_else(|| {
                                let text_height = self.layout.get_pixel_size().1 as f64;
                                text_height + self.attr.padding.top + self.attr.padding.bottom
                            })
    }

    pub fn set_width(&mut self, width: f64) {
        self.width = Some(width);
    }

    pub fn set_height(&mut self, height: f64) {
        self.height = Some(height);
    }

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

    pub fn render(&self, x: f64, y: f64) {
        self.context.save();
        self.context.translate(x, y + self.attr.padding.top);

        if let Some(ref bg_color) = self.attr.bg_color {
            bg_color.apply_to_context(&self.context);
            // FIXME: The use of `height` isnt' right here: we want to do the
            // full height of the bar, not the full height of the text. It
            // would be useful if we could do Surface.get_height(), but that
            // doesn't seem to be available in cairo-rs for some reason?
            self.context
                .rectangle(0.0, 0.0, self.width(), self.height());
            self.context.fill();
        }

        self.attr.fg_color.apply_to_context(&self.context);
        self.context
            .translate(self.attr.padding.left, self.attr.padding.top);
        self.context.show_pango_layout(&self.layout);

        self.context.restore();
    }
}
<commit_msg>Use macro for color definitions.<commit_after>use cairo::{Context, Surface};
use pango::{FontDescription, Layout, LayoutExt};
use pangocairo::CairoContextExt;

#[derive(Clone, Debug)]
pub struct Color {
    red: f64,
    green: f64,
    blue: f64,
}

macro_rules! color {
    ($name:ident, ($r:expr, $g:expr, $b:expr)) => {
        #[allow(dead_code)]
        pub fn $name() -> Color {
            Color {
                red: $r, green: $g, blue: $b,
            }
        }
    }
}

impl Color {
    color!(red, (1.0, 0.0, 0.0));
    color!(green, (0.0, 1.0, 0.0));
    color!(blue, (0.0, 0.0, 1.0));
    color!(white, (1.0, 1.0, 1.0));
    color!(black, (0.0, 0.0, 0.0));

    pub fn apply_to_context(&self, cr: &Context) {
        cr.set_source_rgb(self.red, self.green, self.blue);
    }
}

#[derive(Clone, Debug)]
pub struct Padding {
    left: f64,
    right: f64,
    top: f64,
    bottom: f64,
}

impl Padding {
    pub fn new(left: f64, right: f64, top: f64, bottom: f64) -> Padding {
        Padding {
            left,
            right,
            top,
            bottom,
        }
    }

    pub fn none() -> Padding {
        Padding {
            left: 0.0,
            right: 0.0,
            top: 0.0,
            bottom: 0.0,
        }
    }

    pub fn uniform(value: f64) -> Padding {
        Padding {
            left: value,
            right: value,
            top: value,
            bottom: value,
        }
    }
}

#[derive(Clone)]
pub struct Attributes {
    pub font: FontDescription,
    pub fg_color: Color,
    pub bg_color: Option<Color>,
    pub padding: Padding,
}

impl Attributes {
    pub fn with_font(&self, font: FontDescription) -> Attributes {
        let mut new = self.clone();
        new.font = font;
        new
    }

    pub fn with_fg_color(&self, fg_color: Color) -> Attributes {
        let mut new = self.clone();
        new.fg_color = fg_color;
        new
    }

    pub fn with_bg_color(&self, bg_color: Option<Color>) -> Attributes {
        let mut new = self.clone();
        new.bg_color = bg_color;
        new
    }

    pub fn with_padding(&self, padding: Padding) -> Attributes {
        let mut new = self.clone();
        new.padding = padding;
        new
    }
}

#[derive(Clone)]
pub struct Text {
    pub attr: Attributes,
    pub text: String,
    pub stretch: bool,
}

impl Text {
    pub fn layout(self, surface: &Surface) -> TextLayout {
        let context = Context::new(&surface);

        let layout = context.create_pango_layout();
        layout.set_text(&self.text, self.text.len() as i32);
        layout.set_font_description(Some(&self.attr.font));

        TextLayout {
            attr: self.attr.clone(),
            stretch: self.stretch,
            width: None,
            height: None,
            context: context,
            layout: layout,
        }
    }
}

pub struct TextLayout {
    attr: Attributes,
    stretch: bool,
    width: Option<f64>,
    height: Option<f64>,
    context: Context,
    layout: Layout,
}

impl TextLayout {
    pub fn width(&self) -> f64 {
        self.width
            .unwrap_or_else(|| {
                                let text_width = self.layout.get_pixel_size().0 as f64;
                                text_width + self.attr.padding.left + self.attr.padding.right
                            })
    }

    pub fn height(&self) -> f64 {
        self.height
            .unwrap_or_else(|| {
                                let text_height = self.layout.get_pixel_size().1 as f64;
                                text_height + self.attr.padding.top + self.attr.padding.bottom
                            })
    }

    pub fn set_width(&mut self, width: f64) {
        self.width = Some(width);
    }

    pub fn set_height(&mut self, height: f64) {
        self.height = Some(height);
    }

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

    pub fn render(&self, x: f64, y: f64) {
        self.context.save();
        self.context.translate(x, y + self.attr.padding.top);

        if let Some(ref bg_color) = self.attr.bg_color {
            bg_color.apply_to_context(&self.context);
            // FIXME: The use of `height` isnt' right here: we want to do the
            // full height of the bar, not the full height of the text. It
            // would be useful if we could do Surface.get_height(), but that
            // doesn't seem to be available in cairo-rs for some reason?
            self.context
                .rectangle(0.0, 0.0, self.width(), self.height());
            self.context.fill();
        }

        self.attr.fg_color.apply_to_context(&self.context);
        self.context
            .translate(self.attr.padding.left, self.attr.padding.top);
        self.context.show_pango_layout(&self.layout);

        self.context.restore();
    }
}
<|endoftext|>


<commit_before>use time::Duration;
use std::fmt::Write;
use std::fmt::{Display, Formatter, Result};

macro_rules! println_err {
    ($($arg:tt)*) => {
        let res = writeln!(std::io::stderr(), $($arg)*);
        res.expect("Failed writing to stderr!");
    };
}

macro_rules! expect {
    ($r:expr, $msg:tt) => {
        {
            $r.map_err(|e|{
                println_err!($msg, e.description());
                std::process::exit(1);
            }).unwrap()
        }
    };
}

pub trait PrettyPrint {
    fn pretty_print<W: Write>(&self, &mut W) -> Result;
}

impl PrettyPrint for Duration {
    fn pretty_print<W: Write>(&self, f: &mut W) -> Result {
        const NANOS_PER_MS: i64 = 1_000_000; // / any way to refer directly to time::NANOS_PER_MS?

        let nanos = self.num_nanoseconds().unwrap(); // TODO: Better error handling
        write!(f, "{} ms", (nanos as f64) / (NANOS_PER_MS as f64))
    }
}

pub struct PrettyPrinter<T: PrettyPrint>(pub T);

impl<T: PrettyPrint> Display for PrettyPrinter<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        let mut str = String::new();
        self.0.pretty_print(&mut str).and(f.write_str(&str))
    }
}

impl<T: PrettyPrint> From<T> for PrettyPrinter<T> {
    fn from(t: T) -> Self {
        PrettyPrinter(t)
    }
}<commit_msg>refactor(util::println_err!): enhance println_err! macro<commit_after>use time::Duration;
use std::fmt::Write;
use std::fmt::{Display, Formatter, Result};

macro_rules! println_err {
    ($($arg:tt)*) => {
        {
            let res = writeln!(std::io::stderr(), $($arg)*);
            res.expect("Failed writing to stderr!");
        }
    };
}

macro_rules! expect {
    ($r:expr, $msg:tt) => {
        {
            $r.map_err(|e|{
                println_err!($msg, e.description());
                std::process::exit(1);
            }).unwrap()
        }
    };
}

pub trait PrettyPrint {
    fn pretty_print<W: Write>(&self, &mut W) -> Result;
}

impl PrettyPrint for Duration {
    fn pretty_print<W: Write>(&self, f: &mut W) -> Result {
        const NANOS_PER_MS: i64 = 1_000_000; // / any way to refer directly to time::NANOS_PER_MS?

        let nanos = self.num_nanoseconds().unwrap(); // TODO: Better error handling
        write!(f, "{} ms", (nanos as f64) / (NANOS_PER_MS as f64))
    }
}

pub struct PrettyPrinter<T: PrettyPrint>(pub T);

impl<T: PrettyPrint> Display for PrettyPrinter<T> {
    fn fmt(&self, f: &mut Formatter) -> Result {
        let mut str = String::new();
        self.0.pretty_print(&mut str).and(f.write_str(&str))
    }
}

impl<T: PrettyPrint> From<T> for PrettyPrinter<T> {
    fn from(t: T) -> Self {
        PrettyPrinter(t)
    }
}<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(clippy::all)]

use colored::Colorize;
use signedsource::{sign_file, SIGNING_TOKEN};
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "fixture-tests", about = "Fixture tests")]
struct Options {
    #[structopt(name = "DIR", parse(from_os_str))]
    dirs: Vec<PathBuf>,
}

#[derive(Debug)]
struct TestCase {
    name: String,
    input: Option<PathBuf>,
    expected: Option<PathBuf>,
}

const EXPECTED_EXTENSION: &str = "expected";
const TODO: &str = "TODO";

fn main() {
    let opt = Options::from_args();

    for dir in opt.dirs {
        let test_name = dir.file_name().unwrap().to_str().unwrap();
        let fixtures_dir = dir.join("fixtures");
        let paths = fs::read_dir(&fixtures_dir)
            .unwrap_or_else(|_| panic!("Fixtures dir does not exist: {:?}", &fixtures_dir));
        let mut test_cases: HashMap<String, TestCase> = HashMap::new();
        for dir_entry in paths {
            let path = dir_entry.unwrap().path();
            if path
                .extension()
                .unwrap_or_else(|| panic!("unexpected path {:?}", &path))
                == TODO
            {
                continue;
            }
            let name = sanitize_identifier(path.file_stem().unwrap().to_str().unwrap());
            let mut test_case = test_cases.entry(name.clone()).or_insert_with(|| TestCase {
                name,
                input: None,
                expected: None,
            });
            if path.extension().unwrap() == EXPECTED_EXTENSION {
                if let Some(ref previous) = test_case.expected {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.expected = Some(path);
            } else {
                if let Some(ref previous) = test_case.input {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.input = Some(path);
            }
        }
        for mut test_case in test_cases.values_mut() {
            if test_case.expected.is_none() {
                if let Some(ref input) = test_case.input {
                    let mut expected = input.clone();
                    expected.set_extension(EXPECTED_EXTENSION);
                    File::create(&expected)
                        .unwrap()
                        .write_all(
                            "\x40nocommit\nRun snapshot tests with UPDATE_SNAPSHOTS=1 to update this new file.\n"
                            .as_bytes(),
                        )
                        .unwrap();
                    test_case.expected = Some(expected);
                }
            }
        }
        let mut test_cases: Vec<(_, _)> = test_cases.into_iter().collect();
        test_cases.sort_by_key(|entry| entry.0.to_owned());

        let test_cases = test_cases
            .into_iter()
            .map(|(_, test_case)| {
                let test_case_name = &test_case.name;
                format!(
                    r#"#[test]
fn {0}() {{
    let input = include_str!("{1}/fixtures/{2}");
    let expected = include_str!("{1}/fixtures/{3}");
    test_fixture(transform_fixture, "{2}", "{1}/fixtures/{3}", input, expected);
}}"#,
                    test_case.name,
                    &test_name,
                    test_case
                        .input
                        .unwrap_or_else(|| panic!(
                            "Expected input for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap(),
                    test_case
                        .expected
                        .unwrap_or_else(|| panic!(
                            "Expected output for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap()
                )
            })
            .collect::<Vec<_>>()
            .join("\n\n");

        let mut file = File::create(
            dir.parent()
                .unwrap()
                .join(format!("{}_test.rs", &test_name)),
        )
        .unwrap();
        file.write_all(
            sign_file(&format!(
                "// {0}

mod {1};

use {1}::transform_fixture;
use fixture_tests::test_fixture;

{2}
",
                SIGNING_TOKEN, &test_name, test_cases
            ))
            .as_bytes(),
        )
        .unwrap();

        check_targets_file(&dir);
    }
}

fn check_targets_file(test_dir: &PathBuf) {
    let targets_path = test_dir.parent().unwrap().parent().unwrap().join("TARGETS");
    let targets_content = match std::fs::read_to_string(&targets_path) {
        Ok(content) => content,
        Err(_) => return,
    };

    let expected_substr = format!(
        "\"tests/{}_test.rs\"",
        test_dir.file_stem().unwrap().to_str().unwrap()
    );

    if !targets_content.contains(&expected_substr) {
        eprintln!(
            "{}",
            format!(
                "WARNING: expected {:?} to contain substring {}",
                targets_path, expected_substr
            )
            .yellow()
        );
    }
}

fn sanitize_identifier(input: &str) -> String {
    input
        .chars()
        .map(|chr| match chr {
            'a'..='z' | '0'..='9' | '_' => chr,
            'A'..='Z' => chr.to_ascii_lowercase(),
            _ => '_',
        })
        .collect()
}
<commit_msg>Relax missing file error in fixture test generator<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#![deny(clippy::all)]

use colored::Colorize;
use signedsource::{sign_file, SIGNING_TOKEN};
use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(name = "fixture-tests", about = "Fixture tests")]
struct Options {
    #[structopt(name = "DIR", parse(from_os_str))]
    dirs: Vec<PathBuf>,
}

#[derive(Debug)]
struct TestCase {
    name: String,
    input: Option<PathBuf>,
    expected: Option<PathBuf>,
}

const EXPECTED_EXTENSION: &str = "expected";

fn main() {
    let opt = Options::from_args();

    for dir in opt.dirs {
        let test_name = dir.file_name().unwrap().to_str().unwrap();
        let fixtures_dir = dir.join("fixtures");
        let paths = fs::read_dir(&fixtures_dir)
            .unwrap_or_else(|_| panic!("Fixtures dir does not exist: {:?}", &fixtures_dir));
        let mut test_cases: HashMap<String, TestCase> = HashMap::new();
        for dir_entry in paths {
            let path = dir_entry.unwrap().path();
            if path.extension().is_none() {
                continue;
            }
            let name = sanitize_identifier(path.file_stem().unwrap().to_str().unwrap());
            let mut test_case = test_cases.entry(name.clone()).or_insert_with(|| TestCase {
                name,
                input: None,
                expected: None,
            });
            if path.extension().unwrap() == EXPECTED_EXTENSION {
                if let Some(ref previous) = test_case.expected {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.expected = Some(path);
            } else {
                if let Some(ref previous) = test_case.input {
                    panic!("Conflicting fixture name, {:?} and {:?}", previous, path);
                }
                test_case.input = Some(path);
            }
        }
        for mut test_case in test_cases.values_mut() {
            if test_case.expected.is_none() {
                if let Some(ref input) = test_case.input {
                    let mut expected = input.clone();
                    expected.set_extension(EXPECTED_EXTENSION);
                    File::create(&expected)
                        .unwrap()
                        .write_all(
                            "\x40nocommit\nRun snapshot tests with UPDATE_SNAPSHOTS=1 to update this new file.\n"
                            .as_bytes(),
                        )
                        .unwrap();
                    test_case.expected = Some(expected);
                }
            }
        }
        let mut test_cases: Vec<(_, _)> = test_cases.into_iter().collect();
        test_cases.sort_by_key(|entry| entry.0.to_owned());

        let test_cases = test_cases
            .into_iter()
            .map(|(_, test_case)| {
                let test_case_name = &test_case.name;
                format!(
                    r#"#[test]
fn {0}() {{
    let input = include_str!("{1}/fixtures/{2}");
    let expected = include_str!("{1}/fixtures/{3}");
    test_fixture(transform_fixture, "{2}", "{1}/fixtures/{3}", input, expected);
}}"#,
                    test_case.name,
                    &test_name,
                    test_case
                        .input
                        .unwrap_or_else(|| panic!(
                            "Expected input for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap(),
                    test_case
                        .expected
                        .unwrap_or_else(|| panic!(
                            "Expected output for test {:?} to exist",
                            test_case_name
                        ))
                        .file_name()
                        .and_then(|x| x.to_str())
                        .unwrap()
                )
            })
            .collect::<Vec<_>>()
            .join("\n\n");

        let mut file = File::create(
            dir.parent()
                .unwrap()
                .join(format!("{}_test.rs", &test_name)),
        )
        .unwrap();
        file.write_all(
            sign_file(&format!(
                "// {0}

mod {1};

use {1}::transform_fixture;
use fixture_tests::test_fixture;

{2}
",
                SIGNING_TOKEN, &test_name, test_cases
            ))
            .as_bytes(),
        )
        .unwrap();

        check_targets_file(&dir);
    }
}

fn check_targets_file(test_dir: &PathBuf) {
    let targets_path = test_dir.parent().unwrap().parent().unwrap().join("TARGETS");
    let targets_content = match std::fs::read_to_string(&targets_path) {
        Ok(content) => content,
        Err(_) => return,
    };

    let expected_substr = format!(
        "\"tests/{}_test.rs\"",
        test_dir.file_stem().unwrap().to_str().unwrap()
    );

    if !targets_content.contains(&expected_substr) {
        eprintln!(
            "{}",
            format!(
                "WARNING: expected {:?} to contain substring {}",
                targets_path, expected_substr
            )
            .yellow()
        );
    }
}

fn sanitize_identifier(input: &str) -> String {
    input
        .chars()
        .map(|chr| match chr {
            'a'..='z' | '0'..='9' | '_' => chr,
            'A'..='Z' => chr.to_ascii_lowercase(),
            _ => '_',
        })
        .collect()
}
<|endoftext|>


<commit_before><commit_msg>Add method to convert message to vec of bits<commit_after><|endoftext|>