<commit_before>use error::{Result, Error};
use filters::Filter;
use std::collections::HashMap;
use token::Token::{self, Identifier, StringLiteral, NumberLiteral, BooleanLiteral};
use value::Value;

#[derive(Clone)]
pub enum Interrupt {
    Continue,
    Break,
}

type ValueMap = HashMap<String, Value>;

#[derive(Default)]
pub struct Context {
    stack: Vec<ValueMap>,
    globals: ValueMap,

    /// The current interrupt state. The interrupt state is used by
    /// the `break` and `continue` tags to halt template rendering
    /// at a given point and unwind the `render` call stack until
    /// it reaches an enclosing `for_loop`. At that point the interrupt
    /// is cleared, and the `for_loop` carries on processing as directed.
    interrupt: Option<Interrupt>,

    /// The indices of all the cycles encountered during rendering.
    cycles: HashMap<String, usize>,

    // Public for backwards compatability
    pub filters: HashMap<String, Box<Filter>>,
}

impl Context {
    /// Creates a new, empty rendering context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::Context;
    /// let ctx = Context::new();
    /// assert_eq!(ctx.get_val("test"), None);
    /// ```
    pub fn new() -> Context {
        Context::with_values_and_filters(HashMap::new(), HashMap::new())
    }

    pub fn with_values(values: HashMap<String, Value>) -> Context {
        Context::with_values_and_filters(values, HashMap::new())
    }

    pub fn with_filters(filters: HashMap<String, Box<Filter>>) -> Context {
        Context::with_values_and_filters(HashMap::new(), filters)
    }

    pub fn with_values_and_filters(values: HashMap<String, Value>,
                                   filters: HashMap<String, Box<Filter>>)
                                   -> Context {
        Context {
            stack: vec![HashMap::new()],
            interrupt: None,
            cycles: HashMap::new(),
            globals: values,
            filters: filters,
        }
    }

    pub fn cycle_element(&mut self, name: &str, values: &[Token]) -> Result<Option<Value>> {
        let index = {
            let i = self.cycles.entry(name.to_owned()).or_insert(0);
            let j = *i;
            *i = (*i + 1) % values.len();
            j
        };

        if index >= values.len() {
            return Err(Error::Render(format!("cycle index {} out of bounds {}",
                                             index,
                                             values.len())));
        }

        self.evaluate(&values[index])
    }

    /// Only add the given filter to the context if a filter with this name doesn't already exist.
    pub fn maybe_add_filter(&mut self, name: &str, filter: Box<Filter>) {
        if self.get_filter(name).is_none() {
            self.add_filter(name, filter)
        }
    }

    pub fn add_filter(&mut self, name: &str, filter: Box<Filter>) {
        self.filters.insert(name.to_owned(), filter);
    }

    pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b Box<Filter>> {
        self.filters.get(name)
    }

    pub fn interrupted(&self) -> bool {
        self.interrupt.is_some()
    }

    /// Sets the interrupt state. Any previous state is obliterated.
    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
        self.interrupt = Some(interrupt);
    }

    /// Fetches and clears the interrupt state.
    pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
        let rval = self.interrupt.clone();
        self.interrupt = None;
        rval
    }

    /// Creates a new variable scope chained to a parent scope.
    fn push_scope(&mut self) {
        self.stack.push(HashMap::new());
    }

    /// Removes the topmost stack frame from the local variable stack.
    ///
    /// # Panics
    ///
    /// This method will panic if popping the topmost frame results in an
    /// empty stack. Given that a context is created with a top-level stack
    /// frame already in place, empyting the stack should never happen in a
    /// well-formed program.
    fn pop_scope(&mut self) {
        if self.stack.pop().is_none() {
            panic!("Pop leaves empty stack")
        };
    }

    /// Sets up a new stack frame, executes the supplied function and then
    /// tears the stack frame down before returning the function's result
    /// to the caller.
    ///
    /// # Examples
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// ctx.run_in_scope(|mut stack_frame| {
    ///   // stack_frame inherits values from its parent context
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(42f32)));
    ///
    ///   // but can (optionally) override them
    ///   stack_frame.set_local_val("test", Value::Num(3.14f32));
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(3.14f32)));
    /// });
    /// // the original value is unchanged once the scope exits
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
        where FnT: FnOnce(&mut Context) -> RvalT
    {
        self.push_scope();
        let result = f(self);
        self.pop_scope();
        result
    }

    /// Internal part of get_val. Walks the scope stack to try and find the
    /// reqested variable, and failing that checks the global pool.
    fn get<'a>(&'a self, name: &str) -> Option<&'a Value> {
        for frame in self.stack.iter().rev() {
            if let rval @ Some(_) = frame.get(name) {
                return rval;
            }
        }
        self.globals.get(name)
    }

    /// Gets a value from the rendering context. The name value can be a
    /// dot-separated path to a value. A value will only be returned if
    /// each link in the chain (excluding the final name) refers to a
    /// value of type Object.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
    /// ```
    pub fn get_val<'b>(&'b self, name: &str) -> Option<&'b Value> {
        let mut path = name.split('.');
        let key = path.next().unwrap_or("");
        let mut rval = self.get(key);

        // walk the chain of Object values, as specified by the path
        // passed in name
        for id in path {
            match rval {
                Some(&Value::Object(ref x)) => rval = x.get(id),
                _ => return None,
            }
        }

        rval
    }

    /// Sets a value in the global context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn set_val(&mut self, name: &str, val: Value) -> Option<Value> {
        self.globals.insert(name.to_owned(), val)
    }

    /// Translates a Token to a Value, looking it up in the context if
    /// necessary
    pub fn evaluate(&self, t: &Token) -> Result<Option<Value>> {
        match *t {
            NumberLiteral(f) => Ok(Some(Value::Num(f))),
            StringLiteral(ref s) => Ok(Some(Value::Str(s.clone()))),
            BooleanLiteral(b) => Ok(Some(Value::Bool(b))),
            Identifier(ref id) => Ok(self.get_val(id).cloned()),
            _ => {
                let msg = format!("Cannot evaluate {}", t);
                Err(Error::Other(msg))
            }
        }
    }

    /// Sets a value to the rendering context.
    /// Note that it needs to be wrapped in a liquid::Value.
    ///
    /// # Panics
    ///
    /// Panics if there is no frame on the local values stack. Context
    /// instances are created with a top-level stack frame in place, so
    /// this should never happen in a well-formed program.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.run_in_scope(|mut local_scope| {
    ///   local_scope.set_val("global", Value::Num(42f32));
    ///   local_scope.set_local_val("local", Value::Num(163f32));
    ///
    ///   assert_eq!(local_scope.get_val("global"), Some(&Value::Num(42f32)));
    ///   assert_eq!(local_scope.get_val("local"), Some(&Value::Num(163f32)));
    /// });
    /// assert_eq!(ctx.get_val("global"), Some(&Value::Num(42f32)));
    /// assert_eq!(ctx.get_val("local"), None);
    /// ```
    pub fn set_local_val(&mut self, name: &str, val: Value) -> Option<Value> {
        match self.stack.last_mut() {
            Some(frame) => frame.insert(name.to_owned(), val),
            None => panic!("Cannot insert into an empty stack"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::Context;
    use value::Value;
    use std::collections::HashMap;

    #[test]
    fn get_val() {
        let mut ctx = Context::new();
        let mut post = HashMap::new();
        post.insert("number".to_owned(), Value::Num(42f32));
        ctx.set_val("post", Value::Object(post));
        assert_eq!(ctx.get_val("post.number").unwrap(), &Value::Num(42f32));
    }

    #[test]
    fn scoped_variables() {
        let mut ctx = Context::new();
        ctx.set_val("test", Value::Num(42f32));
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));

        ctx.run_in_scope(|mut new_scope| {
            // assert that values are chained to the parent scope
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(42f32));

            // set a new local value, and assert that it overrides the previous value
            new_scope.set_local_val("test", Value::Num(3.14f32));
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(3.14f32));

            // sat a new val that we will pick up outside the scope
            new_scope.set_val("global", Value::str("some value"));
        });

        // assert that the value has reverted to the old one
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
        assert_eq!(ctx.get_val("global").unwrap(), &Value::str("some value"));
    }

    #[test]
    fn evaluate_handles_string_literals() {
        use token::Token::StringLiteral;

        let ctx = Context::new();
        let t = StringLiteral("hello".to_owned());
        assert_eq!(ctx.evaluate(&t).unwrap(), Some(Value::str("hello")));
    }

    #[test]
    fn evaluate_handles_number_literals() {
        use token::Token::NumberLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&NumberLiteral(42f32)).unwrap(),
                   Some(Value::Num(42f32)));
    }

    #[test]
    fn evaluate_handles_boolean_literals() {
        use token::Token::BooleanLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&BooleanLiteral(true)).unwrap(),
                   Some(Value::Bool(true)));

        assert_eq!(ctx.evaluate(&BooleanLiteral(false)).unwrap(),
                   Some(Value::Bool(false)));
    }

    #[test]
    fn evaluate_handles_identifiers() {
        use token::Token::Identifier;

        let mut ctx = Context::new();
        ctx.set_val("var0", Value::Num(42f32));
        assert_eq!(ctx.evaluate(&Identifier("var0".to_owned())).unwrap(),
                   Some(Value::Num(42f32)));
        assert_eq!(ctx.evaluate(&Identifier("nope".to_owned())).unwrap(), None);
    }

    #[test]
    fn evaluate_returns_none_on_invalid_token() {
        use token::Token::DotDot;
        let ctx = Context::new();
        assert!(ctx.evaluate(&DotDot).is_err());
    }
}
<commit_msg>refactor(Value): Consolidate on new Object type<commit_after>use error::{Result, Error};
use filters::Filter;
use std::collections::HashMap;
use token::Token::{self, Identifier, StringLiteral, NumberLiteral, BooleanLiteral};
use value::{Value, Object};

#[derive(Clone)]
pub enum Interrupt {
    Continue,
    Break,
}

#[derive(Default)]
pub struct Context {
    stack: Vec<Object>,
    globals: Object,

    /// The current interrupt state. The interrupt state is used by
    /// the `break` and `continue` tags to halt template rendering
    /// at a given point and unwind the `render` call stack until
    /// it reaches an enclosing `for_loop`. At that point the interrupt
    /// is cleared, and the `for_loop` carries on processing as directed.
    interrupt: Option<Interrupt>,

    /// The indices of all the cycles encountered during rendering.
    cycles: HashMap<String, usize>,

    // Public for backwards compatability
    pub filters: HashMap<String, Box<Filter>>,
}

impl Context {
    /// Creates a new, empty rendering context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::Context;
    /// let ctx = Context::new();
    /// assert_eq!(ctx.get_val("test"), None);
    /// ```
    pub fn new() -> Context {
        Context::with_values_and_filters(Object::new(), HashMap::new())
    }

    pub fn with_values(values: Object) -> Context {
        Context::with_values_and_filters(values, HashMap::new())
    }

    pub fn with_filters(filters: HashMap<String, Box<Filter>>) -> Context {
        Context::with_values_and_filters(Object::new(), filters)
    }

    pub fn with_values_and_filters(values: Object,
                                   filters: HashMap<String, Box<Filter>>)
                                   -> Context {
        Context {
            stack: vec![Object::new()],
            interrupt: None,
            cycles: HashMap::new(),
            globals: values,
            filters: filters,
        }
    }

    pub fn cycle_element(&mut self, name: &str, values: &[Token]) -> Result<Option<Value>> {
        let index = {
            let i = self.cycles.entry(name.to_owned()).or_insert(0);
            let j = *i;
            *i = (*i + 1) % values.len();
            j
        };

        if index >= values.len() {
            return Err(Error::Render(format!("cycle index {} out of bounds {}",
                                             index,
                                             values.len())));
        }

        self.evaluate(&values[index])
    }

    /// Only add the given filter to the context if a filter with this name doesn't already exist.
    pub fn maybe_add_filter(&mut self, name: &str, filter: Box<Filter>) {
        if self.get_filter(name).is_none() {
            self.add_filter(name, filter)
        }
    }

    pub fn add_filter(&mut self, name: &str, filter: Box<Filter>) {
        self.filters.insert(name.to_owned(), filter);
    }

    pub fn get_filter<'b>(&'b self, name: &str) -> Option<&'b Box<Filter>> {
        self.filters.get(name)
    }

    pub fn interrupted(&self) -> bool {
        self.interrupt.is_some()
    }

    /// Sets the interrupt state. Any previous state is obliterated.
    pub fn set_interrupt(&mut self, interrupt: Interrupt) {
        self.interrupt = Some(interrupt);
    }

    /// Fetches and clears the interrupt state.
    pub fn pop_interrupt(&mut self) -> Option<Interrupt> {
        let rval = self.interrupt.clone();
        self.interrupt = None;
        rval
    }

    /// Creates a new variable scope chained to a parent scope.
    fn push_scope(&mut self) {
        self.stack.push(Object::new());
    }

    /// Removes the topmost stack frame from the local variable stack.
    ///
    /// # Panics
    ///
    /// This method will panic if popping the topmost frame results in an
    /// empty stack. Given that a context is created with a top-level stack
    /// frame already in place, empyting the stack should never happen in a
    /// well-formed program.
    fn pop_scope(&mut self) {
        if self.stack.pop().is_none() {
            panic!("Pop leaves empty stack")
        };
    }

    /// Sets up a new stack frame, executes the supplied function and then
    /// tears the stack frame down before returning the function's result
    /// to the caller.
    ///
    /// # Examples
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// ctx.run_in_scope(|mut stack_frame| {
    ///   // stack_frame inherits values from its parent context
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(42f32)));
    ///
    ///   // but can (optionally) override them
    ///   stack_frame.set_local_val("test", Value::Num(3.14f32));
    ///   assert_eq!(stack_frame.get_val("test"), Some(&Value::Num(3.14f32)));
    /// });
    /// // the original value is unchanged once the scope exits
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn run_in_scope<RvalT, FnT>(&mut self, f: FnT) -> RvalT
        where FnT: FnOnce(&mut Context) -> RvalT
    {
        self.push_scope();
        let result = f(self);
        self.pop_scope();
        result
    }

    /// Internal part of get_val. Walks the scope stack to try and find the
    /// reqested variable, and failing that checks the global pool.
    fn get<'a>(&'a self, name: &str) -> Option<&'a Value> {
        for frame in self.stack.iter().rev() {
            if let rval @ Some(_) = frame.get(name) {
                return rval;
            }
        }
        self.globals.get(name)
    }

    /// Gets a value from the rendering context. The name value can be a
    /// dot-separated path to a value. A value will only be returned if
    /// each link in the chain (excluding the final name) refers to a
    /// value of type Object.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
    /// ```
    pub fn get_val<'b>(&'b self, name: &str) -> Option<&'b Value> {
        let mut path = name.split('.');
        let key = path.next().unwrap_or("");
        let mut rval = self.get(key);

        // walk the chain of Object values, as specified by the path
        // passed in name
        for id in path {
            match rval {
                Some(&Value::Object(ref x)) => rval = x.get(id),
                _ => return None,
            }
        }

        rval
    }

    /// Sets a value in the global context.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.set_val("test", Value::Num(42f32));
    /// assert_eq!(ctx.get_val("test"), Some(&Value::Num(42f32)));
    /// ```
    pub fn set_val(&mut self, name: &str, val: Value) -> Option<Value> {
        self.globals.insert(name.to_owned(), val)
    }

    /// Translates a Token to a Value, looking it up in the context if
    /// necessary
    pub fn evaluate(&self, t: &Token) -> Result<Option<Value>> {
        match *t {
            NumberLiteral(f) => Ok(Some(Value::Num(f))),
            StringLiteral(ref s) => Ok(Some(Value::Str(s.clone()))),
            BooleanLiteral(b) => Ok(Some(Value::Bool(b))),
            Identifier(ref id) => Ok(self.get_val(id).cloned()),
            _ => {
                let msg = format!("Cannot evaluate {}", t);
                Err(Error::Other(msg))
            }
        }
    }

    /// Sets a value to the rendering context.
    /// Note that it needs to be wrapped in a liquid::Value.
    ///
    /// # Panics
    ///
    /// Panics if there is no frame on the local values stack. Context
    /// instances are created with a top-level stack frame in place, so
    /// this should never happen in a well-formed program.
    ///
    /// # Examples
    ///
    /// ```
    /// # use liquid::{Value, Context};
    /// let mut ctx = Context::new();
    /// ctx.run_in_scope(|mut local_scope| {
    ///   local_scope.set_val("global", Value::Num(42f32));
    ///   local_scope.set_local_val("local", Value::Num(163f32));
    ///
    ///   assert_eq!(local_scope.get_val("global"), Some(&Value::Num(42f32)));
    ///   assert_eq!(local_scope.get_val("local"), Some(&Value::Num(163f32)));
    /// });
    /// assert_eq!(ctx.get_val("global"), Some(&Value::Num(42f32)));
    /// assert_eq!(ctx.get_val("local"), None);
    /// ```
    pub fn set_local_val(&mut self, name: &str, val: Value) -> Option<Value> {
        match self.stack.last_mut() {
            Some(frame) => frame.insert(name.to_owned(), val),
            None => panic!("Cannot insert into an empty stack"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::Context;
    use value::Value;
    use std::collections::HashMap;

    #[test]
    fn get_val() {
        let mut ctx = Context::new();
        let mut post = HashMap::new();
        post.insert("number".to_owned(), Value::Num(42f32));
        ctx.set_val("post", Value::Object(post));
        assert_eq!(ctx.get_val("post.number").unwrap(), &Value::Num(42f32));
    }

    #[test]
    fn scoped_variables() {
        let mut ctx = Context::new();
        ctx.set_val("test", Value::Num(42f32));
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));

        ctx.run_in_scope(|mut new_scope| {
            // assert that values are chained to the parent scope
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(42f32));

            // set a new local value, and assert that it overrides the previous value
            new_scope.set_local_val("test", Value::Num(3.14f32));
            assert_eq!(new_scope.get_val("test").unwrap(), &Value::Num(3.14f32));

            // sat a new val that we will pick up outside the scope
            new_scope.set_val("global", Value::str("some value"));
        });

        // assert that the value has reverted to the old one
        assert_eq!(ctx.get_val("test").unwrap(), &Value::Num(42f32));
        assert_eq!(ctx.get_val("global").unwrap(), &Value::str("some value"));
    }

    #[test]
    fn evaluate_handles_string_literals() {
        use token::Token::StringLiteral;

        let ctx = Context::new();
        let t = StringLiteral("hello".to_owned());
        assert_eq!(ctx.evaluate(&t).unwrap(), Some(Value::str("hello")));
    }

    #[test]
    fn evaluate_handles_number_literals() {
        use token::Token::NumberLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&NumberLiteral(42f32)).unwrap(),
                   Some(Value::Num(42f32)));
    }

    #[test]
    fn evaluate_handles_boolean_literals() {
        use token::Token::BooleanLiteral;

        let ctx = Context::new();
        assert_eq!(ctx.evaluate(&BooleanLiteral(true)).unwrap(),
                   Some(Value::Bool(true)));

        assert_eq!(ctx.evaluate(&BooleanLiteral(false)).unwrap(),
                   Some(Value::Bool(false)));
    }

    #[test]
    fn evaluate_handles_identifiers() {
        use token::Token::Identifier;

        let mut ctx = Context::new();
        ctx.set_val("var0", Value::Num(42f32));
        assert_eq!(ctx.evaluate(&Identifier("var0".to_owned())).unwrap(),
                   Some(Value::Num(42f32)));
        assert_eq!(ctx.evaluate(&Identifier("nope".to_owned())).unwrap(), None);
    }

    #[test]
    fn evaluate_returns_none_on_invalid_token() {
        use token::Token::DotDot;
        let ctx = Context::new();
        assert!(ctx.evaluate(&DotDot).is_err());
    }
}
<|endoftext|>


<commit_before>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use ref_into::{RefInto, MutInto};
use types::{Color, Viewport, Capability, GLError};
use buffer::{BufferBinder, BufferBinderRef, BufferBinderMut};
use program::{ProgramBinder, ProgramAttrib};
use framebuffer::FramebufferBinder;
use renderbuffer::RenderbufferBinder;
use texture_units::TextureUnits;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub type ContextRef<'a> = ContextOf<&'a BufferBinder,
                                    &'a FramebufferBinder,
                                    &'a ProgramBinder,
                                    &'a RenderbufferBinder,
                                    &'a TextureUnits>;

pub type ContextMut<'a> = ContextOf<&'a mut BufferBinder,
                                    &'a mut FramebufferBinder,
                                    &'a mut ProgramBinder,
                                    &'a mut RenderbufferBinder,
                                    &'a mut TextureUnits>;

pub type ContextSubRef<'a> = ContextOf<BufferBinderRef<'a>,
                                       &'a FramebufferBinder,
                                       &'a ProgramBinder,
                                       &'a RenderbufferBinder,
                                       &'a TextureUnits>;

pub type ContextSubMut<'a> = ContextOf<BufferBinderMut<'a>,
                                       &'a mut FramebufferBinder,
                                       &'a mut ProgramBinder,
                                       &'a mut RenderbufferBinder,
                                       &'a mut TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn ref_into<'a,
                    BI = &'a B,
                    FI = &'a F,
                    PI = &'a P,
                    RI = &'a R,
                    TI = &'a T>
                   (&'a self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: RefInto<'a, BI>,
              F: RefInto<'a, FI>,
              P: RefInto<'a, PI>,
              R: RefInto<'a, RI>,
              T: RefInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }

    pub fn mut_into<'a,
                    BI = &'a mut B,
                    FI = &'a mut F,
                    PI = &'a mut P,
                    RI = &'a mut R,
                    TI = &'a mut T>
                   (&'a mut self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: MutInto<'a, BI>,
              F: MutInto<'a, FI>,
              P: MutInto<'a, PI>,
              R: MutInto<'a, RI>,
              T: MutInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }

    pub fn split_buffers(self)
        -> (B, ContextOf<(), F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: (),
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn join_buffers<JB>(self, buffers: JB)
        -> ContextOf<JB, F, P, R, T>
    {
        ContextOf {
            buffers: buffers,
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn map_buffers<FN, BO>(self, f: FN) -> ContextOf<BO, F, P, R, T>
        where FN: FnOnce(B) -> BO
    {
        ContextOf {
            buffers: f(self.buffers),
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn split_framebuffer(self)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: (),
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_program(self)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: (),
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_renderbuffer(self)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: (),
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_tex_units(self)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: ()
            }
        )
    }
}

pub trait ContextExt {
    fn clear_color(&mut self, color: Color);
    fn enable(&mut self, cap: Capability);
    fn disable(&mut self, cap: Capability);
    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib);
    fn viewport(&self, viewport: Viewport);
}

impl ContextExt for ContextOf {
    fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }
}

pub trait AContext: ContextExt {

}

impl<C> AContext for C
    where C: ContextExt
{

}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a mut ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<commit_msg>Add `context::ext` module<commit_after>use std::borrow::{Borrow, BorrowMut};
use gl;
use gl::types::*;
use ref_into::{RefInto, MutInto};
use types::{Color, Viewport, Capability, GLError};
use buffer::{BufferBinder, BufferBinderRef, BufferBinderMut};
use program::{ProgramBinder, ProgramAttrib};
use framebuffer::FramebufferBinder;
use renderbuffer::RenderbufferBinder;
use texture_units::TextureUnits;

pub type Context = ContextOf<BufferBinder,
                             FramebufferBinder,
                             ProgramBinder,
                             RenderbufferBinder,
                             TextureUnits>;

pub type ContextRef<'a> = ContextOf<&'a BufferBinder,
                                    &'a FramebufferBinder,
                                    &'a ProgramBinder,
                                    &'a RenderbufferBinder,
                                    &'a TextureUnits>;

pub type ContextMut<'a> = ContextOf<&'a mut BufferBinder,
                                    &'a mut FramebufferBinder,
                                    &'a mut ProgramBinder,
                                    &'a mut RenderbufferBinder,
                                    &'a mut TextureUnits>;

pub type ContextSubRef<'a> = ContextOf<BufferBinderRef<'a>,
                                       &'a FramebufferBinder,
                                       &'a ProgramBinder,
                                       &'a RenderbufferBinder,
                                       &'a TextureUnits>;

pub type ContextSubMut<'a> = ContextOf<BufferBinderMut<'a>,
                                       &'a mut FramebufferBinder,
                                       &'a mut ProgramBinder,
                                       &'a mut RenderbufferBinder,
                                       &'a mut TextureUnits>;

pub struct ContextOf {
    pub buffers: B,
    pub framebuffer: F,
    pub program: P,
    pub renderbuffer: R,
    pub tex_units: T
}

impl ContextOf {
    pub unsafe fn current_context() -> Context {
        ContextOf {
            buffers: BufferBinder::current(),
            framebuffer: FramebufferBinder,
            program: ProgramBinder,
            renderbuffer: RenderbufferBinder,
            tex_units: TextureUnits::current()
        }
    }

    pub fn get_error() -> Option<GLError> {
        unsafe {
            match gl::GetError() {
                gl::INVALID_ENUM =>
                    Some(GLError::InvalidEnum),
                gl::INVALID_VALUE =>
                    Some(GLError::InvalidValue),
                gl::INVALID_OPERATION =>
                    Some(GLError::InvalidOperation),
                gl::INVALID_FRAMEBUFFER_OPERATION =>
                    Some(GLError::InvalidFramebufferOperation),
                gl::OUT_OF_MEMORY =>
                    Some(GLError::OutOfMemory),
                _ =>
                    None
            }
        }
    }

    pub fn borrowed<'a, BB = B, BF = F, BP = P, BR = R, BT = T>(&'a self)
        -> ContextOf<&'a BB, &'a BF, &'a BP, &'a BR, &'a BT>
        where B: Borrow<BB>,
              F: Borrow<BF>,
              P: Borrow<BP>,
              R: Borrow
,
              T: Borrow<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow(),
            framebuffer: self.framebuffer.borrow(),
            program: self.program.borrow(),
            renderbuffer: self.renderbuffer.borrow(),
            tex_units: self.tex_units.borrow()
        }
    }

    pub fn borrowed_mut<'a, BB = B, BF = F, BP = P, BR = R, BT = T>
                       (&'a mut self)
        -> ContextOf<&'a mut BB,
                     &'a mut BF,
                     &'a mut BP,
                     &'a mut BR,
                     &'a mut BT>
        where B: BorrowMut<BB>,
              F: BorrowMut<BF>,
              P: BorrowMut<BP>,
              R: BorrowMut
,
              T: BorrowMut<BT>
    {
        ContextOf {
            buffers: self.buffers.borrow_mut(),
            framebuffer: self.framebuffer.borrow_mut(),
            program: self.program.borrow_mut(),
            renderbuffer: self.renderbuffer.borrow_mut(),
            tex_units: self.tex_units.borrow_mut()
        }
    }

    pub fn ref_into<'a,
                    BI = &'a B,
                    FI = &'a F,
                    PI = &'a P,
                    RI = &'a R,
                    TI = &'a T>
                   (&'a self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: RefInto<'a, BI>,
              F: RefInto<'a, FI>,
              P: RefInto<'a, PI>,
              R: RefInto<'a, RI>,
              T: RefInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }

    pub fn mut_into<'a,
                    BI = &'a mut B,
                    FI = &'a mut F,
                    PI = &'a mut P,
                    RI = &'a mut R,
                    TI = &'a mut T>
                   (&'a mut self)
        -> ContextOf<BI, FI, PI, RI, TI>
        where B: MutInto<'a, BI>,
              F: MutInto<'a, FI>,
              P: MutInto<'a, PI>,
              R: MutInto<'a, RI>,
              T: MutInto<'a, TI>
    {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }

    pub fn split_buffers(self)
        -> (B, ContextOf<(), F, P, R, T>)
    {
        (
            self.buffers,
            ContextOf {
                buffers: (),
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn join_buffers<JB>(self, buffers: JB)
        -> ContextOf<JB, F, P, R, T>
    {
        ContextOf {
            buffers: buffers,
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn map_buffers<FN, BO>(self, f: FN) -> ContextOf<BO, F, P, R, T>
        where FN: FnOnce(B) -> BO
    {
        ContextOf {
            buffers: f(self.buffers),
            framebuffer: self.framebuffer,
            program: self.program,
            renderbuffer: self.renderbuffer,
            tex_units: self.tex_units
        }
    }

    pub fn split_framebuffer(self)
        -> (F, ContextOf)
    {
        (
            self.framebuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: (),
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_program(self)
        -> (P, ContextOf)
    {
        (
            self.program,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: (),
                renderbuffer: self.renderbuffer,
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_renderbuffer(self)
        -> (R, ContextOf)
    {
        (
            self.renderbuffer,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: (),
                tex_units: self.tex_units
            }
        )
    }

    pub fn split_tex_units(self)
        -> (T, ContextOf)
    {
        (
            self.tex_units,
            ContextOf {
                buffers: self.buffers,
                framebuffer: self.framebuffer,
                program: self.program,
                renderbuffer: self.renderbuffer,
                tex_units: ()
            }
        )
    }
}

pub trait ContextExt {
    fn clear_color(&mut self, color: Color);
    fn enable(&mut self, cap: Capability);
    fn disable(&mut self, cap: Capability);
    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib);
    fn viewport(&self, viewport: Viewport);
}

impl ContextExt for ContextOf {
    fn clear_color(&mut self, color: Color) {
        unsafe {
            gl::ClearColor(color.r, color.g, color.b, color.a);
        }
    }

    fn enable(&mut self, cap: Capability) {
        unsafe {
            gl::Enable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn disable(&mut self, cap: Capability) {
        unsafe {
            gl::Disable(cap.gl_enum());
            dbg_gl_sanity_check! {
                GLError::InvalidEnum => "`cap` is not a valid OpenGL capability",
                _ => "Unknown error"
            }
        }
    }

    fn enable_vertex_attrib_array(&self, attrib: ProgramAttrib) {
        unsafe {
            gl::EnableVertexAttribArray(attrib.gl_index);
            dbg_gl_error! {
                GLError::InvalidValue => "`index` is >= GL_MAX_VERTEX_ATTRIBS",
                _ => "Unknown error"
            }
        }
    }

    fn viewport(&self, viewport: Viewport) {
        unsafe {
            gl::Viewport(viewport.x as GLint,
                         viewport.y as GLint,
                         viewport.width as GLsizei,
                         viewport.height as GLsizei);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`width` or `height` is negative",
                _ => "Unknown error"
            }
        }
    }
}

pub mod ext {
    pub use ContextExt;
}

pub trait AContext: ContextExt {

}

impl<C> AContext for C
    where C: ContextExt
{

}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    RefInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn ref_into(&'a self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a ContextOf
    where B: RefInto<'a, BI>,
          F: RefInto<'a, FI>,
          P: RefInto<'a, PI>,
          R: RefInto<'a, RI>,
          T: RefInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.ref_into(),
            framebuffer: self.framebuffer.ref_into(),
            program: self.program.ref_into(),
            renderbuffer: self.renderbuffer.ref_into(),
            tex_units: self.tex_units.ref_into()
        }
    }
}

impl<'a, B, F, P, R, T, BI, FI, PI, RI, TI>
    MutInto<'a, ContextOf<BI, FI, PI, RI, TI>>
    for &'a mut ContextOf
    where B: MutInto<'a, BI>,
          F: MutInto<'a, FI>,
          P: MutInto<'a, PI>,
          R: MutInto<'a, RI>,
          T: MutInto<'a, TI>,
          BI: 'a, FI: 'a, PI: 'a, RI: 'a, TI: 'a
{
    fn mut_into(&'a mut self) -> ContextOf<BI, FI, PI, RI, TI> {
        ContextOf {
            buffers: self.buffers.mut_into(),
            framebuffer: self.framebuffer.mut_into(),
            program: self.program.mut_into(),
            renderbuffer: self.renderbuffer.mut_into(),
            tex_units: self.tex_units.mut_into()
        }
    }
}

// HACK: Workaround for issue described here:
// https://www.reddit.com/r/rust/comments/339yj3/tuple_indexing_in_a_macro/cqiyv4n
#[macro_export]
macro_rules! _glitter_expr {
    ($x:expr) => ($x)
}

#[macro_export]
macro_rules! active_texture {
    ($gl:expr, $idx:tt) => {
        _glitter_expr!($gl.tex_units.$idx.active())
    }
}

#[macro_export]
macro_rules! active_texture_n {
    ($gl:expr, $idx:expr) => {
        $gl.tex_units.nth_unit($idx).active()
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Expression for computing the per-frame score for a scene from that frame's total mass and total
/// mass count and the tick count.
// TODO(zstewar1): Find a better way to handle parsing. S-expressions as a yaml string?
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum Expression {
    /// The current tick.
    Tick,
    /// The total mass for the frame.
    TotalMass,
    /// The number of masses for the frame.
    MassCount,
    /// A floating point constant.
    Constant(f64),
    /// The product of a series of expressions.
    Multiply(Vec<Expression>),
    /// The sum of a series of expressions.
    Add(Vec<Expression>),
    /// A series of expressions raised to the power of each next one.
    Power(Vec<Expression>),
    /// A series of expressions subtracted from each other *or* the negation of the single input
    /// expression.
    Subtract(Vec<Expression>),
    /// A series of expressions successively divided by each other.
    Divide(Vec<Expression>),
}

impl Expression {
    /// Evaluate the expression given the scoring function inputs.
    pub fn eval(&self, tick: f64, total_mass: f64, mass_count: f64) -> f64 {
        match self {
            Expression::Tick => tick,
            Expression::TotalMass => total_mass,
            Expression::MassCount => mass_count,
            Expression::Constant(value) => *value,
            Expression::Multiply(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc * next),
            Expression::Add(ref subexprs) => if subexprs.len() == 1 {
                subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc + next)
            },
            Expression::Power(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc.powf(next)),
            Expression::Subtract(ref subexprs) => if subexprs.len() == 1 {
                -subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc - next)
            },
            Expression::Divide(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc / next),
        }
    }
}

fn fold_eval<F>(
    tick: f64, total_mass: f64, mass_count: f64,
    items: &[Expression],
    mut func: F,
) -> f64
    where F: FnMut(f64, f64) -> f64,
{
    assert!(items.len() >= 2);
    let mut iter = items.iter();
    let first = iter.next().unwrap().eval(tick, total_mass, mass_count);
    iter.fold(first, |acc, next| func(acc, next.eval(tick, total_mass, mass_count)))
}
<commit_msg>add scoring function tests<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// Expression for computing the per-frame score for a scene from that frame's total mass and total
/// mass count and the tick count.
// TODO(zstewar1): Find a better way to handle parsing. S-expressions as a yaml string?
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub enum Expression {
    /// The current tick.
    Tick,
    /// The total mass for the frame.
    TotalMass,
    /// The number of masses for the frame.
    MassCount,
    /// A floating point constant.
    Constant(f64),
    /// The product of a series of expressions.
    Multiply(Vec<Expression>),
    /// The sum of a series of expressions.
    Add(Vec<Expression>),
    /// A series of expressions raised to the power of each next one.
    Power(Vec<Expression>),
    /// A series of expressions subtracted from each other *or* the negation of the single input
    /// expression.
    Subtract(Vec<Expression>),
    /// A series of expressions successively divided by each other.
    Divide(Vec<Expression>),
}

impl Expression {
    /// Evaluate the expression given the scoring function inputs.
    pub fn eval(&self, tick: f64, total_mass: f64, mass_count: f64) -> f64 {
        match self {
            Expression::Tick => tick,
            Expression::TotalMass => total_mass,
            Expression::MassCount => mass_count,
            Expression::Constant(value) => *value,
            Expression::Multiply(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc * next),
            Expression::Add(ref subexprs) => if subexprs.len() == 1 {
                subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc + next)
            },
            Expression::Power(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc.powf(next)),
            Expression::Subtract(ref subexprs) => if subexprs.len() == 1 {
                -subexprs[0].eval(tick, total_mass, mass_count)
            } else {
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc - next)
            },
            Expression::Divide(ref subexprs) =>
                fold_eval(tick, total_mass, mass_count, subexprs, |acc, next| acc / next),
        }
    }
}

fn fold_eval<F>(
    tick: f64, total_mass: f64, mass_count: f64,
    items: &[Expression],
    mut func: F,
) -> f64
    where F: FnMut(f64, f64) -> f64,
{
    assert!(items.len() >= 2);
    let mut iter = items.iter();
    let first = iter.next().unwrap().eval(tick, total_mass, mass_count);
    iter.fold(first, |acc, next| func(acc, next.eval(tick, total_mass, mass_count)))
}

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

    const TICK: f64 = 9.;
    const TOTAL_MASS: f64 = 486.8;
    const MASS_COUNT: f64 = 77.;

    fn assert_eval(expr: Expression, expected: f64) {
        assert_eq!(expr.eval(TICK, TOTAL_MASS, MASS_COUNT), expected);
    }

    #[test]
    fn eval_ticks() {
        assert_eval(Tick, TICK);
    }

    #[test]
    fn eval_total_mass() {
        assert_eval(TotalMass, TOTAL_MASS);
    }

    #[test]
    fn eval_mass_count() {
        assert_eval(MassCount, MASS_COUNT);
    }

    #[test]
    fn eval_constant() {
        assert_eval(Constant(88.97), 88.97);
    }

    #[test]
    fn eval_multiply() {
        assert_eval(
            Multiply(vec![Tick, Constant(2.), TotalMass]),
            TICK * 2. * TOTAL_MASS,
        );
    }

    #[test]
    #[should_panic]
    fn eval_multiply_too_few_items() {
        Multiply(vec![Tick]).eval(TICK, TOTAL_MASS, MASS_COUNT);
    }

    #[test]
    fn eval_add() {
        assert_eval(
            Add(vec![Tick, Constant(2.), TotalMass]),
            TICK + 2. + TOTAL_MASS,
        );
    }

    #[test]
    fn eval_unary_add() {
        assert_eval(
            Add(vec![Tick]),
            TICK,
        );
    }

    #[test]
    #[should_panic]
    fn eval_add_too_few_items() {
        Add(vec![]).eval(TICK, TOTAL_MASS, MASS_COUNT);
    }

    #[test]
    fn eval_power() {
        assert_eval(
            Power(vec![Tick, Constant(2.), TotalMass]),
            TICK.powf(2.).powf(TOTAL_MASS),
        );
    }

    #[test]
    #[should_panic]
    fn eval_power_too_few_items() {
        Power(vec![Tick]).eval(TICK, TOTAL_MASS, MASS_COUNT);
    }

    #[test]
    fn eval_subtract() {
        assert_eval(
            Subtract(vec![Tick, Constant(2.), TotalMass]),
            TICK - 2. - TOTAL_MASS,
        );
    }

    #[test]
    fn eval_unary_subtract() {
        assert_eval(
            Subtract(vec![Tick]),
            -TICK,
        );
    }

    #[test]
    #[should_panic]
    fn eval_subtract_too_few_items() {
        Subtract(vec![]).eval(TICK, TOTAL_MASS, MASS_COUNT);
    }

    #[test]
    fn eval_divide() {
        assert_eval(
            Divide(vec![Tick, Constant(2.), TotalMass]),
            TICK / 2. / TOTAL_MASS,
        );
    }

    #[test]
    #[should_panic]
    fn eval_divide_too_few_items() {
        Divide(vec![Tick]).eval(TICK, TOTAL_MASS, MASS_COUNT);
    }

    #[test]
    fn eval_complex() {
        assert_eval(
            Subtract(vec![
                Multiply(vec![
                    Tick,
                    Constant(8.),
                    Add(vec![
                        Constant(1.),
                        Power(vec![
                            TotalMass,
                            Constant(1.5),
                            Power(vec![MassCount, Constant(1.24)]),
                        ]),
                    ]),
                ]),
            ]),
            -(TICK * 8. * (1. + TOTAL_MASS.powf(1.5).powf(MASS_COUNT.powf(1.24))))
        );
    }
}
<|endoftext|>


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

pub struct Company<'a> {
    name : &'a str,
    code : &'a str,
    category : &'a str,
    product : &'a str,
}

pub fn get_code_list(path : &str) -> Result<Vec<&str>, &str> {
    let mut company_list : Vec<Company> = Vec::new();

    // read csv contents
    let csv_contents = match read_list_file(path) {
        Err(x)  => {
            println!("{}", x);
            return Err("Geting csv contents error!");
        },
        Ok(x)   => x
    };

    // parse
    let item_list : Vec<&str> = csv_contents.split("\r\n").collect();

    // debug
    for item in item_list {
        println!("{:?}", item);
    }

    Err("")
}

fn read_list_file(csv_path : &str) -> Result<String, &str> {
    let mut file = match File::open(csv_path) {
        Err(x)  => {
            println!("{}", x);
            return Err("File open error!");
        },
        Ok(x)   => x
    };

    let mut csv_contents = String::new();
    let contents_len = match file.read_to_string(&mut csv_contents) {
        Err(x)  => {
            println!("{}", x);
            return Err("File read error!");
        },
        Ok(x)   => x
    };

    if contents_len == 0 {
        return Err("File is empty!");
    }

    Ok(csv_contents)
}
<commit_msg>modifying csv handler<commit_after>use std::fs::File;
use std::io::Read;

pub struct Company<'a> {
    name : &'a str,
    code : &'a str,
    category : &'a str,
    product : &'a str,
}

// public methods
pub fn get_code_list(path : &str) -> Result<Vec<Company>, &str> {
    let mut company_list : Vec<Company> = Vec::new();

    // read csv contents
    let csv_contents = match read_list_file(path) {
        Err(x)  => {
            println!("{}", x);
            return Err("Geting csv contents error!");
        },
        Ok(x)   => x
    };

    // parse
    let item_list : Vec<&str> = csv_contents.split("\r\n").collect();
    for item in item_list {
        let value_list : Vec<&str> = item.split(',').collect();
        company_list.push(
            Company {
                name : value_list[0],
                code : value_list[1],
                category : value_list[2],
                product : value_list[3]
            }
        );
    }

    Ok(company_list)
}

// static methods
fn read_list_file(csv_path : &str) -> Result<String, &str> {
    let mut file = match File::open(csv_path) {
        Err(x)  => {
            println!("{}", x);
            return Err("File open error!");
        },
        Ok(x)   => x
    };

    let mut csv_contents = String::new();
    let contents_len = match file.read_to_string(&mut csv_contents) {
        Err(x)  => {
            println!("{}", x);
            return Err("File read error!");
        },
        Ok(x)   => x
    };

    if contents_len == 0 {
        return Err("File is empty!");
    }

    Ok(csv_contents)
}
<|endoftext|>


<commit_before>/// This function is where the kernel sets up IRQ handlers
/// It is increcibly unsafe, and should be minimal in nature
/// It must create the IDT with the correct entries, those entries are
/// defined in other files inside of the `arch` module

use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use acpi;
use allocator;
use device;
use externs::memset;
use gdt;
use idt;
use interrupt;
use memory;
use paging::{self, entry, Page, VirtualAddress};
use paging::mapper::MapperFlushAll;

/// Test of zero values in BSS.
static BSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in data.
static DATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF;
/// Test of zero values in thread BSS
#[thread_local]
static mut TBSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in thread data.
#[thread_local]
static mut TDATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF;

pub static CPU_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
pub static AP_READY: AtomicBool = ATOMIC_BOOL_INIT;
static BSP_READY: AtomicBool = ATOMIC_BOOL_INIT;

extern {
    /// Kernel main function
    fn kmain(cpus: usize) -> !;
    /// Kernel main for APs
    fn kmain_ap(id: usize) -> !;
}

/// The entry to Rust, all things must be initialized
#[no_mangle]
pub unsafe extern fn kstart() -> ! {
    {
        extern {
            /// The starting byte of the _.bss_ (uninitialized data) segment.
            static mut __bss_start: u8;
            /// The ending byte of the _.bss_ (uninitialized data) segment.
            static mut __bss_end: u8;
            /// The end of the kernel
            static mut __end: u8;
        }

        // Zero BSS, this initializes statics that are set to 0
        {
            let start_ptr = &mut __bss_start as *mut u8;
            let end_ptr = & __bss_end as *const u8 as usize;

            if start_ptr as usize <= end_ptr {
                let size = end_ptr - start_ptr as usize;
                memset(start_ptr, 0, size);
            }

            assert_eq!(BSS_TEST_ZERO, 0);
            assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
        }

        // Initialize memory management
        memory::init(0, &__end as *const u8 as usize - ::KERNEL_OFFSET);

        // TODO: allocate a stack
        let stack_start = 0x00080000 + ::KERNEL_OFFSET;
        let stack_end = 0x0009F000 + ::KERNEL_OFFSET;

        // Initialize paging
        let (mut active_table, tcb_offset) = paging::init(0, stack_start, stack_end);

        // Set up GDT
        gdt::init(tcb_offset, stack_end);

        // Set up IDT
        idt::init();

        // Test tdata and tbss
        {
            assert_eq!(TBSS_TEST_ZERO, 0);
            TBSS_TEST_ZERO += 1;
            assert_eq!(TBSS_TEST_ZERO, 1);
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
            TDATA_TEST_NONZERO -= 1;
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE);
        }

        // Reset AP variables
        CPU_COUNT.store(1, Ordering::SeqCst);
        AP_READY.store(false, Ordering::SeqCst);
        BSP_READY.store(false, Ordering::SeqCst);

        // Setup kernel heap
        {
            let mut flush_all = MapperFlushAll::new();

            // Map heap pages
            let heap_start_page = Page::containing_address(VirtualAddress::new(::KERNEL_HEAP_OFFSET));
            let heap_end_page = Page::containing_address(VirtualAddress::new(::KERNEL_HEAP_OFFSET + ::KERNEL_HEAP_SIZE-1));
            for page in Page::range_inclusive(heap_start_page, heap_end_page) {
                let result = active_table.map(page, entry::PRESENT | entry::GLOBAL | entry::WRITABLE | entry::NO_EXECUTE);
                flush_all.consume(result);
            }

            flush_all.flush(&mut active_table);

            // Init the allocator
            allocator::init(::KERNEL_HEAP_OFFSET, ::KERNEL_HEAP_SIZE);
        }

        // Initialize devices
        device::init(&mut active_table);

        // Read ACPI tables, starts APs
        acpi::init(&mut active_table);

        BSP_READY.store(true, Ordering::SeqCst);
    }

    kmain(CPU_COUNT.load(Ordering::SeqCst));
}

/// Entry to rust for an AP
pub unsafe extern fn kstart_ap(cpu_id: usize, bsp_table: usize, stack_start: usize, stack_end: usize) -> ! {
    {
        assert_eq!(BSS_TEST_ZERO, 0);
        assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);

        // Initialize paging
        let tcb_offset = paging::init_ap(cpu_id, bsp_table, stack_start, stack_end);

        // Set up GDT for AP
        gdt::init(tcb_offset, stack_end);

        // Set up IDT for AP
        idt::init();

        // Test tdata and tbss
        {
            assert_eq!(TBSS_TEST_ZERO, 0);
            TBSS_TEST_ZERO += 1;
            assert_eq!(TBSS_TEST_ZERO, 1);
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
            TDATA_TEST_NONZERO -= 1;
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE);
        }

        // Initialize devices (for AP)
        device::init_ap();

        AP_READY.store(true, Ordering::SeqCst);
    }

    while ! BSP_READY.load(Ordering::SeqCst) {
        interrupt::pause();
    }

    kmain_ap(cpu_id);
}

pub unsafe fn usermode(ip: usize, sp: usize) -> ! {
    // Go to usermode
    asm!("mov ds, ax
        mov es, ax
        mov fs, bx
        mov gs, ax
        push rax
        push rcx
        push rdx
        push rsi
        push rdi
        iretq"
        : // No output because it never returns
        :   "{rax}"(gdt::GDT_USER_DATA << 3 | 3), // Data segment
            "{rbx}"(gdt::GDT_USER_TLS << 3 | 3), // TLS segment
            "{rcx}"(sp), // Stack pointer
            "{rdx}"(3 << 12 | 1 << 9), // Flags - Set IOPL and interrupt enable flag
            "{rsi}"(gdt::GDT_USER_CODE << 3 | 3), // Code segment
            "{rdi}"(ip) // IP
        : // No clobers because it never returns
        : "intel", "volatile");
    unreachable!();
}
<commit_msg>Unstick system by acknowledging IRQs on boot<commit_after>/// This function is where the kernel sets up IRQ handlers
/// It is increcibly unsafe, and should be minimal in nature
/// It must create the IDT with the correct entries, those entries are
/// defined in other files inside of the `arch` module

use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, AtomicUsize, ATOMIC_USIZE_INIT, Ordering};

use acpi;
use allocator;
use device;
use externs::memset;
use gdt;
use idt;
use interrupt;
use memory;
use paging::{self, entry, Page, VirtualAddress};
use paging::mapper::MapperFlushAll;

/// Test of zero values in BSS.
static BSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in data.
static DATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF;
/// Test of zero values in thread BSS
#[thread_local]
static mut TBSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in thread data.
#[thread_local]
static mut TDATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF;

pub static CPU_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
pub static AP_READY: AtomicBool = ATOMIC_BOOL_INIT;
static BSP_READY: AtomicBool = ATOMIC_BOOL_INIT;

extern {
    /// Kernel main function
    fn kmain(cpus: usize) -> !;
    /// Kernel main for APs
    fn kmain_ap(id: usize) -> !;
}

/// The entry to Rust, all things must be initialized
#[no_mangle]
pub unsafe extern fn kstart() -> ! {
    {
        extern {
            /// The starting byte of the _.bss_ (uninitialized data) segment.
            static mut __bss_start: u8;
            /// The ending byte of the _.bss_ (uninitialized data) segment.
            static mut __bss_end: u8;
            /// The end of the kernel
            static mut __end: u8;
        }

        // Zero BSS, this initializes statics that are set to 0
        {
            let start_ptr = &mut __bss_start as *mut u8;
            let end_ptr = & __bss_end as *const u8 as usize;

            if start_ptr as usize <= end_ptr {
                let size = end_ptr - start_ptr as usize;
                memset(start_ptr, 0, size);
            }

            assert_eq!(BSS_TEST_ZERO, 0);
            assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
        }

        // Initialize memory management
        memory::init(0, &__end as *const u8 as usize - ::KERNEL_OFFSET);

        // TODO: allocate a stack
        let stack_start = 0x00080000 + ::KERNEL_OFFSET;
        let stack_end = 0x0009F000 + ::KERNEL_OFFSET;

        // Initialize paging
        let (mut active_table, tcb_offset) = paging::init(0, stack_start, stack_end);

        // Set up GDT
        gdt::init(tcb_offset, stack_end);

        // Set up IDT
        idt::init();

        // Test tdata and tbss
        {
            assert_eq!(TBSS_TEST_ZERO, 0);
            TBSS_TEST_ZERO += 1;
            assert_eq!(TBSS_TEST_ZERO, 1);
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
            TDATA_TEST_NONZERO -= 1;
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE);
        }

        // Reset AP variables
        CPU_COUNT.store(1, Ordering::SeqCst);
        AP_READY.store(false, Ordering::SeqCst);
        BSP_READY.store(false, Ordering::SeqCst);

        // Setup kernel heap
        {
            let mut flush_all = MapperFlushAll::new();

            // Map heap pages
            let heap_start_page = Page::containing_address(VirtualAddress::new(::KERNEL_HEAP_OFFSET));
            let heap_end_page = Page::containing_address(VirtualAddress::new(::KERNEL_HEAP_OFFSET + ::KERNEL_HEAP_SIZE-1));
            for page in Page::range_inclusive(heap_start_page, heap_end_page) {
                let result = active_table.map(page, entry::PRESENT | entry::GLOBAL | entry::WRITABLE | entry::NO_EXECUTE);
                flush_all.consume(result);
            }

            flush_all.flush(&mut active_table);

            // Init the allocator
            allocator::init(::KERNEL_HEAP_OFFSET, ::KERNEL_HEAP_SIZE);
        }

        // Initialize devices
        device::init(&mut active_table);

        // Read ACPI tables, starts APs
        acpi::init(&mut active_table);

        // Clear pending IRQs
        interrupt::irq::acknowledge(8);

        BSP_READY.store(true, Ordering::SeqCst);
    }

    kmain(CPU_COUNT.load(Ordering::SeqCst));
}

/// Entry to rust for an AP
pub unsafe extern fn kstart_ap(cpu_id: usize, bsp_table: usize, stack_start: usize, stack_end: usize) -> ! {
    {
        assert_eq!(BSS_TEST_ZERO, 0);
        assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);

        // Initialize paging
        let tcb_offset = paging::init_ap(cpu_id, bsp_table, stack_start, stack_end);

        // Set up GDT for AP
        gdt::init(tcb_offset, stack_end);

        // Set up IDT for AP
        idt::init();

        // Test tdata and tbss
        {
            assert_eq!(TBSS_TEST_ZERO, 0);
            TBSS_TEST_ZERO += 1;
            assert_eq!(TBSS_TEST_ZERO, 1);
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
            TDATA_TEST_NONZERO -= 1;
            assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE);
        }

        // Initialize devices (for AP)
        device::init_ap();

        AP_READY.store(true, Ordering::SeqCst);
    }

    while ! BSP_READY.load(Ordering::SeqCst) {
        interrupt::pause();
    }

    kmain_ap(cpu_id);
}

pub unsafe fn usermode(ip: usize, sp: usize) -> ! {
    // Go to usermode
    asm!("mov ds, ax
        mov es, ax
        mov fs, bx
        mov gs, ax
        push rax
        push rcx
        push rdx
        push rsi
        push rdi
        iretq"
        : // No output because it never returns
        :   "{rax}"(gdt::GDT_USER_DATA << 3 | 3), // Data segment
            "{rbx}"(gdt::GDT_USER_TLS << 3 | 3), // TLS segment
            "{rcx}"(sp), // Stack pointer
            "{rdx}"(3 << 12 | 1 << 9), // Flags - Set IOPL and interrupt enable flag
            "{rsi}"(gdt::GDT_USER_CODE << 3 | 3), // Code segment
            "{rdi}"(ip) // IP
        : // No clobers because it never returns
        : "intel", "volatile");
    unreachable!();
}
<|endoftext|>


<commit_before>use super::StockPrice;
use net::http::tls_client;

use hyper::header::Connection;
use select::document::Document;
use select::node::Node;
use select::predicate::{Class, Element, Name, Predicate, Text};
use serde_urlencoded;
use std::io::Read;

static BASE_URL: &'static str = "https://www.comdirect.de/inf/search/all.html";

error_chain! {
    errors {
        ComdirectScrapingFailed(info: String) {
            description("Did not find information in HTML document")
            display("Did not find {} in HTML document", info)
        }
        ComdirectSearchResultNotUnique {
            description("The search did not returned an unique result")
            display("The search did not returned an unique result")
        }
    }
}

pub fn scrape_stock_price<T: FnMut(String) -> ()>(query: &str, mut progress: Option<T>) -> Result<StockPrice> {
    let parameters = &[("SEARCH_VALUE", query.to_owned())];
    let parameters_enc = serde_urlencoded::to_string(&parameters)
        .chain_err(|| "Could not encode URL parameters")?;
    let url = format!("{}?{}", BASE_URL, parameters_enc);

    if let Some(p) = progress.as_mut() {
        p("Sending search request ...".to_owned());
    }

    let body = get_stock_page(&url).unwrap();

    if let Some(p) = progress.as_mut() {
        p(format!("Received {} bytes", body.len()));
    }

    let stock_price = parse_stock_price(&body)?;

    Ok(stock_price)
}

fn get_stock_page(url: &str) -> Result<Vec<u8>> {
    let client = tls_client().chain_err(|| "Could not create TLS client")?;
    let mut response = client.get(url).header(Connection::close()).send()
        .chain_err(|| "Could not send request")?;

    let mut body = Vec::new();
    let _ = response.read_to_end(&mut body)
        .chain_err(|| "Could not read response body")?;

    Ok(body)
}

// /html/body/div[4]/div/div[1]/div[2]/div[1]/div/div/div[1]/div/span[2]
// body > div.cif-scope-content-wrapper.siteFrame > div > div.key-focus > div:nth-child(2) > div.col-3.col--sm-4 > div > div > div:nth-child(1) > div > span.realtime-indicator--value.text-size--xxlarge.text-weight--medium
fn parse_stock_price(body: &[u8]) -> Result<StockPrice> {
    let document = Document::from_read(body).chain_err(|| "Could not parse HTML in response body")?;

    let no_exact_match = document.find(Class("Informer")).nth(0).is_some();
    if no_exact_match {
        bail!(ErrorKind::ComdirectSearchResultNotUnique);
    }

    let name = document.find(Name("h1")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("name".to_string()))?.text();

    let price_str = document.find(Class("key-focus__quote").descendant(Class("realtime-indicator").descendant(Text))).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price".to_string()))?
        .text()
        .trim()
        .replace(".", "") // thousand separation
        .replace(",", ".") // decimal separation
    ;
    eprintln!("price_str = {:?}", price_str);
    eprintln!("Mööp");
    let price = price_str
        .parse()
        .chain_err(|| ErrorKind::ComdirectScrapingFailed("parsing stock price".to_string()))?;

    let currency = document.find(Class("realtime-indicator").descendant(Text)).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock currency".to_string()))?.text();

    let date_fn = |node: &Node| node.text() == "Stand";
    let date_text = document.find(Class("key-focus").descendant(date_fn)).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (1)".to_string()))?
        .parent()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (2)".to_string()))?
        .find(Element).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (3)".to_string()))?
        .first_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (4)".to_string()))?
        .text();
    let date = date_text
        .split('-')
        .nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (5)".to_string()))?
        .trim();

    let wkn = document.find(Class("key-focus__info")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (1)".to_string()))?
        .last_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (2)".to_string()))?
        .text();

    let stock_price = StockPrice {
        name: name.trim().to_string(),
        wkn: wkn.trim().to_string(),
        date: date.trim().to_string(),
        price,
        currency: currency.trim().to_string(),
    };

    Ok(stock_price)
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::Read;
    use std::fs::File;

    fn get_stock_page(file: &str) -> Result<Vec<u8>> {
        let mut f = File::open(file).unwrap();
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer).unwrap();

        Ok(buffer)
    }

    #[test]
    fn test_parse_file_ok() {
        let body = get_stock_page("test/data/stocks/deutsche_bank.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 14.16f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_with_thousand_separator() {
        let body = get_stock_page("test/data/stocks/amunid_thousand_period.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 1396.47f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_no_exact_match() {
        let body = get_stock_page("test/data/stocks/no_exact_match.html").unwrap();
        let db = parse_stock_price(&body);

        assert!(db.is_err());
    }

    #[test]
    fn test_parse_online_ok() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("Deutsche Bank", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert!(db.price > 0.00f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_with_thousand_separator() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("A0X8ZS", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert!(db.price > 0.00_f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_no_exact_match() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let result = scrape_stock_price("Deutsche", None::<fn(_)>);

        // TODO: This needs to be nicer.
        let result_is_not_unique = match result.unwrap_err() {
            Error(ErrorKind::ComdirectSearchResultNotUnique, _) => true,
            _ => false
        };
        assert!(result_is_not_unique);
    }
}
<commit_msg>Remove debug printlns<commit_after>use super::StockPrice;
use net::http::tls_client;

use hyper::header::Connection;
use select::document::Document;
use select::node::Node;
use select::predicate::{Class, Element, Name, Predicate, Text};
use serde_urlencoded;
use std::io::Read;

static BASE_URL: &'static str = "https://www.comdirect.de/inf/search/all.html";

error_chain! {
    errors {
        ComdirectScrapingFailed(info: String) {
            description("Did not find information in HTML document")
            display("Did not find {} in HTML document", info)
        }
        ComdirectSearchResultNotUnique {
            description("The search did not returned an unique result")
            display("The search did not returned an unique result")
        }
    }
}

pub fn scrape_stock_price<T: FnMut(String) -> ()>(query: &str, mut progress: Option<T>) -> Result<StockPrice> {
    let parameters = &[("SEARCH_VALUE", query.to_owned())];
    let parameters_enc = serde_urlencoded::to_string(&parameters)
        .chain_err(|| "Could not encode URL parameters")?;
    let url = format!("{}?{}", BASE_URL, parameters_enc);

    if let Some(p) = progress.as_mut() {
        p("Sending search request ...".to_owned());
    }

    let body = get_stock_page(&url).unwrap();

    if let Some(p) = progress.as_mut() {
        p(format!("Received {} bytes", body.len()));
    }

    let stock_price = parse_stock_price(&body)?;

    Ok(stock_price)
}

fn get_stock_page(url: &str) -> Result<Vec<u8>> {
    let client = tls_client().chain_err(|| "Could not create TLS client")?;
    let mut response = client.get(url).header(Connection::close()).send()
        .chain_err(|| "Could not send request")?;

    let mut body = Vec::new();
    let _ = response.read_to_end(&mut body)
        .chain_err(|| "Could not read response body")?;

    Ok(body)
}

// /html/body/div[4]/div/div[1]/div[2]/div[1]/div/div/div[1]/div/span[2]
// body > div.cif-scope-content-wrapper.siteFrame > div > div.key-focus > div:nth-child(2) > div.col-3.col--sm-4 > div > div > div:nth-child(1) > div > span.realtime-indicator--value.text-size--xxlarge.text-weight--medium
fn parse_stock_price(body: &[u8]) -> Result<StockPrice> {
    let document = Document::from_read(body).chain_err(|| "Could not parse HTML in response body")?;

    let no_exact_match = document.find(Class("Informer")).nth(0).is_some();
    if no_exact_match {
        bail!(ErrorKind::ComdirectSearchResultNotUnique);
    }

    let name = document.find(Name("h1")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("name".to_string()))?.text();

    let price_str = document.find(Class("key-focus__quote").descendant(Class("realtime-indicator").descendant(Text))).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price".to_string()))?
        .text()
        .trim()
        .replace(".", "") // thousand separation
        .replace(",", "."); // decimal separation
    let price = price_str
        .parse()
        .chain_err(|| ErrorKind::ComdirectScrapingFailed("parsing stock price".to_string()))?;

    let currency = document.find(Class("realtime-indicator").descendant(Text)).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock currency".to_string()))?.text();

    let date_fn = |node: &Node| node.text() == "Stand";
    let date_text = document.find(Class("key-focus").descendant(date_fn)).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (1)".to_string()))?
        .parent()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (2)".to_string()))?
        .find(Element).nth(1)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (3)".to_string()))?
        .first_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (4)".to_string()))?
        .text();
    let date = date_text
        .split('-')
        .nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock price date (5)".to_string()))?
        .trim();

    let wkn = document.find(Class("key-focus__info")).nth(0)
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (1)".to_string()))?
        .last_child()
        .ok_or_else(|| ErrorKind::ComdirectScrapingFailed("stock WKN (2)".to_string()))?
        .text();

    let stock_price = StockPrice {
        name: name.trim().to_string(),
        wkn: wkn.trim().to_string(),
        date: date.trim().to_string(),
        price,
        currency: currency.trim().to_string(),
    };

    Ok(stock_price)
}

#[cfg(test)]
mod test {
    use super::*;
    use std::io::Read;
    use std::fs::File;

    fn get_stock_page(file: &str) -> Result<Vec<u8>> {
        let mut f = File::open(file).unwrap();
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer).unwrap();

        Ok(buffer)
    }

    #[test]
    fn test_parse_file_ok() {
        let body = get_stock_page("test/data/stocks/deutsche_bank.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 14.16f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_with_thousand_separator() {
        let body = get_stock_page("test/data/stocks/amunid_thousand_period.html").unwrap();
        let db = parse_stock_price(&body).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert_eq!(db.date, "27.10.17");
        assert_eq!(db.price, 1396.47f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_file_no_exact_match() {
        let body = get_stock_page("test/data/stocks/no_exact_match.html").unwrap();
        let db = parse_stock_price(&body);

        assert!(db.is_err());
    }

    #[test]
    fn test_parse_online_ok() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("Deutsche Bank", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "DEUTSCHE BANK");
        assert_eq!(db.wkn, "514000");
        assert!(db.price > 0.00f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_with_thousand_separator() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let db = scrape_stock_price("A0X8ZS", None::<fn(_)>).unwrap();

        assert_eq!(db.name, "AMUNDI ETF LEVERAGED MSCI USA DAILY UCITS ETF - EUR ACC");
        assert_eq!(db.wkn, "A0X8ZS");
        assert!(db.price > 0.00_f32);
        assert_eq!(db.currency, "EUR");
    }

    #[test]
    fn test_parse_online_no_exact_match() {
        ::utils::console::init(::config::Verbosity::QUIET);
        let result = scrape_stock_price("Deutsche", None::<fn(_)>);

        // TODO: This needs to be nicer.
        let result_is_not_unique = match result.unwrap_err() {
            Error(ErrorKind::ComdirectSearchResultNotUnique, _) => true,
            _ => false
        };
        assert!(result_is_not_unique);
    }
}
<|endoftext|>