<commit_before>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::process::exit;
use std::fs::File;
use std::io::Write;
use std::time::Duration;
use std::path::PathBuf;
use libc::fork;
use time;
use DocBuilderOptions;
use DocBuilder;
use utils::{update_sources, update_release_activity, github_updater};
use db::{connect_db, update_search_index};

pub fn start_daemon() {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        env::var(v).expect("Environment variable not found");
    }

    let dbopts = opts();

    // check paths once
    dbopts.check_paths().unwrap();

    // fork the process
    let pid = unsafe { fork() };
    if pid > 0 {
        let mut file = File::create(dbopts.prefix.join("cratesfyi.pid"))
            .expect("Failed to create pid file");
        writeln!(&mut file, "{}", pid).expect("Failed to write pid");

        info!("cratesfyi {} daemon started on: {}", ::BUILD_VERSION, pid);
        exit(0);
    }

    // check new crates every 5 minutes
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(300));
            debug!("Checking new crates");
            let doc_builder = DocBuilder::new(opts());
            if let Err(e) = doc_builder.get_new_crates() {
                error!("Failed to get new crates: {}", e);
            }
        }
    });

    // build new crates every 3 minutes
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(180));

            let mut opts = opts();
            opts.skip_if_exists = true;

            // check lock file
            if opts.prefix.join("cratesfyi.lock").exists() {
                warn!("Lock file exits, skipping building new crates");
                continue;
            }

            // update index
            if let Err(e) = update_sources() {
                error!("Failed to update sources: {}", e);
                continue;
            }

            let mut doc_builder = DocBuilder::new(opts);
            if let Err(e) = doc_builder.load_cache() {
                error!("Failed to load cache: {}", e);
                continue;
            }

            debug!("Building new crates");
            if let Err(e) = doc_builder.build_packages_queue() {
                error!("Failed build new crates: {}", e);
            }

            if let Err(e) = doc_builder.save_cache() {
                error!("Failed to save cache: {}", e);
            }
        }
    });

    // update release activity everyday at 02:00
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 2 && now.tm_min == 0 {
                info!("Updating release activity");
                if let Err(e) = update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    });

    // update search index every 3 hours
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 3));
            let conn = connect_db().expect("Failed to connect database");
            if let Err(e) = update_search_index(&conn) {
                error!("Failed to update search index: {}", e);
            }
        }
    });

    // update github stats every 6 hours
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 6));
            if let Err(e) = github_updater() {
                error!("Failed to update github fields: {}", e);
            }
        }
    });

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    ::start_web_server(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
        .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<commit_msg>Increase delay between pull requests<commit_after>//! Simple daemon
//!
//! This daemon will start web server, track new packages and build them

use std::{env, thread};
use std::process::exit;
use std::fs::File;
use std::io::Write;
use std::time::Duration;
use std::path::PathBuf;
use libc::fork;
use time;
use DocBuilderOptions;
use DocBuilder;
use utils::{update_sources, update_release_activity, github_updater};
use db::{connect_db, update_search_index};

pub fn start_daemon() {
    // first check required environment variables
    for v in ["CRATESFYI_PREFIX",
              "CRATESFYI_PREFIX",
              "CRATESFYI_GITHUB_USERNAME",
              "CRATESFYI_GITHUB_ACCESSTOKEN"]
        .iter() {
        env::var(v).expect("Environment variable not found");
    }

    let dbopts = opts();

    // check paths once
    dbopts.check_paths().unwrap();

    // fork the process
    let pid = unsafe { fork() };
    if pid > 0 {
        let mut file = File::create(dbopts.prefix.join("cratesfyi.pid"))
            .expect("Failed to create pid file");
        writeln!(&mut file, "{}", pid).expect("Failed to write pid");

        info!("cratesfyi {} daemon started on: {}", ::BUILD_VERSION, pid);
        exit(0);
    }

    // check new crates every 15 minutes
    // FIXME: why not just check new crates and build them if there is any?
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(900));
            debug!("Checking new crates");
            let doc_builder = DocBuilder::new(opts());
            if let Err(e) = doc_builder.get_new_crates() {
                error!("Failed to get new crates: {}", e);
            }
        }
    });

    // build crates in que every 12 minutes
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(720));

            let mut opts = opts();
            opts.skip_if_exists = true;

            // check lock file
            if opts.prefix.join("cratesfyi.lock").exists() {
                warn!("Lock file exits, skipping building new crates");
                continue;
            }

            // update index
            if let Err(e) = update_sources() {
                error!("Failed to update sources: {}", e);
                continue;
            }

            let mut doc_builder = DocBuilder::new(opts);
            if let Err(e) = doc_builder.load_cache() {
                error!("Failed to load cache: {}", e);
                continue;
            }

            debug!("Building new crates");
            if let Err(e) = doc_builder.build_packages_queue() {
                error!("Failed build new crates: {}", e);
            }

            if let Err(e) = doc_builder.save_cache() {
                error!("Failed to save cache: {}", e);
            }
        }
    });

    // update release activity everyday at 02:00
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60));
            let now = time::now();
            if now.tm_hour == 2 && now.tm_min == 0 {
                info!("Updating release activity");
                if let Err(e) = update_release_activity() {
                    error!("Failed to update release activity: {}", e);
                }
            }
        }
    });

    // update search index every 3 hours
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 3));
            let conn = connect_db().expect("Failed to connect database");
            if let Err(e) = update_search_index(&conn) {
                error!("Failed to update search index: {}", e);
            }
        }
    });

    // update github stats every 6 hours
    thread::spawn(move || {
        loop {
            thread::sleep(Duration::from_secs(60 * 60 * 6));
            if let Err(e) = github_updater() {
                error!("Failed to update github fields: {}", e);
            }
        }
    });

    // TODO: update ssl certificate every 3 months

    // at least start web server
    info!("Starting web server");
    ::start_web_server(None);
}

fn opts() -> DocBuilderOptions {
    let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX")
        .expect("CRATESFYI_PREFIX environment variable not found"));
    DocBuilderOptions::from_prefix(prefix)
}
<|endoftext|>


<commit_before>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).
//!
//! # Examples
//! Here we take a node and execute some instructions and check if double 1 has
//! the expected result.
//!
//! ```rust
//! extern crate tis_100_superoptimizer;
//!
//! use tis_100_superoptimizer::TIS_100::{Node,Instruction,Source,Destination,Register};
//!
//! fn main() {
//!     let node: Node = Node::new();
//!     let last: Node = node
//!         .execute(Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)))
//!         .execute(Instruction::ADD(Source::Register(Register::ACC)));
//!
//!     assert_eq!(2, last.acc);
//! }
//! ```

use std::fmt::{Debug,Formatter,Error};

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing an `Instruction` on it.
#[derive(Debug,PartialEq,Eq)]
pub struct Node {
    /// The accumulator for the basic execution node.
    pub acc: i32,
    bac: i32,
    pc: usize,
    program: Program,
}

/// A `Program` is a sequence of `Instruction`s
pub struct Program(pub Vec<Instruction>);

impl PartialEq for Program {
    fn eq(&self, other: &Program) -> bool {
        let Program(ref self_instructions) = *self;
        let Program(ref other_instructions) = *other;
        if self_instructions.len() == other_instructions.len() {
            for index in 0..(self_instructions.len()) {
                let ref self_instruction = self_instructions[index];
                let ref other_instruction = other_instructions[index];
                if self_instruction != other_instruction {
                    return false
                }
            }
            true
        } else {
            false
        }
    }
}

impl Eq for Program {}

impl Debug for Program {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let Program(ref instructions) = *self;
        write!(f, "[{}:", instructions.len()).unwrap();
        for instruction in instructions {
            write!(f, " {:?}", instruction).unwrap();
        }
        write!(f, "]")
    }
}

impl Clone for Program {
    fn clone(&self) -> Program {
        let mut clone_instructions: Vec<Instruction> = vec![];
        let Program(ref self_instructions) = *self;
        for ref mut instruction in self_instructions {
            clone_instructions.push(instruction.clone());
        }
        Program(clone_instructions)
    }
}

/// `Instruction`s are executed by a `Node`.
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Instruction {
    /// Does nothing
    NOP,
    /// Moves value from `Source` to `Destination`
    MOV(Source, Destination),
    /// Swaps the value of the accumulator (acc) and the backup (bac) register
    SWP,
    /// Saves the value of the accumulator (acc) to the backup register
    SAV,
    /// Add value from `Source` to accumulator (acc), storing result in acc
    ADD(Source),
    /// Subtracts value from `Source` from accumulator (acc), storing result in acc
    SUB(Source),
}

/// `Source` are either ports, registers or literals
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Source {
    /// A register
    Register(Register),
    /// A literal value
    Literal(i32)
}

/// Different types of Registers known in TIS-100
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Register {
    /// the NIL register, reading from it provides with zero
    NIL,
    /// The accumulator
    ACC,
}

/// `Destination` are either ports or registers
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Destination {
    /// A register
    Register(Register),
}

/// The `Status` a `Program` run on a certain `Node`.
pub enum Status {
    /// a successful run of the program
    Successful(Node),
    /// an unsuccessful run of the program, because of a deadlock.
    Deadlock(Node),
}

impl Node {
    /// Create a `Node` with defaults for accumulator, backup registers, program counter and program
    pub fn new() -> Node {
        Node { acc: 0, bac: 0, pc: 0, program: Program(vec![]) }
    }

    /// Loads a program in this `Node`
    pub fn load(&self, program: Program) -> Node {
        Node { program: program.clone(), .. *self }
    }

    /// Run the loaded program, returning an calculation state
    pub fn run(&self) -> Status {
        let mut node = Node { program: self.program.clone(), .. *self };

        loop {
            match node.fetch_instruction() {
                Some(instruction) => {
                    node = node.execute(instruction);
                }
                None => {
                    break
                }
            }
        }

        Status::Successful(Node { program: node.program.clone(), .. node })
    }

    fn fetch_instruction(&self) -> Option<Instruction> {
        let Program(ref instructions) = self.program;
        if self.pc < instructions.len() {
            Some(instructions[self.pc].clone())
        } else {
            None
        }
    }

    /// Create a `Node` from self with the program counter incremented
    pub fn increment_pc(&self) -> Node {
        Node { pc: self.pc + 1, program: self.program.clone(), .. *self }
    }

    /// Create a `Node` from self with a prescribed program counter value
    pub fn set_pc(&self, pc: usize) -> Node {
        Node { pc: pc, program: self.program.clone(), .. *self }
    }

    /// Create a `Node` from self with a prescribed accumulator register value
    pub fn set_acc(&self, acc: i32) -> Node {
        Node { acc: acc, program: self.program.clone(), .. *self }
    }

    /// Create a `Node` from self with a prescribed backup register value
    pub fn set_bac(&self, bac: i32) -> Node {
        Node { bac: bac, program: self.program.clone(), .. *self }
    }

    /// Execute the `instruction` on this `Node`. Returns a `Node` that reflects
    /// the changes the `instruction` would have on this `Node`.
    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    fn nop(&self) -> Node {
        self.increment_pc()
    }

    fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = self.value_from(source);

        self.move_value(value, destination)
    }

    fn value_from(&self, source: Source) -> i32 {
        match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        }
    }

    fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::Register(Register::ACC) => self.increment_pc().set_acc(value),
            _ => self.nop(),
        }
    }

    fn swap(&self) -> Node {
        let acc: i32 = self.acc;
        let bac: i32 = self.bac;

        self.increment_pc().set_acc(bac).set_bac(acc)
    }

    fn save(&self) -> Node {
        self.increment_pc().set_bac(self.acc)
    }

    fn add(&self, source: Source) -> Node{
        let value: i32 = self.value_from(source);

        self.add_value(value)
    }

    fn add_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc + value)
    }

    fn subtract(&self, source: Source) -> Node {
        let value: i32 = self.value_from(source);

        self.subtract_value(value)
    }

    fn subtract_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc - value)
    }
}

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

    fn node_with(acc: i32, bac: i32, pc: usize) -> Node {
        Node::new().set_acc(acc).set_bac(bac).set_pc(pc)
    }

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::NIL),
                                                        Destination::Register(Register::NIL));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::new().set_acc(2).set_bac(1);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::ACC),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(2, 1, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(0, 1, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(2, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::new().set_acc(2);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn programs_should_differ_when_different_size() {
        assert_eq!(false, Program(vec![]) == Program(vec![Instruction::SAV]));
    }

    #[test]
    fn programs_should_differ_when_different_instructions() {
        assert_eq!(false, Program(vec![Instruction::SWP]) == Program(vec![Instruction::SAV]));
    }

    #[test]
    fn programs_should_equal_with_same_instructions() {
        assert_eq!(Program(vec![Instruction::SAV]), Program(vec![Instruction::SAV]));
    }

    #[test]
    fn node_should_execute_program_correctly() {
        let program: Program = Program(vec![// calculate 4 * source - 1
            Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)),
            Instruction::ADD(Source::Register(Register::ACC)),
            Instruction::ADD(Source::Register(Register::ACC)),
            Instruction::SUB(Source::Literal(1))
        ]);
        let node: Node = Node::new().load(program);

        match node.run() {
            Status::Successful(_) => assert!(true),
            Status::Deadlock(_) => assert!(false),
        }
    }
}

<commit_msg>Reduce visibility of builder methods<commit_after>//! The Tessellated Intelligence System is a
//!
//! > massively parallel computer architecture comprised of non-uniformly
//! > interconnected heterogeneous nodes.
//!
//! The manual can be found [online](https://www.vidarholen.net/contents/junk/files/TIS-100%20Reference%20Manual.pdf).
//! Another useful resource is the unofficial [TIS-100 Hackers Guide](https://alandesmet.github.io/TIS-100-Hackers-Guide/).
//!
//! # Examples
//! Here we take a node and execute some instructions and check if double 1 has
//! the expected result.
//!
//! ```rust
//! extern crate tis_100_superoptimizer;
//!
//! use tis_100_superoptimizer::TIS_100::{Node,Instruction,Source,Destination,Register};
//!
//! fn main() {
//!     let node: Node = Node::new();
//!     let last: Node = node
//!         .execute(Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)))
//!         .execute(Instruction::ADD(Source::Register(Register::ACC)));
//!
//!     assert_eq!(2, last.acc);
//! }
//! ```

use std::fmt::{Debug,Formatter,Error};

/// A `Node` models the basic execution node in TIS-100. You change a node state
/// by executing an `Instruction` on it.
#[derive(Debug,PartialEq,Eq)]
pub struct Node {
    /// The accumulator for the basic execution node.
    pub acc: i32,
    bac: i32,
    pc: usize,
    program: Program,
}

/// A `Program` is a sequence of `Instruction`s
pub struct Program(pub Vec<Instruction>);

impl PartialEq for Program {
    fn eq(&self, other: &Program) -> bool {
        let Program(ref self_instructions) = *self;
        let Program(ref other_instructions) = *other;
        if self_instructions.len() == other_instructions.len() {
            for index in 0..(self_instructions.len()) {
                let ref self_instruction = self_instructions[index];
                let ref other_instruction = other_instructions[index];
                if self_instruction != other_instruction {
                    return false
                }
            }
            true
        } else {
            false
        }
    }
}

impl Eq for Program {}

impl Debug for Program {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        let Program(ref instructions) = *self;
        write!(f, "[{}:", instructions.len()).unwrap();
        for instruction in instructions {
            write!(f, " {:?}", instruction).unwrap();
        }
        write!(f, "]")
    }
}

impl Clone for Program {
    fn clone(&self) -> Program {
        let mut clone_instructions: Vec<Instruction> = vec![];
        let Program(ref self_instructions) = *self;
        for ref mut instruction in self_instructions {
            clone_instructions.push(instruction.clone());
        }
        Program(clone_instructions)
    }
}

/// `Instruction`s are executed by a `Node`.
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Instruction {
    /// Does nothing
    NOP,
    /// Moves value from `Source` to `Destination`
    MOV(Source, Destination),
    /// Swaps the value of the accumulator (acc) and the backup (bac) register
    SWP,
    /// Saves the value of the accumulator (acc) to the backup register
    SAV,
    /// Add value from `Source` to accumulator (acc), storing result in acc
    ADD(Source),
    /// Subtracts value from `Source` from accumulator (acc), storing result in acc
    SUB(Source),
}

/// `Source` are either ports, registers or literals
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Source {
    /// A register
    Register(Register),
    /// A literal value
    Literal(i32)
}

/// Different types of Registers known in TIS-100
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Register {
    /// the NIL register, reading from it provides with zero
    NIL,
    /// The accumulator
    ACC,
}

/// `Destination` are either ports or registers
#[derive(Debug,PartialEq,Eq,Clone)]
pub enum Destination {
    /// A register
    Register(Register),
}

/// The `Status` a `Program` run on a certain `Node`.
pub enum Status {
    /// a successful run of the program
    Successful(Node),
    /// an unsuccessful run of the program, because of a deadlock.
    Deadlock(Node),
}

impl Node {
    /// Create a `Node` with defaults for accumulator, backup registers, program counter and program
    pub fn new() -> Node {
        Node { acc: 0, bac: 0, pc: 0, program: Program(vec![]) }
    }

    /// Loads a program in this `Node`
    pub fn load(&self, program: Program) -> Node {
        Node { program: program.clone(), .. *self }
    }

    /// Run the loaded program, returning an calculation state
    pub fn run(&self) -> Status {
        let mut node = Node { program: self.program.clone(), .. *self };

        loop {
            match node.fetch_instruction() {
                Some(instruction) => {
                    node = node.execute(instruction);
                }
                None => {
                    break
                }
            }
        }

        Status::Successful(Node { program: node.program.clone(), .. node })
    }

    fn fetch_instruction(&self) -> Option<Instruction> {
        let Program(ref instructions) = self.program;
        if self.pc < instructions.len() {
            Some(instructions[self.pc].clone())
        } else {
            None
        }
    }

    /// Create a `Node` from self with the program counter incremented
    fn increment_pc(&self) -> Node {
        Node { pc: self.pc + 1, program: self.program.clone(), .. *self }
    }

    /// Create a `Node` from self with a prescribed program counter value
    fn set_pc(&self, pc: usize) -> Node {
        Node { pc: pc, program: self.program.clone(), .. *self }
    }

    /// Create a `Node` from self with a prescribed accumulator register value
    fn set_acc(&self, acc: i32) -> Node {
        Node { acc: acc, program: self.program.clone(), .. *self }
    }

    /// Create a `Node` from self with a prescribed backup register value
    fn set_bac(&self, bac: i32) -> Node {
        Node { bac: bac, program: self.program.clone(), .. *self }
    }

    /// Execute the `instruction` on this `Node`. Returns a `Node` that reflects
    /// the changes the `instruction` would have on this `Node`.
    pub fn execute(&self, instruction: Instruction) -> Node {
        match instruction {
            Instruction::NOP => self.nop(),
            Instruction::MOV(source, destination) => self.mov(source, destination) ,
            Instruction::SWP => self.swap(),
            Instruction::SAV => self.save(),
            Instruction::ADD(source) => self.add(source),
            Instruction::SUB(source) => self.subtract(source),
        }
    }

    fn nop(&self) -> Node {
        self.increment_pc()
    }

    fn mov(&self, source: Source, destination: Destination) -> Node {
        let value: i32 = self.value_from(source);

        self.move_value(value, destination)
    }

    fn value_from(&self, source: Source) -> i32 {
        match source {
            Source::Register(Register::NIL) => 0,
            Source::Register(Register::ACC) => self.acc,
            Source::Literal(value) => value,
        }
    }

    fn move_value(&self, value: i32, destination: Destination) -> Node {
        match destination {
            Destination::Register(Register::ACC) => self.increment_pc().set_acc(value),
            _ => self.nop(),
        }
    }

    fn swap(&self) -> Node {
        let acc: i32 = self.acc;
        let bac: i32 = self.bac;

        self.increment_pc().set_acc(bac).set_bac(acc)
    }

    fn save(&self) -> Node {
        self.increment_pc().set_bac(self.acc)
    }

    fn add(&self, source: Source) -> Node{
        let value: i32 = self.value_from(source);

        self.add_value(value)
    }

    fn add_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc + value)
    }

    fn subtract(&self, source: Source) -> Node {
        let value: i32 = self.value_from(source);

        self.subtract_value(value)
    }

    fn subtract_value(&self, value: i32) -> Node {
        self.increment_pc().set_acc(self.acc - value)
    }
}

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

    fn node_with(acc: i32, bac: i32, pc: usize) -> Node {
        Node::new().set_acc(acc).set_bac(bac).set_pc(pc)
    }

    #[test]
    fn node_should_start_with_accumulator_zero() {
        let node: Node = Node::new();

        assert_eq!(0, node.acc);
    }

    #[test]
    fn node_should_start_with_backup_zero() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(0, next.acc);
    }

    #[test]
    fn node_should_execute_NOP_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::NOP;

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_NIL_to_NIL_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::NIL),
                                                        Destination::Register(Register::NIL));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_ACC_to_ACC_correctly() {
        let node: Node = Node::new().set_acc(2).set_bac(1);
        let instruction: Instruction = Instruction::MOV(Source::Register(Register::ACC),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(2, 1, 1), next);
    }

    #[test]
    fn node_should_execute_MOV_from_Literal_to_ACC_correctly() {
        let node: Node = Node::new();
        let instruction: Instruction = Instruction::MOV(Source::Literal(1),
                                                        Destination::Register(Register::ACC));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SWP_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::SWP;

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(0, 1, 1), next);
    }

    #[test]
    fn node_should_execute_SAV_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::SAV;

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 1, 1), next);
    }

    #[test]
    fn node_should_execute_ADD_from_Literal_correctly() {
        let node: Node = Node::new().set_acc(1);
        let instruction: Instruction = Instruction::ADD(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(2, 0, 1), next);
    }

    #[test]
    fn node_should_execute_SUB_from_Literal_correctly() {
        let node: Node = Node::new().set_acc(2);
        let instruction: Instruction = Instruction::SUB(Source::Literal(1));

        let next: Node = node.execute(instruction);

        assert_eq!(node_with(1, 0, 1), next);
    }

    #[test]
    fn programs_should_differ_when_different_size() {
        assert_eq!(false, Program(vec![]) == Program(vec![Instruction::SAV]));
    }

    #[test]
    fn programs_should_differ_when_different_instructions() {
        assert_eq!(false, Program(vec![Instruction::SWP]) == Program(vec![Instruction::SAV]));
    }

    #[test]
    fn programs_should_equal_with_same_instructions() {
        assert_eq!(Program(vec![Instruction::SAV]), Program(vec![Instruction::SAV]));
    }

    #[test]
    fn node_should_execute_program_correctly() {
        let program: Program = Program(vec![// calculate 4 * source - 1
            Instruction::MOV(Source::Literal(1), Destination::Register(Register::ACC)),
            Instruction::ADD(Source::Register(Register::ACC)),
            Instruction::ADD(Source::Register(Register::ACC)),
            Instruction::SUB(Source::Literal(1))
        ]);
        let node: Node = Node::new().load(program);

        match node.run() {
            Status::Successful(_) => assert!(true),
            Status::Deadlock(_) => assert!(false),
        }
    }
}

<|endoftext|>


<commit_before>#![cfg(all(target_os = "linux", feature = "window"))]

use BuilderAttribs;
use CreationError;
use GlContext;
use GlProfile;
use GlRequest;
use Api;
use PixelFormat;

use libc;
use std::ffi::CString;
use std::{mem, ptr};

use api::x11::ffi;

use platform::Window as PlatformWindow;

pub struct Context {
    glx: ffi::glx::Glx,
    display: *mut ffi::Display,
    window: ffi::Window,
    context: ffi::GLXContext,
}

// TODO: remove me
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
    use std::ffi::CString;
    let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
    f(c_str.as_ptr())
}

impl Context {
    pub fn new(glx: ffi::glx::Glx, builder: BuilderAttribs, display: *mut ffi::Display, window: ffi::Window,
               fb_config: ffi::glx::types::GLXFBConfig, mut visual_infos: ffi::glx::types::XVisualInfo)
               -> Result<Context, CreationError>
    {
        // creating GL context
        let (context, extra_functions) = unsafe {
            let mut attributes = Vec::new();

            match builder.gl_version {
                GlRequest::Latest => {},
                GlRequest::Specific(Api::OpenGl, (major, minor)) => {
                    attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
                    attributes.push(major as libc::c_int);
                    attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
                    attributes.push(minor as libc::c_int);
                },
                GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
                GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
                    attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
                    attributes.push(major as libc::c_int);
                    attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
                    attributes.push(minor as libc::c_int);
                },
            }

            if let Some(profile) = builder.gl_profile {
                let flag = match profile {
                    GlProfile::Compatibility =>
                        ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
                    GlProfile::Core =>
                        ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
                };
                attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int);
                attributes.push(flag as libc::c_int);
            }

            if builder.gl_debug {
                attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int);
                attributes.push(ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int);
            }

            attributes.push(0);

            // loading the extra GLX functions
            let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
                with_c_str(addr, |s| {
                    glx.GetProcAddress(s as *const u8) as *const libc::c_void
                })
            });

            let share = if let Some(win) = builder.sharing {
                match win {
                    &PlatformWindow::X(ref win) => match win.x.context {
                        ::api::x11::Context::Glx(ref c) => c.context,
                        _ => panic!("Cannot share contexts between different APIs")
                    },
                    _ => panic!("Cannot use glx on a non-X11 window.")
                }
            } else {
                ptr::null()
            };

            let mut context = if extra_functions.CreateContextAttribsARB.is_loaded() {
                extra_functions.CreateContextAttribsARB(display as *mut ffi::glx_extra::types::Display,
                    fb_config, share, 1, attributes.as_ptr())
            } else {
                ptr::null()
            };

            if context.is_null() {
                context = glx.CreateContext(display as *mut _, &mut visual_infos, share, 1)
            }

            if context.is_null() {
                return Err(CreationError::OsError(format!("GL context creation failed")));
            }

            (context, extra_functions)
        };

        // vsync
        if builder.vsync {
            unsafe { glx.MakeCurrent(display as *mut _, window, context) };

            if extra_functions.SwapIntervalEXT.is_loaded() {
                // this should be the most common extension
                unsafe {
                    extra_functions.SwapIntervalEXT(display as *mut _, window, 1);
                }

                // checking that it worked
                if builder.strict {
                    let mut swap = unsafe { mem::uninitialized() };
                    unsafe {
                        glx.QueryDrawable(display as *mut _, window,
                                                ffi::glx_extra::SWAP_INTERVAL_EXT as i32,
                                                &mut swap);
                    }

                    if swap != 1 {
                        return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \
                                                    interval `1` but got `{}`", swap)));
                    }
                }

            // GLX_MESA_swap_control is not official
            /*} else if extra_functions.SwapIntervalMESA.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalMESA(1);
                }*/

            } else if extra_functions.SwapIntervalSGI.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalSGI(1);
                }

            } else if builder.strict {
                return Err(CreationError::OsError(format!("Couldn't find any available vsync extension")));
            }

            unsafe { glx.MakeCurrent(display as *mut _, 0, ptr::null()) };
        }

        Ok(Context {
            glx: glx,
            display: display,
            window: window,
            context: context,
        })
    }
}

impl GlContext for Context {
    unsafe fn make_current(&self) {
        let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context);
        if res == 0 {
            panic!("glx::MakeCurrent failed");
        }
    }

    fn is_current(&self) -> bool {
        unsafe { self.glx.GetCurrentContext() == self.context }
    }

    fn get_proc_address(&self, addr: &str) -> *const libc::c_void {
        let addr = CString::new(addr.as_bytes()).unwrap();
        let addr = addr.as_ptr();
        unsafe {
            self.glx.GetProcAddress(addr as *const _) as *const _
        }
    }

    fn swap_buffers(&self) {
        unsafe {
            self.glx.SwapBuffers(self.display as *mut _, self.window)
        }
    }

    fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    fn get_pixel_format(&self) -> PixelFormat {
        unimplemented!();
    }
}

unsafe impl Send for Context {}
unsafe impl Sync for Context {}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            // we don't call MakeCurrent(0, 0) because we are not sure that the context
            // is still the current one
            self.glx.DestroyContext(self.display as *mut _, self.context);
        }
    }
}
<commit_msg>Fix GLX creating only 3.0 contexts<commit_after>#![cfg(all(target_os = "linux", feature = "window"))]

use BuilderAttribs;
use CreationError;
use GlContext;
use GlProfile;
use GlRequest;
use Api;
use PixelFormat;

use libc;
use std::ffi::CString;
use std::{mem, ptr};

use api::x11::ffi;

use platform::Window as PlatformWindow;

pub struct Context {
    glx: ffi::glx::Glx,
    display: *mut ffi::Display,
    window: ffi::Window,
    context: ffi::GLXContext,
}

// TODO: remove me
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
    use std::ffi::CString;
    let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
    f(c_str.as_ptr())
}

impl Context {
    pub fn new(glx: ffi::glx::Glx, builder: BuilderAttribs, display: *mut ffi::Display, window: ffi::Window,
               fb_config: ffi::glx::types::GLXFBConfig, mut visual_infos: ffi::glx::types::XVisualInfo)
               -> Result<Context, CreationError>
    {
        let share = if let Some(win) = builder.sharing {
            match win {
                &PlatformWindow::X(ref win) => match win.x.context {
                    ::api::x11::Context::Glx(ref c) => c.context,
                    _ => panic!("Cannot share contexts between different APIs")
                },
                _ => panic!("Cannot use glx on a non-X11 window.")
            }
        } else {
            ptr::null()
        };

        // loading the extra GLX functions
        let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
            with_c_str(addr, |s| {
                unsafe { glx.GetProcAddress(s as *const u8) as *const _ }
            })
        });

        // creating GL context
        let context = match builder.gl_version {
            GlRequest::Latest => {
                if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 2),
                                                 builder.gl_profile, builder.gl_debug, share,
                                                 display, fb_config, &mut visual_infos)
                {
                    ctxt
                } else if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 1),
                                                        builder.gl_profile, builder.gl_debug,
                                                        share, display, fb_config,
                                                        &mut visual_infos)
                {
                    ctxt

                } else {
                    try!(create_context(&glx, &extra_functions, (1, 0), builder.gl_profile,
                                        builder.gl_debug, share, display, fb_config,
                                        &mut visual_infos))
                }
            },
            GlRequest::Specific(Api::OpenGl, (major, minor)) => {
                try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile,
                                    builder.gl_debug, share, display, fb_config, &mut visual_infos))
            },
            GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
            GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
                try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile,
                                    builder.gl_debug, share, display, fb_config, &mut visual_infos))
            },
        };

        // vsync
        if builder.vsync {
            unsafe { glx.MakeCurrent(display as *mut _, window, context) };

            if extra_functions.SwapIntervalEXT.is_loaded() {
                // this should be the most common extension
                unsafe {
                    extra_functions.SwapIntervalEXT(display as *mut _, window, 1);
                }

                // checking that it worked
                if builder.strict {
                    let mut swap = unsafe { mem::uninitialized() };
                    unsafe {
                        glx.QueryDrawable(display as *mut _, window,
                                                ffi::glx_extra::SWAP_INTERVAL_EXT as i32,
                                                &mut swap);
                    }

                    if swap != 1 {
                        return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \
                                                    interval `1` but got `{}`", swap)));
                    }
                }

            // GLX_MESA_swap_control is not official
            /*} else if extra_functions.SwapIntervalMESA.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalMESA(1);
                }*/

            } else if extra_functions.SwapIntervalSGI.is_loaded() {
                unsafe {
                    extra_functions.SwapIntervalSGI(1);
                }

            } else if builder.strict {
                return Err(CreationError::OsError(format!("Couldn't find any available vsync extension")));
            }

            unsafe { glx.MakeCurrent(display as *mut _, 0, ptr::null()) };
        }

        Ok(Context {
            glx: glx,
            display: display,
            window: window,
            context: context,
        })
    }
}

impl GlContext for Context {
    unsafe fn make_current(&self) {
        let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context);
        if res == 0 {
            panic!("glx::MakeCurrent failed");
        }
    }

    fn is_current(&self) -> bool {
        unsafe { self.glx.GetCurrentContext() == self.context }
    }

    fn get_proc_address(&self, addr: &str) -> *const libc::c_void {
        let addr = CString::new(addr.as_bytes()).unwrap();
        let addr = addr.as_ptr();
        unsafe {
            self.glx.GetProcAddress(addr as *const _) as *const _
        }
    }

    fn swap_buffers(&self) {
        unsafe {
            self.glx.SwapBuffers(self.display as *mut _, self.window)
        }
    }

    fn get_api(&self) -> ::Api {
        ::Api::OpenGl
    }

    fn get_pixel_format(&self) -> PixelFormat {
        unimplemented!();
    }
}

unsafe impl Send for Context {}
unsafe impl Sync for Context {}

impl Drop for Context {
    fn drop(&mut self) {
        unsafe {
            // we don't call MakeCurrent(0, 0) because we are not sure that the context
            // is still the current one
            self.glx.DestroyContext(self.display as *mut _, self.context);
        }
    }
}

fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx,
                  version: (u8, u8), profile: Option<GlProfile>, debug: bool,
                  share: ffi::GLXContext, display: *mut ffi::Display,
                  fb_config: ffi::glx::types::GLXFBConfig,
                  visual_infos: &mut ffi::glx::types::XVisualInfo)
                  -> Result<ffi::GLXContext, CreationError>
{
    unsafe {
        let context = if extra_functions.CreateContextAttribsARB.is_loaded() {
            let mut attributes = Vec::with_capacity(9);

            attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
            attributes.push(version.0 as libc::c_int);
            attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
            attributes.push(version.1 as libc::c_int);

            if let Some(profile) = profile {
                let flag = match profile {
                    GlProfile::Compatibility =>
                        ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
                    GlProfile::Core =>
                        ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB,
                };

                attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int);
                attributes.push(flag as libc::c_int);
            }

            if debug {
                attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int);
                attributes.push(ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int);
            }

            attributes.push(0);

            extra_functions.CreateContextAttribsARB(display as *mut _, fb_config, share, 1,
                                                    attributes.as_ptr())

        } else {
            glx.CreateContext(display as *mut _, visual_infos, share, 1)
        };

        if context.is_null() {
            return Err(CreationError::OsError(format!("GL context creation failed")));
        }

        Ok(context)
    }
}
<|endoftext|>


<commit_before>use std::{self, env};

pub fn master() -> String {
    if let Some(v) = find_with_prefix("__master:=") {
        return v;
    }
    env::var("ROS_MASTER_URI").unwrap_or(String::from("http://localhost:11311/"))
}

pub fn hostname() -> String {
    if let Some(v) = find_with_prefix("__hostname:=") {
        return v;
    }
    if let Some(v) = find_with_prefix("__ip:=") {
        return v;
    }
    if let Ok(v) = env::var("ROS_HOSTNAME") {
        return v;
    }
    if let Ok(v) = env::var("ROS_IP") {
        return v;
    }
    system_hostname()
}

pub fn namespace() -> String {
    if let Some(v) = find_with_prefix("__ns:=") {
        return v;
    }
    env::var("ROS_NAMESPACE").unwrap_or(String::new())
}

pub fn name(default: &str) -> String {
    find_with_prefix("__name:=").unwrap_or(String::from(default))
}

pub fn mappings() -> Vec<(String, String)> {
    args()
        .skip(1)
        .filter(|v| !v.starts_with("__"))
        .map(|v| v.split(":=").map(String::from).collect::<Vec<String>>())
        .filter(|v| v.len() == 2)
        .map(|v| v.into_iter().map(fix_prefix))
        .map(|mut v| (v.next().unwrap(), v.next().unwrap()))
        .collect()
}

fn fix_prefix(v: String) -> String {
    if v.starts_with("_") {
        format!("~{}", v.trim_left_matches("_"))
    } else {
        v
    }
}

fn find_with_prefix(prefix: &str) -> Option<String> {
    args()
        .skip(1)
        .find(|v| v.starts_with(prefix))
        .map(|v| String::from(v.trim_left_matches(prefix)))
}

#[cfg(not(test))]
fn system_hostname() -> String {
    use nix::unistd::gethostname;
    let mut hostname = [0u8; 200];
    gethostname(&mut hostname)
        .expect("Hostname is either unavailable or too long to fit into buffer");
    let hostname = hostname.into_iter().take_while(|&v| *v != 0u8).map(|v| *v).collect::<Vec<_>>();
    String::from_utf8(hostname).expect("Hostname is not legal UTF-8")
}

#[cfg(test)]
fn system_hostname() -> String {
    String::from("myhostname")
}

#[cfg(not(test))]
fn args() -> std::env::Args {
    env::args()
}

#[cfg(test)]
fn args() -> std::vec::IntoIter<String> {
    tests::args_mock()
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::Mutex;
    use std::{self, env};

    lazy_static!{
        static ref DATA: Mutex<Vec<String>> = Mutex::new(Vec::new());
        static ref TESTCASE: Mutex<()> = Mutex::new(());
    }

    pub fn args_mock() -> std::vec::IntoIter<String> {
        DATA.lock().unwrap().clone().into_iter()
    }

    fn set_args(args: &Vec<&str>) {
        let mut data = DATA.lock().unwrap();
        data.clear();
        data.push(String::from("IGNORE"));
        for &arg in args {
            data.push(String::from(arg));
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn mappings_default_to_empty_vector() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(Vec::<(String, String)>::new(), mappings());
    }

    #[test]
    #[allow(unused_variables)]
    fn maps_good_and_ignores_everything_else() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(Vec::<(String, String)>::new(), mappings());
        set_args(&vec!["a:=x",
                       "b=e",
                       "/c:=d",
                       "e:=/f_g",
                       "__name:=something",
                       "a:=b:=c",
                       "_oo_e:=/ab_c",
                       "/x_y:=_i"]);
        assert_eq!(vec![(String::from("a"), String::from("x")),
                        (String::from("/c"), String::from("d")),
                        (String::from("e"), String::from("/f_g")),
                        (String::from("~oo_e"), String::from("/ab_c")),
                        (String::from("/x_y"), String::from("~i"))],
                   mappings());
    }

    #[test]
    #[allow(unused_variables)]
    fn name_uses_passed_value_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(String::from("myname"), name("myname"));
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from("othername"), name("othername"));
    }

    #[test]
    #[allow(unused_variables)]
    fn name_uses_argument_when_provided() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(String::from("myname"), name("myname"));
        set_args(&vec!["__name:=othername"]);
        assert_eq!(String::from("othername"), name("myname"));
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_uses_empty_string_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from(""), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_uses_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        env::set_var("ROS_NAMESPACE", "/myns");
        assert_eq!(String::from("/myns"), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_uses_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        set_args(&vec!["__ns:=/myns"]);
        assert_eq!(String::from("/myns"), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_prioritizes_argument_when_both_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        env::set_var("ROS_NAMESPACE", "/myns1");
        set_args(&vec!["__ns:=/myns2"]);
        assert_eq!(String::from("/myns2"), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_uses_default_uri_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from("http://localhost:11311/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_uses_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        env::set_var("ROS_MASTER_URI", "http://somebody:21212/");
        assert_eq!(String::from("http://somebody:21212/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_uses_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        set_args(&vec!["__master:=http://somebody:21212/"]);
        assert_eq!(String::from("http://somebody:21212/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_prioritizes_argument_when_both_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        env::set_var("ROS_MASTER_URI", "http://somebody1:21212/");
        set_args(&vec!["__master:=http://somebody2:21212/"]);
        assert_eq!(String::from("http://somebody2:21212/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_default_uri_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from("myhostname"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_hostname_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_HOSTNAME", "host");
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_ip_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_IP", "192.168.0.1");
        assert_eq!(String::from("192.168.0.1"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_prioritizes_hostname_over_ip_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_HOSTNAME", "host");
        env::set_var("ROS_IP", "192.168.0.1");
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_hostname_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["__hostname:=host"]);
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_ip_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["__ip:=192.168.0.1"]);
        assert_eq!(String::from("192.168.0.1"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_prioritizes_hostname_over_ip_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["__hostname:=host", "__ip:=192.168.0.1"]);
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_prioritizes_argument_when_both_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_HOSTNAME", "host");
        env::set_var("ROS_IP", "192.168.0.1");
        set_args(&vec!["__hostname:=host2", "__ip:=127.0.0.1"]);
        assert_eq!(String::from("host2"), hostname());
    }

}
<commit_msg>Set standard hostname length limit<commit_after>use std::{self, env};

pub fn master() -> String {
    if let Some(v) = find_with_prefix("__master:=") {
        return v;
    }
    env::var("ROS_MASTER_URI").unwrap_or(String::from("http://localhost:11311/"))
}

pub fn hostname() -> String {
    if let Some(v) = find_with_prefix("__hostname:=") {
        return v;
    }
    if let Some(v) = find_with_prefix("__ip:=") {
        return v;
    }
    if let Ok(v) = env::var("ROS_HOSTNAME") {
        return v;
    }
    if let Ok(v) = env::var("ROS_IP") {
        return v;
    }
    system_hostname()
}

pub fn namespace() -> String {
    if let Some(v) = find_with_prefix("__ns:=") {
        return v;
    }
    env::var("ROS_NAMESPACE").unwrap_or(String::new())
}

pub fn name(default: &str) -> String {
    find_with_prefix("__name:=").unwrap_or(String::from(default))
}

pub fn mappings() -> Vec<(String, String)> {
    args()
        .skip(1)
        .filter(|v| !v.starts_with("__"))
        .map(|v| v.split(":=").map(String::from).collect::<Vec<String>>())
        .filter(|v| v.len() == 2)
        .map(|v| v.into_iter().map(fix_prefix))
        .map(|mut v| (v.next().unwrap(), v.next().unwrap()))
        .collect()
}

fn fix_prefix(v: String) -> String {
    if v.starts_with("_") {
        format!("~{}", v.trim_left_matches("_"))
    } else {
        v
    }
}

fn find_with_prefix(prefix: &str) -> Option<String> {
    args()
        .skip(1)
        .find(|v| v.starts_with(prefix))
        .map(|v| String::from(v.trim_left_matches(prefix)))
}

#[cfg(not(test))]
fn system_hostname() -> String {
    use nix::unistd::gethostname;
    let mut hostname = [0u8; 256];
    gethostname(&mut hostname)
        .expect("Hostname is either unavailable or too long to fit into buffer");
    let hostname = hostname.into_iter().take_while(|&v| *v != 0u8).map(|v| *v).collect::<Vec<_>>();
    String::from_utf8(hostname).expect("Hostname is not legal UTF-8")
}

#[cfg(test)]
fn system_hostname() -> String {
    String::from("myhostname")
}

#[cfg(not(test))]
fn args() -> std::env::Args {
    env::args()
}

#[cfg(test)]
fn args() -> std::vec::IntoIter<String> {
    tests::args_mock()
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::Mutex;
    use std::{self, env};

    lazy_static!{
        static ref DATA: Mutex<Vec<String>> = Mutex::new(Vec::new());
        static ref TESTCASE: Mutex<()> = Mutex::new(());
    }

    pub fn args_mock() -> std::vec::IntoIter<String> {
        DATA.lock().unwrap().clone().into_iter()
    }

    fn set_args(args: &Vec<&str>) {
        let mut data = DATA.lock().unwrap();
        data.clear();
        data.push(String::from("IGNORE"));
        for &arg in args {
            data.push(String::from(arg));
        }
    }

    #[test]
    #[allow(unused_variables)]
    fn mappings_default_to_empty_vector() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(Vec::<(String, String)>::new(), mappings());
    }

    #[test]
    #[allow(unused_variables)]
    fn maps_good_and_ignores_everything_else() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(Vec::<(String, String)>::new(), mappings());
        set_args(&vec!["a:=x",
                       "b=e",
                       "/c:=d",
                       "e:=/f_g",
                       "__name:=something",
                       "a:=b:=c",
                       "_oo_e:=/ab_c",
                       "/x_y:=_i"]);
        assert_eq!(vec![(String::from("a"), String::from("x")),
                        (String::from("/c"), String::from("d")),
                        (String::from("e"), String::from("/f_g")),
                        (String::from("~oo_e"), String::from("/ab_c")),
                        (String::from("/x_y"), String::from("~i"))],
                   mappings());
    }

    #[test]
    #[allow(unused_variables)]
    fn name_uses_passed_value_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(String::from("myname"), name("myname"));
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from("othername"), name("othername"));
    }

    #[test]
    #[allow(unused_variables)]
    fn name_uses_argument_when_provided() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        assert_eq!(String::from("myname"), name("myname"));
        set_args(&vec!["__name:=othername"]);
        assert_eq!(String::from("othername"), name("myname"));
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_uses_empty_string_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from(""), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_uses_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        env::set_var("ROS_NAMESPACE", "/myns");
        assert_eq!(String::from("/myns"), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_uses_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        set_args(&vec!["__ns:=/myns"]);
        assert_eq!(String::from("/myns"), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn namespace_prioritizes_argument_when_both_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_NAMESPACE");
        assert_eq!(String::from(""), namespace());
        env::set_var("ROS_NAMESPACE", "/myns1");
        set_args(&vec!["__ns:=/myns2"]);
        assert_eq!(String::from("/myns2"), namespace());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_uses_default_uri_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from("http://localhost:11311/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_uses_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        env::set_var("ROS_MASTER_URI", "http://somebody:21212/");
        assert_eq!(String::from("http://somebody:21212/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_uses_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        set_args(&vec!["__master:=http://somebody:21212/"]);
        assert_eq!(String::from("http://somebody:21212/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn master_prioritizes_argument_when_both_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_MASTER_URI");
        assert_eq!(String::from("http://localhost:11311/"), master());
        env::set_var("ROS_MASTER_URI", "http://somebody1:21212/");
        set_args(&vec!["__master:=http://somebody2:21212/"]);
        assert_eq!(String::from("http://somebody2:21212/"), master());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_default_uri_by_default() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["unimportant", "also_unimportant"]);
        assert_eq!(String::from("myhostname"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_hostname_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_HOSTNAME", "host");
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_ip_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_IP", "192.168.0.1");
        assert_eq!(String::from("192.168.0.1"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_prioritizes_hostname_over_ip_environment_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_HOSTNAME", "host");
        env::set_var("ROS_IP", "192.168.0.1");
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_hostname_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["__hostname:=host"]);
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_uses_ip_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["__ip:=192.168.0.1"]);
        assert_eq!(String::from("192.168.0.1"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_prioritizes_hostname_over_ip_argument_when_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        set_args(&vec!["__hostname:=host", "__ip:=192.168.0.1"]);
        assert_eq!(String::from("host"), hostname());
    }

    #[test]
    #[allow(unused_variables)]
    fn hostname_prioritizes_argument_when_both_passed() {
        let testcase = TESTCASE.lock().unwrap();
        set_args(&vec![]);
        env::remove_var("ROS_HOSTNAME");
        env::remove_var("ROS_IP");
        assert_eq!(String::from("myhostname"), hostname());
        env::set_var("ROS_HOSTNAME", "host");
        env::set_var("ROS_IP", "192.168.0.1");
        set_args(&vec!["__hostname:=host2", "__ip:=127.0.0.1"]);
        assert_eq!(String::from("host2"), hostname());
    }

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

//! Non-modal message dialogs.

use gtk::Continue;
use mg_settings::{EnumFromStr, EnumMetaData, SettingCompletion};
use mg_settings::settings;

use app::Application;
use SpecialCommand;

const INFO_MESSAGE_DURATION: u32 = 5000;

impl<Comm, Sett, Spec> Application<Comm, Sett, Spec>
    where Spec: SpecialCommand + 'static,
          Comm: EnumFromStr + EnumMetaData + 'static,
          Sett: settings::Settings + EnumMetaData + SettingCompletion + 'static,
{
    /// Show an alert message to the user.
    pub fn alert(&self, message: &str) {
        self.message.set_text(message);
        self.status_bar.color_blue();
    }

    /// Show an error to the user.
    pub fn error(&mut self, error: &str) {
        error!("{}", error);
        self.message.set_text(error);
        self.status_bar.hide_entry();
        self.status_bar.color_red();
    }

    /// Hide the information message.
    fn hide_colored_message(&self, message: &str) -> Continue {
        if let Some(current_message) = self.message.get_text() {
            if current_message == message {
                self.message.set_text("");
                self.reset_colors();
            }
        }
        Continue(false)
    }

    /// Hide the information message.
    fn hide_info(&self, message: &str) -> Continue {
        if let Some(current_message) = self.message.get_text() {
            if current_message == message {
                self.message.set_text("");
            }
        }
        Continue(false)
    }

    /// Show an information message to the user for 5 seconds.
    pub fn info(&mut self, message: &str) {
        info!("{}", message);
        self.message.set_text(message);
        self.reset_colors();
        let message = message.to_string();
        timeout_add!(INFO_MESSAGE_DURATION, self, hide_info(&message));
    }

    /// Show a message to the user.
    pub fn message(&self, message: &str) {
        self.message.set_text(message);
    }

    /// Show a warning message to the user for 5 seconds.
    pub fn warning(&mut self, message: &str) {
        warn!("{}", message);
        self.message.set_text(message);
        let message = message.to_string();
        self.status_bar.color_orange();
        timeout_add!(INFO_MESSAGE_DURATION, self, hide_colored_message(&message));
    }
}
<commit_msg>Fixed color for message method.<commit_after>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

//! Non-modal message dialogs.

use gtk::Continue;
use mg_settings::{EnumFromStr, EnumMetaData, SettingCompletion};
use mg_settings::settings;

use app::Application;
use SpecialCommand;

const INFO_MESSAGE_DURATION: u32 = 5000;

impl<Comm, Sett, Spec> Application<Comm, Sett, Spec>
    where Spec: SpecialCommand + 'static,
          Comm: EnumFromStr + EnumMetaData + 'static,
          Sett: settings::Settings + EnumMetaData + SettingCompletion + 'static,
{
    /// Show an alert message to the user.
    pub fn alert(&self, message: &str) {
        self.message.set_text(message);
        self.status_bar.color_blue();
    }

    /// Show an error to the user.
    pub fn error(&mut self, error: &str) {
        error!("{}", error);
        self.message.set_text(error);
        self.status_bar.hide_entry();
        self.status_bar.color_red();
    }

    /// Hide the information message.
    fn hide_colored_message(&self, message: &str) -> Continue {
        if let Some(current_message) = self.message.get_text() {
            if current_message == message {
                self.message.set_text("");
                self.reset_colors();
            }
        }
        Continue(false)
    }

    /// Hide the information message.
    fn hide_info(&self, message: &str) -> Continue {
        if let Some(current_message) = self.message.get_text() {
            if current_message == message {
                self.message.set_text("");
            }
        }
        Continue(false)
    }

    /// Show an information message to the user for 5 seconds.
    pub fn info(&mut self, message: &str) {
        info!("{}", message);
        self.message.set_text(message);
        self.reset_colors();
        let message = message.to_string();
        timeout_add!(INFO_MESSAGE_DURATION, self, hide_info(&message));
    }

    /// Show a message to the user.
    pub fn message(&self, message: &str) {
        self.reset_colors();
        self.message.set_text(message);
    }

    /// Show a warning message to the user for 5 seconds.
    pub fn warning(&mut self, message: &str) {
        warn!("{}", message);
        self.message.set_text(message);
        let message = message.to_string();
        self.status_bar.color_orange();
        timeout_add!(INFO_MESSAGE_DURATION, self, hide_colored_message(&message));
    }
}
<|endoftext|>


<commit_before>use {Atlas, ImageBuffer, ImageData, CharData, FontData};
use euclid::{rect, Rect, size2, vec2};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::slice;
use tilesheet;

/// Fetch key for atlas images.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct SubImageSpec<T> {
    pub id: T,
    pub bounds: Rect<u32>,
}

impl<T> SubImageSpec<T> {
    pub fn new<U: Into<T>>(id: U, x: u32, y: u32, width: u32, height: u32) -> SubImageSpec<T> {
        SubImageSpec {
            id: id.into(),
            bounds: rect(x, y, width, height),
        }
    }
}

/// Updateable incremental cache for multiple texture atlases.
pub struct AtlasCache<T> {
    image_sheets: HashMap<T, ImageBuffer>,
    atlas_images: HashMap<SubImageSpec<T>, ImageData>,
    atlases: Vec<Atlas>,
    atlas_size: u32,
    next_index: usize,
}

impl<T: Eq + Hash + Clone + Debug> AtlasCache<T> {
    pub fn new(atlas_size: u32, starting_index: usize) -> AtlasCache<T> {
        let mut ret = AtlasCache {
            image_sheets: HashMap::new(),
            atlas_images: HashMap::new(),
            atlases: Vec::new(),
            atlas_size: atlas_size,
            next_index: starting_index,
        };

        ret.new_atlas();

        ret
    }

    pub fn atlases_mut(&mut self) -> slice::IterMut<Atlas> { self.atlases.iter_mut() }

    pub fn atlas_size(&self) -> u32 { self.atlas_size }

    /// Get a drawable `ImageData` corresponding to a subimage specification.
    ///
    /// The named sheet in the `SubImageSpec` key must have been added to the atlas cache before
    /// calling `get`.
    ///
    /// If the added image is larger than `atlas_size` of the atlas cache in either x or y
    /// dimension, the image cannot be added and the method will panic.
    pub fn get<'a>(&'a mut self, key: &SubImageSpec<T>) -> &'a ImageData {
        // This subimage already exists, return it.
        if self.atlas_images.contains_key(key) {
            return &self.atlas_images[key];
        }

        // Subimage does not exist yet, construct the ImageData for it.
        let sub_image = if let Some(image) = self.image_sheets.get(&key.id) {
            // Add a new image to the atlas.

            // First create the buffer image.
            ImageBuffer::from_fn(key.bounds.size.width, key.bounds.size.height, |x, y| {
                image.get_pixel(x + key.bounds.origin.x, y + key.bounds.origin.y)
            })
        } else {
            panic!("Image sheet {:?} not found in cache", key.id);
        };

        assert!(!self.atlases.is_empty());
        let atlas_id = self.atlases.len() - 1;

        if let Some(image_data) = self.atlases[atlas_id].add(&sub_image) {
            // Try to fit it into the current atlas sheet.
            self.atlas_images.insert(key.clone(), image_data);
            &self.atlas_images[key]
        } else if self.atlases[atlas_id].is_empty() {
            // The atlas is empty but adding the image still failed. Assuming image is too large to
            // fit on an atlas sheet even on its own.
            panic!("Image {:?} too large, won't fit in empty atlas.", key);
        } else {
            // Add an empty atlas sheet and retry.
            self.new_atlas();
            assert!(self.atlases[self.atlases.len() - 1].is_empty());
            self.get(key)
        }
    }

    fn new_atlas(&mut self) {
        self.atlases.push(Atlas::new(
            self.next_index,
            size2(self.atlas_size, self.atlas_size),
        ));
        self.next_index += 1;
    }

    /// Add a named tile source sheet into the cache.
    ///
    /// Return the `SubImageSpec` for the entire sheet in case it is a single image that should be
    /// used as is.
    pub fn add_sheet<U, I>(&mut self, id: U, sheet: I) -> SubImageSpec<T>
    where
        U: Into<T>,
        I: Into<ImageBuffer>,
    {
        let id = id.into();
        let sheet = sheet.into();

        let ret = SubImageSpec {
            id: id.clone(),
            bounds: rect(0, 0, sheet.size.width, sheet.size.height),
        };

        self.image_sheets.insert(id, sheet);

        ret
    }

    /// Add a named tile source and generate tile data using image properties.
    ///
    /// Calls the `tilesheet_bounds` function to determine tilesheet subimages.
    pub fn add_tilesheet<U, I>(&mut self, id: U, sheet: I) -> Vec<SubImageSpec<T>>
    where
        U: Into<T>,
        I: Into<ImageBuffer>,
    {
        let id = id.into();
        let sheet = sheet.into();

        let ret = tilesheet::tilesheet_bounds(&sheet)
            .into_iter()
            .map(|r| SubImageSpec {
                bounds: r,
                id: id.clone(),
            })
            .collect();

        self.add_sheet(id, sheet);
        ret
    }

    pub fn add_tilesheet_font<U, I, J>(&mut self, id: U, sheet: I, span: J) -> FontData
    where
        U: Into<T>,
        I: Into<ImageBuffer>,
        J: IntoIterator<Item = char> {
        let tiles = self.add_tilesheet(id, sheet);

    let glyphs = tiles
        .into_iter()
        .map(|i| CharData {
            image: self.get(&i).clone(),
            draw_offset: vec2(0.0, 0.0),
            advance: i.bounds.size.width as f32,
        })
        .collect::<Vec<_>>();

    let height = glyphs[0].image.size.height as f32;
    let chars = span.into_iter().zip(glyphs.into_iter()).collect();

    FontData { chars, height }
    }
}
<commit_msg>Rustfmt<commit_after>use {Atlas, CharData, FontData, ImageBuffer, ImageData};
use euclid::{rect, Rect, size2, vec2};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::slice;
use tilesheet;

/// Fetch key for atlas images.
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct SubImageSpec<T> {
    pub id: T,
    pub bounds: Rect<u32>,
}

impl<T> SubImageSpec<T> {
    pub fn new<U: Into<T>>(id: U, x: u32, y: u32, width: u32, height: u32) -> SubImageSpec<T> {
        SubImageSpec {
            id: id.into(),
            bounds: rect(x, y, width, height),
        }
    }
}

/// Updateable incremental cache for multiple texture atlases.
pub struct AtlasCache<T> {
    image_sheets: HashMap<T, ImageBuffer>,
    atlas_images: HashMap<SubImageSpec<T>, ImageData>,
    atlases: Vec<Atlas>,
    atlas_size: u32,
    next_index: usize,
}

impl<T: Eq + Hash + Clone + Debug> AtlasCache<T> {
    pub fn new(atlas_size: u32, starting_index: usize) -> AtlasCache<T> {
        let mut ret = AtlasCache {
            image_sheets: HashMap::new(),
            atlas_images: HashMap::new(),
            atlases: Vec::new(),
            atlas_size: atlas_size,
            next_index: starting_index,
        };

        ret.new_atlas();

        ret
    }

    pub fn atlases_mut(&mut self) -> slice::IterMut<Atlas> { self.atlases.iter_mut() }

    pub fn atlas_size(&self) -> u32 { self.atlas_size }

    /// Get a drawable `ImageData` corresponding to a subimage specification.
    ///
    /// The named sheet in the `SubImageSpec` key must have been added to the atlas cache before
    /// calling `get`.
    ///
    /// If the added image is larger than `atlas_size` of the atlas cache in either x or y
    /// dimension, the image cannot be added and the method will panic.
    pub fn get<'a>(&'a mut self, key: &SubImageSpec<T>) -> &'a ImageData {
        // This subimage already exists, return it.
        if self.atlas_images.contains_key(key) {
            return &self.atlas_images[key];
        }

        // Subimage does not exist yet, construct the ImageData for it.
        let sub_image = if let Some(image) = self.image_sheets.get(&key.id) {
            // Add a new image to the atlas.

            // First create the buffer image.
            ImageBuffer::from_fn(key.bounds.size.width, key.bounds.size.height, |x, y| {
                image.get_pixel(x + key.bounds.origin.x, y + key.bounds.origin.y)
            })
        } else {
            panic!("Image sheet {:?} not found in cache", key.id);
        };

        assert!(!self.atlases.is_empty());
        let atlas_id = self.atlases.len() - 1;

        if let Some(image_data) = self.atlases[atlas_id].add(&sub_image) {
            // Try to fit it into the current atlas sheet.
            self.atlas_images.insert(key.clone(), image_data);
            &self.atlas_images[key]
        } else if self.atlases[atlas_id].is_empty() {
            // The atlas is empty but adding the image still failed. Assuming image is too large to
            // fit on an atlas sheet even on its own.
            panic!("Image {:?} too large, won't fit in empty atlas.", key);
        } else {
            // Add an empty atlas sheet and retry.
            self.new_atlas();
            assert!(self.atlases[self.atlases.len() - 1].is_empty());
            self.get(key)
        }
    }

    fn new_atlas(&mut self) {
        self.atlases.push(Atlas::new(
            self.next_index,
            size2(self.atlas_size, self.atlas_size),
        ));
        self.next_index += 1;
    }

    /// Add a named tile source sheet into the cache.
    ///
    /// Return the `SubImageSpec` for the entire sheet in case it is a single image that should be
    /// used as is.
    pub fn add_sheet<U, I>(&mut self, id: U, sheet: I) -> SubImageSpec<T>
    where
        U: Into<T>,
        I: Into<ImageBuffer>,
    {
        let id = id.into();
        let sheet = sheet.into();

        let ret = SubImageSpec {
            id: id.clone(),
            bounds: rect(0, 0, sheet.size.width, sheet.size.height),
        };

        self.image_sheets.insert(id, sheet);

        ret
    }

    /// Add a named tile source and generate tile data using image properties.
    ///
    /// Calls the `tilesheet_bounds` function to determine tilesheet subimages.
    pub fn add_tilesheet<U, I>(&mut self, id: U, sheet: I) -> Vec<SubImageSpec<T>>
    where
        U: Into<T>,
        I: Into<ImageBuffer>,
    {
        let id = id.into();
        let sheet = sheet.into();

        let ret = tilesheet::tilesheet_bounds(&sheet)
            .into_iter()
            .map(|r| SubImageSpec {
                bounds: r,
                id: id.clone(),
            })
            .collect();

        self.add_sheet(id, sheet);
        ret
    }

    pub fn add_tilesheet_font<U, I, J>(&mut self, id: U, sheet: I, span: J) -> FontData
    where
        U: Into<T>,
        I: Into<ImageBuffer>,
        J: IntoIterator<Item = char>,
    {
        let tiles = self.add_tilesheet(id, sheet);

        let glyphs = tiles
            .into_iter()
            .map(|i| CharData {
                image: self.get(&i).clone(),
                draw_offset: vec2(0.0, 0.0),
                advance: i.bounds.size.width as f32,
            })
            .collect::<Vec<_>>();

        let height = glyphs[0].image.size.height as f32;
        let chars = span.into_iter().zip(glyphs.into_iter()).collect();

        FontData { chars, height }
    }
}
<|endoftext|>