<commit_before>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

//! `sysinfo` is a crate used to get a system's information.
//!
//! Before any attempt to read the different structs' information, you need to update them to
//! get up-to-date information.
//!
//! # Examples
//!
//! ```
//! use sysinfo::{ProcessExt, SystemExt};
//!
//! let mut system = sysinfo::System::new_all();
//!
//! // First we update all information of our system struct.
//! system.refresh_all();
//!
//! // Now let's print every process' id and name:
//! for (pid, proc_) in system.get_processes() {
//!     println!("{}:{} => status: {:?}", pid, proc_.name(), proc_.status());
//! }
//!
//! // Then let's print the temperature of the different components:
//! for component in system.get_components() {
//!     println!("{:?}", component);
//! }
//!
//! // And then all disks' information:
//! for disk in system.get_disks() {
//!     println!("{:?}", disk);
//! }
//!
//! // And finally the RAM and SWAP information:
//! println!("total memory: {} KiB", system.get_total_memory());
//! println!("used memory : {} KiB", system.get_used_memory());
//! println!("total swap  : {} KiB", system.get_total_swap());
//! println!("used swap   : {} KiB", system.get_used_swap());
//! ```

#![crate_name = "sysinfo"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![deny(missing_docs)]
#![deny(intra_doc_link_resolution_failure)]
//#![deny(warnings)]
#![allow(unknown_lints)]

#[macro_use]
extern crate cfg_if;
#[cfg(not(any(target_os = "unknown", target_arch = "wasm32")))]
extern crate libc;
extern crate rayon;

#[macro_use]
extern crate doc_comment;

#[cfg(test)]
doctest!("../README.md");

cfg_if! {
    if #[cfg(target_os = "macos")] {
        mod mac;
        use mac as sys;
    } else if #[cfg(windows)] {
        mod windows;
        use windows as sys;
        extern crate winapi;
        extern crate ntapi;
    } else if #[cfg(unix)] {
        mod linux;
        use linux as sys;
    } else {
        mod unknown;
        use unknown as sys;
    }
}

pub use common::{AsU32, DiskType, NetworksIter, Pid, RefreshKind};
pub use sys::{Component, Disk, NetworkData, Networks, Process, ProcessStatus, Processor, System};
pub use traits::{
    ComponentExt, DiskExt, NetworkExt, NetworksExt, ProcessExt, ProcessorExt, SystemExt, UserExt,
};

#[cfg(feature = "c-interface")]
pub use c_interface::*;
pub use utils::get_current_pid;

#[cfg(feature = "c-interface")]
mod c_interface;
mod common;
mod debug;
mod system;
mod traits;
mod utils;

/// This function is only used on linux targets, on the other platforms it does nothing.
///
/// On linux, to improve performance, we keep a `/proc` file open for each process we index with
/// a maximum number of files open equivalent to half of the system limit.
///
/// The problem is that some users might need all the available file descriptors so we need to
/// allow them to change this limit. Reducing
///
/// Note that if you set a limit bigger than the system limit, the system limit will be set.
///
/// Returns `true` if the new value has been set.
pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
    #[cfg(all(not(target_os = "macos"), unix))]
    {
        if _new_limit < 0 {
            _new_limit = 0;
        }
        let max = sys::system::get_max_nb_fds();
        if _new_limit > max {
            _new_limit = max;
        }
        if let Ok(ref mut x) = unsafe { sys::system::REMAINING_FILES.lock() } {
            // If files are already open, to be sure that the number won't be bigger when those
            // files are closed, we subtract the current number of opened files to the new limit.
            let diff = max - **x;
            **x = _new_limit - diff;
            true
        } else {
            false
        }
    }
    #[cfg(any(not(unix), target_os = "macos"))]
    {
        false
    }
}

/// An enum representing signal on UNIX-like systems.
#[repr(C)]
#[derive(Clone, PartialEq, PartialOrd, Debug, Copy)]
pub enum Signal {
    /// Hangup detected on controlling terminal or death of controlling process.
    Hangup = 1,
    /// Interrupt from keyboard.
    Interrupt = 2,
    /// Quit from keyboard.
    Quit = 3,
    /// Illegal instruction.
    Illegal = 4,
    /// Trace/breakpoint trap.
    Trap = 5,
    /// Abort signal from C abort function.
    Abort = 6,
    // IOT trap. A synonym for SIGABRT.
    // IOT = 6,
    /// Bus error (bad memory access).
    Bus = 7,
    /// Floating point exception.
    FloatingPointException = 8,
    /// Kill signal.
    Kill = 9,
    /// User-defined signal 1.
    User1 = 10,
    /// Invalid memory reference.
    Segv = 11,
    /// User-defined signal 2.
    User2 = 12,
    /// Broken pipe: write to pipe with no readers.
    Pipe = 13,
    /// Timer signal from C alarm function.
    Alarm = 14,
    /// Termination signal.
    Term = 15,
    /// Stack fault on coprocessor (unused).
    Stklft = 16,
    /// Child stopped or terminated.
    Child = 17,
    /// Continue if stopped.
    Continue = 18,
    /// Stop process.
    Stop = 19,
    /// Stop typed at terminal.
    TSTP = 20,
    /// Terminal input for background process.
    TTIN = 21,
    /// Terminal output for background process.
    TTOU = 22,
    /// Urgent condition on socket.
    Urgent = 23,
    /// CPU time limit exceeded.
    XCPU = 24,
    /// File size limit exceeded.
    XFSZ = 25,
    /// Virtual alarm clock.
    VirtualAlarm = 26,
    /// Profiling time expired.
    Profiling = 27,
    /// Windows resize signal.
    Winch = 28,
    /// I/O now possible.
    IO = 29,
    // Pollable event (Sys V). Synonym for IO
    //Poll = 29,
    /// Power failure (System V).
    Power = 30,
    /// Bad argument to routine (SVr4).
    Sys = 31,
}

/// A struct represents system load average value.
#[repr(C)]
#[derive(Default, Debug, Clone)]
pub struct LoadAvg {
    /// Average load within one minute.
    pub one: f64,
    /// Average load within five minutes.
    pub five: f64,
    /// Average load within fifteen minutes.
    pub fifteen: f64,
}

/// Type containing user information.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct User {
    name: String,
    groups: Vec<String>,
}

impl UserExt for User {
    fn get_name(&self) -> &str {
        &self.name
    }

    fn get_groups(&self) -> &[String] {
        &self.groups
    }
}

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

    #[test]
    fn check_memory_usage() {
        let mut s = ::System::new();

        s.refresh_all();
        assert_eq!(
            s.get_processes()
                .iter()
                .all(|(_, proc_)| proc_.memory() == 0),
            false
        );
    }
}
<commit_msg>Add check that the users function is working as expected<commit_after>//
// Sysinfo
//
// Copyright (c) 2015 Guillaume Gomez
//

//! `sysinfo` is a crate used to get a system's information.
//!
//! Before any attempt to read the different structs' information, you need to update them to
//! get up-to-date information.
//!
//! # Examples
//!
//! ```
//! use sysinfo::{ProcessExt, SystemExt};
//!
//! let mut system = sysinfo::System::new_all();
//!
//! // First we update all information of our system struct.
//! system.refresh_all();
//!
//! // Now let's print every process' id and name:
//! for (pid, proc_) in system.get_processes() {
//!     println!("{}:{} => status: {:?}", pid, proc_.name(), proc_.status());
//! }
//!
//! // Then let's print the temperature of the different components:
//! for component in system.get_components() {
//!     println!("{:?}", component);
//! }
//!
//! // And then all disks' information:
//! for disk in system.get_disks() {
//!     println!("{:?}", disk);
//! }
//!
//! // And finally the RAM and SWAP information:
//! println!("total memory: {} KiB", system.get_total_memory());
//! println!("used memory : {} KiB", system.get_used_memory());
//! println!("total swap  : {} KiB", system.get_total_swap());
//! println!("used swap   : {} KiB", system.get_used_swap());
//! ```

#![crate_name = "sysinfo"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![deny(missing_docs)]
#![deny(intra_doc_link_resolution_failure)]
//#![deny(warnings)]
#![allow(unknown_lints)]

#[macro_use]
extern crate cfg_if;
#[cfg(not(any(target_os = "unknown", target_arch = "wasm32")))]
extern crate libc;
extern crate rayon;

#[macro_use]
extern crate doc_comment;

#[cfg(test)]
doctest!("../README.md");

cfg_if! {
    if #[cfg(target_os = "macos")] {
        mod mac;
        use mac as sys;

        #[cfg(test)]
        const MIN_USERS: usize = 1;
    } else if #[cfg(windows)] {
        mod windows;
        use windows as sys;
        extern crate winapi;
        extern crate ntapi;

        #[cfg(test)]
        const MIN_USERS: usize = 1;
    } else if #[cfg(unix)] {
        mod linux;
        use linux as sys;

        #[cfg(test)]
        const MIN_USERS: usize = 1;
    } else {
        mod unknown;
        use unknown as sys;

        #[cfg(test)]
        const MIN_USERS: usize = 0;
    }
}

pub use common::{AsU32, DiskType, NetworksIter, Pid, RefreshKind};
pub use sys::{Component, Disk, NetworkData, Networks, Process, ProcessStatus, Processor, System};
pub use traits::{
    ComponentExt, DiskExt, NetworkExt, NetworksExt, ProcessExt, ProcessorExt, SystemExt, UserExt,
};

#[cfg(feature = "c-interface")]
pub use c_interface::*;
pub use utils::get_current_pid;

#[cfg(feature = "c-interface")]
mod c_interface;
mod common;
mod debug;
mod system;
mod traits;
mod utils;

/// This function is only used on linux targets, on the other platforms it does nothing.
///
/// On linux, to improve performance, we keep a `/proc` file open for each process we index with
/// a maximum number of files open equivalent to half of the system limit.
///
/// The problem is that some users might need all the available file descriptors so we need to
/// allow them to change this limit. Reducing
///
/// Note that if you set a limit bigger than the system limit, the system limit will be set.
///
/// Returns `true` if the new value has been set.
pub fn set_open_files_limit(mut _new_limit: isize) -> bool {
    #[cfg(all(not(target_os = "macos"), unix))]
    {
        if _new_limit < 0 {
            _new_limit = 0;
        }
        let max = sys::system::get_max_nb_fds();
        if _new_limit > max {
            _new_limit = max;
        }
        if let Ok(ref mut x) = unsafe { sys::system::REMAINING_FILES.lock() } {
            // If files are already open, to be sure that the number won't be bigger when those
            // files are closed, we subtract the current number of opened files to the new limit.
            let diff = max - **x;
            **x = _new_limit - diff;
            true
        } else {
            false
        }
    }
    #[cfg(any(not(unix), target_os = "macos"))]
    {
        false
    }
}

/// An enum representing signal on UNIX-like systems.
#[repr(C)]
#[derive(Clone, PartialEq, PartialOrd, Debug, Copy)]
pub enum Signal {
    /// Hangup detected on controlling terminal or death of controlling process.
    Hangup = 1,
    /// Interrupt from keyboard.
    Interrupt = 2,
    /// Quit from keyboard.
    Quit = 3,
    /// Illegal instruction.
    Illegal = 4,
    /// Trace/breakpoint trap.
    Trap = 5,
    /// Abort signal from C abort function.
    Abort = 6,
    // IOT trap. A synonym for SIGABRT.
    // IOT = 6,
    /// Bus error (bad memory access).
    Bus = 7,
    /// Floating point exception.
    FloatingPointException = 8,
    /// Kill signal.
    Kill = 9,
    /// User-defined signal 1.
    User1 = 10,
    /// Invalid memory reference.
    Segv = 11,
    /// User-defined signal 2.
    User2 = 12,
    /// Broken pipe: write to pipe with no readers.
    Pipe = 13,
    /// Timer signal from C alarm function.
    Alarm = 14,
    /// Termination signal.
    Term = 15,
    /// Stack fault on coprocessor (unused).
    Stklft = 16,
    /// Child stopped or terminated.
    Child = 17,
    /// Continue if stopped.
    Continue = 18,
    /// Stop process.
    Stop = 19,
    /// Stop typed at terminal.
    TSTP = 20,
    /// Terminal input for background process.
    TTIN = 21,
    /// Terminal output for background process.
    TTOU = 22,
    /// Urgent condition on socket.
    Urgent = 23,
    /// CPU time limit exceeded.
    XCPU = 24,
    /// File size limit exceeded.
    XFSZ = 25,
    /// Virtual alarm clock.
    VirtualAlarm = 26,
    /// Profiling time expired.
    Profiling = 27,
    /// Windows resize signal.
    Winch = 28,
    /// I/O now possible.
    IO = 29,
    // Pollable event (Sys V). Synonym for IO
    //Poll = 29,
    /// Power failure (System V).
    Power = 30,
    /// Bad argument to routine (SVr4).
    Sys = 31,
}

/// A struct represents system load average value.
#[repr(C)]
#[derive(Default, Debug, Clone)]
pub struct LoadAvg {
    /// Average load within one minute.
    pub one: f64,
    /// Average load within five minutes.
    pub five: f64,
    /// Average load within fifteen minutes.
    pub fifteen: f64,
}

/// Type containing user information.
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct User {
    name: String,
    groups: Vec<String>,
}

impl UserExt for User {
    fn get_name(&self) -> &str {
        &self.name
    }

    fn get_groups(&self) -> &[String] {
        &self.groups
    }
}

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

    #[test]
    fn check_memory_usage() {
        let mut s = ::System::new();

        s.refresh_all();
        assert_eq!(
            s.get_processes()
                .iter()
                .all(|(_, proc_)| proc_.memory() == 0),
            false
        );
    }

    #[test]
    fn check_users() {
        let mut s = ::System::new();

        assert!(s.get_users().is_empty());
        s.refresh_users_list();
        assert!(s.get_users().len() >= MIN_USERS);
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::collections::HashMap;
use std::collections::HashSet;
use std::borrow::BorrowMut;
use std::fmt;
use std::fmt::Display;

type Id = String;

#[derive(Debug)]
pub enum EvaluationError {
    UnboundVariable(Rc<Id>),
    IllformedExpression,
}

impl fmt::Display for EvaluationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::EvaluationError::*;
        match *self {
            UnboundVariable(ref id) => write!(f, "Encountered unbound variable: {}", id),
            IllformedExpression => write!(f, "Illformed expression"),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Type {
    Var(Rc<Id>),
    Fun(Rc<Type>, Rc<Type>),
    Forall(Rc<Id>, Rc<Type>),
}

impl Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Type::Var(ref x) => write!(f,"{}",x),
            Type::Fun(ref a, ref b) => write!(f,"({}) -> {}",a,b),
            Type::Forall(ref x, ref t) => write!(f,"(forall {}. {})", x, t),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr {
    Var(Rc<Id>),
    Lam(Rc<Id>, Rc<Type>, Rc<Expr>),
    App(Rc<Expr>, Rc<Expr>),
    TLam(Rc<Id>, Rc<Expr>),
    TApp(Rc<Expr>, Rc<Type>),
    Let(Rc<Id>, Rc<Type>, Rc<Expr>, Rc<Expr>),
    TLet(Rc<Id>, Rc<Type>, Rc<Expr>),
}

impl Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Expr::Var(ref x) => write!(f,"{}",x),
            Expr::Lam(ref x, ref t, ref e) => write!(f,"(\\{}:{}.{})",x,t,e),
            Expr::App(ref e1, ref e2) => write!(f,"({} {})",e1,e2),
            Expr::TLam(ref X, ref e) => write!(f,"(/\\{}.{})",X,e),
            Expr::TApp(ref e, ref t) => write!(f,"({} [{}])",e,t),
            Expr::Let(ref x, ref t, ref e1, ref e2) => write!(f,"(let {}:{} = {} in {})",x,t,e1,e2),
            Expr::TLet(ref X, ref t, ref e) => write!(f,"(Let {} = {} in {})",X,t,e),
        }
    }
}

impl Expr {
    /// Evaluates an expression and returns either the normal form or an error
    /// if it is illformed
    pub fn eval(self) -> Result<Rc<Expr>, EvaluationError> {
        Expr::eval_expr(Rc::new(self))
    }

    /// Evaluates an expression and returns either the normal form or an error
    /// if it is illformed
    pub fn eval_expr(e: Rc<Expr>) -> Result<Rc<Expr>, EvaluationError> {
        Expr::eval_helper(e, &mut HashMap::new(), &mut HashMap::new())
    }

    /// Workhorse for `eval` and `eval_expr`
    fn eval_helper(e: Rc<Expr>,
            emap: &mut HashMap<&str, Rc<Expr>>,
            tmap: &mut HashMap<&str, Rc<Type>>)
            -> Result<Rc<Expr>, EvaluationError> {
        use self::EvaluationError::*;

        let result = match *e {
            Expr::Var(ref id) => {
                match emap.get::<str>(id) {
                    Some(ref e) => Ok((*e).clone()),
                    None => Err(UnboundVariable(id.clone())),
                }
            }
            Expr::Lam(_, _, _) |
            Expr::TLam(_, _) => Ok(e.clone()),
            Expr::App(ref e1, ref e2) => {
                let v1 = Expr::eval_helper(e1.clone(), emap, tmap)?;
                let v2 = Expr::eval_helper(e2.clone(), emap, tmap)?;
                match *v1 {
                    Expr::Lam(ref x, _, ref e3) => {
                        let mut new_map = emap.clone();
                        new_map.insert(x, e3.clone());
                        Expr::eval_helper(e2.clone(), &mut new_map, tmap)
                    },
                    _ => Err(IllformedExpression),
                }
            }
            Expr::TApp(ref e, ref t) => {
                let v = Expr::eval_helper(e.clone(), emap, tmap)?;
                match *v {
                    Expr::TLam(ref x, ref e) => {
                        let mut new_map = tmap.clone();
                        new_map.insert(x, t.clone());
                        Expr::eval_helper(e.clone(), emap, &mut new_map)
                    },
                    _ => Err(IllformedExpression),
                }
            },
            Expr::Let(ref x, ref t, ref e1, ref e2) => {
                let desugared = Expr::App(Rc::new(Expr::Lam(x.clone(), t.clone(), e2.clone())),
                                          e1.clone());
                Expr::eval_helper(Rc::new(desugared), emap, tmap)
            },
            Expr::TLet(ref X, ref t, ref e) => {
                let desugared = Expr::TApp(Rc::new(Expr::TLam(X.clone(), e.clone())), t.clone());
                Expr::eval_helper(Rc::new(desugared), emap, tmap)
            }
        }?;

        Expr::expand_types(result, tmap, &mut HashSet::new())
    }

    /// Recursively expand the free variables of the types of the expression
    fn expand_types(e: Rc<Expr>, map: &HashMap<&str,Rc<Type>>, bvs: &mut HashSet<&str>)
    -> Result<Rc<Expr>,EvaluationError> {
        match *e {
            Expr::Var(_) => {
                Ok(e.clone())
            },
            Expr::App(ref e1, ref e2) => {
                let r1 = Expr::expand_types(e1.clone(), map, bvs)?;
                let r2 = Expr::expand_types(e2.clone(), map, bvs)?;
                Ok(Rc::new(Expr::App(r1,r2)))
            },
            Expr::TApp(ref e, ref t) => {
                let re = Expr::expand_types(e.clone(), map, bvs)?;
                let te = Type::eval(t.clone(), map, bvs)?;
                Ok(Rc::new(Expr::TApp(re, te)))
            },
            Expr::Let(ref x, ref t, ref e1, ref e2) => {
                let r1 = Expr::expand_types(e1.clone(), map, bvs)?;
                let r2 = Expr::expand_types(e2.clone(), map, bvs)?;
                let te = Type::eval(t.clone(), map, bvs)?;
                Ok(Rc::new(Expr::Let(x.clone(), te, r1, r2)))
            },
            Expr::TLet(ref X, ref t, ref e) => {
                let te = Type::eval(t.clone(), map, bvs)?;
                let mut new_bvs = bvs.clone();
                new_bvs.insert(X);
                let re = Expr::expand_types(e.clone(), map, &mut new_bvs)?;
                Ok(Rc::new(Expr::TLet(X.clone(), te, re)))
            },
            Expr::Lam(ref x, ref t, ref e) => {
                let te = Type::eval(t.clone(), map, bvs)?;
                let re = Expr::expand_types(e.clone(), map, bvs)?;
                Ok(Rc::new(Expr::Lam(x.clone(), te, re)))
            },
            Expr::TLam(ref X, ref e) => {
                let mut new_bvs = bvs.clone();
                new_bvs.insert(X);
                let re = Expr::expand_types(e.clone(), map, &mut new_bvs)?;
                Ok(Rc::new(Expr::TLam(X.clone(), re)))
            },
        }
    }

    pub fn type_check(&self) -> Option<Type> {
        self.type_check_helper(&HashMap::new(), &HashMap::new(), &HashSet::new())
    }

    fn type_check_helper(&self,
        emap: &HashMap<&str,&Type>,
        tmap: &HashMap<&str,&Type>,
        bvs: &HashSet<&str>) -> Option<Type> {
        match *self {
            Expr::Var(ref id) => {
                emap.get::<str>(&**id).map(|t| { (*t).clone() } )
            },
            Expr::Lam(ref x, ref t, ref e) => {
                let mut new_emap = emap.clone();
                new_emap.insert(x, t);
                let tbod = e.type_check_helper(&new_emap, tmap, bvs);
                tbod.map(|tb| { Type::Fun((*t).clone(), Rc::new(tb)) })
            },
            Expr::App(ref e1, ref e2) => {
                let t1 = e1.type_check_helper(emap, tmap, bvs);
                let t2 = e2.type_check_helper(emap, tmap, bvs);
                match t1 {
                    Some(Type::Fun(ta,tb)) => match t2 {
                        Some(tc) => {
                            if Type::alpha_equiv(&ta,&tc) {
                                Some((*tb).clone())
                            }
                            else {
                                None
                            }
                        },
                        _ => None,
                    },
                    _ => None,
                }
            },
            Expr::TLam(ref X, ref e) => {
                let mut new_bvs = bvs.clone();
                new_bvs.insert(X);
                e.type_check_helper(emap, tmap, &new_bvs)
                    .map(|t| { Type::Forall((*X).clone(),Rc::new(t)) })
            },
            Expr::TApp(ref e, ref t) => {
                let t1 = e.type_check_helper(emap, tmap, bvs);
                match t1 {
                    Some(Type::Forall(X,t2)) => {
                        let mut new_tmap = tmap.clone();
                        new_tmap.insert(&*X, t);
                        let new_tmap = {
                            let mut map: HashMap<&str,Rc<Type>> = HashMap::new();
                            for (k,v) in new_tmap {
                                map.insert(k, Rc::new((*v).clone()));
                            }
                            map
                        };
                        Some((*Type::eval(t2, &new_tmap, bvs).unwrap()).clone())
                    },
                    _ => None,
                }
            },
            Expr::Let(ref x, ref t, ref e1, ref e2) => {
                (Expr::App(
                    Rc::new(Expr::Lam(x.clone(),t.clone(),e2.clone())),
                    e2.clone())).type_check_helper(emap, tmap, bvs)
            },
            Expr::TLet(ref X, ref t, ref e) => {
                (Expr::TApp(
                    Rc::new(Expr::TLam(X.clone(),e.clone())),
                    t.clone())).type_check_helper(emap, tmap, bvs)
            },
        }
    }
}

impl Type {
    /// Recursively expand the free variables of a type using the given map
    fn eval(t: Rc<Type>, map: &HashMap<&str,Rc<Type>>, bvs: &HashSet<&str>)
    -> Result<Rc<Type>,EvaluationError> {
        match *t {
            Type::Var(ref id) => {
                if bvs.contains::<str>(id) {
                    Ok(t.clone())
                } else {
                    match map.get::<str>(id) {
                        Some(ref t) => Type::eval((*t).clone(), map, bvs),
                        None => Err(EvaluationError::UnboundVariable(id.clone())),
                    }
                }
            },
            Type::Fun(ref t1, ref t2) => {
                let rt1 = Type::eval(t1.clone(), map, bvs)?;
                let rt2 = Type::eval(t2.clone(), map, bvs)?;
                Ok(Rc::new(Type::Fun(rt1, rt2)))
            },
            Type::Forall(ref id, ref t) => {
                let mut new_bvs = bvs.clone();
                new_bvs.insert(&**id);
                let rt = Type::eval(t.clone(), map, &mut new_bvs)?;
                Ok(Rc::new(Type::Forall(id.clone(), rt)))
            },
        }
    }

    fn alpha_equiv(t1: &Type, t2: &Type) -> bool {
        // TODO
        unimplemented!()
    }
}<commit_msg>Fix serious error in typechecking of let exprs<commit_after>use std::rc::Rc;
use std::collections::HashMap;
use std::collections::HashSet;
use std::borrow::BorrowMut;
use std::fmt;
use std::fmt::Display;

type Id = String;

#[derive(Debug)]
pub enum EvaluationError {
    UnboundVariable(Rc<Id>),
    IllformedExpression,
}

impl fmt::Display for EvaluationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::EvaluationError::*;
        match *self {
            UnboundVariable(ref id) => write!(f, "Encountered unbound variable: {}", id),
            IllformedExpression => write!(f, "Illformed expression"),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Type {
    Var(Rc<Id>),
    Fun(Rc<Type>, Rc<Type>),
    Forall(Rc<Id>, Rc<Type>),
}

impl Display for Type {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Type::Var(ref x) => write!(f,"{}",x),
            Type::Fun(ref a, ref b) => write!(f,"({}) -> {}",a,b),
            Type::Forall(ref x, ref t) => write!(f,"(forall {}. {})", x, t),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr {
    Var(Rc<Id>),
    Lam(Rc<Id>, Rc<Type>, Rc<Expr>),
    App(Rc<Expr>, Rc<Expr>),
    TLam(Rc<Id>, Rc<Expr>),
    TApp(Rc<Expr>, Rc<Type>),
    Let(Rc<Id>, Rc<Type>, Rc<Expr>, Rc<Expr>),
    TLet(Rc<Id>, Rc<Type>, Rc<Expr>),
}

impl Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Expr::Var(ref x) => write!(f,"{}",x),
            Expr::Lam(ref x, ref t, ref e) => write!(f,"(\\{}:{}.{})",x,t,e),
            Expr::App(ref e1, ref e2) => write!(f,"({} {})",e1,e2),
            Expr::TLam(ref X, ref e) => write!(f,"(/\\{}.{})",X,e),
            Expr::TApp(ref e, ref t) => write!(f,"({} [{}])",e,t),
            Expr::Let(ref x, ref t, ref e1, ref e2) => write!(f,"(let {}:{} = {} in {})",x,t,e1,e2),
            Expr::TLet(ref X, ref t, ref e) => write!(f,"(Let {} = {} in {})",X,t,e),
        }
    }
}

impl Expr {
    /// Evaluates an expression and returns either the normal form or an error
    /// if it is illformed
    pub fn eval(self) -> Result<Rc<Expr>, EvaluationError> {
        Expr::eval_expr(Rc::new(self))
    }

    /// Evaluates an expression and returns either the normal form or an error
    /// if it is illformed
    pub fn eval_expr(e: Rc<Expr>) -> Result<Rc<Expr>, EvaluationError> {
        Expr::eval_helper(e, &mut HashMap::new(), &mut HashMap::new())
    }

    /// Workhorse for `eval` and `eval_expr`
    fn eval_helper(e: Rc<Expr>,
            emap: &mut HashMap<&str, Rc<Expr>>,
            tmap: &mut HashMap<&str, Rc<Type>>)
            -> Result<Rc<Expr>, EvaluationError> {
        use self::EvaluationError::*;

        let result = match *e {
            Expr::Var(ref id) => {
                match emap.get::<str>(id) {
                    Some(ref e) => Ok((*e).clone()),
                    None => Err(UnboundVariable(id.clone())),
                }
            }
            Expr::Lam(_, _, _) |
            Expr::TLam(_, _) => Ok(e.clone()),
            Expr::App(ref e1, ref e2) => {
                let v1 = Expr::eval_helper(e1.clone(), emap, tmap)?;
                let v2 = Expr::eval_helper(e2.clone(), emap, tmap)?;
                match *v1 {
                    Expr::Lam(ref x, _, ref e3) => {
                        let mut new_map = emap.clone();
                        new_map.insert(x, e3.clone());
                        Expr::eval_helper(e2.clone(), &mut new_map, tmap)
                    },
                    _ => Err(IllformedExpression),
                }
            }
            Expr::TApp(ref e, ref t) => {
                let v = Expr::eval_helper(e.clone(), emap, tmap)?;
                match *v {
                    Expr::TLam(ref x, ref e) => {
                        let mut new_map = tmap.clone();
                        new_map.insert(x, t.clone());
                        Expr::eval_helper(e.clone(), emap, &mut new_map)
                    },
                    _ => Err(IllformedExpression),
                }
            },
            Expr::Let(ref x, ref t, ref e1, ref e2) => {
                let desugared = Expr::App(Rc::new(Expr::Lam(x.clone(), t.clone(), e2.clone())),
                                          e1.clone());
                Expr::eval_helper(Rc::new(desugared), emap, tmap)
            },
            Expr::TLet(ref X, ref t, ref e) => {
                let desugared = Expr::TApp(Rc::new(Expr::TLam(X.clone(), e.clone())), t.clone());
                Expr::eval_helper(Rc::new(desugared), emap, tmap)
            }
        }?;

        Expr::expand_types(result, tmap, &mut HashSet::new())
    }

    /// Recursively expand the free variables of the types of the expression
    fn expand_types(e: Rc<Expr>, map: &HashMap<&str,Rc<Type>>, bvs: &mut HashSet<&str>)
    -> Result<Rc<Expr>,EvaluationError> {
        match *e {
            Expr::Var(_) => {
                Ok(e.clone())
            },
            Expr::App(ref e1, ref e2) => {
                let r1 = Expr::expand_types(e1.clone(), map, bvs)?;
                let r2 = Expr::expand_types(e2.clone(), map, bvs)?;
                Ok(Rc::new(Expr::App(r1,r2)))
            },
            Expr::TApp(ref e, ref t) => {
                let re = Expr::expand_types(e.clone(), map, bvs)?;
                let te = Type::eval(t.clone(), map, bvs)?;
                Ok(Rc::new(Expr::TApp(re, te)))
            },
            Expr::Let(ref x, ref t, ref e1, ref e2) => {
                let r1 = Expr::expand_types(e1.clone(), map, bvs)?;
                let r2 = Expr::expand_types(e2.clone(), map, bvs)?;
                let te = Type::eval(t.clone(), map, bvs)?;
                Ok(Rc::new(Expr::Let(x.clone(), te, r1, r2)))
            },
            Expr::TLet(ref X, ref t, ref e) => {
                let te = Type::eval(t.clone(), map, bvs)?;
                let mut new_bvs = bvs.clone();
                new_bvs.insert(X);
                let re = Expr::expand_types(e.clone(), map, &mut new_bvs)?;
                Ok(Rc::new(Expr::TLet(X.clone(), te, re)))
            },
            Expr::Lam(ref x, ref t, ref e) => {
                let te = Type::eval(t.clone(), map, bvs)?;
                let re = Expr::expand_types(e.clone(), map, bvs)?;
                Ok(Rc::new(Expr::Lam(x.clone(), te, re)))
            },
            Expr::TLam(ref X, ref e) => {
                let mut new_bvs = bvs.clone();
                new_bvs.insert(X);
                let re = Expr::expand_types(e.clone(), map, &mut new_bvs)?;
                Ok(Rc::new(Expr::TLam(X.clone(), re)))
            },
        }
    }

    pub fn type_check(&self) -> Option<Type> {
        self.type_check_helper(&HashMap::new(), &HashMap::new(), &HashSet::new())
    }

    fn type_check_helper(&self,
        emap: &HashMap<&str,&Type>,
        tmap: &HashMap<&str,&Type>,
        bvs: &HashSet<&str>) -> Option<Type> {
        match *self {
            Expr::Var(ref id) => {
                emap.get::<str>(&**id).map(|t| { (*t).clone() } )
            },
            Expr::Lam(ref x, ref t, ref e) => {
                let mut new_emap = emap.clone();
                new_emap.insert(x, t);
                let tbod = e.type_check_helper(&new_emap, tmap, bvs);
                tbod.map(|tb| { Type::Fun((*t).clone(), Rc::new(tb)) })
            },
            Expr::App(ref e1, ref e2) => {
                let t1 = e1.type_check_helper(emap, tmap, bvs);
                let t2 = e2.type_check_helper(emap, tmap, bvs);
                match t1 {
                    Some(Type::Fun(ta,tb)) => match t2 {
                        Some(tc) => {
                            if Type::alpha_equiv(&ta,&tc) {
                                Some((*tb).clone())
                            }
                            else {
                                None
                            }
                        },
                        _ => None,
                    },
                    _ => None,
                }
            },
            Expr::TLam(ref X, ref e) => {
                let mut new_bvs = bvs.clone();
                new_bvs.insert(X);
                e.type_check_helper(emap, tmap, &new_bvs)
                    .map(|t| { Type::Forall((*X).clone(),Rc::new(t)) })
            },
            Expr::TApp(ref e, ref t) => {
                let t1 = e.type_check_helper(emap, tmap, bvs);
                match t1 {
                    Some(Type::Forall(X,t2)) => {
                        let mut new_tmap = tmap.clone();
                        new_tmap.insert(&*X, t);
                        let new_tmap = {
                            let mut map: HashMap<&str,Rc<Type>> = HashMap::new();
                            for (k,v) in new_tmap {
                                map.insert(k, Rc::new((*v).clone()));
                            }
                            map
                        };
                        Some((*Type::eval(t2, &new_tmap, bvs).unwrap()).clone())
                    },
                    _ => None,
                }
            },
            Expr::Let(ref x, ref t, ref e1, ref e2) => {
                (Expr::App(
                    Rc::new(Expr::Lam(x.clone(),t.clone(),e2.clone())),
                    e1.clone())).type_check_helper(emap, tmap, bvs)
            },
            Expr::TLet(ref X, ref t, ref e) => {
                (Expr::TApp(
                    Rc::new(Expr::TLam(X.clone(),e.clone())),
                    t.clone())).type_check_helper(emap, tmap, bvs)
            },
        }
    }
}

impl Type {
    /// Recursively expand the free variables of a type using the given map
    fn eval(t: Rc<Type>, map: &HashMap<&str,Rc<Type>>, bvs: &HashSet<&str>)
    -> Result<Rc<Type>,EvaluationError> {
        match *t {
            Type::Var(ref id) => {
                if bvs.contains::<str>(id) {
                    Ok(t.clone())
                } else {
                    match map.get::<str>(id) {
                        Some(ref t) => Type::eval((*t).clone(), map, bvs),
                        None => Err(EvaluationError::UnboundVariable(id.clone())),
                    }
                }
            },
            Type::Fun(ref t1, ref t2) => {
                let rt1 = Type::eval(t1.clone(), map, bvs)?;
                let rt2 = Type::eval(t2.clone(), map, bvs)?;
                Ok(Rc::new(Type::Fun(rt1, rt2)))
            },
            Type::Forall(ref id, ref t) => {
                let mut new_bvs = bvs.clone();
                new_bvs.insert(&**id);
                let rt = Type::eval(t.clone(), map, &mut new_bvs)?;
                Ok(Rc::new(Type::Forall(id.clone(), rt)))
            },
        }
    }

    fn alpha_equiv(t1: &Type, t2: &Type) -> bool {
        // TODO
        unimplemented!()
    }
}<|endoftext|>


<commit_before>use std::io::fs::File;

use gl;
use gl::types::GLuint;
use libc::c_void;

use image;
use image::Image;

use graphics::ImageSize;

/// Wraps OpenGL texture data.
/// The texture gets deleted when running out of scope.
pub struct Texture {
    id: GLuint,
    width: u32,
    height: u32,
}

impl Texture {
    /// Creates a new texture.
    #[inline(always)]
    pub fn new(id: GLuint, width: u32, height: u32) -> Texture {
        Texture {
            id: id,
            width: width,
            height: height,
        }
    }

    /// Gets the OpenGL id of the texture.
    #[inline(always)]
    pub fn get_id(&self) -> GLuint {
        self.id
    }

    /// Loads image from memory, the format is 8-bit greyscale.
    pub fn from_memory_alpha(buf: &[u8], width: u32, height: u32) -> Result<Texture, String> {
        let mut pixels = Vec::new();
        for alpha in buf.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                pixels.as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }

    /// Loads image by relative file name to the asset root.
    pub fn from_path(path: &Path) -> Result<Texture, String> {
        let fin = match File::open(path) {
            Ok(fin) => fin,
            Err(e)  => return Err(format!("Could not load '{}': {}",
                path.filename_str().unwrap(), e)),
        };

        let img = match Image::load(fin, image::PNG) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{}': {}",
                path.filename_str().unwrap(), e)),
        };

        match img.colortype() {
            image::RGBA(8) => {},
            c => fail!("Unsupported color type {} in png", c),
        };

        let (width, height) = img.dimensions();

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.raw_pixels().as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, [self.id].as_ptr());
        }
    }
}

impl ImageSize for Texture {
    fn get_size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
}
<commit_msg>Upgraded to latest rust-image<commit_after>use gl;
use gl::types::GLuint;
use libc::c_void;

use image;
use image::GenericImage;

use graphics::ImageSize;

/// Wraps OpenGL texture data.
/// The texture gets deleted when running out of scope.
pub struct Texture {
    id: GLuint,
    width: u32,
    height: u32,
}

impl Texture {
    /// Creates a new texture.
    #[inline(always)]
    pub fn new(id: GLuint, width: u32, height: u32) -> Texture {
        Texture {
            id: id,
            width: width,
            height: height,
        }
    }

    /// Gets the OpenGL id of the texture.
    #[inline(always)]
    pub fn get_id(&self) -> GLuint {
        self.id
    }

    /// Loads image from memory, the format is 8-bit greyscale.
    pub fn from_memory_alpha(buf: &[u8], width: u32, height: u32) -> Result<Texture, String> {
        let mut pixels = Vec::new();
        for alpha in buf.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                pixels.as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }

    /// Loads image by relative file name to the asset root.
    pub fn from_path(path: &Path) -> Result<Texture, String> {
        let img = match image::open(path) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{}': {}",
                path.filename_str().unwrap(), e)),
        };

        match img.color() {
            image::RGBA(8) => {},
            c => fail!("Unsupported color type {} in png", c),
        };

        let (width, height) = img.dimensions();

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MIN_FILTER,
                gl::LINEAR as i32
            );
            gl::TexParameteri(
                gl::TEXTURE_2D,
                gl::TEXTURE_MAG_FILTER,
                gl::LINEAR as i32
            );
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                width as i32,
                height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.raw_pixels().as_ptr() as *const c_void
            );
        }

        Ok(Texture::new(id, width, height))
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, [self.id].as_ptr());
        }
    }
}

impl ImageSize for Texture {
    fn get_size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
}
<|endoftext|>


<commit_before>use cssparser::Parser;

use parsers::Parse;
use parsers::ParseError;

#[derive(Debug, Clone, PartialEq)]
pub enum IRI {
    None,
    Resource(String),
}

impl Default for IRI {
    fn default() -> IRI {
        IRI::None
    }
}

impl Parse for IRI {
    type Data = ();
    type Err = ParseError;

    fn parse(parser: &mut Parser, _: Self::Data) -> Result<IRI, ParseError> {
        if parser.try(|i| i.expect_ident_matching("none")).is_ok() {
            Ok(IRI::None)
        } else {
            let url = parser
                .expect_url()
                .map_err(|_| ParseError::new("expected url"))?;

            parser
                .expect_exhausted()
                .map_err(|_| ParseError::new("expected url"))?;

            Ok(IRI::Resource(url.as_ref().to_owned()))
        }
    }
}

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

    #[test]
    fn parses_none() {
        assert_eq!(IRI::parse_str("none", ()), Ok(IRI::None));
    }

    #[test]
    fn parses_url() {
        assert_eq!(
            IRI::parse_str("url(foo)", ()),
            Ok(IRI::Resource("foo".to_string()))
        );

        // be permissive if the closing ) is missing
        assert_eq!(
            IRI::parse_str("url(", ()),
            Ok(IRI::Resource("".to_string()))
        );
        assert_eq!(
            IRI::parse_str("url(foo", ()),
            Ok(IRI::Resource("foo".to_string()))
        );

        assert!(IRI::parse_str("", ()).is_err());
        assert!(IRI::parse_str("foo", ()).is_err());
        assert!(IRI::parse_str("url(foo)bar", ()).is_err());
    }
}
<commit_msg>IRI::get() - new method<commit_after>use cssparser::Parser;

use parsers::Parse;
use parsers::ParseError;

#[derive(Debug, Clone, PartialEq)]
pub enum IRI {
    None,
    Resource(String),
}

impl Default for IRI {
    fn default() -> IRI {
        IRI::None
    }
}

impl IRI {
    /// Returns the contents of an `IRI::Resource`, or `None`
    pub fn get(&self) -> Option<&str> {
        match *self {
            IRI::None => None,
            IRI::Resource(ref s) => Some(s.as_ref()),
        }
    }
}

impl Parse for IRI {
    type Data = ();
    type Err = ParseError;

    fn parse(parser: &mut Parser, _: Self::Data) -> Result<IRI, ParseError> {
        if parser.try(|i| i.expect_ident_matching("none")).is_ok() {
            Ok(IRI::None)
        } else {
            let url = parser
                .expect_url()
                .map_err(|_| ParseError::new("expected url"))?;

            parser
                .expect_exhausted()
                .map_err(|_| ParseError::new("expected url"))?;

            Ok(IRI::Resource(url.as_ref().to_owned()))
        }
    }
}

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

    #[test]
    fn parses_none() {
        assert_eq!(IRI::parse_str("none", ()), Ok(IRI::None));
    }

    #[test]
    fn parses_url() {
        assert_eq!(
            IRI::parse_str("url(foo)", ()),
            Ok(IRI::Resource("foo".to_string()))
        );

        // be permissive if the closing ) is missing
        assert_eq!(
            IRI::parse_str("url(", ()),
            Ok(IRI::Resource("".to_string()))
        );
        assert_eq!(
            IRI::parse_str("url(foo", ()),
            Ok(IRI::Resource("foo".to_string()))
        );

        assert!(IRI::parse_str("", ()).is_err());
        assert!(IRI::parse_str("foo", ()).is_err());
        assert!(IRI::parse_str("url(foo)bar", ()).is_err());
    }

    #[test]
    fn get() {
        assert_eq!(IRI::None.get(), None);
        assert_eq!(IRI::Resource(String::from("foo")).get(), Some("foo"));
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::collections::HashMap;
use std::string::ToString;

use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{BlendMode, Texture, TextureCreator, WindowCanvas};
use sdl2::surface::Surface;
use sdl2::ttf::Font as Sdl2Font;
use sdl2::video::WindowContext;

use crate::ui::types::ScreenText;

const ASCII: &str = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";

pub struct Font<'a> {
    texture: Texture<'a>,
    glyphs: Vec<GlyphRegion>,
    line_height: u32,
    space_advance: i32,

    texture_creator: &'a TextureCreator<WindowContext>,
    cached_texts: HashMap<String, (Texture<'a>, u32, u32)>,
}

struct GlyphRegion {
    start: i32,
    advance: i32,
    width: u32,
    height: u32,
}

impl<'a> Font<'a> {
    pub fn from_font(
        texture_creator: &'a TextureCreator<sdl2::video::WindowContext>,
        font: Sdl2Font,
    ) -> Result<Self, String> {
        let mut total_width = 0;
        let mut total_height = 0;
        let mut glyphs: Vec<GlyphRegion> = Vec::new();
        let mut space_advance = 0;

        for c in ASCII.chars() {
            if let Some(metric) = font.find_glyph_metrics(c) {
                let (w, h) = font.size_of_char(c).map_err(to_string)?;

                glyphs.push(GlyphRegion {
                    start: total_width as i32,
                    width: w,
                    height: h,
                    advance: metric.advance,
                });

                if c == ' ' {
                    space_advance = metric.advance;
                }

                total_width += w;
                total_height = h;
            } else {
                return Err(format!("Unsupported character: {}", c));
            }
        }

        let mut font_canvas = Surface::new(
            total_width,
            total_height,
            texture_creator.default_pixel_format(),
        )?
        .into_canvas()?;

        let font_texture_creator = font_canvas.texture_creator();
        let mut x = 0;

        for (i, c) in ASCII.char_indices() {
            let GlyphRegion { width, .. } = glyphs[i];

            let char_surface = font
                .render(&c.to_string())
                .blended(Color::RGBA(255, 255, 255, 255))
                .map_err(to_string)?;

            let char_tex = font_texture_creator
                .create_texture_from_surface(&char_surface)
                .map_err(to_string)?;

            let target = Rect::new(x, 0, width, total_height);
            font_canvas.copy(&char_tex, None, Some(target))?;

            x += width as i32;
        }

        let texture = texture_creator
            .create_texture_from_surface(font_canvas.into_surface())
            .map_err(to_string)?;

        Ok(Font {
            texture,
            glyphs,
            line_height: total_height,
            space_advance,
            texture_creator,
            cached_texts: HashMap::new(),
        })
    }

    pub fn draw(&mut self, screen_txt: ScreenText, cvs: &mut WindowCanvas) -> Result<(), String> {
        let cache_key = screen_txt.text.to_string();

        if let Some((tex, w, h)) = self.cached_texts.get(&cache_key) {
            let (x, y) = screen_txt.pos.to_xy();
            return cvs.copy(tex, Rect::new(0, 0, *w, *h), Rect::new(x, y, *w, *h));
        }

        let (x, y) = screen_txt.pos.to_xy();
        let prepared_text = prepare(screen_txt, self);
        let (w, h) = prepared_text.dim;
        let pixel_format = self.texture_creator.default_pixel_format();
        let mut target_tex = self
            .texture_creator
            // !!! ATTENSION: there seems to be a very wierd issue if texture is to small
            // the background/transparency of small textures is broken
            .create_texture_target(pixel_format, max(w, 65), max(h, 33))
            .map_err(to_string)?;

        target_tex.set_blend_mode(BlendMode::Blend);

        draw_text(
            prepared_text,
            cvs,
            &mut self.texture,
            &mut target_tex,
            (x, y, w, h),
        )?;

        self.cached_texts.insert(cache_key, (target_tex, w, h));

        Ok(())
    }
}

struct PreparedWord {
    chars: Vec<(i32, i32, u32, u32)>,
    width: u32,
}

impl PreparedWord {
    fn prepare(glyphs: &Vec<GlyphRegion>, txt: &str) -> Self {
        let mut x = 0;
        let mut chars = Vec::new();

        for c in txt.chars() {
            if let Some(r) = find_glyph_region(c, glyphs) {
                chars.push((r.start, r.advance, r.width, r.height));
                x = x + r.advance;
            }
        }

        PreparedWord {
            chars,
            width: x as u32,
        }
    }

    fn draw(
        self: &Self,
        texture: &Texture,
        cvs: &mut WindowCanvas,
        target: &mut Texture,
        pos: (i32, i32),
    ) -> Result<(), String> {
        let (mut x, y) = pos;

        for (start, advance, width, height) in self.chars.iter() {
            let from = Rect::new(*start, 0, *width, *height);
            let to = Rect::new(x, y, *width, *height);

            cvs.with_texture_canvas(target, |texture_canvas| {
                texture_canvas.copy(&texture, Some(from), Some(to)).unwrap();
            })
            .map_err(to_string)?;

            x = x + advance;
        }

        Ok(())
    }
}

struct PreparedText {
    words: Vec<((i32, i32), PreparedWord)>,
    dim: (u32, u32),
    color: (u8, u8, u8, u8),
    background: Option<Color>,
    padding: u32,
    border: Option<(u32, Color)>,
}

fn prepare<'a>(text: ScreenText, font: &'a Font) -> PreparedText {
    let (mut x, mut y) = (0, 0);
    let mut words = Vec::new();
    let mut width_so_far: u32 = 0;
    let border_width = text.border.map(|(w, _)| w).unwrap_or(0);
    let spacing = 2 * text.padding + 2 * border_width;
    let max_width = text.max_width - spacing;

    for line in text.text.into_string().lines() {
        for t in line.split_whitespace() {
            let word = PreparedWord::prepare(&font.glyphs, t);
            let text_width = word.width;
            let advance = font.space_advance + text_width as i32;

            if x > 0 && (x + advance) as u32 > max_width {
                // text does not fit in current line
                // => wrap text (no wrap if first word in line)
                x = 0;
                y += font.line_height as i32;
                width_so_far = max_width;
            }

            words.push(((x, y), word));

            x += advance;

            if x as u32 > width_so_far {
                width_so_far = x as u32;
            }
        }

        x = 0;
        y += font.line_height as i32;
    }

    let width = max(text.min_width, width_so_far + spacing);
    let height = y as u32 + spacing;

    PreparedText {
        words,
        dim: (width, height),
        color: text.color,
        background: text.background.map(|(r, g, b, a)| Color::RGBA(r, g, b, a)),
        padding: text.padding,
        border: text
            .border
            .map(|(w, (r, g, b, a))| (w, Color::RGBA(r, g, b, a))),
    }
}

fn find_glyph_region(c: char, metrics: &Vec<GlyphRegion>) -> Option<&GlyphRegion> {
    let ascii_index = c as usize;
    if ascii_index >= 32 && ascii_index <= 126 {
        metrics.get(ascii_index - 32)
    } else {
        None
    }
}

fn to_string(s: impl ToString) -> String {
    s.to_string()
}

fn draw_background(
    cvs: &mut WindowCanvas,
    target_texture: &mut Texture,
    color: Color,
    x: i32,
    y: i32,
    w: u32,
    h: u32,
) -> Result<(), String> {
    cvs.with_texture_canvas(target_texture, |texture_canvas| {
        texture_canvas.set_blend_mode(BlendMode::Blend); // TODO test performance impact
        texture_canvas.set_draw_color(color);
        texture_canvas.fill_rect(Rect::new(x, y, w, h)).unwrap();
    })
    .map_err(to_string)
}

fn draw_border(
    cvs: &mut WindowCanvas,
    target_texture: &mut Texture,
    color: Color,
    bw: u32,
    x: i32,
    y: i32,
    w: u32,
    h: u32,
) -> Result<(), String> {
    let xl = x;
    let xr = x + w as i32 - bw as i32;
    let yt = y;
    let yb = y + h as i32 - bw as i32;

    cvs.with_texture_canvas(target_texture, |texture_canvas| {
        texture_canvas.set_draw_color(color);
        texture_canvas.fill_rect(Rect::new(xl, yt, w, bw)).unwrap(); // top
        texture_canvas.fill_rect(Rect::new(xl, yt, bw, h)).unwrap(); // left
        texture_canvas.fill_rect(Rect::new(xr, yt, bw, h)).unwrap(); // right
        texture_canvas.fill_rect(Rect::new(xl, yb, w, bw)).unwrap(); // bottom
    })
    .map_err(to_string)
}

fn draw_text(
    text: PreparedText,
    cvs: &mut WindowCanvas,
    texture: &mut Texture,
    target: &mut Texture,
    (x, y, w, h): (i32, i32, u32, u32),
) -> Result<(), String> {

    if let Some(color) = text.background {
        draw_background(cvs, target, color, 0, 0, w, h)?;
    }

    if let Some((bw, border_color)) = text.border {
        draw_border(cvs, target, border_color, bw, 0, 0, w, h)?;
    }

    let shift = text.border.map(|(val, _)| val).unwrap_or(0) as i32 + text.padding as i32;

    texture.set_alpha_mod(text.color.3);
    texture.set_color_mod(text.color.0, text.color.1, text.color.2);

    for ((offset_x, offset_y), word) in text.words.iter() {
        word.draw(texture, cvs, target, (shift + offset_x, shift + offset_y))?;
    }

    texture.set_alpha_mod(255);
    texture.set_color_mod(0, 0, 0);

    cvs.copy(&target, Rect::new(0, 0, w, h), Rect::new(x, y, w, h))?;

    Ok(())
}
<commit_msg>[FIX] fixed artefacts when rendering texts without transparent backgrounds<commit_after>use std::cmp::max;
use std::collections::HashMap;
use std::string::ToString;

use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{BlendMode, Texture, TextureCreator, WindowCanvas};
use sdl2::surface::Surface;
use sdl2::ttf::Font as Sdl2Font;
use sdl2::video::WindowContext;

use crate::ui::types::ScreenText;

const ASCII: &str = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";

pub struct Font<'a> {
    texture: Texture<'a>,
    glyphs: Vec<GlyphRegion>,
    line_height: u32,
    space_advance: i32,

    texture_creator: &'a TextureCreator<WindowContext>,
    cached_texts: HashMap<String, (Texture<'a>, u32, u32)>,
}

struct GlyphRegion {
    start: i32,
    advance: i32,
    width: u32,
    height: u32,
}

impl<'a> Font<'a> {
    pub fn from_font(
        texture_creator: &'a TextureCreator<sdl2::video::WindowContext>,
        font: Sdl2Font,
    ) -> Result<Self, String> {
        let mut total_width = 0;
        let mut total_height = 0;
        let mut glyphs: Vec<GlyphRegion> = Vec::new();
        let mut space_advance = 0;

        for c in ASCII.chars() {
            if let Some(metric) = font.find_glyph_metrics(c) {
                let (w, h) = font.size_of_char(c).map_err(to_string)?;

                glyphs.push(GlyphRegion {
                    start: total_width as i32,
                    width: w,
                    height: h,
                    advance: metric.advance,
                });

                if c == ' ' {
                    space_advance = metric.advance;
                }

                total_width += w;
                total_height = h;
            } else {
                return Err(format!("Unsupported character: {}", c));
            }
        }

        let mut font_canvas = Surface::new(
            total_width,
            total_height,
            texture_creator.default_pixel_format(),
        )?
        .into_canvas()?;

        let font_texture_creator = font_canvas.texture_creator();
        let mut x = 0;

        for (i, c) in ASCII.char_indices() {
            let GlyphRegion { width, .. } = glyphs[i];

            let char_surface = font
                .render(&c.to_string())
                .blended(Color::RGBA(255, 255, 255, 255))
                .map_err(to_string)?;

            let char_tex = font_texture_creator
                .create_texture_from_surface(&char_surface)
                .map_err(to_string)?;

            let target = Rect::new(x, 0, width, total_height);
            font_canvas.copy(&char_tex, None, Some(target))?;

            x += width as i32;
        }

        let texture = texture_creator
            .create_texture_from_surface(font_canvas.into_surface())
            .map_err(to_string)?;

        Ok(Font {
            texture,
            glyphs,
            line_height: total_height,
            space_advance,
            texture_creator,
            cached_texts: HashMap::new(),
        })
    }

    pub fn draw(&mut self, screen_txt: ScreenText, cvs: &mut WindowCanvas) -> Result<(), String> {
        let cache_key = screen_txt.text.to_string();

        if let Some((tex, w, h)) = self.cached_texts.get(&cache_key) {
            let (x, y) = screen_txt.pos.to_xy();
            return cvs.copy(tex, Rect::new(0, 0, *w, *h), Rect::new(x, y, *w, *h));
        }

        let (x, y) = screen_txt.pos.to_xy();
        let prepared_text = prepare(screen_txt, self);
        let (w, h) = prepared_text.dim;
        let pixel_format = self.texture_creator.default_pixel_format();
        let mut target_tex = self
            .texture_creator
            // !!! ATTENSION: there seems to be a very wierd issue if texture is to small
            // the background/transparency of small textures is broken
            .create_texture_target(pixel_format, max(w, 65), max(h, 65))
            .map_err(to_string)?;

        target_tex.set_blend_mode(BlendMode::Blend);

        draw_text(
            prepared_text,
            cvs,
            &mut self.texture,
            &mut target_tex,
            (x, y, w, h),
        )?;

        self.cached_texts.insert(cache_key, (target_tex, w, h));

        Ok(())
    }
}

struct PreparedWord {
    chars: Vec<(i32, i32, u32, u32)>,
    width: u32,
}

impl PreparedWord {
    fn prepare(glyphs: &Vec<GlyphRegion>, txt: &str) -> Self {
        let mut x = 0;
        let mut chars = Vec::new();

        for c in txt.chars() {
            if let Some(r) = find_glyph_region(c, glyphs) {
                chars.push((r.start, r.advance, r.width, r.height));
                x = x + r.advance;
            }
        }

        PreparedWord {
            chars,
            width: x as u32,
        }
    }

    fn draw(
        self: &Self,
        texture: &Texture,
        cvs: &mut WindowCanvas,
        target: &mut Texture,
        pos: (i32, i32),
    ) -> Result<(), String> {
        let (mut x, y) = pos;

        for (start, advance, width, height) in self.chars.iter() {
            let from = Rect::new(*start, 0, *width, *height);
            let to = Rect::new(x, y, *width, *height);

            cvs.with_texture_canvas(target, |texture_canvas| {
                texture_canvas.copy(&texture, Some(from), Some(to)).unwrap();
            })
            .map_err(to_string)?;

            x = x + advance;
        }

        Ok(())
    }
}

struct PreparedText {
    words: Vec<((i32, i32), PreparedWord)>,
    dim: (u32, u32),
    color: (u8, u8, u8, u8),
    background: Color,
    // background: Option<Color>,
    padding: u32,
    border: Option<(u32, Color)>,
}

fn prepare<'a>(text: ScreenText, font: &'a Font) -> PreparedText {
    let (mut x, mut y) = (0, 0);
    let mut words = Vec::new();
    let mut width_so_far: u32 = 0;
    let border_width = text.border.map(|(w, _)| w).unwrap_or(0);
    let spacing = 2 * text.padding + 2 * border_width;
    let max_width = text.max_width - spacing;

    for line in text.text.into_string().lines() {
        for t in line.split_whitespace() {
            let word = PreparedWord::prepare(&font.glyphs, t);
            let text_width = word.width;
            let advance = font.space_advance + text_width as i32;

            if x > 0 && (x + advance) as u32 > max_width {
                // text does not fit in current line
                // => wrap text (no wrap if first word in line)
                x = 0;
                y += font.line_height as i32;
                width_so_far = max_width;
            }

            words.push(((x, y), word));

            x += advance;

            if x as u32 > width_so_far {
                width_so_far = x as u32;
            }
        }

        x = 0;
        y += font.line_height as i32;
    }

    let width = max(text.min_width, width_so_far + spacing);
    let height = y as u32 + spacing;

    PreparedText {
        words,
        dim: (width, height),
        color: text.color,
        background: text.background.map(|(r, g, b, a)| Color::RGBA(r, g, b, a)).unwrap_or(Color::RGBA(0, 0, 0, 0)),
        padding: text.padding,
        border: text
            .border
            .map(|(w, (r, g, b, a))| (w, Color::RGBA(r, g, b, a))),
    }
}

fn find_glyph_region(c: char, metrics: &Vec<GlyphRegion>) -> Option<&GlyphRegion> {
    let ascii_index = c as usize;
    if ascii_index >= 32 && ascii_index <= 126 {
        metrics.get(ascii_index - 32)
    } else {
        None
    }
}

fn to_string(s: impl ToString) -> String {
    s.to_string()
}

fn draw_background(
    cvs: &mut WindowCanvas,
    target_texture: &mut Texture,
    color: Color,
    x: i32,
    y: i32,
    w: u32,
    h: u32,
) -> Result<(), String> {
    cvs.with_texture_canvas(target_texture, |texture_canvas| {
        texture_canvas.set_blend_mode(BlendMode::Blend); // TODO test performance impact
        texture_canvas.set_draw_color(color);
        texture_canvas.fill_rect(Rect::new(x, y, w, h)).unwrap();
    })
    .map_err(to_string)
}

fn draw_border(
    cvs: &mut WindowCanvas,
    target_texture: &mut Texture,
    color: Color,
    bw: u32,
    x: i32,
    y: i32,
    w: u32,
    h: u32,
) -> Result<(), String> {
    let xl = x;
    let xr = x + w as i32 - bw as i32;
    let yt = y;
    let yb = y + h as i32 - bw as i32;

    cvs.with_texture_canvas(target_texture, |texture_canvas| {
        texture_canvas.set_draw_color(color);
        texture_canvas.fill_rect(Rect::new(xl, yt, w, bw)).unwrap(); // top
        texture_canvas.fill_rect(Rect::new(xl, yt, bw, h)).unwrap(); // left
        texture_canvas.fill_rect(Rect::new(xr, yt, bw, h)).unwrap(); // right
        texture_canvas.fill_rect(Rect::new(xl, yb, w, bw)).unwrap(); // bottom
    })
    .map_err(to_string)
}

fn draw_text(
    text: PreparedText,
    cvs: &mut WindowCanvas,
    texture: &mut Texture,
    target: &mut Texture,
    (x, y, w, h): (i32, i32, u32, u32),
) -> Result<(), String> {
    draw_background(cvs, target, text.background, 0, 0, w, h)?;

    if let Some((bw, border_color)) = text.border {
        draw_border(cvs, target, border_color, bw, 0, 0, w, h)?;
    }

    let shift = text.border.map(|(val, _)| val).unwrap_or(0) as i32 + text.padding as i32;

    texture.set_alpha_mod(text.color.3);
    texture.set_color_mod(text.color.0, text.color.1, text.color.2);

    for ((offset_x, offset_y), word) in text.words.iter() {
        word.draw(texture, cvs, target, (shift + offset_x, shift + offset_y))?;
    }

    texture.set_alpha_mod(255);
    texture.set_color_mod(0, 0, 0);

    cvs.copy(&target, Rect::new(0, 0, w, h), Rect::new(x, y, w, h))?;

    Ok(())
}
<|endoftext|>


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

use pipeline::{self, PipelineId};
use std;
use std::collections::HashMap;
use std::convert::{From, Into};
use std::fmt::{self, Debug, Display, Formatter};
use std::fs::File;
use std::io::Read;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
use std::sync::mpsc::{Sender, Receiver};
use vcs;

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Repo {
    pub path: PathBuf,
    pub origin: String,
    pub master_branch: String,
    pub staging_branch: String,
}

pub struct Worker {
    repos: HashMap<PipelineId, Repo>,
    executable: String,
}

impl Worker {
    pub fn new(
        executable: String,
    ) -> Worker {
        Worker{
            repos: HashMap::new(),
            executable: executable,
        }
    }
    pub fn add_pipeline(&mut self, pipeline_id: PipelineId, repo: Repo) {
        self.repos.insert(pipeline_id, repo);
    }
}

impl pipeline::Worker<vcs::Event<Commit>, vcs::Message<Commit>> for Worker {
    fn run(
        &mut self,
        recv_msg: Receiver<vcs::Message<Commit>>,
        mut send_event: Sender<vcs::Event<Commit>>
    ) {
        loop {
            self.handle_message(
                recv_msg.recv().expect("Pipeline went away"),
                &mut send_event,
            );
        }
    }
}

macro_rules! try_cmd {
    ($e:expr) => ({
        let cmd = try!($e);
        if !cmd.status.success() {
            return Err(GitError::Cli(
                cmd.status,
                String::from_utf8_lossy(&cmd.stderr).into_owned()
            ));
        }
        cmd
    })
}

impl Worker {
    fn handle_message(
        &self,
        msg: vcs::Message<Commit>,
        send_event: &mut Sender<vcs::Event<Commit>>
    ) {
        match msg {
            vcs::Message::MergeToStaging(
                pipeline_id, pull_commit, message, remote
            ) => {
                let repo = match self.repos.get(&pipeline_id) {
                    Some(repo) => repo,
                    None => {
                        warn!("Got wrong pipeline ID {:?}", pipeline_id);
                        return;
                    }
                };
                info!("Merging {} ...", pull_commit);
                match self.merge_to_staging(
                    repo, pull_commit, &message, &remote
                ) {
                    Err(e) => {
                        warn!(
                            "Failed to merge {} to staging: {:?}",
                            pull_commit,
                            e
                        );
                        send_event.send(vcs::Event::FailedMergeToStaging(
                            pipeline_id,
                            pull_commit,
                        )).expect("Pipeline gone merge to staging error");
                    }
                    Ok(merge_commit) => {
                        info!("Merged {} to {}", pull_commit, merge_commit);
                        send_event.send(vcs::Event::MergedToStaging(
                            pipeline_id,
                            pull_commit,
                            merge_commit,
                        )).expect("Pipeline gone merge to staging");
                    }
                }
            }
            vcs::Message::MoveStagingToMaster(pipeline_id, merge_commit) => {
                let repo = match self.repos.get(&pipeline_id) {
                    Some(repo) => repo,
                    None => {
                        warn!("Got wrong pipeline ID {:?}", pipeline_id);
                        return;
                    }
                };
                info!("Moving {} ...", merge_commit);
                match self.move_staging_to_master(repo, merge_commit) {
                    Err(e) => {
                        warn!(
                            "Failed to move {} to master: {:?}",
                            merge_commit,
                            e
                        );
                        send_event.send(vcs::Event::FailedMoveToMaster(
                            pipeline_id,
                            merge_commit,
                        )).expect("Pipeline gone move to master error");
                    }
                    Ok(()) => {
                        info!("Moved {} to master", merge_commit);
                        send_event.send(vcs::Event::MovedToMaster(
                            pipeline_id,
                            merge_commit,
                        )).expect("Pipeline gone move to master");
                    }
                }
            }
        }
    }
    fn merge_to_staging(
        &self,
        repo: &Repo,
        pull_commit: Commit,
        message: &str,
        remote: &str,
    ) -> Result<Commit, GitError> {
        try!(self.setup_dir(repo));
        try_cmd!(Command::new(&self.executable)
            .arg("-C")
            .arg(&repo.path)
            .arg("fetch")
            .arg("origin")
            .arg(&repo.master_branch)
            .arg(remote)
            .output());
        try_cmd!(Command::new(&self.executable)
            .arg("-C")
            .arg(&repo.path)
            .arg("checkout")
            .arg(format!("origin/{}", repo.master_branch))
            .output());
        try_cmd!(Command::new(&self.executable)
            .arg("-C")
            .arg(&repo.path)
            .arg("branch")
            .arg("-f")
            .arg(&repo.staging_branch)
            .arg(format!("origin/{}", repo.master_branch))
            .output());
        try_cmd!(Command::new(&self.executable)
            .arg("-C")
            .arg(&repo.path)
            .arg("checkout")
            .arg(&repo.staging_branch)
            .output());
        try_cmd!(Command::new(&self.executable)
            .arg("-C")
            .arg(&repo.path)
            .arg("merge")
            .arg("--no-ff")
            .arg("-m")
            .arg(message)
            .arg(&pull_commit.to_string())
            .output());
        try_cmd!(Command::new(&self.executable)
            .arg("-C")
            .arg(&repo.path)
            .arg("push")
            .arg("-f")
            .arg("origin")
            .arg(&repo.staging_branch)
            .output());
        let mut commit_string = String::new();
        try!(try!(File::open(
            Path::new(&repo.path)
                .join(".git/refs/heads/")
                .join(&repo.staging_branch)
        )).read_to_string(&mut commit_string));
        commit_string = commit_string.replace("\n", "").replace("\r", "");
        Commit::from_str(&commit_string).map_err(|e| e.into())
    }
    fn move_staging_to_master(
        &self,
        repo: &Repo,
        merge_commit: Commit,
    ) -> Result<(), GitError> {
        try!(self.setup_dir(repo));
        try_cmd!(Command::new(&self.executable)
            .arg("-C")
            .arg(&repo.path)
            .arg("push")
            .arg("-f")
            .arg("origin")
            .arg(format!("{}:{}", merge_commit, &repo.master_branch))
            .output());
        Ok(())
    }
    fn setup_dir(&self, repo: &Repo) -> Result<(), GitError> {
        if !repo.path.exists() {
            try_cmd!(Command::new(&self.executable)
                .arg("init")
                .arg(&repo.path)
                .output());
            try_cmd!(Command::new(&self.executable)
                .arg("-C")
                .arg(&repo.path)
                .arg("remote")
                .arg("add")
                .arg("origin")
                .arg(&repo.origin)
                .output());
        } else {
            try!(Command::new(&self.executable)
                .arg("-C")
                .arg(&repo.path)
                .arg("merge")
                .arg("--abort")
                .output());
        }
        Ok(())
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum GitError {
        Int(err: std::num::ParseIntError) {
            cause(err)
            from()
        }
        Io(err: std::io::Error) {
            cause(err)
            from()
        }
        Cli(status: std::process::ExitStatus, output: String) {}
    }
}

// A git commit is a SHA1 sum. A SHA1 sum is a 160-bit number.
#[derive(Copy, Clone, Deserialize, Eq, PartialEq, Serialize)]
pub struct Commit(u64, u64, u32);

impl vcs::Commit for Commit {}

impl Display for Commit {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{:016x}{:016x}{:08x}", self.0, self.1, self.2)
    }
}

impl Debug for Commit {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Commit({:016x}{:016x}{:08x})", self.0, self.1, self.2)
    }
}

// It should be easy to get from hexadecimal.

impl FromStr for Commit {
    type Err = ParseIntError;
    fn from_str(mut s: &str) -> Result<Commit, ParseIntError> {
        if s.len() != 40 {
            s = "THIS_IS_NOT_A_NUMBER_BUT_I_CANT_MAKE_PARSEINTERROR_MYSELF";
        }
        let a = try!(u64::from_str_radix(&s[0..16], 16));
        let b = try!(u64::from_str_radix(&s[16..32], 16));
        let c = try!(u32::from_str_radix(&s[32..40], 16));
        Ok(Commit(a, b, c))
    }
}

impl Into<String> for Commit {
    fn into(self) -> String {
        self.to_string()
    }
}
<commit_msg>Just set the CWD ourselves.<commit_after>// This file is released under the same terms as Rust itself.

use pipeline::{self, PipelineId};
use std;
use std::collections::HashMap;
use std::convert::{From, Into};
use std::fmt::{self, Debug, Display, Formatter};
use std::fs::File;
use std::io::Read;
use std::num::ParseIntError;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
use std::sync::mpsc::{Sender, Receiver};
use vcs;

#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Repo {
    pub path: PathBuf,
    pub origin: String,
    pub master_branch: String,
    pub staging_branch: String,
}

pub struct Worker {
    repos: HashMap<PipelineId, Repo>,
    executable: String,
}

impl Worker {
    pub fn new(
        executable: String,
    ) -> Worker {
        Worker{
            repos: HashMap::new(),
            executable: executable,
        }
    }
    pub fn add_pipeline(&mut self, pipeline_id: PipelineId, repo: Repo) {
        self.repos.insert(pipeline_id, repo);
    }
}

impl pipeline::Worker<vcs::Event<Commit>, vcs::Message<Commit>> for Worker {
    fn run(
        &mut self,
        recv_msg: Receiver<vcs::Message<Commit>>,
        mut send_event: Sender<vcs::Event<Commit>>
    ) {
        loop {
            self.handle_message(
                recv_msg.recv().expect("Pipeline went away"),
                &mut send_event,
            );
        }
    }
}

macro_rules! try_cmd {
    ($e:expr) => ({
        let cmd = try!($e);
        if !cmd.status.success() {
            return Err(GitError::Cli(
                cmd.status,
                String::from_utf8_lossy(&cmd.stderr).into_owned()
            ));
        }
        cmd
    })
}

impl Worker {
    fn handle_message(
        &self,
        msg: vcs::Message<Commit>,
        send_event: &mut Sender<vcs::Event<Commit>>
    ) {
        match msg {
            vcs::Message::MergeToStaging(
                pipeline_id, pull_commit, message, remote
            ) => {
                let repo = match self.repos.get(&pipeline_id) {
                    Some(repo) => repo,
                    None => {
                        warn!("Got wrong pipeline ID {:?}", pipeline_id);
                        return;
                    }
                };
                info!("Merging {} ...", pull_commit);
                match self.merge_to_staging(
                    repo, pull_commit, &message, &remote
                ) {
                    Err(e) => {
                        warn!(
                            "Failed to merge {} to staging: {:?}",
                            pull_commit,
                            e
                        );
                        send_event.send(vcs::Event::FailedMergeToStaging(
                            pipeline_id,
                            pull_commit,
                        )).expect("Pipeline gone merge to staging error");
                    }
                    Ok(merge_commit) => {
                        info!("Merged {} to {}", pull_commit, merge_commit);
                        send_event.send(vcs::Event::MergedToStaging(
                            pipeline_id,
                            pull_commit,
                            merge_commit,
                        )).expect("Pipeline gone merge to staging");
                    }
                }
            }
            vcs::Message::MoveStagingToMaster(pipeline_id, merge_commit) => {
                let repo = match self.repos.get(&pipeline_id) {
                    Some(repo) => repo,
                    None => {
                        warn!("Got wrong pipeline ID {:?}", pipeline_id);
                        return;
                    }
                };
                info!("Moving {} ...", merge_commit);
                match self.move_staging_to_master(repo, merge_commit) {
                    Err(e) => {
                        warn!(
                            "Failed to move {} to master: {:?}",
                            merge_commit,
                            e
                        );
                        send_event.send(vcs::Event::FailedMoveToMaster(
                            pipeline_id,
                            merge_commit,
                        )).expect("Pipeline gone move to master error");
                    }
                    Ok(()) => {
                        info!("Moved {} to master", merge_commit);
                        send_event.send(vcs::Event::MovedToMaster(
                            pipeline_id,
                            merge_commit,
                        )).expect("Pipeline gone move to master");
                    }
                }
            }
        }
    }
    fn merge_to_staging(
        &self,
        repo: &Repo,
        pull_commit: Commit,
        message: &str,
        remote: &str,
    ) -> Result<Commit, GitError> {
        try!(self.setup_dir(repo));
        try_cmd!(Command::new(&self.executable)
            .current_dir(&repo.path)
            .arg("fetch")
            .arg("origin")
            .arg(&repo.master_branch)
            .arg(remote)
            .output());
        try_cmd!(Command::new(&self.executable)
            .current_dir(&repo.path)
            .arg("checkout")
            .arg(format!("origin/{}", repo.master_branch))
            .output());
        try_cmd!(Command::new(&self.executable)
            .current_dir(&repo.path)
            .arg("branch")
            .arg("-f")
            .arg(&repo.staging_branch)
            .arg(format!("origin/{}", repo.master_branch))
            .output());
        try_cmd!(Command::new(&self.executable)
            .current_dir(&repo.path)
            .arg("checkout")
            .arg(&repo.staging_branch)
            .output());
        try_cmd!(Command::new(&self.executable)
            .current_dir(&repo.path)
            .arg("merge")
            .arg("--no-ff")
            .arg("-m")
            .arg(message)
            .arg(&pull_commit.to_string())
            .output());
        try_cmd!(Command::new(&self.executable)
            .current_dir(&repo.path)
            .arg("push")
            .arg("-f")
            .arg("origin")
            .arg(&repo.staging_branch)
            .output());
        let mut commit_string = String::new();
        try!(try!(File::open(
            Path::new(&repo.path)
                .join(".git/refs/heads/")
                .join(&repo.staging_branch)
        )).read_to_string(&mut commit_string));
        commit_string = commit_string.replace("\n", "").replace("\r", "");
        Commit::from_str(&commit_string).map_err(|e| e.into())
    }
    fn move_staging_to_master(
        &self,
        repo: &Repo,
        merge_commit: Commit,
    ) -> Result<(), GitError> {
        try!(self.setup_dir(repo));
        try_cmd!(Command::new(&self.executable)
            .current_dir(&repo.path)
            .arg("push")
            .arg("-f")
            .arg("origin")
            .arg(format!("{}:{}", merge_commit, &repo.master_branch))
            .output());
        Ok(())
    }
    fn setup_dir(&self, repo: &Repo) -> Result<(), GitError> {
        if !repo.path.exists() {
            try_cmd!(Command::new(&self.executable)
                .arg("init")
                .arg(&repo.path)
                .output());
            try_cmd!(Command::new(&self.executable)
                .current_dir(&repo.path)
                .arg("remote")
                .arg("add")
                .arg("origin")
                .arg(&repo.origin)
                .output());
        } else {
            try!(Command::new(&self.executable)
                .current_dir(&repo.path)
                .arg("merge")
                .arg("--abort")
                .output());
        }
        Ok(())
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum GitError {
        Int(err: std::num::ParseIntError) {
            cause(err)
            from()
        }
        Io(err: std::io::Error) {
            cause(err)
            from()
        }
        Cli(status: std::process::ExitStatus, output: String) {}
    }
}

// A git commit is a SHA1 sum. A SHA1 sum is a 160-bit number.
#[derive(Copy, Clone, Deserialize, Eq, PartialEq, Serialize)]
pub struct Commit(u64, u64, u32);

impl vcs::Commit for Commit {}

impl Display for Commit {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{:016x}{:016x}{:08x}", self.0, self.1, self.2)
    }
}

impl Debug for Commit {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Commit({:016x}{:016x}{:08x})", self.0, self.1, self.2)
    }
}

// It should be easy to get from hexadecimal.

impl FromStr for Commit {
    type Err = ParseIntError;
    fn from_str(mut s: &str) -> Result<Commit, ParseIntError> {
        if s.len() != 40 {
            s = "THIS_IS_NOT_A_NUMBER_BUT_I_CANT_MAKE_PARSEINTERROR_MYSELF";
        }
        let a = try!(u64::from_str_radix(&s[0..16], 16));
        let b = try!(u64::from_str_radix(&s[16..32], 16));
        let c = try!(u32::from_str_radix(&s[32..40], 16));
        Ok(Commit(a, b, c))
    }
}

impl Into<String> for Commit {
    fn into(self) -> String {
        self.to_string()
    }
}
<|endoftext|>