<commit_before>use super::interconnect::Interconnect;
use super::memory::END_RESERVED;
use super::rand;
use super::time::{Duration, SteadyTime};

const INSTRUCTION_SIZE: u16 = 2;

pub struct Cpu {
    v: [u8; 16],
    pc: u16,
    i: u16,
    stack: Vec<u16>,

    delay_timer: u8,
    sound_timer: u8,

    delay_start: SteadyTime,
}

impl Cpu {
    pub fn new() -> Cpu {
        return Cpu{
            v: [0; 16],
            pc: END_RESERVED as u16,
            i: 0,
            stack: Vec::new(),
            delay_timer: 0,
            sound_timer: 0,
            delay_start: SteadyTime::now(),
        }
    }

    pub fn run(&mut self, interconnect: &mut Interconnect) {
        loop {
            let instr = interconnect.mem.read_word(self.pc);

            self.execute_instruction(instr, interconnect);

            self.handle_timers();

            interconnect.graphics.render();
            interconnect.input.handle_input();

            if interconnect.input.quit {
                break;
            }
        }
    }

    pub fn execute_instruction(&mut self, instr: u16, interconnect: &mut Interconnect) {
        let opcode = (instr >> 12) as u8;
        let nnn = instr & 0xfff;
        let x = ((instr >> 8) & 0xf) as usize;
        let y = ((instr >> 4) & 0xf) as usize;
        let n = (instr & 0xf) as usize;
        let kk = (instr & 0xff) as u8;
        let mut jmp = false;

        match opcode {
            0x0 => {
                match kk {
                    0xe0 => {
                        // 00E0 - CLS
                        interconnect.graphics.clear();
                    }
                    0xee => {
                        // 00EE - RET
                        let ret = self.stack.pop().expect("ret without func call");
                        self.pc = ret;
                    }
                    _ => {
                        // 0nnn - SYS addr
                        // This instruction is only used on the old computers on which Chip-8 was
                        // originally implemented. It is ignored by modern interpreters.
                    }
                }
            }
            0x1 => {
                // 1nnn - JP addr
                self.pc = nnn;
                jmp = true;
            }
            0x2 => {
                // 2nnn - CALL addr
                self.stack.push(self.pc);
                self.pc = nnn;
                jmp = true;
            }
            0x3 => {
                // 3xkk - SE Vx, byte
                if self.v[x] == kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x4 => {
                // 4xkk - SNE Vx, byte
                if self.v[x] != kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x5 => {
                // 5xy0 - SE Vx, Vy
                if self.v[x] == self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x6 => {
                // 6xkk - LD Vx, byte
                self.v[x as usize] = kk;
            }
            0x7 => {
                // 7xkk - ADD Vx, byte
                self.v[x] = self.v[x].wrapping_add(kk as u8);
            }
            0x8 => {
                match n {
                    0x0 => {
                        // 8xy0 - LD Vx, Vy
                        self.v[x] = self.v[y];
                    }
                    0x1 => {
                        // 8xy1 - OR Vx, Vy
                        self.v[x] = self.v[x] | self.v[y];
                    }
                    0x2 => {
                        // 8xy2 - AND Vx, Vy
                        self.v[x] = self.v[x] & self.v[y];
                    }
                    0x3 => {
                        // 8xy3 - XOR Vx, Vy
                        self.v[x] = self.v[x] ^ self.v[y];
                    }
                    0x4 => {
                        // 8xy4 - ADD Vx, Vy
                        let ret = (self.v[x] as u16) + (self.v[y] as u16);
                        if ret > 255 {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = ret as u8;
                    }
                    0x5 => {
                        // 8xy5 - SUB Vx, Vy
                        if self.v[x] > self.v[y] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[x].wrapping_sub(self.v[y]);
                    }
                    0x6 => {
                        // 8xy6 - SHR Vx {, Vy}
                        self.v[0xf] = self.v[x] & 0x1;
                        self.v[x] >>= 1;
                    }
                    0x7 => {
                        // 8xy7 - SUBN Vx, Vy
                        if self.v[y] > self.v[x] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[y].wrapping_sub(self.v[x]);
                    }
                    0xE => {
                        self.v[0xf] = self.v[x] >> 7;
                        self.v[x] <<= 1;
                    }
                    _ => panic!("Unrecognized 8 variant {:x}({:x})", instr, n),
                }
            }
            0x9 => {
                // 9xy0 - SNE Vx, Vy
                if self.v[x] != self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0xa => {
                // Annn - LD I, addr
                self.i = nnn;
            }
            0xb => {
                // Bnnn - JP V0, addr
                self.pc = (self.v[0] as u16) + nnn;
                jmp = true;
            }
            0xc => {
                // Cxkk - RND Vx, byte
                self.v[x] = rand::random::<u8>() & kk;
            }
            0xd => {
                // Dxyn - DRW Vx, Vy, nibble
                let mut sprite = vec![0; n as usize];
                let vx = self.v[x] as usize;
                let vy = self.v[y] as usize;

                for i in 0..n {
                    sprite[i] = interconnect.mem.read_byte(self.i + i as u16);
                }

                self.v[0xf] = interconnect.graphics.draw(vx, vy, sprite);
            }
            0xe => {
                match kk {
                    0x9e => {
                        // Ex9E - SKP Vx
                        if interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    0xa1 => {
                        // ExA1 - SKNP Vx
                        if !interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    _ => panic!("Unrecognized e variant {:x}({:x})", instr, kk),
                }
            }
            0xf => {
                match kk {
                    0x07 => {
                        // Fx07 - LD Vx, DT
                        self.v[x] = self.delay_timer;
                    }
                    0x0a => {
                        // Fx0A - LD Vx, K
                        match interconnect.input.any_key_pressed() {
                            Some(index) => self.v[x] = index,
                            None => jmp = true,
                        }
                    }
                    0x15 => {
                        // Fx15 - LD DT, Vx
                        let val = self.v[x];
                        self.delay_timer = val;
                    }
                    0x18 => {
                        // Fx18 - LD ST, Vx
                        self.sound_timer = self.v[x];
                    }
                    0x1E => {
                        // Fx1E - ADD I, Vx
                        self.i += self.v[x] as u16;
                    }
                    0x29 => {
                        // Fx29 - LD F, Vx
                        let val = self.v[x];
                        self.i = interconnect.mem.get_digit_sprite(val);
                    }
                    0x33 => {
                        // Fx33 - LD B, Vx
                        let val = self.v[x];
                        interconnect.mem.write_byte(self.i, val / 100);
                        interconnect.mem.write_byte(self.i + 1, (val % 100) / 10);
                        interconnect.mem.write_byte(self.i + 2, val % 10);
                    }
                    0x55 => {
                        // Fx55 - LD [I], Vx
                        for i in 0..(x + 1) {
                            interconnect.mem.write_byte(self.i + i as u16, self.v[i]);
                        }
                    }
                    0x65 => {
                        // Fx65 - LD Vx, [I]
                        for i in 0..(x+1) {
                            self.v[i] = interconnect.mem.read_byte(self.i + i as u16);
                        }
                    }
                    _ => panic!("Unrecognized f variant {:x}({:x})", instr, kk),
                }
            }

            _ => panic!("Unrecognized instruction 0x{:x} ({:x})", instr, opcode),
        }

        if !jmp {
            self.pc += INSTRUCTION_SIZE;
        }
    }

    fn handle_timers(&mut self) {
        if self.delay_timer > 0 {
            let now = SteadyTime::now();
            if now - self.delay_start > Duration::milliseconds(16) {
                self.delay_start = now;
                self.delay_timer -= 1;
            }
        }

    }
}
<commit_msg>Tidy up the handling of the pc register<commit_after>use super::interconnect::Interconnect;
use super::memory::END_RESERVED;
use super::rand;
use super::time::{Duration, SteadyTime};

const INSTRUCTION_SIZE: u16 = 2;

pub struct Cpu {
    v: [u8; 16],
    pc: u16,
    i: u16,
    stack: Vec<u16>,

    delay_timer: u8,
    sound_timer: u8,

    delay_start: SteadyTime,
}

impl Cpu {
    pub fn new() -> Cpu {
        return Cpu{
            v: [0; 16],
            pc: END_RESERVED as u16,
            i: 0,
            stack: Vec::new(),
            delay_timer: 0,
            sound_timer: 0,
            delay_start: SteadyTime::now(),
        }
    }

    pub fn run(&mut self, interconnect: &mut Interconnect) {
        loop {
            let instr = interconnect.mem.read_word(self.pc);

            self.execute_instruction(instr, interconnect);

            self.handle_timers();

            interconnect.graphics.render();
            interconnect.input.handle_input();

            if interconnect.input.quit {
                break;
            }
        }
    }

    pub fn execute_instruction(&mut self, instr: u16, interconnect: &mut Interconnect) {
        let opcode = (instr >> 12) as u8;
        let nnn = instr & 0xfff;
        let x = ((instr >> 8) & 0xf) as usize;
        let y = ((instr >> 4) & 0xf) as usize;
        let n = (instr & 0xf) as usize;
        let kk = (instr & 0xff) as u8;
        self.pc += INSTRUCTION_SIZE;

        match opcode {
            0x0 => {
                match kk {
                    0xe0 => {
                        // 00E0 - CLS
                        interconnect.graphics.clear();
                    }
                    0xee => {
                        // 00EE - RET
                        let ret = self.stack.pop().expect("ret without func call");
                        self.pc = ret;
                    }
                    _ => {
                        // 0nnn - SYS addr
                        // This instruction is only used on the old computers on which Chip-8 was
                        // originally implemented. It is ignored by modern interpreters.
                    }
                }
            }
            0x1 => {
                // 1nnn - JP addr
                self.pc = nnn;
            }
            0x2 => {
                // 2nnn - CALL addr
                self.stack.push(self.pc);
                self.pc = nnn;
            }
            0x3 => {
                // 3xkk - SE Vx, byte
                if self.v[x] == kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x4 => {
                // 4xkk - SNE Vx, byte
                if self.v[x] != kk {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x5 => {
                // 5xy0 - SE Vx, Vy
                if self.v[x] == self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0x6 => {
                // 6xkk - LD Vx, byte
                self.v[x as usize] = kk;
            }
            0x7 => {
                // 7xkk - ADD Vx, byte
                self.v[x] = self.v[x].wrapping_add(kk as u8);
            }
            0x8 => {
                match n {
                    0x0 => {
                        // 8xy0 - LD Vx, Vy
                        self.v[x] = self.v[y];
                    }
                    0x1 => {
                        // 8xy1 - OR Vx, Vy
                        self.v[x] = self.v[x] | self.v[y];
                    }
                    0x2 => {
                        // 8xy2 - AND Vx, Vy
                        self.v[x] = self.v[x] & self.v[y];
                    }
                    0x3 => {
                        // 8xy3 - XOR Vx, Vy
                        self.v[x] = self.v[x] ^ self.v[y];
                    }
                    0x4 => {
                        // 8xy4 - ADD Vx, Vy
                        let ret = (self.v[x] as u16) + (self.v[y] as u16);
                        if ret > 255 {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = ret as u8;
                    }
                    0x5 => {
                        // 8xy5 - SUB Vx, Vy
                        if self.v[x] > self.v[y] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[x].wrapping_sub(self.v[y]);
                    }
                    0x6 => {
                        // 8xy6 - SHR Vx {, Vy}
                        self.v[0xf] = self.v[x] & 0x1;
                        self.v[x] >>= 1;
                    }
                    0x7 => {
                        // 8xy7 - SUBN Vx, Vy
                        if self.v[y] > self.v[x] {
                            self.v[0xf] = 1;
                        } else {
                            self.v[0xf] = 0;
                        }
                        self.v[x] = self.v[y].wrapping_sub(self.v[x]);
                    }
                    0xE => {
                        self.v[0xf] = self.v[x] >> 7;
                        self.v[x] <<= 1;
                    }
                    _ => panic!("Unrecognized 8 variant {:x}({:x})", instr, n),
                }
            }
            0x9 => {
                // 9xy0 - SNE Vx, Vy
                if self.v[x] != self.v[y] {
                    self.pc += INSTRUCTION_SIZE;
                }
            }
            0xa => {
                // Annn - LD I, addr
                self.i = nnn;
            }
            0xb => {
                // Bnnn - JP V0, addr
                self.pc = (self.v[0] as u16) + nnn;
            }
            0xc => {
                // Cxkk - RND Vx, byte
                self.v[x] = rand::random::<u8>() & kk;
            }
            0xd => {
                // Dxyn - DRW Vx, Vy, nibble
                let mut sprite = vec![0; n as usize];
                let vx = self.v[x] as usize;
                let vy = self.v[y] as usize;

                for i in 0..n {
                    sprite[i] = interconnect.mem.read_byte(self.i + i as u16);
                }

                self.v[0xf] = interconnect.graphics.draw(vx, vy, sprite);
            }
            0xe => {
                match kk {
                    0x9e => {
                        // Ex9E - SKP Vx
                        if interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    0xa1 => {
                        // ExA1 - SKNP Vx
                        if !interconnect.input.key_pressed(self.v[x]) {
                            self.pc += INSTRUCTION_SIZE;
                        }
                    }
                    _ => panic!("Unrecognized e variant {:x}({:x})", instr, kk),
                }
            }
            0xf => {
                match kk {
                    0x07 => {
                        // Fx07 - LD Vx, DT
                        self.v[x] = self.delay_timer;
                    }
                    0x0a => {
                        // Fx0A - LD Vx, K
                        match interconnect.input.any_key_pressed() {
                            Some(index) => self.v[x] = index,
                            None => self.pc -= INSTRUCTION_SIZE,
                        }
                    }
                    0x15 => {
                        // Fx15 - LD DT, Vx
                        let val = self.v[x];
                        self.delay_timer = val;
                    }
                    0x18 => {
                        // Fx18 - LD ST, Vx
                        self.sound_timer = self.v[x];
                    }
                    0x1E => {
                        // Fx1E - ADD I, Vx
                        self.i += self.v[x] as u16;
                    }
                    0x29 => {
                        // Fx29 - LD F, Vx
                        let val = self.v[x];
                        self.i = interconnect.mem.get_digit_sprite(val);
                    }
                    0x33 => {
                        // Fx33 - LD B, Vx
                        let val = self.v[x];
                        interconnect.mem.write_byte(self.i, val / 100);
                        interconnect.mem.write_byte(self.i + 1, (val % 100) / 10);
                        interconnect.mem.write_byte(self.i + 2, val % 10);
                    }
                    0x55 => {
                        // Fx55 - LD [I], Vx
                        for i in 0..(x + 1) {
                            interconnect.mem.write_byte(self.i + i as u16, self.v[i]);
                        }
                    }
                    0x65 => {
                        // Fx65 - LD Vx, [I]
                        for i in 0..(x+1) {
                            self.v[i] = interconnect.mem.read_byte(self.i + i as u16);
                        }
                    }
                    _ => panic!("Unrecognized f variant {:x}({:x})", instr, kk),
                }
            }

            _ => panic!("Unrecognized instruction 0x{:x} ({:x})", instr, opcode),
        }
    }

    fn handle_timers(&mut self) {
        if self.delay_timer > 0 {
            let now = SteadyTime::now();
            if now - self.delay_start > Duration::milliseconds(16) {
                self.delay_start = now;
                self.delay_timer -= 1;
            }
        }

    }
}
<|endoftext|>


<commit_before>use ::ScriptResult;
use iter::TableIterator;
use libc::{c_int, c_void};
use lua::{self, ffi};
use rule::Rule;
use std::cell::RefCell;
use std::clone::Clone;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
use std::rc::{Rc, Weak};
use task::{Task, NamedTask};

// Just a tiny key used to obtain a unique registry location to store the environment pointer.
static KEY: f64 = 264299.0;

/// A function that can be bound to be callable inside the Lua runtime.
pub type Function = fn(Environment) -> ScriptResult;
pub type Closure = Box<FnMut(Environment) -> ScriptResult>;

/// Stores the state of an entire task execution environment.
struct EnvironmentStruct {
    /// A map of all named tasks.
    tasks: RefCell<HashMap<String, Rc<NamedTask>>>,

    /// A vector of all defined file rules.
    rules: RefCell<Vec<Rc<Rule>>>,

    /// The default task to run.
    default_task: RefCell<Option<String>>,

    path: PathBuf,

    directory: PathBuf,

    /// A Lua interpreter state.
    state: lua::State,
}

pub struct Environment(Rc<EnvironmentStruct>);

impl Environment {
    /// Creates a new environment for a given script file.
    ///
    /// The instance is placed inside a box to ensure the runner has a constant location in memory
    /// so that it can be referenced by native closures in the runtime.
    pub fn new<P: Into<PathBuf>>(script: P) -> Result<Environment, Box<Error>> {
        let script = script.into();
        let directory = match script.parent() {
            Some(path) => path.into(),
            None => {
                return Err("failed to parse script directory".into());
            }
        };

        let environment = Environment(Rc::new(EnvironmentStruct {
            tasks: RefCell::new(HashMap::new()),
            rules: RefCell::new(Vec::new()),
            default_task: RefCell::new(None),
            path: script,
            directory: directory,
            state: lua::State::new(),
        }));

        // Create a weak pointer to the environment and push it into the registry so that you can
        // access the environment object by its Lua state.
        environment.state().push(KEY);
        let ptr = environment.state().new_userdata_typed();
        let weak = Rc::downgrade(&environment.0);
        unsafe {
            ptr::write(ptr, weak);
        }
        environment.state().set_table(lua::REGISTRYINDEX);

        Ok(environment)
    }

    /// Fetches the environment by its Lua pointer.
    ///
    /// This function is far from safe, and should be used with care.
    pub unsafe fn from_ptr(ptr: ::StatePtr) -> Environment {
        // First, get a State object from the raw pointer.
        let mut state = lua::State::from_ptr(ptr);

        // Fetch the weak environment pointer from the registry.
        state.push(KEY);
        state.get_table(lua::REGISTRYINDEX);

        // Read the weak pointer.
        let weak = match state.to_userdata_typed::<Weak<EnvironmentStruct>>(-1) {
            Some(weak) => {
                lua::State::from_ptr(ptr).pop(1);
                weak
            },
            None => panic!("unable to read environment pointer")
        };

        // Upgrade the pointer and convert it to an environment.
        Environment(match Weak::upgrade(&weak) {
            Some(rc) => rc,
            None => panic!("unable to upgrade environment pointer")
        })
    }

    /// Executes the script.
    pub fn load(&self) -> Result<(), Box<Error>> {
        let path_str = if let Some(s) = self.0.path.to_str() {
            s
        } else {
            return Err("path contains invalid characters".into());
        };

        // Load the given file.
        match self.state().do_file(path_str) {
            lua::ThreadStatus::Ok => { }
            lua::ThreadStatus::FileError => {
                return Err(format!("the file \"{}\" could not be read", path_str).into());
            }
            _ => {
                return Err(self.state().to_str(-1).unwrap().into());
            }
        };

        Ok(())
    }

    /// Gets the full path of the script file.
    pub fn path(&self) -> &Path {
        &self.0.path
    }

    /// Gets the full path of the directory containing the script file.
    pub fn directory(&self) -> &Path {
        &self.0.directory
    }

    /// Gets a list of all registered tasks.
    pub fn tasks(&self) -> Vec<Rc<NamedTask>> {
        self.0.tasks.borrow().values().map(|rc| rc.clone()).collect()
    }

    /// Gets a list of all registered rules.
    pub fn rules(&self) -> Vec<Rc<Rule>> {
        self.0.rules.borrow().iter().map(|rc| rc.clone()).collect()
    }

    /// Creates a new task.
    pub fn create_task(&self, task: Rc<NamedTask>) {
        // Add it to the master list of tasks.
        self.0.tasks.borrow_mut().insert(task.name().into(), task.clone());
    }

    /// Creates a new rule.
    pub fn create_rule(&self, rule: Rc<Rule>) {
        self.0.rules.borrow_mut().push(rule);
    }

    /// Gets a task by name.
    pub fn get_task<S: AsRef<str>>(&self, name: S) -> Option<Rc<NamedTask>> {
        self.0.tasks.borrow().get(name.as_ref()).map(|rc| rc.clone())
    }

    /// Gets the default task to run.
    pub fn default_task<'a>(&'a self) -> Option<String> {
        match *self.0.default_task.borrow() {
            Some(ref task) => Some(task.clone()),
            None => None,
        }
    }

    /// Sets the default task.
    pub fn set_default_task<S: Into<String>>(&self, name: S) {
        *self.0.default_task.borrow_mut() = Some(name.into());
    }

    /// Gets a variable value.
    pub fn var<S: AsRef<str>>(&self, name: S) -> Option<String> {
        let name = name.as_ref();

        // Attempt to match an environment variable, or fallback to a global variable.
        env::var(name)
            .ok()
            .map(|value| value.to_string())
            .or_else(|| {
                let value = if self.state().get_global(name) != lua::Type::Nil {
                    Some(self.state().check_string(-1).to_string())
                } else {
                    None
                };

                self.state().pop(1);
                value
            })
    }

    /// Sets a variable value.
    pub fn set_var<S: AsRef<str>, V: lua::ToLua>(&self, name: S, value: V) {
        self.state().push(value);
        self.state().set_global(name.as_ref());
    }

    /// Adds a path to Lua's require path for modules.
    pub fn include_path<P: Into<PathBuf>>(&self, path: P) {
        let mut lua_path = path.into();
        let mut native_path = lua_path.clone();
        lua_path.push("?.lua");
        native_path.push("?.so");

        self.state().get_global("package");

        // Set the Lua file path.
        self.state().get_field(-1, "path");
        let mut search_path = self.state().to_str(-1).unwrap().to_string();
        search_path.push(';');
        search_path.push_str(&lua_path.to_string_lossy());

        self.state().push_string(&search_path);
        self.state().set_field(-4, "path");
        self.state().pop(2);

        // Set the native file path.
        self.state().get_field(-1, "cpath");
        let mut search_path = self.state().to_str(-1).unwrap().to_string();
        search_path.push(';');
        search_path.push_str(&native_path.to_string_lossy());

        self.state().push_string(&search_path);
        self.state().set_field(-4, "cpath");
        self.state().pop(3);
    }

    /// Gets a mutable instance of the Lua interpreter state.
    ///
    /// This function uses the direct lua_State pointer, so multiple owners can all mutate the same
    /// state simultaneously. Obviously this is a little unsafe, so use responsibly.
    pub fn state(&self) -> lua::State {
        unsafe {
            lua::State::from_ptr(self.0.state.as_ptr())
        }
    }

    /// Evaluates a Lua string inside the runtime.
    pub fn eval<S: AsRef<str>>(&self, code: S) -> Result<(), Box<Error>> {
        if self.state().do_string(code.as_ref()).is_err() {
            return Err(self.state().to_str(-1).unwrap().into());
        }

        Ok(())
    }

    /// Registers a global function in the runtime that can be called by Lua scripts.
    pub fn register_fn(&self, name: &str, f: Function) {
        self.push_fn(f);
        self.state().set_global(name);
    }

    /// Registers a new dynamic module library.
    pub fn register_lib(&self, mtable: &[(&str, Function)]) {
        self.state().create_table(0, mtable.len() as i32);

        for &(name, func) in mtable {
            self.push_fn(func);
            self.state().set_field(-2, name);
        }
    }

    /// Pushes a safe Rust function onto the stack.
    pub fn push_fn(&self, function: Function) {
        unsafe {
            // Push a pointer to the given function so that we know what function to delegate to.
            self.state().push_light_userdata(function as *mut c_void);
        }

        // Push a wrapper function onto the stack, which delegates to the given function.
        self.state().push_closure(Some(fn_wrapper), 1);

        // Wrapper function for invoking Rust functions from inside Lua.
        unsafe extern fn fn_wrapper(ptr: *mut ffi::lua_State) -> c_int {
            // Get the environment from the raw pointer.
            let environment = Environment::from_ptr(ptr);

            // Get the raw pointer and turn it back into a Rust function pointer.
            let raw_ptr = environment.state().to_userdata(ffi::lua_upvalueindex(1));
            let function: Function = mem::transmute(raw_ptr);

            // Invoke the function.
            function(environment).unwrap_or_else(|err: Box<Error>| {
                let mut state = lua::State::from_ptr(ptr);

                state.location(1);
                state.push_string(err.description());
                state.concat(2);
                state.error();
            }) as c_int
        }
    }

    /// Pushes a safe Rust closure onto the stack.
    ///
    /// Note that this currently leaks memory as there is no way to cleanup resources, so it should be used sparingly.
    pub fn push_closure(&self, closure: Closure) {
        let cb = Box::new(closure);

        unsafe {
            // Push a pointer to the closure so that we know what to delegate to.
            self.state().push_light_userdata(Box::into_raw(cb) as *mut c_void);
        }

        // Push a wrapper function onto the stack, which delegates to the given function.
        self.state().push_closure(Some(fn_wrapper), 1);

        // Wrapper function for invoking Rust functions from inside Lua.
        unsafe extern fn fn_wrapper(ptr: *mut ffi::lua_State) -> c_int {
            // Get the environment from the raw pointer.
            let environment = Environment::from_ptr(ptr);

            // Get the raw pointer and turn it back into a Rust closure pointer.
            let raw_ptr = environment.state().to_userdata(ffi::lua_upvalueindex(1));
            let closure: &mut Closure = mem::transmute(raw_ptr);

            // Invoke the closure.
            closure(environment).unwrap_or_else(|err: Box<Error>| {
                let mut state = lua::State::from_ptr(ptr);

                state.location(1);
                state.push_string(err.description());
                state.concat(2);
                state.error();
            }) as c_int
        }
    }

    /// Returns an iterator for iterating over the table at the top of the stack.
    pub fn iter(&self, index: lua::Index) -> TableIterator {
        TableIterator::new(self.state(), index)
    }

    /// Wrapper around `lua_pcall()` that catches errors as a result.
    pub fn call(&self, nargs: i32, nresults: i32, msgh: i32) -> Result<lua::ThreadStatus, Box<Error>> {
        let status = self.state().pcall(nargs, nresults, msgh);

        if status.is_err() {
            if self.state().is_string(-1) {
                Err(self.state().to_str(-1).unwrap().to_string().into())
            } else {
                Err("unknown error".into())
            }
        } else {
            Ok(status)
        }
    }

    /// Pushes the value of a registry key onto the stack.
    pub fn reg_get(&self, name: &str) {
        self.state().push(name);
        self.state().get_table(lua::REGISTRYINDEX);
    }

    /// Sets a registry key to the value at the top of the stack.
    pub fn reg_set(&self, name: &str) {
        self.state().push(name);
        self.state().push_value(-2);
        self.state().set_table(lua::REGISTRYINDEX);
        self.state().pop(1);
    }
}

/// Implement cloning for environment references, with the same semantics as an Rc.
impl Clone for Environment {
    fn clone(&self) -> Self {
        Environment(self.0.clone())
    }
}
<commit_msg>Closures no longer leak memory<commit_after>use ::ScriptResult;
use iter::TableIterator;
use libc::{c_int, c_void};
use lua::{self, ffi};
use rule::Rule;
use std::cell::RefCell;
use std::clone::Clone;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::mem;
use std::path::{Path, PathBuf};
use std::ptr;
use std::rc::{Rc, Weak};
use task::{Task, NamedTask};

// Just a tiny key used to obtain a unique registry location to store the environment pointer.
static KEY: f64 = 264299.0;

/// A function that can be bound to be callable inside the Lua runtime.
pub type Function = fn(Environment) -> ScriptResult;
pub type Closure = FnMut(Environment) -> ScriptResult;

/// Stores the state of an entire task execution environment.
struct EnvironmentStruct {
    /// A map of all named tasks.
    tasks: RefCell<HashMap<String, Rc<NamedTask>>>,

    /// A vector of all defined file rules.
    rules: RefCell<Vec<Rc<Rule>>>,

    /// The default task to run.
    default_task: RefCell<Option<String>>,

    path: PathBuf,

    directory: PathBuf,

    /// A Lua interpreter state.
    state: lua::State,
}

pub struct Environment(Rc<EnvironmentStruct>);

impl Environment {
    /// Creates a new environment for a given script file.
    ///
    /// The instance is placed inside a box to ensure the runner has a constant location in memory
    /// so that it can be referenced by native closures in the runtime.
    pub fn new<P: Into<PathBuf>>(script: P) -> Result<Environment, Box<Error>> {
        let script = script.into();
        let directory = match script.parent() {
            Some(path) => path.into(),
            None => {
                return Err("failed to parse script directory".into());
            }
        };

        let environment = Environment(Rc::new(EnvironmentStruct {
            tasks: RefCell::new(HashMap::new()),
            rules: RefCell::new(Vec::new()),
            default_task: RefCell::new(None),
            path: script,
            directory: directory,
            state: lua::State::new(),
        }));

        // Create a weak pointer to the environment and push it into the registry so that you can
        // access the environment object by its Lua state.
        environment.state().push(KEY);
        let ptr = environment.state().new_userdata_typed();
        let weak = Rc::downgrade(&environment.0);
        unsafe {
            ptr::write(ptr, weak);
        }
        environment.state().set_table(lua::REGISTRYINDEX);

        Ok(environment)
    }

    /// Fetches the environment by its Lua pointer.
    ///
    /// This function is far from safe, and should be used with care.
    pub unsafe fn from_ptr(ptr: ::StatePtr) -> Environment {
        // First, get a State object from the raw pointer.
        let mut state = lua::State::from_ptr(ptr);

        // Fetch the weak environment pointer from the registry.
        state.push(KEY);
        state.get_table(lua::REGISTRYINDEX);

        // Read the weak pointer.
        let weak = match state.to_userdata_typed::<Weak<EnvironmentStruct>>(-1) {
            Some(weak) => {
                lua::State::from_ptr(ptr).pop(1);
                weak
            },
            None => panic!("unable to read environment pointer")
        };

        // Upgrade the pointer and convert it to an environment.
        Environment(match Weak::upgrade(&weak) {
            Some(rc) => rc,
            None => panic!("unable to upgrade environment pointer")
        })
    }

    /// Executes the script.
    pub fn load(&self) -> Result<(), Box<Error>> {
        let path_str = if let Some(s) = self.0.path.to_str() {
            s
        } else {
            return Err("path contains invalid characters".into());
        };

        // Load the given file.
        match self.state().do_file(path_str) {
            lua::ThreadStatus::Ok => { }
            lua::ThreadStatus::FileError => {
                return Err(format!("the file \"{}\" could not be read", path_str).into());
            }
            _ => {
                return Err(self.state().to_str(-1).unwrap().into());
            }
        };

        Ok(())
    }

    /// Gets the full path of the script file.
    pub fn path(&self) -> &Path {
        &self.0.path
    }

    /// Gets the full path of the directory containing the script file.
    pub fn directory(&self) -> &Path {
        &self.0.directory
    }

    /// Gets a list of all registered tasks.
    pub fn tasks(&self) -> Vec<Rc<NamedTask>> {
        self.0.tasks.borrow().values().map(|rc| rc.clone()).collect()
    }

    /// Gets a list of all registered rules.
    pub fn rules(&self) -> Vec<Rc<Rule>> {
        self.0.rules.borrow().iter().map(|rc| rc.clone()).collect()
    }

    /// Creates a new task.
    pub fn create_task(&self, task: Rc<NamedTask>) {
        // Add it to the master list of tasks.
        self.0.tasks.borrow_mut().insert(task.name().into(), task.clone());
    }

    /// Creates a new rule.
    pub fn create_rule(&self, rule: Rc<Rule>) {
        self.0.rules.borrow_mut().push(rule);
    }

    /// Gets a task by name.
    pub fn get_task<S: AsRef<str>>(&self, name: S) -> Option<Rc<NamedTask>> {
        self.0.tasks.borrow().get(name.as_ref()).map(|rc| rc.clone())
    }

    /// Gets the default task to run.
    pub fn default_task<'a>(&'a self) -> Option<String> {
        match *self.0.default_task.borrow() {
            Some(ref task) => Some(task.clone()),
            None => None,
        }
    }

    /// Sets the default task.
    pub fn set_default_task<S: Into<String>>(&self, name: S) {
        *self.0.default_task.borrow_mut() = Some(name.into());
    }

    /// Gets a variable value.
    pub fn var<S: AsRef<str>>(&self, name: S) -> Option<String> {
        let name = name.as_ref();

        // Attempt to match an environment variable, or fallback to a global variable.
        env::var(name)
            .ok()
            .map(|value| value.to_string())
            .or_else(|| {
                let value = if self.state().get_global(name) != lua::Type::Nil {
                    Some(self.state().check_string(-1).to_string())
                } else {
                    None
                };

                self.state().pop(1);
                value
            })
    }

    /// Sets a variable value.
    pub fn set_var<S: AsRef<str>, V: lua::ToLua>(&self, name: S, value: V) {
        self.state().push(value);
        self.state().set_global(name.as_ref());
    }

    /// Adds a path to Lua's require path for modules.
    pub fn include_path<P: Into<PathBuf>>(&self, path: P) {
        let mut lua_path = path.into();
        let mut native_path = lua_path.clone();
        lua_path.push("?.lua");
        native_path.push("?.so");

        self.state().get_global("package");

        // Set the Lua file path.
        self.state().get_field(-1, "path");
        let mut search_path = self.state().to_str(-1).unwrap().to_string();
        search_path.push(';');
        search_path.push_str(&lua_path.to_string_lossy());

        self.state().push_string(&search_path);
        self.state().set_field(-4, "path");
        self.state().pop(2);

        // Set the native file path.
        self.state().get_field(-1, "cpath");
        let mut search_path = self.state().to_str(-1).unwrap().to_string();
        search_path.push(';');
        search_path.push_str(&native_path.to_string_lossy());

        self.state().push_string(&search_path);
        self.state().set_field(-4, "cpath");
        self.state().pop(3);
    }

    /// Gets a mutable instance of the Lua interpreter state.
    ///
    /// This function uses the direct lua_State pointer, so multiple owners can all mutate the same
    /// state simultaneously. Obviously this is a little unsafe, so use responsibly.
    pub fn state(&self) -> lua::State {
        unsafe {
            lua::State::from_ptr(self.0.state.as_ptr())
        }
    }

    /// Evaluates a Lua string inside the runtime.
    pub fn eval<S: AsRef<str>>(&self, code: S) -> Result<(), Box<Error>> {
        if self.state().do_string(code.as_ref()).is_err() {
            return Err(self.state().to_str(-1).unwrap().into());
        }

        Ok(())
    }

    /// Registers a global function in the runtime that can be called by Lua scripts.
    pub fn register_fn(&self, name: &str, f: Function) {
        self.push_fn(f);
        self.state().set_global(name);
    }

    /// Registers a new dynamic module library.
    pub fn register_lib(&self, mtable: &[(&str, Function)]) {
        self.state().create_table(0, mtable.len() as i32);

        for &(name, func) in mtable {
            self.push_fn(func);
            self.state().set_field(-2, name);
        }
    }

    /// Pushes a safe Rust function onto the stack.
    pub fn push_fn(&self, function: Function) {
        unsafe {
            // Push a pointer to the given function so that we know what function to delegate to.
            self.state().push_light_userdata(function as *mut c_void);
        }

        // Push a wrapper function onto the stack, which delegates to the given function.
        self.state().push_closure(Some(fn_wrapper), 1);

        // Wrapper function for invoking Rust functions from inside Lua.
        unsafe extern fn fn_wrapper(ptr: *mut ffi::lua_State) -> c_int {
            // Get the environment from the raw pointer.
            let environment = Environment::from_ptr(ptr);

            // Get the raw pointer and turn it back into a Rust function pointer.
            let fn_ptr = environment.state().to_userdata(ffi::lua_upvalueindex(1));
            let function: Function = mem::transmute(fn_ptr);

            // Invoke the function.
            function(environment).unwrap_or_else(|err: Box<Error>| {
                let mut state = lua::State::from_ptr(ptr);

                state.location(1);
                state.push_string(err.description());
                state.concat(2);
                state.error();
            }) as c_int
        }
    }

    /// Pushes a safe Rust closure onto the stack.
    pub fn push_closure(&self, closure: Box<Closure>) {
        unsafe {
            // Push a pointer to the closure so that we know what to delegate to.
            let closure_ptr = self.state().new_userdata_typed();
            ptr::write(closure_ptr, Box::into_raw(closure));

            // Tell Lua how to clean up the closure.
            if self.state().get_metatable(-1) {
                self.state().push_fn(Some(drop_closure));
                self.state().set_field(-2, "__gc");
                self.state().pop(1);
            }
        }

        // Push a wrapper function onto the stack, which delegates to the given function.
        self.state().push_closure(Some(closure_wrapper), 1);

        // Wrapper function for invoking Rust functions from inside Lua.
        unsafe extern fn closure_wrapper(ptr: *mut ffi::lua_State) -> c_int {
            // Get the environment from the raw pointer.
            let environment = Environment::from_ptr(ptr);

            // Get the upvalue and turn it back into a Rust closure pointer.
            let closure_ptr = environment.state().to_userdata(ffi::lua_upvalueindex(1));
            let closure: *mut *mut Closure = mem::transmute(closure_ptr);

            // Invoke the closure.
            (**closure)(environment).unwrap_or_else(|err: Box<Error>| {
                let mut state = lua::State::from_ptr(ptr);

                state.location(1);
                state.push_string(err.description());
                state.concat(2);
                state.error();
            }) as c_int
        }

        // Cleans up the memory of a closure.
        unsafe extern fn drop_closure(ptr: *mut ffi::lua_State) -> c_int {
            let mut state = lua::State::from_ptr(ptr);

            // Get the closure to free.
            let closure_ptr = state.to_userdata(1);
            let closure: *mut *mut Closure = mem::transmute(closure_ptr);
            let closure_box = Box::from_raw(*closure);

            // Free the closure.
            drop(closure_box);

            0
        }
    }

    /// Returns an iterator for iterating over the table at the top of the stack.
    pub fn iter(&self, index: lua::Index) -> TableIterator {
        TableIterator::new(self.state(), index)
    }

    /// Wrapper around `lua_pcall()` that catches errors as a result.
    pub fn call(&self, nargs: i32, nresults: i32, msgh: i32) -> Result<lua::ThreadStatus, Box<Error>> {
        let status = self.state().pcall(nargs, nresults, msgh);

        if status.is_err() {
            if self.state().is_string(-1) {
                Err(self.state().to_str(-1).unwrap().to_string().into())
            } else {
                Err("unknown error".into())
            }
        } else {
            Ok(status)
        }
    }

    /// Pushes the value of a registry key onto the stack.
    pub fn reg_get(&self, name: &str) {
        self.state().push(name);
        self.state().get_table(lua::REGISTRYINDEX);
    }

    /// Sets a registry key to the value at the top of the stack.
    pub fn reg_set(&self, name: &str) {
        self.state().push(name);
        self.state().push_value(-2);
        self.state().set_table(lua::REGISTRYINDEX);
        self.state().pop(1);
    }
}

/// Implement cloning for environment references, with the same semantics as an Rc.
impl Clone for Environment {
    fn clone(&self) -> Self {
        Environment(self.0.clone())
    }
}
<|endoftext|>


<commit_before>// can you solve the passcode riddle https://www.youtube.com/watch?v=7Vd1dTBVbFg
fn main() {
    const MAX:u8 = 9;
    //casting to different types! how nice is that ;-)
    //println!("{}",1u8 as u16+1u16 as u16);
//    let x:u16;
    for x in 1..(1+MAX as u16) {
        for y in 1..1u16+MAX as u16 {
            for z in 1..1+(MAX as u16) {
                if (x<=y)&&(y<=z)&&(z != y)&&(x*y*z==36) {
                    println!("{} {} {} sum={}",x,y,z,x+y+z);
                } else {
//                    println!("{} {} {} rejected",x,y,z);
                }
            }
        }
    }
    //println!("{}",x);
}
<commit_msg>find the actual passcode<commit_after>// can you solve the passcode riddle https://www.youtube.com/watch?v=7Vd1dTBVbFg

//use std::cmp::Ord;
//use std::cmp::Eq;
use std::cmp::Ordering;

#[derive(Debug,Clone)]
struct Moo {
    x:u8,
    y:u8,
    z:u8,
    sum:u16,
}

impl std::cmp::PartialEq for Moo {
    fn eq(&self, other: &Self) -> bool {
        if (self.sum == other.sum)&&(self.x == other.x)&&(self.y==other.y)&&(self.z==other.z) {
            return true;
        } else {
            return false;
        }
    }
}

impl std::cmp::Eq for Moo {

}

impl std::cmp::PartialOrd for Moo {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.sum > other.sum {
            return Option::Some(Ordering::Greater);
        }else if self.sum < other.sum {
            return Option::Some(Ordering::Less);
        } else {
            return Option::Some(Ordering::Equal);
        }
    }
}

impl std::cmp::Ord for Moo {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.sum < other.sum {
            return Ordering::Less;
        }else if self.sum > other.sum {
            return Ordering::Greater;
        } else {
            return Ordering::Equal;
        }
    }
}

fn main() {
    const MAX:u8 = 9;
    //casting to different types! how nice is that ;-)
    //println!("{}",1u8 as u16+1u16 as u16);
//    let x:u16;
    let mut candidates: Vec<Moo>=vec![];
    for x in 1..1+MAX {//(1+MAX as u16) {
        for y in 1..MAX+1 { //1u16+MAX as u16 {
            for z in 1..MAX+1 {//1+(MAX as u16) {
                if (x<=y)&&(y<=z)
//                    &&(z != y) not yet!
                        && (x as u16 * y as u16 * z as u16) as u16 == 36 //clue1
                        {
                            let m:Moo = Moo {x:x, y:y,z:z,sum:(x+y+z) as u16};
                            candidates.push(m);//clue2 - storing all sums, for now
                    println!("{} {} {} sum={}",x,y,z,x+y+z);
                } else {
//                    println!("{} {} {} rejected",x,y,z);
                }
            }
        }
    }
    //println!("{}",x);
    candidates.sort();
    println!("Candidates(clue1): {:?}",candidates);
    //should keep only dups, because that's why she's unsure - and needed clue3
    //irrelevant block, ignore:
/*    let mut prev=0;
    for i in candidates.iter() {
        if (i.z != i.y) {//clue3 - largest number appears only once
            println!("{:?} clue3 ok", i);
            if prev == i.sum {
                println!("dup: {:?}", i);//coincidence that 229 is last!
            }
        }
        prev=i.sum;
    }*/

    let mut cand2:Vec<Moo> = Vec::new();
    for i in 0..candidates.len()-1 {
        if candidates[i].sum == candidates[i+1].sum {
            cand2.push(candidates[i].clone());
            cand2.push(candidates[i+1].clone());
        }
    }
    println!("Candidates(after clue2): {:?}", cand2);//when she's unsure
    for i in cand2.iter() {
        if i.z != i.y {//clue3 - largest number appears only once
            println!("Found valid passcode {:?}", i);
        }
    }

    /*for x in 1..1+MAX {
        for y in 1..1+MAX {
            for z in 1..1+MAX {

            }
        }
    }*/
}
<|endoftext|>


<commit_before>use story_builder::article_provider::*;
use std::borrow::Borrow;

pub struct StoryBuilder<'a> {
    article_provider: &'a ArticleProvider,
    max_depth: u8,
}

impl <'a> StoryBuilder<'a>  {
    pub fn new(article_provider: &'a ArticleProvider) -> StoryBuilder<'a> {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
        }
    }

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

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

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

                   // Load the end article
        let end_article = match self.article_provider.get(end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(end_topic));},
        };

                   /* To build a story, we need to build a tree starting at the start_article
                      node and going down in a "breadth-first" way; that way, once we find
                      the end note, we know it is the shortest path to it. Also, going depth-first
                      will be impossibly long to complete sincethe depth of the wikipedia
                      article tree is almost infinite. */

                   // Initialize the last level vector with the start article:
        let last_level = vec![start_article];
                   /* We look for a paragraph that holds a reference to our end topic
                      somewhere in the last level we fetched: */
        for article in last_level.iter() {
            if let Some(text) = StoryBuilder::find_text_for_topic_in_article(article.borrow(), end_topic) {
                // Found the topic. Format and return.
                return Ok(format!("{}\r\n", text));
            }
        }
                   // Then, if we did not find anything, we go down one more level:
       let last_level: Vec<Box<Article>> = last_level.iter().flat_map(|article| {
                   /* Iterate on each paragraph of this article, then map on it to
                      get the article for each related topic in it. */
           article.get_paragraphs().iter().flat_map(|paragraph| {
               paragraph.topics.iter().map(|topic| -> Box<Article> {
                   self.article_provider.get(topic).unwrap()
                   })
               })
           }).collect();

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

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

        msg
    }

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

struct TreeNode<T> {
    data: T,
}

impl <T> TreeNode<T> {
    fn new(data: T) -> TreeNode<T> {
        TreeNode {
            data
        }
    }
}

#[cfg(test)]
mod tests;
<commit_msg>Completed code to find X levels deep. Now needs to backtrack for path<commit_after>use story_builder::article_provider::*;
use std::borrow::Borrow;

pub struct StoryBuilder<'a> {
    article_provider: &'a ArticleProvider,
    max_depth: u8,
}

impl <'a> StoryBuilder<'a>  {
    pub fn new(article_provider: &'a ArticleProvider) -> StoryBuilder<'a> {
        StoryBuilder {
            article_provider,
            max_depth: 5, // default value for now
        }
    }

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

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

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

                   // Load the end article
        let end_article = match self.article_provider.get(end_topic) {
            Some(end_topic) => end_topic,
            None => {return Err(self.build_suggestions_msg(end_topic));},
        };

                   /* To build a story, we need to build a tree starting at the start_article
                      node and going down in a "breadth-first" way; that way, once we find
                      the end note, we know it is the shortest path to it. Also, going depth-first
                      will be impossibly long to complete sincethe depth of the wikipedia
                      article tree is almost infinite. */
                   /* We look for a paragraph that holds a reference to our end topic
                      somewhere in the last level we fetched: */
        let mut last_level: Vec<Box<Article>> = vec![start_article]; // starts with start article
        for i in 0..self.max_depth { // To prevent overloading the system, stop after X level deep
                       // Start by loading the next level of articles:
           if i > 0 {
               // Any other iteration: go one level deeper:
               last_level = last_level.iter().flat_map(|article| {
                       /* Iterate on each paragraph of this article, then map on it to
                          get the article for each related topic in it. */
               article.get_paragraphs().iter().flat_map(|paragraph| {
                   paragraph.topics.iter().map(|topic| -> Box<Article> {
                       self.article_provider.get(topic).unwrap()
                       })
                   })
               }).collect();
           }

           for article in last_level.iter() {
               if let Some(text) = StoryBuilder::find_text_for_topic_in_article(article.borrow(), end_topic) {
                   // Found the topic. Format and return.
                   return Ok(format!("{}\r\n", text));
               }
           }
       }

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

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

        msg
    }

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

struct TreeNode<T> {
    data: T,
}

impl <T> TreeNode<T> {
    fn new(data: T) -> TreeNode<T> {
        TreeNode {
            data
        }
    }
}

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


<commit_before>// STD Dependencies -----------------------------------------------------------
use std::fmt;
use std::sync::mpsc;
use std::collections::HashMap;

// Discord Dependencies -------------------------------------------------------
use discord::model::{
    ChannelId, UserId, ServerId,
    Server as DiscordServer,
    Channel as DiscordChannel,
    PossibleServer,
    LiveServer,
    Permissions
};

// External Dependencies ------------------------------------------------------
use clock_ticks;

// Internal Dependencies ------------------------------------------------------
use ::audio::{MixerCommand, MixerEvent};
use ::bot::BotConfig;
use ::core::{Channel, EventQueue, Member};
use ::effect::EffectRegistry;

// Modules --------------------------------------------------------------------
mod alias;
mod ban;
mod channel;
mod config;
mod effect;
mod greeting;
mod member;
mod voice;

// Re-Exports -----------------------------------------------------------------
pub use self::config::ServerConfig;

// Server Voice Abstraction ---------------------------------------------------
#[derive(Debug, PartialEq)]
pub enum ServerVoiceStatus {
    Pending,
    Joined,
    Left
}

#[derive(Debug, PartialEq)]
pub enum ServerRecordingStatus {
    Recording,
    Stopped
}

// Server Abstraction ---------------------------------------------------------
pub struct Server {

    pub id: ServerId,
    pub name: String,

    region: String,
    config: ServerConfig,
    startup_time: u64,

    effects: EffectRegistry,
    voice_channel_id: Option<ChannelId>,
    pinned_channel_id: Option<ChannelId>,
    voice_status: ServerVoiceStatus,
    recording_status: ServerRecordingStatus,

    mixer_commands: Option<mpsc::Sender<MixerCommand>>,
    mixer_events: Option<mpsc::Receiver<MixerEvent>>,

    channels: HashMap<ChannelId, Channel>,
    members: HashMap<UserId, Member>

}

// Public Interface -----------------------------------------------------------
impl Server {

    pub fn id_from_possible_server(server: &PossibleServer<LiveServer>) -> ServerId {
        match *server {
            PossibleServer::Offline(server_id) => server_id,
            PossibleServer::Online(ref live_server) => live_server.id
        }
    }

    pub fn from_possible_server(
        server: PossibleServer<LiveServer>,
        bot_config: &BotConfig,
        queue: &mut EventQueue

    ) -> Server {
        let server = match server {

            PossibleServer::Offline(server_id) => {
                Server {
                    id: server_id,
                    name: "".to_string(),
                    region: "".to_string(),
                    startup_time: clock_ticks::precise_time_ms(),
                    config: ServerConfig::new(&server_id, bot_config),
                    effects: EffectRegistry::new(),
                    voice_channel_id: None,
                    pinned_channel_id: None,
                    voice_status: ServerVoiceStatus::Left,
                    recording_status: ServerRecordingStatus::Stopped,
                    mixer_commands: None,
                    mixer_events: None,
                    channels: HashMap::new(),
                    members: HashMap::new()
                }
            },

            PossibleServer::Online(live_server) => {

                let mut server = Server {
                    id: live_server.id,
                    name: live_server.name,
                    region: live_server.region,
                    startup_time: clock_ticks::precise_time_ms(),
                    config: ServerConfig::new(&live_server.id, bot_config),
                    effects: EffectRegistry::new(),
                    voice_channel_id: None,
                    pinned_channel_id: None,
                    voice_status: ServerVoiceStatus::Left,
                    recording_status: ServerRecordingStatus::Stopped,
                    mixer_commands: None,
                    mixer_events: None,
                    channels: HashMap::new(),
                    members: HashMap::new()
                };

                server.reload();

                for member in live_server.members {
                    server.add_member(member, bot_config);
                }

                for channel in live_server.channels {
                    server.add_discord_channel(DiscordChannel::Public(channel));
                }

                for voice_state in live_server.voice_states {
                    server.update_member_voice_state(
                        voice_state,
                        queue,
                        bot_config
                    );
                }

                server

            }

        };

        info!("{} Initiated.", server);

        server

    }

    pub fn update(&mut self, server: DiscordServer) {
        self.name = server.name;
        self.region = server.region;
        info!("{} Updated", self);
    }

    pub fn reload(&mut self) {

        match self.load_config() {
            Ok(_) => info!("{} Configuration loaded", self),
            Err(reason) => warn!("{} Failed to load configuration: {}", self, reason)
        }

        self.effects.reload(&self.config);

    }

    pub fn get_bot(&self) -> Option<&Member> {
        self.members.values().find(|m| m.is_active_bot)
    }

    pub fn get_bot_permissions(&self, channel_id: &ChannelId) -> Permissions {

        if let Some(channel) = self.channels.get(channel_id) {
            if let Some(member) = self.get_bot() {
                channel.get_member_permissions(member)

            } else {
                Permissions::empty()
            }

        } else {
            Permissions::empty()
        }

    }

    pub fn events(&self) -> Vec<MixerEvent> {
        if let Some(ref mixer_events) = self.mixer_events {
            let mut events = Vec::new();
            while let Ok(event) = mixer_events.try_recv() {
                events.push(event)
            }
            events

        } else {
            vec![]
        }
    }

}

// Internal Interface ---------------------------------------------------------
impl Server {

    fn add_discord_channel(&mut self, channel: DiscordChannel) {
        self.add_channel(Channel::from_discord_channel(channel));
    }

    fn load_config(&mut self) -> Result<(), String> {
        let server_name = format!("{}", self);
        self.config.load(&server_name).and_then(|_| {
            self.sync_config();
            Ok(())
        })
    }

    fn store_config(&mut self) -> Result<(), String> {
        let server_name = format!("{}", self);
        self.sync_config();
        self.config.store(&server_name)
    }

    fn sync_config(&mut self) {
        for mut member in self.members.values_mut() {
            member.is_admin = self.config.admins.contains(&member.nickname);
            member.is_uploader = self.config.uploaders.contains(&member.nickname);
            member.is_banned = self.config.banned.contains(&member.nickname);
        }
    }

}

// Traits ---------------------------------------------------------------------
impl fmt::Display for Server {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "[Server {} {} / {}]",
            self.name, self.channels.len(), self.members.len()
        )
    }
}

<commit_msg>Fix member status not being updated after initial server load.<commit_after>// STD Dependencies -----------------------------------------------------------
use std::fmt;
use std::sync::mpsc;
use std::collections::HashMap;

// Discord Dependencies -------------------------------------------------------
use discord::model::{
    ChannelId, UserId, ServerId,
    Server as DiscordServer,
    Channel as DiscordChannel,
    PossibleServer,
    LiveServer,
    Permissions
};

// External Dependencies ------------------------------------------------------
use clock_ticks;

// Internal Dependencies ------------------------------------------------------
use ::audio::{MixerCommand, MixerEvent};
use ::bot::BotConfig;
use ::core::{Channel, EventQueue, Member};
use ::effect::EffectRegistry;

// Modules --------------------------------------------------------------------
mod alias;
mod ban;
mod channel;
mod config;
mod effect;
mod greeting;
mod member;
mod voice;

// Re-Exports -----------------------------------------------------------------
pub use self::config::ServerConfig;

// Server Voice Abstraction ---------------------------------------------------
#[derive(Debug, PartialEq)]
pub enum ServerVoiceStatus {
    Pending,
    Joined,
    Left
}

#[derive(Debug, PartialEq)]
pub enum ServerRecordingStatus {
    Recording,
    Stopped
}

// Server Abstraction ---------------------------------------------------------
pub struct Server {

    pub id: ServerId,
    pub name: String,

    region: String,
    config: ServerConfig,
    startup_time: u64,

    effects: EffectRegistry,
    voice_channel_id: Option<ChannelId>,
    pinned_channel_id: Option<ChannelId>,
    voice_status: ServerVoiceStatus,
    recording_status: ServerRecordingStatus,

    mixer_commands: Option<mpsc::Sender<MixerCommand>>,
    mixer_events: Option<mpsc::Receiver<MixerEvent>>,

    channels: HashMap<ChannelId, Channel>,
    members: HashMap<UserId, Member>

}

// Public Interface -----------------------------------------------------------
impl Server {

    pub fn id_from_possible_server(server: &PossibleServer<LiveServer>) -> ServerId {
        match *server {
            PossibleServer::Offline(server_id) => server_id,
            PossibleServer::Online(ref live_server) => live_server.id
        }
    }

    pub fn from_possible_server(
        server: PossibleServer<LiveServer>,
        bot_config: &BotConfig,
        queue: &mut EventQueue

    ) -> Server {
        let server = match server {

            PossibleServer::Offline(server_id) => {
                Server {
                    id: server_id,
                    name: "".to_string(),
                    region: "".to_string(),
                    startup_time: clock_ticks::precise_time_ms(),
                    config: ServerConfig::new(&server_id, bot_config),
                    effects: EffectRegistry::new(),
                    voice_channel_id: None,
                    pinned_channel_id: None,
                    voice_status: ServerVoiceStatus::Left,
                    recording_status: ServerRecordingStatus::Stopped,
                    mixer_commands: None,
                    mixer_events: None,
                    channels: HashMap::new(),
                    members: HashMap::new()
                }
            },

            PossibleServer::Online(live_server) => {

                let mut server = Server {
                    id: live_server.id,
                    name: live_server.name,
                    region: live_server.region,
                    startup_time: clock_ticks::precise_time_ms(),
                    config: ServerConfig::new(&live_server.id, bot_config),
                    effects: EffectRegistry::new(),
                    voice_channel_id: None,
                    pinned_channel_id: None,
                    voice_status: ServerVoiceStatus::Left,
                    recording_status: ServerRecordingStatus::Stopped,
                    mixer_commands: None,
                    mixer_events: None,
                    channels: HashMap::new(),
                    members: HashMap::new()
                };

                server.reload();

                for member in live_server.members {
                    server.add_member(member, bot_config);
                }

                for channel in live_server.channels {
                    server.add_discord_channel(DiscordChannel::Public(channel));
                }

                for voice_state in live_server.voice_states {
                    server.update_member_voice_state(
                        voice_state,
                        queue,
                        bot_config
                    );
                }

                server.sync_config();

                server

            }

        };

        info!("{} Initiated.", server);

        server

    }

    pub fn update(&mut self, server: DiscordServer) {
        self.name = server.name;
        self.region = server.region;
        info!("{} Updated", self);
    }

    pub fn reload(&mut self) {

        match self.load_config() {
            Ok(_) => info!("{} Configuration loaded", self),
            Err(reason) => warn!("{} Failed to load configuration: {}", self, reason)
        }

        self.effects.reload(&self.config);

    }

    pub fn get_bot(&self) -> Option<&Member> {
        self.members.values().find(|m| m.is_active_bot)
    }

    pub fn get_bot_permissions(&self, channel_id: &ChannelId) -> Permissions {

        if let Some(channel) = self.channels.get(channel_id) {
            if let Some(member) = self.get_bot() {
                channel.get_member_permissions(member)

            } else {
                Permissions::empty()
            }

        } else {
            Permissions::empty()
        }

    }

    pub fn events(&self) -> Vec<MixerEvent> {
        if let Some(ref mixer_events) = self.mixer_events {
            let mut events = Vec::new();
            while let Ok(event) = mixer_events.try_recv() {
                events.push(event)
            }
            events

        } else {
            vec![]
        }
    }

}

// Internal Interface ---------------------------------------------------------
impl Server {

    fn add_discord_channel(&mut self, channel: DiscordChannel) {
        self.add_channel(Channel::from_discord_channel(channel));
    }

    fn load_config(&mut self) -> Result<(), String> {
        let server_name = format!("{}", self);
        self.config.load(&server_name).and_then(|_| {
            self.sync_config();
            Ok(())
        })
    }

    fn store_config(&mut self) -> Result<(), String> {
        let server_name = format!("{}", self);
        self.sync_config();
        self.config.store(&server_name)
    }

    fn sync_config(&mut self) {
        for mut member in self.members.values_mut() {
            member.is_admin = self.config.admins.contains(&member.nickname);
            member.is_uploader = self.config.uploaders.contains(&member.nickname);
            member.is_banned = self.config.banned.contains(&member.nickname);
        }
    }

}

// Traits ---------------------------------------------------------------------
impl fmt::Display for Server {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "[Server {} {} / {}]",
            self.name, self.channels.len(), self.members.len()
        )
    }
}

<|endoftext|>


<commit_before>/// Elf reader module
///

const EI_NIDENT: usize = 4;

// ELF32 types
#[allow(non_camel_case_types)]
type Elf32_Addr = u32;
#[allow(non_camel_case_types)]
type Elf32_Half = u16;
#[allow(non_camel_case_types)]
type Elf32_Lworld = u64;
#[allow(non_camel_case_types)]
type Elf32_Off = u32;
#[allow(non_camel_case_types)]
type Elf32_Sword = i32;
#[allow(non_camel_case_types)]
type Elf32_Word = u32;

/// Elf32 header.
/// Source: http://www.freebsd.org/cgi/man.cgi?elf(5)
#[repr(C)]
pub struct Elf32_Ehdr {
    e_ident: [u8; EI_NIDENT],
    e_type: Elf32_Half,
    e_machine: Elf32_Half,
    e_version: Elf32_Word,
    e_entry: Elf32_Addr,
    e_phoff: Elf32_Off,
    e_shoff: Elf32_Off,
    e_flags: Elf32_Word,
    e_ehsize: Elf32_Half,
    e_phentsize: Elf32_Half,
    e_phnum: Elf32_Half,
    e_shentsize: Elf32_Half,
    e_shnum: Elf32_Half,
    e_shstrndx: Elf32_Half,
}

<commit_msg>Add Elf32 program header definition<commit_after>/// Elf reader module
///

const EI_NIDENT: usize = 4;

// ELF32 types
#[allow(non_camel_case_types)]
type Elf32_Addr = u32;
#[allow(non_camel_case_types)]
type Elf32_Half = u16;
#[allow(non_camel_case_types)]
type Elf32_Lworld = u64;
#[allow(non_camel_case_types)]
type Elf32_Off = u32;
#[allow(non_camel_case_types)]
type Elf32_Sword = i32;
#[allow(non_camel_case_types)]
type Elf32_Word = u32;

/// Elf32 header.
/// Source: http://www.freebsd.org/cgi/man.cgi?elf(5)
#[repr(C)]
pub struct Elf32_Ehdr {
    e_ident: [u8; EI_NIDENT],
    e_type: Elf32_Half,
    e_machine: Elf32_Half,
    e_version: Elf32_Word,
    e_entry: Elf32_Addr,
    e_phoff: Elf32_Off,
    e_shoff: Elf32_Off,
    e_flags: Elf32_Word,
    e_ehsize: Elf32_Half,
    e_phentsize: Elf32_Half,
    e_phnum: Elf32_Half,
    e_shentsize: Elf32_Half,
    e_shnum: Elf32_Half,
    e_shstrndx: Elf32_Half,
}

/// Elf32 Program header
#[repr(C)]
pub struct Elf32_Phdr {
    p_type: Elf32_Word,
    p_offset: Elf32_Off,
    p_vaddr: Elf32_Addr,
    p_paddr: Elf32_Addr,
    p_filesz: Elf32_Word,
    p_memsz: Elf32_Word,
    p_flags: Elf32_Word,
    p_align: Elf32_Word,
}
<|endoftext|>


<commit_before>use std::io::{self, Read};
use std::iter::Iterator;
use std::path::{Component, Path, PathBuf};
use std::slice;

use flate2::read::GzDecoder;
use tar;
use time::Timespec;

use backend::Backend;
use collections::{CollectionsStatus, SignatureFile};

pub struct BackupFiles {
    chains: Vec<Chain>
}

pub struct Snapshot<'a> {
    index: u8,
    chain: &'a Chain
}

#[derive(Debug)]
pub struct File<'a> {
    pub name: &'a str,
    pub last_modified: Timespec
}

/// Iterator over a list of backup snapshots.
pub struct Snapshots<'a> {
    chain_iter: slice::Iter<'a, Chain>,
    chain: Option<&'a Chain>,
    snapshot_id: u8
}

pub struct SnapshotFiles<'a> {
    index: u8,
    iter: slice::Iter<'a, PathSnapshots>
}

enum DiffType {
    Signature,
    Snapshot,
    Deleted
}

/// Store separately informations about the signatures and informations about the paths in the
/// signatures. This allows to reuse informations between snapshots and avoid duplicating them.
struct Chain {
    timestamps: Vec<Timespec>,
    files: Vec<PathSnapshots>
}

struct PathSnapshots {
    // the directory or file path
    path: PathBuf,
    // all the snapshots for this path
    snapshots: Vec<PathSnapshot>
}

struct PathSnapshot {
    // info are None if the snapshot has deleted this path
    info: Option<PathInfo>,
    // the index of the snapshot in the chain
    index: u8
}

/// Informations about a path inside a snapshot.
struct PathInfo {
    mtime: Timespec
}

impl BackupFiles {
    pub fn new<B: Backend>(backend: &B) -> io::Result<BackupFiles> {
        let collection = {
            let filenames = try!(backend.get_file_names());
            CollectionsStatus::from_filenames(&filenames)
        };
        let mut chains: Vec<Chain> = Vec::new();
        let coll_chains = collection.signature_chains();
        for coll_chain in coll_chains {
            // translate collections::SignatureChain into a Chain
            let mut chain = Chain{ timestamps: Vec::new(), files: Vec::new() };
            // add to the chain the full signature and all the incremental signatures
            // if an error occurs in the full signature exit
            let file = try!(backend.open_file(coll_chain.fullsig.file_name.as_ref()));
            try!(add_sigfile_to_chain(&mut chain, file, &coll_chain.fullsig));
            for inc in coll_chain.inclist.iter() {
                // TODO: if an error occurs here, do not exit with an error, instead
                // break the iteration and store the error inside the chain
                let file = try!(backend.open_file(inc.file_name.as_ref()));
                try!(add_sigfile_to_chain(&mut chain, file, &inc));
            }
            chains.push(chain);
        }
        Ok(BackupFiles{ chains: chains })
    }

    pub fn snapshots(&self) -> Snapshots {
        let mut iter = self.chains.iter();
        let first_chain = iter.next();
        Snapshots{ chain_iter: iter, chain: first_chain, snapshot_id: 0 }
    }
}

impl<'a> Iterator for Snapshots<'a> {
    type Item = Snapshot<'a>;

    fn next(&mut self) -> Option<Snapshot<'a>> {
        loop {
            if let Some(chain) = self.chain {
                if let Some(_) = chain.timestamps.get(self.snapshot_id as usize) {
                    let result = Some(Snapshot{ index: self.snapshot_id, chain: chain });
                    self.snapshot_id += 1;
                    return result;
                }
                else {
                    // this chain is completed
                    // go to next chain
                    self.chain = self.chain_iter.next();
                    self.snapshot_id = 0;
                }
            }
            else {
                // no other chains are present
                return None
            }
        }
    }
}

impl<'a> Snapshot<'a> {
    pub fn time(&self) -> Timespec {
        self.chain.timestamps[self.index as usize]
    }

    pub fn files(&self) -> SnapshotFiles<'a> {
        SnapshotFiles{ index: self.index, iter: self.chain.files.iter() }
    }
}

impl<'a> Iterator for SnapshotFiles<'a> {
    type Item = File<'a>;

    fn next(&mut self) -> Option<File<'a>> {
        while let Some(path_snapshots) = self.iter.next() {
            if let Some(s) = path_snapshots.snapshots.iter().rev().find(|s| s.index <= self.index) {
                // now we have a path info present in this snapshot
                // if it is not deleted return it
                if let Some(ref info) = s.info {
                    return Some(File{ name: path_snapshots.path.to_str().unwrap(), last_modified: info.mtime })
                }
            }
        }
        None
    }
}

fn add_sigfile_to_chain<R: Read>(chain: &mut Chain,
                                 file: R,
                                 sigfile: &SignatureFile) -> io::Result<()>
{
    let result = {
        let snapshot_id = chain.files.len() as u8;
        if sigfile.compressed {
            let gz_decoder = try!(GzDecoder::new(file));
            add_sigtar_to_snapshots(&mut chain.files, tar::Archive::new(gz_decoder), snapshot_id)
        }
        else {
            add_sigtar_to_snapshots(&mut chain.files, tar::Archive::new(file), snapshot_id)
        }
    };
    if result.is_ok() {
        // add to the list of signatures only if everything is ok
        // we do not need to cleanup the chain if someting went wrong, because if the list of
        // signatures is not updated, the change is not observable
        chain.timestamps.push(sigfile.time);
    }
    result
}

fn add_sigtar_to_snapshots<R: Read>(snapshots: &mut Vec<PathSnapshots>,
                                    mut tar: tar::Archive<R>,
                                    snapshot_id: u8) -> io::Result<()>
{
    let mut new_files: Vec<PathSnapshots> = Vec::new();
    {
        let mut old_snapshots = snapshots.iter_mut();
        for tarfile in try!(tar.files_mut()) {
            // we can ignore paths with errors
            // the only problem here is that we miss some change in the chain, but it is better
            // than abort the whole signature
            let tarfile = unwrap_or_continue!(tarfile);
            let header = tarfile.header();
            let path = unwrap_or_continue!(header.path());
            let (difftype, path) = unwrap_opt_or_continue!(parse_snapshot_path(&path));
            let info = match difftype {
                DiffType::Signature | DiffType::Snapshot => {
                    let time = Timespec::new(header.mtime().unwrap_or(0 as u64) as i64, 0);
                    Some(PathInfo{ mtime: time })
                }
                _ => None
            };
            let new_snapshot = PathSnapshot{ info: info, index: snapshot_id };
            // find the current path in the old snapshots
            // note: they are ordered
            while let Some(path_snapshots) = old_snapshots.next() {
                if path_snapshots.path.as_path() < path {
                    continue;
                }
                if path_snapshots.path.as_path() == path {
                    // this path is already present in old snapshots: update them
                    path_snapshots.snapshots.push(new_snapshot);
                }
                else {
                    // the path is not present in the old snapshots: add to new list
                    new_files.push(PathSnapshots{
                        path: path.to_path_buf(),
                        snapshots: vec![new_snapshot]
                    });
                }
                break;
            }
        }
    }
    // merge the new files with old snapshots
    if !new_files.is_empty() {
        // TODO: Performance hurt here: we have two sorted arrays to merge,
        // better to use this algorithm: http://stackoverflow.com/a/4553321/1667955
        snapshots.extend(new_files.into_iter());
        snapshots.sort_by(|a, b| a.path.cmp(&b.path));
    }
    Ok(())
}

fn parse_snapshot_path(path: &Path) -> Option<(DiffType, &Path)> {
    // split the path in (first directory, the remaining path)
    // the first is the type, the remaining is the real path
    let mut pcomps = path.components();
    let pfirst = try_opt!(pcomps.next());
    if let Component::Normal(strfirst) = pfirst {
        let difftype = match strfirst.to_str() {
            Some("signature") => DiffType::Signature,
            Some("snapshot")  => DiffType::Snapshot,
            Some("deleted")   => DiffType::Deleted,
            _                 => { return None; }
        };
        let realpath = pcomps.as_path();
        Some((difftype, realpath))
    }
    else {
        None
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use backend::local::LocalBackend;
    use time_utils::to_pretty_local;

    #[test]
    fn open_from_local() {
        let backend = LocalBackend::new("tests/backups/single_vol").unwrap();
        let files = BackupFiles::new(&backend).unwrap();
        println!("Backup snapshots:");
        for snapshot in files.snapshots() {
            println!("Snapshot {}", to_pretty_local(snapshot.time()));
            for file in snapshot.files() {
                println!("    {:?}", file);
            }
        }
    }
}
<commit_msg>Fixed a bug in adding paths when nothing present<commit_after>use std::io::{self, Read};
use std::iter::Iterator;
use std::path::{Component, Path, PathBuf};
use std::slice;

use flate2::read::GzDecoder;
use tar;
use time::Timespec;

use backend::Backend;
use collections::{CollectionsStatus, SignatureFile};

pub struct BackupFiles {
    chains: Vec<Chain>
}

pub struct Snapshot<'a> {
    index: u8,
    chain: &'a Chain
}

#[derive(Debug)]
pub struct File<'a> {
    pub name: &'a str,
    pub last_modified: Timespec
}

/// Iterator over a list of backup snapshots.
pub struct Snapshots<'a> {
    chain_iter: slice::Iter<'a, Chain>,
    chain: Option<&'a Chain>,
    snapshot_id: u8
}

pub struct SnapshotFiles<'a> {
    index: u8,
    iter: slice::Iter<'a, PathSnapshots>
}

enum DiffType {
    Signature,
    Snapshot,
    Deleted
}

/// Store separately informations about the signatures and informations about the paths in the
/// signatures. This allows to reuse informations between snapshots and avoid duplicating them.
struct Chain {
    timestamps: Vec<Timespec>,
    files: Vec<PathSnapshots>
}

struct PathSnapshots {
    // the directory or file path
    path: PathBuf,
    // all the snapshots for this path
    snapshots: Vec<PathSnapshot>
}

struct PathSnapshot {
    // info are None if the snapshot has deleted this path
    info: Option<PathInfo>,
    // the index of the snapshot in the chain
    index: u8
}

/// Informations about a path inside a snapshot.
struct PathInfo {
    mtime: Timespec
}

impl BackupFiles {
    pub fn new<B: Backend>(backend: &B) -> io::Result<BackupFiles> {
        let collection = {
            let filenames = try!(backend.get_file_names());
            CollectionsStatus::from_filenames(&filenames)
        };
        let mut chains: Vec<Chain> = Vec::new();
        let coll_chains = collection.signature_chains();
        for coll_chain in coll_chains {
            // translate collections::SignatureChain into a Chain
            let mut chain = Chain{ timestamps: Vec::new(), files: Vec::new() };
            // add to the chain the full signature and all the incremental signatures
            // if an error occurs in the full signature exit
            let file = try!(backend.open_file(coll_chain.fullsig.file_name.as_ref()));
            try!(add_sigfile_to_chain(&mut chain, file, &coll_chain.fullsig));
            for inc in coll_chain.inclist.iter() {
                // TODO: if an error occurs here, do not exit with an error, instead
                // break the iteration and store the error inside the chain
                let file = try!(backend.open_file(inc.file_name.as_ref()));
                try!(add_sigfile_to_chain(&mut chain, file, &inc));
            }
            chains.push(chain);
        }
        Ok(BackupFiles{ chains: chains })
    }

    pub fn snapshots(&self) -> Snapshots {
        let mut iter = self.chains.iter();
        let first_chain = iter.next();
        Snapshots{ chain_iter: iter, chain: first_chain, snapshot_id: 0 }
    }
}

impl<'a> Iterator for Snapshots<'a> {
    type Item = Snapshot<'a>;

    fn next(&mut self) -> Option<Snapshot<'a>> {
        loop {
            if let Some(chain) = self.chain {
                if let Some(_) = chain.timestamps.get(self.snapshot_id as usize) {
                    let result = Some(Snapshot{ index: self.snapshot_id, chain: chain });
                    self.snapshot_id += 1;
                    return result;
                }
                else {
                    // this chain is completed
                    // go to next chain
                    self.chain = self.chain_iter.next();
                    self.snapshot_id = 0;
                }
            }
            else {
                // no other chains are present
                return None
            }
        }
    }
}

impl<'a> Snapshot<'a> {
    pub fn time(&self) -> Timespec {
        self.chain.timestamps[self.index as usize]
    }

    pub fn files(&self) -> SnapshotFiles<'a> {
        SnapshotFiles{ index: self.index, iter: self.chain.files.iter() }
    }
}

impl<'a> Iterator for SnapshotFiles<'a> {
    type Item = File<'a>;

    fn next(&mut self) -> Option<File<'a>> {
        while let Some(path_snapshots) = self.iter.next() {
            if let Some(s) = path_snapshots.snapshots.iter().rev().find(|s| s.index <= self.index) {
                // now we have a path info present in this snapshot
                // if it is not deleted return it
                if let Some(ref info) = s.info {
                    return Some(File{ name: path_snapshots.path.to_str().unwrap(), last_modified: info.mtime })
                }
            }
        }
        None
    }
}

fn add_sigfile_to_chain<R: Read>(chain: &mut Chain,
                                 file: R,
                                 sigfile: &SignatureFile) -> io::Result<()>
{
    let result = {
        let snapshot_id = chain.files.len() as u8;
        if sigfile.compressed {
            let gz_decoder = try!(GzDecoder::new(file));
            add_sigtar_to_snapshots(&mut chain.files, tar::Archive::new(gz_decoder), snapshot_id)
        }
        else {
            add_sigtar_to_snapshots(&mut chain.files, tar::Archive::new(file), snapshot_id)
        }
    };
    if result.is_ok() {
        // add to the list of signatures only if everything is ok
        // we do not need to cleanup the chain if someting went wrong, because if the list of
        // signatures is not updated, the change is not observable
        chain.timestamps.push(sigfile.time);
    }
    result
}

fn add_sigtar_to_snapshots<R: Read>(snapshots: &mut Vec<PathSnapshots>,
                                    mut tar: tar::Archive<R>,
                                    snapshot_id: u8) -> io::Result<()>
{
    let mut new_files: Vec<PathSnapshots> = Vec::new();
    {
        let mut old_snapshots = snapshots.iter_mut();
        for tarfile in try!(tar.files_mut()) {
            // we can ignore paths with errors
            // the only problem here is that we miss some change in the chain, but it is better
            // than abort the whole signature
            let tarfile = unwrap_or_continue!(tarfile);
            let header = tarfile.header();
            let path = unwrap_or_continue!(header.path());
            let (difftype, path) = unwrap_opt_or_continue!(parse_snapshot_path(&path));
            let info = match difftype {
                DiffType::Signature | DiffType::Snapshot => {
                    let time = Timespec::new(header.mtime().unwrap_or(0 as u64) as i64, 0);
                    Some(PathInfo{ mtime: time })
                }
                _ => None
            };
            let new_snapshot = PathSnapshot{ info: info, index: snapshot_id };
            // find the current path in the old snapshots
            // note: they are ordered
            let position = {
                let mut position: Option<&mut PathSnapshots> = None;
                while let Some(path_snapshots) = old_snapshots.next() {
                    if path_snapshots.path.as_path() < path {
                        continue;
                    }
                    if path_snapshots.path.as_path() == path {
                        // this path is already present in old snapshots: update them
                        position = Some(path_snapshots);
                    }
                    else {
                        // we've already reached the first item next to the current path
                        // so, the path is not present in old snapshots
                        position = None;
                    }
                    break;
                };
                position
            };
            if let Some(path_snapshots) = position {
                path_snapshots.snapshots.push(new_snapshot);
            }
            else {
                // the path is not present in the old snapshots: add to new list
                new_files.push(PathSnapshots{
                    path: path.to_path_buf(),
                    snapshots: vec![new_snapshot]
                });
            }
        }
    }
    // merge the new files with old snapshots
    if !new_files.is_empty() {
        // TODO: Performance hurt here: we have two sorted arrays to merge,
        // better to use this algorithm: http://stackoverflow.com/a/4553321/1667955
        snapshots.extend(new_files.into_iter());
        snapshots.sort_by(|a, b| a.path.cmp(&b.path));
    }
    Ok(())
}

fn parse_snapshot_path(path: &Path) -> Option<(DiffType, &Path)> {
    // split the path in (first directory, the remaining path)
    // the first is the type, the remaining is the real path
    let mut pcomps = path.components();
    let pfirst = try_opt!(pcomps.next());
    if let Component::Normal(strfirst) = pfirst {
        let difftype = match strfirst.to_str() {
            Some("signature") => DiffType::Signature,
            Some("snapshot")  => DiffType::Snapshot,
            Some("deleted")   => DiffType::Deleted,
            _                 => { return None; }
        };
        let realpath = pcomps.as_path();
        Some((difftype, realpath))
    }
    else {
        None
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use backend::local::LocalBackend;
    use time_utils::to_pretty_local;

    #[test]
    fn open_from_local() {
        let backend = LocalBackend::new("tests/backups/single_vol").unwrap();
        let files = BackupFiles::new(&backend).unwrap();
        println!("Backup snapshots:");
        for snapshot in files.snapshots() {
            println!("Snapshot {}", to_pretty_local(snapshot.time()));
            for file in snapshot.files() {
                println!("    {:?}", file);
            }
        }
    }
}
<|endoftext|>