<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssrule::CSSRule;
use dom::element::Element;
use dom::node::{Node, NodeDamage, window_from_node};
use dom::window::Window;
use parking_lot::RwLock;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::Arc;
use style::parser::ParserContextExtraData;
use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{parse_one_declaration, parse_style_attribute};
use style::selector_parser::PseudoElement;
use style_traits::ToCss;

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: CSSStyleOwner,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(HeapSizeOf, JSTraceable)]
#[must_root]
pub enum CSSStyleOwner {
    Element(JS<Element>),
    CSSRule(JS<CSSRule>,
            #[ignore_heap_size_of = "Arc"]
            Arc<RwLock<PropertyDeclarationBlock>>),
}

impl CSSStyleOwner {
    // Mutate the declaration block associated to this style owner, and
    // optionally indicate if it has changed (assumed to be true).
    fn mutate_associated_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut PropertyDeclarationBlock, &mut bool) -> R,
    {
        // TODO(emilio): This has some duplication just to avoid dummy clones.
        //
        // This is somewhat complex but the complexity is encapsulated.
        let mut changed = true;
        match *self {
            CSSStyleOwner::Element(ref el) => {
                let mut attr = el.style_attribute().borrow_mut();
                let (result, needs_clear) = if attr.is_some() {
                    let lock = attr.as_ref().unwrap();
                    let mut pdb = lock.write();
                    let result = f(&mut pdb, &mut changed);
                    if changed {
                        el.set_style_attr(pdb.to_css_string());
                        el.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged);
                    }
                    (result, pdb.declarations.is_empty())
                } else {
                    let mut pdb = PropertyDeclarationBlock {
                        important_count: 0,
                        declarations: vec![],
                    };
                    let result = f(&mut pdb, &mut changed);

                    // Here `changed` is somewhat silly, because we know the
                    // exact conditions under it changes.
                    if !pdb.declarations.is_empty() {
                        el.set_style_attr(pdb.to_css_string());
                        el.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged);
                        *attr = Some(Arc::new(RwLock::new(pdb)));
                    }

                    (result, false)
                };

                if needs_clear {
                    *attr = None;
                }
                result
            }
            CSSStyleOwner::CSSRule(ref rule, ref pdb) => {
                let result = f(&mut *pdb.write(), &mut changed);
                if changed {
                    rule.global().as_window().Document().invalidate_stylesheets();
                }
                result
            }
        }
    }

    fn with_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&PropertyDeclarationBlock) -> R,
    {
        match *self {
            CSSStyleOwner::Element(ref el) => {
                match *el.style_attribute().borrow() {
                    Some(ref pdb) => f(&pdb.read()),
                    None => {
                        let pdb = PropertyDeclarationBlock {
                            important_count: 0,
                            declarations: vec![],
                        };
                        f(&pdb)
                    }
                }
            }
            CSSStyleOwner::CSSRule(_, ref pdb) => {
                f(&pdb.read())
            }
        }
    }

    fn window(&self) -> Root<Window> {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el),
            CSSStyleOwner::CSSRule(ref rule, _) => Root::from_ref(rule.global().as_window()),
        }
    }

    fn base_url(&self) -> ServoUrl {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el).get_url(),
            CSSStyleOwner::CSSRule(ref rule, _) => {
                rule.parent_stylesheet().style_stylesheet().base_url.clone()
            }
        }
    }
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly,
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $id:expr],)* ) => (
        $(
            fn $getter(&self) -> DOMString {
                self.get_property_value($id)
            }
            fn $setter(&self, value: DOMString) -> ErrorResult {
                self.set_property($id, value, DOMString::new())
            }
        )*
    );
);

impl CSSStyleDeclaration {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(owner: CSSStyleOwner,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess)
                         -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: owner,
            readonly: modification_access == CSSModificationAccess::Readonly,
            pseudo: pseudo,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(global: &Window,
               owner: CSSStyleOwner,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess)
               -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner,
                                                                  pseudo,
                                                                  modification_access),
                           global,
                           CSSStyleDeclarationBinding::Wrap)
    }

    fn get_computed_style(&self, property: PropertyId) -> DOMString {
        match self.owner {
            CSSStyleOwner::CSSRule(..) =>
                panic!("get_computed_style called on CSSStyleDeclaration with a CSSRule owner"),
            CSSStyleOwner::Element(ref el) => {
                let node = el.upcast::<Node>();
                if !node.is_in_doc() {
                    // TODO: Node should be matched against the style rules of this window.
                    // Firefox is currently the only browser to implement this.
                    return DOMString::new();
                }
                let addr = node.to_trusted_node_address();
                window_from_node(node).resolved_style_query(addr, self.pseudo.clone(), property)
            }
        }
    }

    fn get_property_value(&self, id: PropertyId) -> DOMString {
        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(id);
        }

        let mut string = String::new();

        self.owner.with_block(|ref pdb| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
        });

        DOMString::from(string)
    }

    fn set_property(&self, id: PropertyId, value: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            if value.is_empty() {
                // Step 4
                *changed = pdb.remove_property(&id);
                return Ok(());
            }

            // Step 5
            let importance = match &*priority {
                "" => Importance::Normal,
                p if p.eq_ignore_ascii_case("important") => Importance::Important,
                _ => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 6
            let window = self.owner.window();
            let declarations =
                parse_one_declaration(id, &value, &self.owner.base_url(),
                                      window.css_error_reporter(),
                                      ParserContextExtraData::default());

            // Step 7
            let declarations = match declarations {
                Ok(declarations) => declarations,
                Err(_) => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 8
            // Step 9
            // We could try to be better I guess?
            *changed = !declarations.is_empty();
            for declaration in declarations {
                // TODO(emilio): We could check it changed
                pdb.set_parsed_declaration(declaration.0, importance);
            }

            Ok(())
        })
    }
}

impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(&self) -> u32 {
        self.owner.with_block(|ref pdb| {
            pdb.declarations.len() as u32
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(&self, index: u32) -> DOMString {
        self.IndexedGetter(index).unwrap_or_default()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };
        self.get_property_value(id)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };

        self.owner.with_block(|ref pdb| {
            if pdb.property_priority(&id).important() {
                DOMString::from("important")
            } else {
                // Step 4
                DOMString::new()
            }
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(&self,
                   property: DOMString,
                   value: DOMString,
                   priority: DOMString)
                   -> ErrorResult {
        // Step 3
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unknown property
            return Ok(())
        };
        self.set_property(id, value, priority)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        let id = match PropertyId::parse(property.into()) {
            Ok(id) => id,
            Err(..) => return Ok(()), // Unkwown property
        };

        // Step 4
        let importance = match &*priority {
            "" => Importance::Normal,
            p if p.eq_ignore_ascii_case("important") => Importance::Important,
            _ => return Ok(()),
        };

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            // Step 5 & 6
            *changed = pdb.set_importance(&id, importance);
        });

        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(&self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, DOMString::new())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(&self, property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property, cannot be there to remove.
            return Ok(DOMString::new())
        };

        let mut string = String::new();
        self.owner.mutate_associated_block(|mut pdb, mut changed| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
            *changed = pdb.remove_property(&id);
        });

        // Step 6
        Ok(DOMString::from(string))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(&self) -> DOMString {
        self.GetPropertyValue(DOMString::from("float"))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(&self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue(DOMString::from("float"), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
        self.owner.with_block(|ref pdb| {
            pdb.declarations.get(index as usize).map(|entry| {
                let (ref declaration, importance) = *entry;
                let mut css = declaration.to_css_string();
                if importance.important() {
                    css += " !important";
                }
                DOMString::from(css)
            })
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn CssText(&self) -> DOMString {
        self.owner.with_block(|ref pdb| {
            DOMString::from(pdb.to_css_string())
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn SetCssText(&self, value: DOMString) -> ErrorResult {
        let window = self.owner.window();

        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|mut pdb, mut _changed| {
            // Step 3
            *pdb = parse_style_attribute(&value,
                                         &self.owner.base_url(),
                                         window.css_error_reporter(),
                                         ParserContextExtraData::default());
        });

        Ok(())
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-_camel_cased_attribute
    css_properties_accessors!(css_properties);
}
<commit_msg>script: Also do so for element owners.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::{self, CSSStyleDeclarationMethods};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use dom::cssrule::CSSRule;
use dom::element::Element;
use dom::node::{Node, NodeDamage, window_from_node};
use dom::window::Window;
use parking_lot::RwLock;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::Arc;
use style::parser::ParserContextExtraData;
use style::properties::{Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{parse_one_declaration, parse_style_attribute};
use style::selector_parser::PseudoElement;
use style_traits::ToCss;

// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
#[dom_struct]
pub struct CSSStyleDeclaration {
    reflector_: Reflector,
    owner: CSSStyleOwner,
    readonly: bool,
    pseudo: Option<PseudoElement>,
}

#[derive(HeapSizeOf, JSTraceable)]
#[must_root]
pub enum CSSStyleOwner {
    Element(JS<Element>),
    CSSRule(JS<CSSRule>,
            #[ignore_heap_size_of = "Arc"]
            Arc<RwLock<PropertyDeclarationBlock>>),
}

impl CSSStyleOwner {
    // Mutate the declaration block associated to this style owner, and
    // optionally indicate if it has changed (assumed to be true).
    fn mutate_associated_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&mut PropertyDeclarationBlock, &mut bool) -> R,
    {
        // TODO(emilio): This has some duplication just to avoid dummy clones.
        //
        // This is somewhat complex but the complexity is encapsulated.
        let mut changed = true;
        match *self {
            CSSStyleOwner::Element(ref el) => {
                let mut attr = el.style_attribute().borrow_mut();
                let (result, needs_clear) = if attr.is_some() {
                    let lock = attr.as_ref().unwrap();
                    let mut pdb = lock.write();
                    let result = f(&mut pdb, &mut changed);
                    if changed {
                        el.set_style_attr(pdb.to_css_string());
                        el.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged);
                    }
                    (result, pdb.declarations.is_empty())
                } else {
                    let mut pdb = PropertyDeclarationBlock {
                        important_count: 0,
                        declarations: vec![],
                    };
                    let result = f(&mut pdb, &mut changed);

                    // Here `changed` is somewhat silly, because we know the
                    // exact conditions under it changes.
                    if !pdb.declarations.is_empty() {
                        el.set_style_attr(pdb.to_css_string());
                        el.upcast::<Node>().dirty(NodeDamage::NodeStyleDamaged);
                        *attr = Some(Arc::new(RwLock::new(pdb)));
                    }

                    (result, false)
                };

                if needs_clear {
                    *attr = None;
                }
                result
            }
            CSSStyleOwner::CSSRule(ref rule, ref pdb) => {
                let result = f(&mut *pdb.write(), &mut changed);
                if changed {
                    rule.global().as_window().Document().invalidate_stylesheets();
                }
                result
            }
        }
    }

    fn with_block<F, R>(&self, f: F) -> R
        where F: FnOnce(&PropertyDeclarationBlock) -> R,
    {
        match *self {
            CSSStyleOwner::Element(ref el) => {
                match *el.style_attribute().borrow() {
                    Some(ref pdb) => f(&pdb.read()),
                    None => {
                        let pdb = PropertyDeclarationBlock {
                            important_count: 0,
                            declarations: vec![],
                        };
                        f(&pdb)
                    }
                }
            }
            CSSStyleOwner::CSSRule(_, ref pdb) => {
                f(&pdb.read())
            }
        }
    }

    fn window(&self) -> Root<Window> {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el),
            CSSStyleOwner::CSSRule(ref rule, _) => Root::from_ref(rule.global().as_window()),
        }
    }

    fn base_url(&self) -> ServoUrl {
        match *self {
            CSSStyleOwner::Element(ref el) => window_from_node(&**el).Document().base_url(),
            CSSStyleOwner::CSSRule(ref rule, _) => {
                rule.parent_stylesheet().style_stylesheet().base_url.clone()
            }
        }
    }
}

#[derive(PartialEq, HeapSizeOf)]
pub enum CSSModificationAccess {
    ReadWrite,
    Readonly,
}

macro_rules! css_properties(
    ( $([$getter:ident, $setter:ident, $id:expr],)* ) => (
        $(
            fn $getter(&self) -> DOMString {
                self.get_property_value($id)
            }
            fn $setter(&self, value: DOMString) -> ErrorResult {
                self.set_property($id, value, DOMString::new())
            }
        )*
    );
);

impl CSSStyleDeclaration {
    #[allow(unrooted_must_root)]
    pub fn new_inherited(owner: CSSStyleOwner,
                         pseudo: Option<PseudoElement>,
                         modification_access: CSSModificationAccess)
                         -> CSSStyleDeclaration {
        CSSStyleDeclaration {
            reflector_: Reflector::new(),
            owner: owner,
            readonly: modification_access == CSSModificationAccess::Readonly,
            pseudo: pseudo,
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(global: &Window,
               owner: CSSStyleOwner,
               pseudo: Option<PseudoElement>,
               modification_access: CSSModificationAccess)
               -> Root<CSSStyleDeclaration> {
        reflect_dom_object(box CSSStyleDeclaration::new_inherited(owner,
                                                                  pseudo,
                                                                  modification_access),
                           global,
                           CSSStyleDeclarationBinding::Wrap)
    }

    fn get_computed_style(&self, property: PropertyId) -> DOMString {
        match self.owner {
            CSSStyleOwner::CSSRule(..) =>
                panic!("get_computed_style called on CSSStyleDeclaration with a CSSRule owner"),
            CSSStyleOwner::Element(ref el) => {
                let node = el.upcast::<Node>();
                if !node.is_in_doc() {
                    // TODO: Node should be matched against the style rules of this window.
                    // Firefox is currently the only browser to implement this.
                    return DOMString::new();
                }
                let addr = node.to_trusted_node_address();
                window_from_node(node).resolved_style_query(addr, self.pseudo.clone(), property)
            }
        }
    }

    fn get_property_value(&self, id: PropertyId) -> DOMString {
        if self.readonly {
            // Readonly style declarations are used for getComputedStyle.
            return self.get_computed_style(id);
        }

        let mut string = String::new();

        self.owner.with_block(|ref pdb| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
        });

        DOMString::from(string)
    }

    fn set_property(&self, id: PropertyId, value: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            if value.is_empty() {
                // Step 4
                *changed = pdb.remove_property(&id);
                return Ok(());
            }

            // Step 5
            let importance = match &*priority {
                "" => Importance::Normal,
                p if p.eq_ignore_ascii_case("important") => Importance::Important,
                _ => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 6
            let window = self.owner.window();
            let declarations =
                parse_one_declaration(id, &value, &self.owner.base_url(),
                                      window.css_error_reporter(),
                                      ParserContextExtraData::default());

            // Step 7
            let declarations = match declarations {
                Ok(declarations) => declarations,
                Err(_) => {
                    *changed = false;
                    return Ok(());
                }
            };

            // Step 8
            // Step 9
            // We could try to be better I guess?
            *changed = !declarations.is_empty();
            for declaration in declarations {
                // TODO(emilio): We could check it changed
                pdb.set_parsed_declaration(declaration.0, importance);
            }

            Ok(())
        })
    }
}

impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length
    fn Length(&self) -> u32 {
        self.owner.with_block(|ref pdb| {
            pdb.declarations.len() as u32
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item
    fn Item(&self, index: u32) -> DOMString {
        self.IndexedGetter(index).unwrap_or_default()
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
    fn GetPropertyValue(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };
        self.get_property_value(id)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
    fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property
            return DOMString::new()
        };

        self.owner.with_block(|ref pdb| {
            if pdb.property_priority(&id).important() {
                DOMString::from("important")
            } else {
                // Step 4
                DOMString::new()
            }
        })
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setproperty
    fn SetProperty(&self,
                   property: DOMString,
                   value: DOMString,
                   priority: DOMString)
                   -> ErrorResult {
        // Step 3
        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unknown property
            return Ok(())
        };
        self.set_property(id, value, priority)
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertypriority
    fn SetPropertyPriority(&self, property: DOMString, priority: DOMString) -> ErrorResult {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        // Step 2 & 3
        let id = match PropertyId::parse(property.into()) {
            Ok(id) => id,
            Err(..) => return Ok(()), // Unkwown property
        };

        // Step 4
        let importance = match &*priority {
            "" => Importance::Normal,
            p if p.eq_ignore_ascii_case("important") => Importance::Important,
            _ => return Ok(()),
        };

        self.owner.mutate_associated_block(|ref mut pdb, mut changed| {
            // Step 5 & 6
            *changed = pdb.set_importance(&id, importance);
        });

        Ok(())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-setpropertyvalue
    fn SetPropertyValue(&self, property: DOMString, value: DOMString) -> ErrorResult {
        self.SetProperty(property, value, DOMString::new())
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-removeproperty
    fn RemoveProperty(&self, property: DOMString) -> Fallible<DOMString> {
        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        let id = if let Ok(id) = PropertyId::parse(property.into()) {
            id
        } else {
            // Unkwown property, cannot be there to remove.
            return Ok(DOMString::new())
        };

        let mut string = String::new();
        self.owner.mutate_associated_block(|mut pdb, mut changed| {
            pdb.property_value_to_css(&id, &mut string).unwrap();
            *changed = pdb.remove_property(&id);
        });

        // Step 6
        Ok(DOMString::from(string))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn CssFloat(&self) -> DOMString {
        self.GetPropertyValue(DOMString::from("float"))
    }

    // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-cssfloat
    fn SetCssFloat(&self, value: DOMString) -> ErrorResult {
        self.SetPropertyValue(DOMString::from("float"), value)
    }

    // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
    fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
        self.owner.with_block(|ref pdb| {
            pdb.declarations.get(index as usize).map(|entry| {
                let (ref declaration, importance) = *entry;
                let mut css = declaration.to_css_string();
                if importance.important() {
                    css += " !important";
                }
                DOMString::from(css)
            })
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn CssText(&self) -> DOMString {
        self.owner.with_block(|ref pdb| {
            DOMString::from(pdb.to_css_string())
        })
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
    fn SetCssText(&self, value: DOMString) -> ErrorResult {
        let window = self.owner.window();

        // Step 1
        if self.readonly {
            return Err(Error::NoModificationAllowed);
        }

        self.owner.mutate_associated_block(|mut pdb, mut _changed| {
            // Step 3
            *pdb = parse_style_attribute(&value,
                                         &self.owner.base_url(),
                                         window.css_error_reporter(),
                                         ParserContextExtraData::default());
        });

        Ok(())
    }

    // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-_camel_cased_attribute
    css_properties_accessors!(css_properties);
}
<|endoftext|>


<commit_before>use std;
use std::path::Path;

use sdl2::event::Event;
use sdl2::gfx::primitives::DrawRenderer;
use sdl2::keyboard::Keycode;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::rect::{Point, Rect};
use sdl2::render::{BlendMode, Renderer, Texture, TextureQuery};
use sdl2::surface::Surface;
use sdl2::ttf::{Font, Sdl2TtfContext, STYLE_BOLD};

use position::Position;
use text::Text;

const BORDER_COLOR: Color = Color::RGBA(255, 255, 255, 64);

const PADDING: i32 = 10;

const FONT_FILE: &'static str = "../assets/FantasqueSansMono-Bold.ttf";
const FONT_COLOR: Color = Color::RGBA(45, 200, 45, 255);
const FONT_SIZE: u16 = 18;

pub struct Console<'a> {
    pub visible: bool,

    leader: Text,
    input_buffer: String,
    last_command: String,
    command_history: Vec<String>,
    history_position: usize,
    cursor_position: usize,
    buffer: Vec<String>,
    backbuffer_y: i32,
    texture: Texture,
    backbuffer_texture: Texture,
    ttf_context: &'a Sdl2TtfContext,
    size: (u32, u32),
    font: Font<'a>,
    line_ending: bool, // Tracks where the next print call should append to
    ctrl: bool, // Tracks the Ctrl key being pressed
}

impl<'a> Console<'a> {
    /// Creates a new empty Console
    pub fn new(ttf_context: &'a Sdl2TtfContext, mut renderer: &mut Renderer) -> Console<'a> {
        let (width, height) = renderer.window().unwrap().size();
        let mut texture =
            renderer.create_texture_streaming(PixelFormatEnum::RGBA8888, width / 2, height)
                .unwrap();
        texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                for y in 0..height {
                    for x in 0..width / 2 {
                        let x = x as usize;
                        let y = y as usize;
                        let offset = y * pitch + x * 4;
                        buffer[offset + 0] = 182;
                        buffer[offset + 1] = 0;
                        buffer[offset + 2] = 0;
                        buffer[offset + 3] = 0;
                    }
                }
            })
            .unwrap();

        let mut backbuffer_texture =
            renderer.create_texture_streaming(PixelFormatEnum::RGBA8888, width / 2, height)
                .unwrap();
        backbuffer_texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                for y in 0..height {
                    for x in 0..width / 2 {
                        let x = x as usize;
                        let y = y as usize;
                        let offset = y * pitch + x * 4;
                        buffer[offset + 0] = 182;
                        buffer[offset + 1] = 0;
                        buffer[offset + 2] = 0;
                        buffer[offset + 3] = 0;
                    }
                }
            })
            .unwrap();

        let mut font = ttf_context.load_font(Path::new(FONT_FILE), FONT_SIZE).unwrap();
        font.set_style(STYLE_BOLD);

        Console {
            visible: false,
            leader: Text::new(&ttf_context,
                              &mut renderer,
                              "hakka>",
                              Position::XY(PADDING, height as i32 - FONT_SIZE as i32 - PADDING),
                              FONT_SIZE,
                              FONT_COLOR,
                              FONT_FILE),
            input_buffer: "".into(),
            last_command: "".into(),
            command_history: Vec::new(),
            history_position: 0,
            cursor_position: 0,
            buffer: Vec::new(),
            backbuffer_y: 0,
            texture: texture,
            backbuffer_texture: backbuffer_texture,
            ttf_context: ttf_context,
            size: (width / 2, height),
            font: font,
            line_ending: true,
            ctrl: false,
        }
    }

    pub fn process(&mut self, event: &Event) {
        match event {
            &Event::KeyUp { keycode: Option::Some(Keycode::Backquote), .. } => {
                self.toggle();
            }
            &Event::TextInput { ref text, .. } => {
                if self.visible {
                    if text == "`" {
                        return;
                    }
                    self.add_text(text);
                }
            }
            &Event::MouseWheel { y, .. } => {
                if self.visible {
                    if self.buffer.len() * FONT_SIZE as usize >
                       (self.size.1 - (FONT_SIZE as u32 * 2)) as usize {
                        self.backbuffer_y += y * 2;
                        if self.backbuffer_y < 0 {
                            self.backbuffer_y = 0;
                        }
                    }
                }
            }
            &Event::KeyDown { keycode, .. } => {
                if self.visible {
                    match keycode {
                        Some(Keycode::LCtrl) |
                        Some(Keycode::RCtrl) => self.ctrl = true,
                        Some(Keycode::C) => {
                            if self.ctrl {
                                self.input_buffer = "".into();
                                self.commit();
                            }
                        }
                        Some(Keycode::Left) => {
                            self.cursor_left();
                        }
                        Some(Keycode::Right) => {
                            self.cursor_right();
                        }
                        Some(Keycode::Backspace) => {
                            self.backspace();
                        }
                        Some(Keycode::Delete) => {
                            if self.cursor_position < self.input_buffer.len() {
                                self.cursor_position += 1;
                                self.backspace();
                            }
                        }
                        _ => (),
                    }
                }
            }
            &Event::KeyUp { keycode, .. } => {
                if self.visible {
                    match keycode {
                        Some(Keycode::LCtrl) |
                        Some(Keycode::RCtrl) => self.ctrl = false,
                        Some(Keycode::Up) => {
                            self.history_navigate_back();
                        }
                        Some(Keycode::Down) => {
                            self.history_navigate_forward();
                        }
                        Some(Keycode::Return) => {
                            self.commit();
                        }
                        Some(Keycode::End) => {
                            self.cursor_position = self.input_buffer.len();
                        }
                        Some(Keycode::Home) => {
                            self.cursor_position = 0;
                        }
                        _ => (),
                    }
                }
            }
            _ => (),
        }
    }

    pub fn process_command(&mut self) {
        let command = self.input_buffer.clone();
        if command.len() > 0 {
            self.command_history.push(command.clone());
            self.last_command = command.clone();

            if command == "exit" {
                std::process::exit(0);
            }
        }
    }

    pub fn clear(&mut self) {
        self.buffer.clear();
    }

    fn history_navigate_back(&mut self) {
        if self.history_position > 0 {
            self.input_buffer = self.command_history[self.history_position - 1].clone();
            self.cursor_position = self.input_buffer.len();

            if self.history_position > 0 {
                self.history_position -= 1;
            }
        }
    }

    fn history_navigate_forward(&mut self) {
        if self.history_position < self.command_history.len() - 1 {
            self.input_buffer = self.command_history[self.history_position + 1].clone();
            self.cursor_position = self.input_buffer.len();
            if self.history_position < self.command_history.len() {
                self.history_position += 1;
            }
        }
    }

    pub fn try_process_command(&mut self) -> Option<String> {
        if self.last_command.len() > 0 {
            let cmd = self.last_command.clone();
            self.last_command.clear();
            Some(cmd)
        } else {
            None
        }
    }

    pub fn print<S>(&mut self, text: S)
        where S: Into<String>
    {
        if !self.line_ending {
            let last = self.buffer.last_mut().unwrap();
            last.push_str(&text.into());
        } else {
            self.buffer.push(text.into());
        }
        self.line_ending = false;
    }

    pub fn println<S>(&mut self, text: S)
        where S: Into<String>
    {
        self.buffer.push(text.into());
        self.line_ending = true;
    }

    pub fn print_lines<S>(&mut self, text: S)
        where S: Into<String>
    {
        for line in text.into().lines() {
            self.println(line);
        }
    }

    pub fn wrap_line(&mut self) {
        self.buffer.push("".into());
        self.line_ending = false;
    }

    /// Toggles the visibility of the Console
    pub fn toggle(&mut self) {
        self.visible = !self.visible;
    }

    pub fn add_text(&mut self, input: &str) {
        self.input_buffer.insert(self.cursor_position, input.chars().next().unwrap());
        self.cursor_position += 1;
    }

    pub fn commit(&mut self) {
        self.buffer.push(format!("hakka> {}", self.input_buffer.clone()));
        self.process_command();
        self.input_buffer.clear();
        self.cursor_position = 0;
        self.history_position = self.command_history.len();
    }

    pub fn cursor_left(&mut self) {
        if self.cursor_position > 0 {
            self.cursor_position -= 1;
        }
    }

    pub fn cursor_right(&mut self) {
        if self.cursor_position < self.input_buffer.len() {
            self.cursor_position += 1;
        }
    }

    pub fn backspace(&mut self) {
        if self.visible {
            if self.cursor_position > 0 {
                self.input_buffer.remove(self.cursor_position - 1);
                self.cursor_position -= 1;
            }
        }
    }

    /// Renders the Console
    pub fn render(&mut self, mut renderer: &mut Renderer) {
        if self.visible {

            renderer.set_blend_mode(BlendMode::Blend);
            self.texture.set_blend_mode(BlendMode::Blend);
            renderer.copy(&self.texture,
                      None,
                      Some(Rect::new(0, 0, self.size.0, self.size.1)))
                .unwrap();
            self.render_leader(&mut renderer);
            self.generate_backbuffer_texture(&mut renderer);

            // Insert the cursor via a dodgy vertical line
            let mut output_text = self.input_buffer.clone();
            let cursor_x =
                60 + PADDING as i16 +
                self.font.size_of(&self.input_buffer[..self.cursor_position]).unwrap().0 as i16;
            // Draw a dodgy cursor
            renderer.thick_line(cursor_x,
                            self.size.1 as i16 - FONT_SIZE as i16 - PADDING as i16,
                            cursor_x,
                            self.size.1 as i16 - PADDING as i16,
                            1,
                            FONT_COLOR)
                .unwrap();

            if output_text.len() > 0 {
                let text = Text::new(&self.ttf_context,
                                     &mut renderer,
                                     &output_text[..],
                                     Position::XY(60 + PADDING,
                                                  self.size.1 as i32 - FONT_SIZE as i32 - PADDING),
                                     FONT_SIZE,
                                     FONT_COLOR,
                                     FONT_FILE);
                text.render(&mut renderer);
            }

            self.render_border(&mut renderer);
        }
    }

    fn render_border(&self, mut renderer: &mut Renderer) {
        // Render the border
        renderer.set_draw_color(Color::RGBA(255, 255, 255, 255));
        // North
        renderer.thick_line(0, 0, self.size.0 as i16, 0, 1, BORDER_COLOR);

        // East
        renderer.thick_line(self.size.0 as i16,
                            0,
                            self.size.0 as i16,
                            self.size.1 as i16,
                            1,
                            BORDER_COLOR);

        // South
        renderer.thick_line(0,
                            self.size.1 as i16 - 1,
                            self.size.0 as i16,
                            self.size.1 as i16 - 1,
                            1,
                            BORDER_COLOR);
    }

    fn render_leader(&self, mut renderer: &mut Renderer) {
        self.leader.render(&mut renderer);
    }

    fn generate_backbuffer_texture(&mut self, mut renderer: &mut Renderer) {
        let mut main_surface = Surface::new(self.size.0,
                                            (self.size.1 - (FONT_SIZE as u32)),
                                            PixelFormatEnum::RGBA8888)
            .unwrap();
        let mut counter = 2;
        for line in self.buffer.iter().rev().take(100) {
            let mut y_pos = self.size.1 as i32 - (FONT_SIZE as i32 * counter) + self.backbuffer_y;
            counter += 1;

            if line.trim().len() == 0 {
                continue;
            }

            let surface = self.font
                .render(&line)
                .blended(FONT_COLOR)
                .unwrap();
            surface.blit(None,
                      &mut main_surface,
                      Some(Rect::new(PADDING, y_pos - PADDING, self.size.1, FONT_SIZE as u32)))
                .unwrap();
        }
        let texture = renderer.create_texture_from_surface(&main_surface)
            .unwrap();

        let TextureQuery { width, height, .. } = texture.query();

        renderer.copy(&texture, None, Some(Rect::new(0, 0, width, height)));
    }
}<commit_msg>Add black background behind leader<commit_after>use std;
use std::path::Path;

use sdl2::event::Event;
use sdl2::gfx::primitives::DrawRenderer;
use sdl2::keyboard::Keycode;
use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::rect::{Point, Rect};
use sdl2::render::{BlendMode, Renderer, Texture, TextureQuery};
use sdl2::surface::Surface;
use sdl2::ttf::{Font, Sdl2TtfContext, STYLE_BOLD};

use position::Position;
use text::Text;

const BORDER_COLOR: Color = Color::RGBA(255, 255, 255, 64);

const PADDING: i32 = 10;

const FONT_FILE: &'static str = "../assets/FantasqueSansMono-Bold.ttf";
const FONT_COLOR: Color = Color::RGBA(45, 200, 45, 255);
const FONT_SIZE: u16 = 18;

pub struct Console<'a> {
    pub visible: bool,

    leader: Text,
    input_buffer: String,
    last_command: String,
    command_history: Vec<String>,
    history_position: usize,
    cursor_position: usize,
    buffer: Vec<String>,
    backbuffer_y: i32,
    texture: Texture,
    backbuffer_texture: Texture,
    ttf_context: &'a Sdl2TtfContext,
    size: (u32, u32),
    font: Font<'a>,
    line_ending: bool, // Tracks where the next print call should append to
    ctrl: bool, // Tracks the Ctrl key being pressed
}

impl<'a> Console<'a> {
    /// Creates a new empty Console
    pub fn new(ttf_context: &'a Sdl2TtfContext, mut renderer: &mut Renderer) -> Console<'a> {
        let (width, height) = renderer.window().unwrap().size();
        let mut texture =
            renderer.create_texture_streaming(PixelFormatEnum::RGBA8888, width / 2, height)
                .unwrap();
        texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                for y in 0..height {
                    for x in 0..width / 2 {
                        let x = x as usize;
                        let y = y as usize;
                        let offset = y * pitch + x * 4;
                        buffer[offset + 0] = 182;
                        buffer[offset + 1] = 0;
                        buffer[offset + 2] = 0;
                        buffer[offset + 3] = 0;
                    }
                }
            })
            .unwrap();

        let mut backbuffer_texture =
            renderer.create_texture_streaming(PixelFormatEnum::RGBA8888, width / 2, height)
                .unwrap();
        backbuffer_texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
                for y in 0..height {
                    for x in 0..width / 2 {
                        let x = x as usize;
                        let y = y as usize;
                        let offset = y * pitch + x * 4;
                        buffer[offset + 0] = 182;
                        buffer[offset + 1] = 0;
                        buffer[offset + 2] = 0;
                        buffer[offset + 3] = 0;
                    }
                }
            })
            .unwrap();

        let mut font = ttf_context.load_font(Path::new(FONT_FILE), FONT_SIZE).unwrap();
        font.set_style(STYLE_BOLD);

        Console {
            visible: false,
            leader: Text::new(&ttf_context,
                              &mut renderer,
                              "hakka>",
                              Position::XY(PADDING, height as i32 - FONT_SIZE as i32 - PADDING),
                              FONT_SIZE,
                              FONT_COLOR,
                              FONT_FILE),
            input_buffer: "".into(),
            last_command: "".into(),
            command_history: Vec::new(),
            history_position: 0,
            cursor_position: 0,
            buffer: Vec::new(),
            backbuffer_y: 0,
            texture: texture,
            backbuffer_texture: backbuffer_texture,
            ttf_context: ttf_context,
            size: (width / 2, height),
            font: font,
            line_ending: true,
            ctrl: false,
        }
    }

    pub fn process(&mut self, event: &Event) {
        match event {
            &Event::KeyUp { keycode: Option::Some(Keycode::Backquote), .. } => {
                self.toggle();
            }
            &Event::TextInput { ref text, .. } => {
                if self.visible {
                    if text == "`" {
                        return;
                    }
                    self.add_text(text);
                }
            }
            &Event::MouseWheel { y, .. } => {
                if self.visible {
                    if self.buffer.len() * FONT_SIZE as usize >
                       (self.size.1 - (FONT_SIZE as u32 * 2)) as usize {
                        self.backbuffer_y += y * 2;
                        if self.backbuffer_y < 0 {
                            self.backbuffer_y = 0;
                        }
                    }
                }
            }
            &Event::KeyDown { keycode, .. } => {
                if self.visible {
                    match keycode {
                        Some(Keycode::LCtrl) |
                        Some(Keycode::RCtrl) => self.ctrl = true,
                        Some(Keycode::C) => {
                            if self.ctrl {
                                self.input_buffer = "".into();
                                self.commit();
                            }
                        }
                        Some(Keycode::Left) => {
                            self.cursor_left();
                        }
                        Some(Keycode::Right) => {
                            self.cursor_right();
                        }
                        Some(Keycode::Backspace) => {
                            self.backspace();
                        }
                        Some(Keycode::Delete) => {
                            if self.cursor_position < self.input_buffer.len() {
                                self.cursor_position += 1;
                                self.backspace();
                            }
                        }
                        _ => (),
                    }
                }
            }
            &Event::KeyUp { keycode, .. } => {
                if self.visible {
                    match keycode {
                        Some(Keycode::LCtrl) |
                        Some(Keycode::RCtrl) => self.ctrl = false,
                        Some(Keycode::Up) => {
                            self.history_navigate_back();
                        }
                        Some(Keycode::Down) => {
                            self.history_navigate_forward();
                        }
                        Some(Keycode::Return) => {
                            self.commit();
                        }
                        Some(Keycode::End) => {
                            self.cursor_position = self.input_buffer.len();
                        }
                        Some(Keycode::Home) => {
                            self.cursor_position = 0;
                        }
                        _ => (),
                    }
                }
            }
            _ => (),
        }
    }

    pub fn process_command(&mut self) {
        let command = self.input_buffer.clone();
        if command.len() > 0 {
            self.command_history.push(command.clone());
            self.last_command = command.clone();

            if command == "exit" {
                std::process::exit(0);
            }
        }
    }

    pub fn clear(&mut self) {
        self.buffer.clear();
    }

    fn history_navigate_back(&mut self) {
        if self.history_position > 0 {
            self.input_buffer = self.command_history[self.history_position - 1].clone();
            self.cursor_position = self.input_buffer.len();

            if self.history_position > 0 {
                self.history_position -= 1;
            }
        }
    }

    fn history_navigate_forward(&mut self) {
        if self.history_position < self.command_history.len() - 1 {
            self.input_buffer = self.command_history[self.history_position + 1].clone();
            self.cursor_position = self.input_buffer.len();
            if self.history_position < self.command_history.len() {
                self.history_position += 1;
            }
        }
    }

    pub fn try_process_command(&mut self) -> Option<String> {
        if self.last_command.len() > 0 {
            let cmd = self.last_command.clone();
            self.last_command.clear();
            Some(cmd)
        } else {
            None
        }
    }

    pub fn print<S>(&mut self, text: S)
        where S: Into<String>
    {
        if !self.line_ending {
            let last = self.buffer.last_mut().unwrap();
            last.push_str(&text.into());
        } else {
            self.buffer.push(text.into());
        }
        self.line_ending = false;
    }

    pub fn println<S>(&mut self, text: S)
        where S: Into<String>
    {
        self.buffer.push(text.into());
        self.line_ending = true;
    }

    pub fn print_lines<S>(&mut self, text: S)
        where S: Into<String>
    {
        for line in text.into().lines() {
            self.println(line);
        }
    }

    pub fn wrap_line(&mut self) {
        self.buffer.push("".into());
        self.line_ending = false;
    }

    /// Toggles the visibility of the Console
    pub fn toggle(&mut self) {
        self.visible = !self.visible;
    }

    pub fn add_text(&mut self, input: &str) {
        self.input_buffer.insert(self.cursor_position, input.chars().next().unwrap());
        self.cursor_position += 1;
    }

    pub fn commit(&mut self) {
        self.buffer.push(format!("hakka> {}", self.input_buffer.clone()));
        self.process_command();
        self.input_buffer.clear();
        self.cursor_position = 0;
        self.history_position = self.command_history.len();
    }

    pub fn cursor_left(&mut self) {
        if self.cursor_position > 0 {
            self.cursor_position -= 1;
        }
    }

    pub fn cursor_right(&mut self) {
        if self.cursor_position < self.input_buffer.len() {
            self.cursor_position += 1;
        }
    }

    pub fn backspace(&mut self) {
        if self.visible {
            if self.cursor_position > 0 {
                self.input_buffer.remove(self.cursor_position - 1);
                self.cursor_position -= 1;
            }
        }
    }

    /// Renders the Console
    pub fn render(&mut self, mut renderer: &mut Renderer) {
        if self.visible {

            renderer.set_blend_mode(BlendMode::Blend);
            self.texture.set_blend_mode(BlendMode::Blend);
            renderer.copy(&self.texture,
                      None,
                      Some(Rect::new(0, 0, self.size.0, self.size.1)))
                .unwrap();
            self.generate_backbuffer_texture(&mut renderer);
            self.render_leader(&mut renderer);

            // Insert the cursor via a dodgy vertical line
            let mut output_text = self.input_buffer.clone();
            let cursor_x =
                60 + PADDING as i16 +
                self.font.size_of(&self.input_buffer[..self.cursor_position]).unwrap().0 as i16;
            // Draw a dodgy cursor
            renderer.thick_line(cursor_x,
                            self.size.1 as i16 - FONT_SIZE as i16 - PADDING as i16,
                            cursor_x,
                            self.size.1 as i16 - PADDING as i16,
                            1,
                            FONT_COLOR)
                .unwrap();

            if output_text.len() > 0 {
                let text = Text::new(&self.ttf_context,
                                     &mut renderer,
                                     &output_text[..],
                                     Position::XY(60 + PADDING,
                                                  self.size.1 as i32 - FONT_SIZE as i32 - PADDING),
                                     FONT_SIZE,
                                     FONT_COLOR,
                                     FONT_FILE);
                text.render(&mut renderer);
            }

            self.render_border(&mut renderer);
        }
    }

    fn render_border(&self, mut renderer: &mut Renderer) {
        // Render the border
        renderer.set_draw_color(Color::RGBA(255, 255, 255, 255));
        // North
        renderer.thick_line(0, 0, self.size.0 as i16, 0, 1, BORDER_COLOR);

        // East
        renderer.thick_line(self.size.0 as i16,
                            0,
                            self.size.0 as i16,
                            self.size.1 as i16,
                            1,
                            BORDER_COLOR);

        // South
        renderer.thick_line(0,
                            self.size.1 as i16 - 1,
                            self.size.0 as i16,
                            self.size.1 as i16 - 1,
                            1,
                            BORDER_COLOR);
    }

    fn render_leader(&self, mut renderer: &mut Renderer) {
        // Render a black background behind it so the buffer scrolling looks
        // nicer.
        let rect_y = self.size.1 as i32 - FONT_SIZE as i32 - PADDING;
        renderer.set_draw_color(Color::RGBA(0, 0, 0, 255));
        renderer.fill_rect(Rect::new(0, rect_y, self.size.0, rect_y as u32));
        self.leader.render(&mut renderer);
    }

    fn generate_backbuffer_texture(&mut self, mut renderer: &mut Renderer) {
        let mut main_surface = Surface::new(self.size.0,
                                            (self.size.1 - (FONT_SIZE as u32)),
                                            PixelFormatEnum::RGBA8888)
            .unwrap();
        let mut counter = 2;
        for line in self.buffer.iter().rev().take(100) {
            let mut y_pos = self.size.1 as i32 - (FONT_SIZE as i32 * counter) + self.backbuffer_y;
            counter += 1;

            if line.trim().len() == 0 {
                continue;
            }

            let surface = self.font
                .render(&line)
                .blended(FONT_COLOR)
                .unwrap();
            surface.blit(None,
                      &mut main_surface,
                      Some(Rect::new(PADDING, y_pos - PADDING, self.size.1, FONT_SIZE as u32)))
                .unwrap();
        }
        let texture = renderer.create_texture_from_surface(&main_surface)
            .unwrap();

        let TextureQuery { width, height, .. } = texture.query();

        renderer.copy(&texture, None, Some(Rect::new(0, 0, width, height)));
    }
}<|endoftext|>


<commit_before>use std::result;

#[derive(Debug)]
pub enum Error {
    UnknownNodeIndex,
    NodeStateMismatch,
    UnexpectedNodeType,
    StackEmpty,
    StackNotEmpty,
    Yielding,
    NotYielding,
}

pub type Result<T> = result::Result<T, Error>;

pub type NodeIndex = usize;

pub enum LeafResolution<A> {
    Return(bool),
    Yield(A),
}

pub enum SwitchResolution {
    Select(NodeIndex),
    Reset(NodeIndex),
}

pub enum SwitchReturn {
    Return(bool),
    Select(NodeIndex),
}

enum StepResolution<A> {
    Yield(A),
    Call(NodeIndex),
    Return(bool),
}

enum RunResolution<A> {
    Yield(A),
    Return(bool),
}

enum Resolution<A> {
    Return(bool),
    Call(NodeIndex),
    Yield(A),
}

pub trait LeafFn<K, A> {
    fn call(&self, knowledge: K) -> LeafResolution<A>;
}

pub trait SwitchFn<K> {
    fn call(&self, knowledge: K) -> SwitchResolution;
    fn return_to(&self, value: bool) -> SwitchReturn;
}

enum Node<Leaf, Switch> {
    Leaf(Leaf),
    Collection(CollectionNode),
    Switch(Switch),
}

pub enum CollectionNode {
    Forever(NodeIndex),
    All(Vec<NodeIndex>),
}

#[derive(Clone, Serialize, Deserialize)]
struct ArrayTraverse {
    index: usize,
    length: usize,
    value: bool,
}

#[derive(Clone, Serialize, Deserialize)]
enum CollectionState {
    Forever,
    All(ArrayTraverse),
}

#[derive(Clone, Serialize, Deserialize)]
enum StackFrame {
    Leaf {
        index: NodeIndex,
        value: Option<bool>,
    },
    Collection {
        index: NodeIndex,
        state: CollectionState,
    },
    Switch {
        index: NodeIndex,
        child: Option<NodeIndex>,
        value: Option<bool>,
    },
}

struct StackTruncate {
    len: usize,
    index: NodeIndex,
}

pub struct Graph<Leaf, Switch> {
    nodes: Vec<Node<Leaf, Switch>>,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct State {
    stack: Vec<StackFrame>,
    yielding: bool,
}

impl StackTruncate {
    fn new(len: usize, index: NodeIndex) -> Self {
        StackTruncate {
            len: len,
            index: index,
        }
    }
}

impl SwitchResolution {
    fn to_index(&self) -> NodeIndex {
        match self {
            &SwitchResolution::Select(index) => index,
            &SwitchResolution::Reset(index) => index,
        }
    }
}

impl<A> RunResolution<A> {
    fn to_step_resolution(self) -> StepResolution<A> {
        match self {
            RunResolution::Return(value) => StepResolution::Return(value),
            RunResolution::Yield(action) => StepResolution::Yield(action),
        }
    }
}

impl ArrayTraverse {
    fn new(length: usize, value: bool) -> Self {
        ArrayTraverse {
            index: 0,
            length: length,
            value: value,
        }
    }

    fn next_index(&mut self) -> Option<usize> {
        if self.index < self.length {
            let index = self.index;
            self.index += 1;
            Some(index)
        } else {
            None
        }
    }
}

impl CollectionNode {
    fn create_state(&self) -> CollectionState {
        match self {
            &CollectionNode::Forever(_) => CollectionState::Forever,
            &CollectionNode::All(ref v) => CollectionState::All(ArrayTraverse::new(v.len(), false)),
        }
    }

    fn next_index(&self, state: &mut CollectionState) -> Result<Option<NodeIndex>> {
        match (self, state) {
            (&CollectionNode::Forever(index), &mut CollectionState::Forever) => Ok(Some(index)),
            (&CollectionNode::All(ref indices), &mut CollectionState::All(ref mut at)) => {
                Ok(at.next_index().map(|i| indices[i]))
            }
            _ => Err(Error::NodeStateMismatch),
        }
    }
}

impl CollectionState {
    fn handle_return(&mut self, value: bool) {
        match self {
            &mut CollectionState::All(ref mut at) => at.value = value,
            _ => {}
        }
    }

    fn return_value(&self) -> bool {
        match self {
            &CollectionState::All(ref at) => at.value,
            _ => panic!("impossible return"),
        }
    }
}

impl<A> LeafResolution<A> {
    fn to_resolution<'a>(self) -> Resolution<A> {
        match self {
            LeafResolution::Return(value) => Resolution::Return(value),
            LeafResolution::Yield(action) => Resolution::Yield(action),
        }
    }
}

impl StackFrame {
    fn handle_return(&mut self, return_value: bool) -> Result<()> {
        match self {
            &mut StackFrame::Leaf { ref mut value, .. } => {
                *value = Some(return_value);
            }
            &mut StackFrame::Collection { ref mut state, .. } => {
                state.handle_return(return_value);
            }
            &mut StackFrame::Switch { ref mut value, .. } => {
                *value = Some(return_value);
            }
        }

        Ok(())
    }
}

impl<Leaf, Switch> Node<Leaf, Switch> {
    fn leaf_fn(&self) -> Result<&Leaf> {
        if let &Node::Leaf(ref f) = self {
            Ok(f)
        } else {
            Err(Error::UnexpectedNodeType)
        }
    }

    fn collection_node(&self) -> Result<&CollectionNode> {
        if let &Node::Collection(ref n) = self {
            Ok(n)
        } else {
            Err(Error::UnexpectedNodeType)
        }
    }

    fn switch_fn(&self) -> Result<&Switch> {
        if let &Node::Switch(ref f) = self {
            Ok(f)
        } else {
            Err(Error::UnexpectedNodeType)
        }
    }
}

impl<Leaf, Switch> Graph<Leaf, Switch> {
    pub fn new() -> Self {
        Graph { nodes: Vec::new() }
    }

    fn add_node(&mut self, node: Node<Leaf, Switch>) -> NodeIndex {
        let index = self.nodes.len();
        self.nodes.push(node);
        return index;
    }

    pub fn add_leaf(&mut self, leaf: Leaf) -> NodeIndex {
        self.add_node(Node::Leaf(leaf))
    }

    pub fn add_collection(&mut self, collection: CollectionNode) -> NodeIndex {
        self.add_node(Node::Collection(collection))
    }

    pub fn add_switch(&mut self, switch: Switch) -> NodeIndex {
        self.add_node(Node::Switch(switch))
    }

    fn node(&self, index: NodeIndex) -> Result<&Node<Leaf, Switch>> {
        if index < self.nodes.len() {
            Ok(&self.nodes[index])
        } else {
            Err(Error::UnknownNodeIndex)
        }
    }

    fn create_stack_frame(&self, index: NodeIndex) -> Result<StackFrame> {
        match try!(self.node(index)) {
            &Node::Leaf(_) => {
                Ok(StackFrame::Leaf {
                    index: index,
                    value: None,
                })
            }
            &Node::Collection(ref c) => {
                Ok(StackFrame::Collection {
                    index: index,
                    state: c.create_state(),
                })
            }
            &Node::Switch(_) => {
                Ok(StackFrame::Switch {
                    index: index,
                    child: None,
                    value: None,
                })
            }
        }
    }

    fn resolve_frame<K, A>(&self, frame: &mut StackFrame, knowledge: K) -> Result<Resolution<A>>
        where Leaf: LeafFn<K, A>,
              Switch: SwitchFn<K>
    {
        match frame {
            &mut StackFrame::Leaf { index, value } => {
                if let Some(value) = value {
                    Ok(Resolution::Return(value))
                } else {
                    let node = try!(self.node(index));
                    let f = try!(node.leaf_fn());
                    Ok(f.call(knowledge).to_resolution())
                }
            }
            &mut StackFrame::Collection { index, ref mut state } => {
                let node = try!(self.node(index));
                let collection_node = try!(node.collection_node());
                if let Some(child_index) = try!(collection_node.next_index(state)) {
                    Ok(Resolution::Call(child_index))
                } else {
                    Ok(Resolution::Return(state.return_value()))
                }
            }
            &mut StackFrame::Switch { index, ref mut child, value } => {
                let switch_fn = try!(try!(self.node(index)).switch_fn());
                Ok(if let Some(value) = value {
                    match switch_fn.return_to(value) {
                        SwitchReturn::Return(return_value) => Resolution::Return(return_value),
                        SwitchReturn::Select(index) => Resolution::Call(index),
                    }
                } else {
                    let index = switch_fn.call(knowledge).to_index();
                    *child = Some(index);
                    Resolution::Call(index)
                })
            }
        }
    }
}
impl State {
    pub fn new() -> Self {
        State {
            stack: Vec::new(),
            yielding: false,
        }
    }

    pub fn is_initialised(&self) -> bool {
        !self.stack.is_empty()
    }

    pub fn initialise<Leaf, Switch>(&mut self,
                                    graph: &Graph<Leaf, Switch>,
                                    index: NodeIndex)
                                    -> Result<()> {

        if !self.stack.is_empty() {
            return Err(Error::StackNotEmpty);
        }

        let frame = try!(graph.create_stack_frame(index));
        self.stack.push(frame);

        Ok(())
    }

    fn current_frame(&mut self) -> Result<&mut StackFrame> {
        if let Some(frame) = self.stack.last_mut() {
            Ok(frame)
        } else {
            Err(Error::StackEmpty)
        }
    }

    fn apply_resolution<K, A, Leaf, Switch>(stack: &mut Vec<StackFrame>,
                                            graph: &Graph<Leaf, Switch>,
                                            resolution: Resolution<A>)
                                            -> Result<Option<A>>
        where Leaf: LeafFn<K, A>
    {
        match resolution {
            Resolution::Call(index) => {
                stack.push(try!(graph.create_stack_frame(index)));
            }
            Resolution::Return(value) => {
                if stack.pop().is_none() {
                    return Err(Error::StackEmpty);
                }

                if let Some(frame) = stack.last_mut() {
                    try!(frame.handle_return(value));
                }
            }
            Resolution::Yield(action) => return Ok(Some(action)),
        }

        Ok(None)
    }

    fn resolve_switches<K, Leaf, Switch>(stack: &mut Vec<StackFrame>,
                                         graph: &Graph<Leaf, Switch>,
                                         knowledge: K)
                                         -> Result<Option<StackTruncate>>
        where K: Copy,
              Switch: SwitchFn<K>
    {
        let mut i = 0;
        for frame in stack {
            if let &mut StackFrame::Switch { index, ref mut child, .. } = frame {
                if let Some(current) = child.as_mut() {
                    let switch = try!(try!(graph.node(index)).switch_fn());
                    let resolution = switch.call(knowledge);
                    match resolution {
                        SwitchResolution::Select(child_index) => {
                            if child_index != *current {
                                *current = child_index;
                                return Ok(Some(StackTruncate::new(i + 1, child_index)));
                            }
                        }
                        SwitchResolution::Reset(child_index) => {
                            *current = child_index;
                            return Ok(Some(StackTruncate::new(i + 1, child_index)));
                        }
                    }
                }
            }

            i += 1;
        }

        Ok(None)
    }

    pub fn run<K, A, Leaf, Switch>(&mut self,
                                   graph: &Graph<Leaf, Switch>,
                                   knowledge: K)
                                   -> Result<A>
        where K: Copy,
              Leaf: LeafFn<K, A>,
              Switch: SwitchFn<K>
    {

        if self.yielding {
            return Err(Error::Yielding);
        }

        if let Some(truncate) = try!(Self::resolve_switches(&mut self.stack, graph, knowledge)) {
            let frame = try!(graph.create_stack_frame(truncate.index));
            self.stack.truncate(truncate.len);
            self.stack.push(frame);
        }

        loop {
            let resolution = {
                let frame = try!(self.current_frame());
                try!(graph.resolve_frame(frame, knowledge))
            };

            if let Some(action) = try!(Self::apply_resolution(&mut self.stack, graph, resolution)) {
                self.yielding = true;
                return Ok(action);
            }
        }
    }

    pub fn declare_return(&mut self, value: bool) -> Result<()> {
        if !self.yielding {
            return Err(Error::NotYielding);
        }

        self.yielding = false;

        if let Some(frame) = self.stack.last_mut() {
            frame.handle_return(value)
        } else {
            Err(Error::StackEmpty)
        }
    }
}
<commit_msg>Replaced try with ? in behaviour implementation<commit_after>use std::result;

#[derive(Debug)]
pub enum Error {
    UnknownNodeIndex,
    NodeStateMismatch,
    UnexpectedNodeType,
    StackEmpty,
    StackNotEmpty,
    Yielding,
    NotYielding,
}

pub type Result<T> = result::Result<T, Error>;

pub type NodeIndex = usize;

pub enum LeafResolution<A> {
    Return(bool),
    Yield(A),
}

pub enum SwitchResolution {
    Select(NodeIndex),
    Reset(NodeIndex),
}

pub enum SwitchReturn {
    Return(bool),
    Select(NodeIndex),
}

enum StepResolution<A> {
    Yield(A),
    Call(NodeIndex),
    Return(bool),
}

enum RunResolution<A> {
    Yield(A),
    Return(bool),
}

enum Resolution<A> {
    Return(bool),
    Call(NodeIndex),
    Yield(A),
}

pub trait LeafFn<K, A> {
    fn call(&self, knowledge: K) -> LeafResolution<A>;
}

pub trait SwitchFn<K> {
    fn call(&self, knowledge: K) -> SwitchResolution;
    fn return_to(&self, value: bool) -> SwitchReturn;
}

enum Node<Leaf, Switch> {
    Leaf(Leaf),
    Collection(CollectionNode),
    Switch(Switch),
}

pub enum CollectionNode {
    Forever(NodeIndex),
    All(Vec<NodeIndex>),
}

#[derive(Clone, Serialize, Deserialize)]
struct ArrayTraverse {
    index: usize,
    length: usize,
    value: bool,
}

#[derive(Clone, Serialize, Deserialize)]
enum CollectionState {
    Forever,
    All(ArrayTraverse),
}

#[derive(Clone, Serialize, Deserialize)]
enum StackFrame {
    Leaf {
        index: NodeIndex,
        value: Option<bool>,
    },
    Collection {
        index: NodeIndex,
        state: CollectionState,
    },
    Switch {
        index: NodeIndex,
        child: Option<NodeIndex>,
        value: Option<bool>,
    },
}

struct StackTruncate {
    len: usize,
    index: NodeIndex,
}

pub struct Graph<Leaf, Switch> {
    nodes: Vec<Node<Leaf, Switch>>,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct State {
    stack: Vec<StackFrame>,
    yielding: bool,
}

impl StackTruncate {
    fn new(len: usize, index: NodeIndex) -> Self {
        StackTruncate {
            len: len,
            index: index,
        }
    }
}

impl SwitchResolution {
    fn to_index(&self) -> NodeIndex {
        match self {
            &SwitchResolution::Select(index) => index,
            &SwitchResolution::Reset(index) => index,
        }
    }
}

impl<A> RunResolution<A> {
    fn to_step_resolution(self) -> StepResolution<A> {
        match self {
            RunResolution::Return(value) => StepResolution::Return(value),
            RunResolution::Yield(action) => StepResolution::Yield(action),
        }
    }
}

impl ArrayTraverse {
    fn new(length: usize, value: bool) -> Self {
        ArrayTraverse {
            index: 0,
            length: length,
            value: value,
        }
    }

    fn next_index(&mut self) -> Option<usize> {
        if self.index < self.length {
            let index = self.index;
            self.index += 1;
            Some(index)
        } else {
            None
        }
    }
}

impl CollectionNode {
    fn create_state(&self) -> CollectionState {
        match self {
            &CollectionNode::Forever(_) => CollectionState::Forever,
            &CollectionNode::All(ref v) => CollectionState::All(ArrayTraverse::new(v.len(), false)),
        }
    }

    fn next_index(&self, state: &mut CollectionState) -> Result<Option<NodeIndex>> {
        match (self, state) {
            (&CollectionNode::Forever(index), &mut CollectionState::Forever) => Ok(Some(index)),
            (&CollectionNode::All(ref indices), &mut CollectionState::All(ref mut at)) => {
                Ok(at.next_index().map(|i| indices[i]))
            }
            _ => Err(Error::NodeStateMismatch),
        }
    }
}

impl CollectionState {
    fn handle_return(&mut self, value: bool) {
        match self {
            &mut CollectionState::All(ref mut at) => at.value = value,
            _ => {}
        }
    }

    fn return_value(&self) -> bool {
        match self {
            &CollectionState::All(ref at) => at.value,
            _ => panic!("impossible return"),
        }
    }
}

impl<A> LeafResolution<A> {
    fn to_resolution<'a>(self) -> Resolution<A> {
        match self {
            LeafResolution::Return(value) => Resolution::Return(value),
            LeafResolution::Yield(action) => Resolution::Yield(action),
        }
    }
}

impl StackFrame {
    fn handle_return(&mut self, return_value: bool) -> Result<()> {
        match self {
            &mut StackFrame::Leaf { ref mut value, .. } => {
                *value = Some(return_value);
            }
            &mut StackFrame::Collection { ref mut state, .. } => {
                state.handle_return(return_value);
            }
            &mut StackFrame::Switch { ref mut value, .. } => {
                *value = Some(return_value);
            }
        }

        Ok(())
    }
}

impl<Leaf, Switch> Node<Leaf, Switch> {
    fn leaf_fn(&self) -> Result<&Leaf> {
        if let &Node::Leaf(ref f) = self {
            Ok(f)
        } else {
            Err(Error::UnexpectedNodeType)
        }
    }

    fn collection_node(&self) -> Result<&CollectionNode> {
        if let &Node::Collection(ref n) = self {
            Ok(n)
        } else {
            Err(Error::UnexpectedNodeType)
        }
    }

    fn switch_fn(&self) -> Result<&Switch> {
        if let &Node::Switch(ref f) = self {
            Ok(f)
        } else {
            Err(Error::UnexpectedNodeType)
        }
    }
}

impl<Leaf, Switch> Graph<Leaf, Switch> {
    pub fn new() -> Self {
        Graph { nodes: Vec::new() }
    }

    fn add_node(&mut self, node: Node<Leaf, Switch>) -> NodeIndex {
        let index = self.nodes.len();
        self.nodes.push(node);
        return index;
    }

    pub fn add_leaf(&mut self, leaf: Leaf) -> NodeIndex {
        self.add_node(Node::Leaf(leaf))
    }

    pub fn add_collection(&mut self, collection: CollectionNode) -> NodeIndex {
        self.add_node(Node::Collection(collection))
    }

    pub fn add_switch(&mut self, switch: Switch) -> NodeIndex {
        self.add_node(Node::Switch(switch))
    }

    fn node(&self, index: NodeIndex) -> Result<&Node<Leaf, Switch>> {
        if index < self.nodes.len() {
            Ok(&self.nodes[index])
        } else {
            Err(Error::UnknownNodeIndex)
        }
    }

    fn create_stack_frame(&self, index: NodeIndex) -> Result<StackFrame> {
        match self.node(index)? {
            &Node::Leaf(_) => {
                Ok(StackFrame::Leaf {
                    index: index,
                    value: None,
                })
            }
            &Node::Collection(ref c) => {
                Ok(StackFrame::Collection {
                    index: index,
                    state: c.create_state(),
                })
            }
            &Node::Switch(_) => {
                Ok(StackFrame::Switch {
                    index: index,
                    child: None,
                    value: None,
                })
            }
        }
    }

    fn resolve_frame<K, A>(&self, frame: &mut StackFrame, knowledge: K) -> Result<Resolution<A>>
        where Leaf: LeafFn<K, A>,
              Switch: SwitchFn<K>
    {
        match frame {
            &mut StackFrame::Leaf { index, value } => {
                if let Some(value) = value {
                    Ok(Resolution::Return(value))
                } else {
                    let node = self.node(index)?;
                    let f = node.leaf_fn()?;
                    Ok(f.call(knowledge).to_resolution())
                }
            }
            &mut StackFrame::Collection { index, ref mut state } => {
                let node = self.node(index)?;
                let collection_node = node.collection_node()?;
                if let Some(child_index) = collection_node.next_index(state)? {
                    Ok(Resolution::Call(child_index))
                } else {
                    Ok(Resolution::Return(state.return_value()))
                }
            }
            &mut StackFrame::Switch { index, ref mut child, value } => {
                let switch_fn = self.node(index)?.switch_fn()?;
                Ok(if let Some(value) = value {
                    match switch_fn.return_to(value) {
                        SwitchReturn::Return(return_value) => Resolution::Return(return_value),
                        SwitchReturn::Select(index) => Resolution::Call(index),
                    }
                } else {
                    let index = switch_fn.call(knowledge).to_index();
                    *child = Some(index);
                    Resolution::Call(index)
                })
            }
        }
    }
}
impl State {
    pub fn new() -> Self {
        State {
            stack: Vec::new(),
            yielding: false,
        }
    }

    pub fn is_initialised(&self) -> bool {
        !self.stack.is_empty()
    }

    pub fn initialise<Leaf, Switch>(&mut self,
                                    graph: &Graph<Leaf, Switch>,
                                    index: NodeIndex)
                                    -> Result<()> {

        if !self.stack.is_empty() {
            return Err(Error::StackNotEmpty);
        }

        let frame = graph.create_stack_frame(index)?;
        self.stack.push(frame);

        Ok(())
    }

    fn current_frame(&mut self) -> Result<&mut StackFrame> {
        if let Some(frame) = self.stack.last_mut() {
            Ok(frame)
        } else {
            Err(Error::StackEmpty)
        }
    }

    fn apply_resolution<K, A, Leaf, Switch>(stack: &mut Vec<StackFrame>,
                                            graph: &Graph<Leaf, Switch>,
                                            resolution: Resolution<A>)
                                            -> Result<Option<A>>
        where Leaf: LeafFn<K, A>
    {
        match resolution {
            Resolution::Call(index) => {
                stack.push(graph.create_stack_frame(index)?);
            }
            Resolution::Return(value) => {
                if stack.pop().is_none() {
                    return Err(Error::StackEmpty);
                }

                if let Some(frame) = stack.last_mut() {
                    frame.handle_return(value)?;
                }
            }
            Resolution::Yield(action) => return Ok(Some(action)),
        }

        Ok(None)
    }

    fn resolve_switches<K, Leaf, Switch>(stack: &mut Vec<StackFrame>,
                                         graph: &Graph<Leaf, Switch>,
                                         knowledge: K)
                                         -> Result<Option<StackTruncate>>
        where K: Copy,
              Switch: SwitchFn<K>
    {
        let mut i = 0;
        for frame in stack {
            if let &mut StackFrame::Switch { index, ref mut child, .. } = frame {
                if let Some(current) = child.as_mut() {
                    let switch = graph.node(index)?.switch_fn()?;
                    let resolution = switch.call(knowledge);
                    match resolution {
                        SwitchResolution::Select(child_index) => {
                            if child_index != *current {
                                *current = child_index;
                                return Ok(Some(StackTruncate::new(i + 1, child_index)));
                            }
                        }
                        SwitchResolution::Reset(child_index) => {
                            *current = child_index;
                            return Ok(Some(StackTruncate::new(i + 1, child_index)));
                        }
                    }
                }
            }

            i += 1;
        }

        Ok(None)
    }

    pub fn run<K, A, Leaf, Switch>(&mut self,
                                   graph: &Graph<Leaf, Switch>,
                                   knowledge: K)
                                   -> Result<A>
        where K: Copy,
              Leaf: LeafFn<K, A>,
              Switch: SwitchFn<K>
    {

        if self.yielding {
            return Err(Error::Yielding);
        }

        if let Some(truncate) = Self::resolve_switches(&mut self.stack, graph, knowledge)? {
            let frame = graph.create_stack_frame(truncate.index)?;
            self.stack.truncate(truncate.len);
            self.stack.push(frame);
        }

        loop {
            let resolution = {
                let frame = self.current_frame()?;
                graph.resolve_frame(frame, knowledge)?
            };

            if let Some(action) = Self::apply_resolution(&mut self.stack, graph, resolution)? {
                self.yielding = true;
                return Ok(action);
            }
        }
    }

    pub fn declare_return(&mut self, value: bool) -> Result<()> {
        if !self.yielding {
            return Err(Error::NotYielding);
        }

        self.yielding = false;

        if let Some(frame) = self.stack.last_mut() {
            frame.handle_return(value)
        } else {
            Err(Error::StackEmpty)
        }
    }
}
<|endoftext|>


<commit_before>// Rust Bitcoin Library
// Written in 2014 by
//   Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # Blockdata constants
//!
//! This module provides various constants relating to the blockchain and
//! consensus code. In particular, it defines the genesis block and its
//! single transaction
//!

use std::default::Default;
use std::num::from_u64;

use blockdata::opcodes;
use blockdata::script::Script;
use blockdata::transaction::{Transaction, TxOut, TxIn};
use blockdata::block::{Block, BlockHeader};
use network::constants::{Network, Bitcoin, BitcoinTestnet};
use util::misc::hex_bytes;
use util::hash::MerkleRoot;
use util::uint::Uint256;

pub static MAX_SEQUENCE: u32 = 0xFFFFFFFF;
pub static COIN_VALUE: u64 = 100000000;
pub static TARGET_BLOCK_SPACING: u32 = 600;
pub static DIFFCHANGE_INTERVAL: u32 = 2016;
pub static DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600;

/// In Bitcoind this is insanely described as ~((u256)0 >> 32)
pub fn max_target(_: Network) -> Uint256 {
  from_u64::<Uint256>(0xFFFF).unwrap() << 208u
}

/// Constructs and returns the coinbase (and only) transaction of the Bitcoin genesis block
fn bitcoin_genesis_tx() -> Transaction {
  // Base
  let mut ret = Transaction {
    version: 1,
    lock_time: 0,
    input: vec![],
    output: vec![]
  };

  // Inputs
  let mut in_script = Script::new();
  in_script.push_scriptint(486604799);
  in_script.push_scriptint(4);
  in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
  ret.input.push(TxIn {
    prev_hash: Default::default(),
    prev_index: 0xFFFFFFFF,
    script_sig: in_script,
    sequence: MAX_SEQUENCE
  });

  // Outputs
  let mut out_script = Script::new();
  out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
  out_script.push_opcode(opcodes::all::OP_CHECKSIG);
  ret.output.push(TxOut {
    value: 50 * COIN_VALUE,
    script_pubkey: out_script
  });

  // end
  ret
}

/// Constructs and returns the genesis block
pub fn genesis_block(network: Network) -> Block {
  match network {
    Bitcoin => {
      let txdata = vec![bitcoin_genesis_tx()];
      Block {
        header: BlockHeader {
          version: 1,
          prev_blockhash: Default::default(),
          merkle_root: txdata.merkle_root(),
          time: 1231006505,
          bits: 0x1d00ffff,
          nonce: 2083236893
        },
        txdata: txdata
      }
    }
    BitcoinTestnet => {
      let txdata = vec![bitcoin_genesis_tx()];
      Block {
        header: BlockHeader {
          version: 1,
          prev_blockhash: Default::default(),
          merkle_root: txdata.merkle_root(),
          time: 1296688602,
          bits: 0x1d00ffff,
          nonce: 414098458
        },
        txdata: txdata
      }
    }
  }
}

#[cfg(test)]
mod test {
  use std::default::Default;
  use serialize::hex::FromHex;

  use network::constants::{Bitcoin, BitcoinTestnet};
  use network::serialize::{BitcoinHash, serialize};
  use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
  use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};

  #[test]
  fn bitcoin_genesis_first_transaction() {
    let gen = bitcoin_genesis_tx();

    assert_eq!(gen.version, 1);
    assert_eq!(gen.input.len(), 1);
    assert_eq!(gen.input[0].prev_hash, Default::default());
    assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
    assert_eq!(serialize(&gen.input[0].script_sig),
               Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));

    assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
    assert_eq!(gen.output.len(), 1);
    assert_eq!(serialize(&gen.output[0].script_pubkey),
               Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
    assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
    assert_eq!(gen.lock_time, 0);

    assert_eq!(gen.bitcoin_hash().be_hex_string(),
               "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
  }

  #[test]
  fn bitcoin_genesis_full_block() {
    let gen = genesis_block(Bitcoin);

    assert_eq!(gen.header.version, 1);
    assert_eq!(gen.header.prev_blockhash, Default::default());
    assert_eq!(gen.header.merkle_root.be_hex_string(),
               "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
    assert_eq!(gen.header.time, 1231006505);
    assert_eq!(gen.header.bits, 0x1d00ffff);
    assert_eq!(gen.header.nonce, 2083236893);
    assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
               "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
  }

  #[test]
  fn testnet_genesis_full_block() {
    let gen = genesis_block(BitcoinTestnet);
    assert_eq!(gen.header.version, 1);
    assert_eq!(gen.header.prev_blockhash, Default::default());
    assert_eq!(gen.header.merkle_root.be_hex_string(),
               "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
    assert_eq!(gen.header.time, 1296688602);
    assert_eq!(gen.header.bits, 0x1d00ffff);
    assert_eq!(gen.header.nonce, 414098458);
    assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
               "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
  }
}

<commit_msg>Add blockdata::constants::max_money<commit_after>// Rust Bitcoin Library
// Written in 2014 by
//   Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # Blockdata constants
//!
//! This module provides various constants relating to the blockchain and
//! consensus code. In particular, it defines the genesis block and its
//! single transaction
//!

use std::default::Default;
use std::num::from_u64;

use blockdata::opcodes;
use blockdata::script::Script;
use blockdata::transaction::{Transaction, TxOut, TxIn};
use blockdata::block::{Block, BlockHeader};
use network::constants::{Network, Bitcoin, BitcoinTestnet};
use util::misc::hex_bytes;
use util::hash::MerkleRoot;
use util::uint::Uint256;

pub static MAX_SEQUENCE: u32 = 0xFFFFFFFF;
pub static COIN_VALUE: u64 = 100_000_000;
pub static TARGET_BLOCK_SPACING: u32 = 600;
pub static DIFFCHANGE_INTERVAL: u32 = 2016;
pub static DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600;

/// In Bitcoind this is insanely described as ~((u256)0 >> 32)
pub fn max_target(_: Network) -> Uint256 {
  from_u64::<Uint256>(0xFFFF).unwrap() << 208u
}

/// The maximum value allowed in an output (useful for sanity checking,
/// since keeping everything below this value should prevent overflows
/// if you are doing anything remotely sane with monetary values).
pub fn max_money(_: Network) -> u64 {
  21_000_000 * COIN_VALUE
}

/// Constructs and returns the coinbase (and only) transaction of the Bitcoin genesis block
fn bitcoin_genesis_tx() -> Transaction {
  // Base
  let mut ret = Transaction {
    version: 1,
    lock_time: 0,
    input: vec![],
    output: vec![]
  };

  // Inputs
  let mut in_script = Script::new();
  in_script.push_scriptint(486604799);
  in_script.push_scriptint(4);
  in_script.push_slice("The Times 03/Jan/2009 Chancellor on brink of second bailout for banks".as_bytes());
  ret.input.push(TxIn {
    prev_hash: Default::default(),
    prev_index: 0xFFFFFFFF,
    script_sig: in_script,
    sequence: MAX_SEQUENCE
  });

  // Outputs
  let mut out_script = Script::new();
  out_script.push_slice(hex_bytes("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f").unwrap().as_slice());
  out_script.push_opcode(opcodes::all::OP_CHECKSIG);
  ret.output.push(TxOut {
    value: 50 * COIN_VALUE,
    script_pubkey: out_script
  });

  // end
  ret
}

/// Constructs and returns the genesis block
pub fn genesis_block(network: Network) -> Block {
  match network {
    Bitcoin => {
      let txdata = vec![bitcoin_genesis_tx()];
      Block {
        header: BlockHeader {
          version: 1,
          prev_blockhash: Default::default(),
          merkle_root: txdata.merkle_root(),
          time: 1231006505,
          bits: 0x1d00ffff,
          nonce: 2083236893
        },
        txdata: txdata
      }
    }
    BitcoinTestnet => {
      let txdata = vec![bitcoin_genesis_tx()];
      Block {
        header: BlockHeader {
          version: 1,
          prev_blockhash: Default::default(),
          merkle_root: txdata.merkle_root(),
          time: 1296688602,
          bits: 0x1d00ffff,
          nonce: 414098458
        },
        txdata: txdata
      }
    }
  }
}

#[cfg(test)]
mod test {
  use std::default::Default;
  use serialize::hex::FromHex;

  use network::constants::{Bitcoin, BitcoinTestnet};
  use network::serialize::{BitcoinHash, serialize};
  use blockdata::constants::{genesis_block, bitcoin_genesis_tx};
  use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE};

  #[test]
  fn bitcoin_genesis_first_transaction() {
    let gen = bitcoin_genesis_tx();

    assert_eq!(gen.version, 1);
    assert_eq!(gen.input.len(), 1);
    assert_eq!(gen.input[0].prev_hash, Default::default());
    assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF);
    assert_eq!(serialize(&gen.input[0].script_sig),
               Ok("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73".from_hex().unwrap()));

    assert_eq!(gen.input[0].sequence, MAX_SEQUENCE);
    assert_eq!(gen.output.len(), 1);
    assert_eq!(serialize(&gen.output[0].script_pubkey),
               Ok("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac".from_hex().unwrap()));
    assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
    assert_eq!(gen.lock_time, 0);

    assert_eq!(gen.bitcoin_hash().be_hex_string(),
               "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
  }

  #[test]
  fn bitcoin_genesis_full_block() {
    let gen = genesis_block(Bitcoin);

    assert_eq!(gen.header.version, 1);
    assert_eq!(gen.header.prev_blockhash, Default::default());
    assert_eq!(gen.header.merkle_root.be_hex_string(),
               "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
    assert_eq!(gen.header.time, 1231006505);
    assert_eq!(gen.header.bits, 0x1d00ffff);
    assert_eq!(gen.header.nonce, 2083236893);
    assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
               "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f".to_string());
  }

  #[test]
  fn testnet_genesis_full_block() {
    let gen = genesis_block(BitcoinTestnet);
    assert_eq!(gen.header.version, 1);
    assert_eq!(gen.header.prev_blockhash, Default::default());
    assert_eq!(gen.header.merkle_root.be_hex_string(),
               "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b".to_string());
    assert_eq!(gen.header.time, 1296688602);
    assert_eq!(gen.header.bits, 0x1d00ffff);
    assert_eq!(gen.header.nonce, 414098458);
    assert_eq!(gen.header.bitcoin_hash().be_hex_string(),
               "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943".to_string());
  }
}

<|endoftext|>