<commit_before>use std::any::TypeId;

use crate::{
    css_engine::*,
    prelude::*,
    utils::{Brush, String16, Thickness},
};

use dces::prelude::{Component, Entity, EntityComponentManager};

/// The `WidgetContainer` wraps the entity of a widget and provides access to its properties, its children properties and its parent properties.
pub struct WidgetContainer<'a> {
    ecm: &'a mut EntityComponentManager<Tree, StringComponentStore>,
    current_node: Entity,
    theme: &'a ThemeValue,
}

impl<'a> WidgetContainer<'a> {
    /// Creates a new widget container for the given `entity`.
    pub fn new(
        root: Entity,
        ecm: &'a mut EntityComponentManager<Tree, StringComponentStore>,
        theme: &'a ThemeValue,
    ) -> Self {
        WidgetContainer {
            ecm,
            current_node: root,
            theme,
        }
    }

    /// Gets the entity of the widget.
    pub fn entity(&self) -> Entity {
        self.current_node
    }

    /// Gets the property.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn get
(&self, key: &str) -> &P
    where
        P: Clone + Component,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return property;
        }

        let name = self
            .ecm
            .component_store()
            .get::<String>("name", self.current_node)
            .unwrap()
            .clone();

        panic!(
        "Widget with name: {} and entity: {} does not contain property width type_id {:?} for key: {}",
        name,
        self.current_node.0,
        TypeId::of::
(),
        key
    );
    }

    /// Gets a mutable reference of the property of type `P`.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn get_mut
(&mut self, key: &str) -> &mut P
    where
        P: Clone + Component,
    {
        if let Ok(property) = self
            .ecm
            .component_store_mut()
            .get_mut::
(key, self.current_node)
        {
            return property;
        }

        panic!(
            "Entity {} does not contain property type {:?}, with key: {}",
            self.current_node.0,
            TypeId::of::
(),
            key
        );
    }

    /// Clones the property. If the property does not exists for the widget the
    /// default of the property value will be returned,
    pub fn clone_or_default
(&self, key: &str) -> P
    where
        P: Clone + Component + Default,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return property.clone();
        }

        P::default()
    }

    /// Clones the property.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn clone
(&self, key: &str) -> P
    where
        P: Clone + Component,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return property.clone();
        }

        let name = self
            .ecm
            .component_store()
            .get::<String>("name", self.current_node)
            .unwrap()
            .clone();

        panic!(
        "Widget with name: {} and entity: {} does not contain property width type_id {:?} for key: {}",
        name,
        self.current_node.0,
        TypeId::of::
(),
        key
    );
    }

    /// Clones the property of type `P` from the given widget entity. If the entity does
    /// not exists or it doesn't have a component of type `P` `None` will be returned.
    pub fn try_clone
(&self, key: &str) -> Option

    where
        P: Clone + Component,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return Some(property.clone());
        }

        None
    }

    /// Sets the property of type `P`.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn set
(&mut self, key: &str, value: P)
    where
        P: Component + Clone,
    {
        if let Ok(property) = self
            .ecm
            .component_store_mut()
            .get_mut::
(key, self.current_node)
        {
            *property = value;
            return;
        }

        let name = self
            .ecm
            .component_store()
            .get::<String>("name", self.current_node)
            .unwrap()
            .clone();

        panic!(
            "Widget with name: {} and entity: {} does not contain property width type_id {:?} for key: {}",
            name,
            self.current_node.0,
            TypeId::of::
(),
            key
        );
    }

    /// Returns `true` if the widget has a property of type `P` otherwise `false`.
    pub fn has
(&self, key: &str) -> bool
    where
        P: Clone + Component,
    {
        self.ecm
            .component_store()
            .get::
(key, self.current_node)
            .is_ok()
    }

    /// Returns a reference of a property of type `P` from the given widget entity. If the entity does
    /// not exists or it doesn't have a component of type `P` `None` will be returned.
    pub fn try_get
(&self, key: &str) -> Option<&P> {
        self.ecm
            .component_store()
            .get::
(key, self.current_node)
            .ok()
    }

    /// Returns a mutable reference of a property of type `P` from the given widget entity. If the entity does
    /// not exists or it doesn't have a component of type `P` `None` will be returned.
    pub fn try_get_mut
(&mut self, key: &str) -> Option<&mut P> {
        self.ecm
            .component_store_mut()
            .get_mut::
(key, self.current_node)
            .ok()
    }

    /// Checks if the given value is equal to the given property.
    pub fn eq
(&self, key: &str, other: &P) -> bool {
        if let Some(value) = self.try_get::
(key) {
            return value.eq(other);
        }

        false
    }

    fn update_internal_theme_by_state(&mut self, force: bool, entity: &Entity) {
        for child in &(self.ecm.entity_store().children.clone())[&entity] {
            self.update_internal_theme_by_state(force, child);
        }

        self.current_node = *entity;

        if let Some(selector) = self.try_clone::<Selector>("selector") {
            let mut update = false;

            if let Some(focus) = self.try_clone::<bool>("focused") {
                if focus && !selector.pseudo_classes.contains("focus") {
                    add_selector_to_widget("focus", self);
                    update = true;
                } else if !focus && selector.pseudo_classes.contains("focus") {
                    remove_selector_from_widget("focus", self);
                    update = true;
                }
            }

            if let Some(selected) = self.try_clone::<bool>("selected") {
                if selected && !selector.pseudo_classes.contains("selected") {
                    add_selector_to_widget("selected", self);
                    update = true;
                } else if !selected && selector.pseudo_classes.contains("selected") {
                    remove_selector_from_widget("selected", self);
                    update = true;
                }
            }

            if let Some(pressed) = self.try_clone::<bool>("pressed") {
                if pressed && !selector.pseudo_classes.contains("active") {
                    add_selector_to_widget("active", self);
                    update = true;
                } else if !pressed && selector.pseudo_classes.contains("active") {
                    remove_selector_from_widget("active", self);
                    update = true;
                }
            }

            if let Some(enabled) = self.try_clone::<bool>("enabled") {
                if !enabled && !selector.pseudo_classes.contains("disabled") {
                    add_selector_to_widget("disabled", self);
                    update = true;
                } else if enabled && selector.pseudo_classes.contains("disabled") {
                    remove_selector_from_widget("disabled", self);
                    update = true;
                }
            }

            if let Some(text) = self.try_clone::<String16>("text") {
                if text.is_empty() && !selector.pseudo_classes.contains("empty") {
                    add_selector_to_widget("empty", self);
                    update = true;
                } else if !text.is_empty() && selector.pseudo_classes.contains("empty") {
                    remove_selector_from_widget("empty", self);
                    update = true;
                }
            }

            if let Some(expanded) = self.try_clone::<bool>("expanded") {
                if expanded && !selector.pseudo_classes.contains("expanded") {
                    add_selector_to_widget("expanded", self);
                    update = true;
                } else if !expanded && selector.pseudo_classes.contains("expanded") {
                    remove_selector_from_widget("expanded", self);
                    update = true;
                }
            }

            if update || force {
                self.update_properties_by_theme();
            }
        }
    }

    /// Updates the theme by the inner state e.g. `selected` or `pressed`.
    pub fn update_theme_by_state(&mut self, force: bool) {
        self.update_internal_theme_by_state(force, &(self.current_node.clone()));
    }

    /// Update all properties for the theme.
    pub fn update_properties_by_theme(&mut self) {
        if !self.has::<Selector>("selector") {
            return;
        }

        let selector = self.clone::<Selector>("selector");

        if !selector.dirty() {
            return;
        }

        if self.has::<Brush>("foreground") {
            if let Some(color) = self.theme.brush("color", &selector) {
                self.set::<Brush>("foreground", color);
            }
        }

        if self.has::<Brush>("background") {
            if let Some(background) = self.theme.brush("background", &selector) {
                self.set::<Brush>("background", background);
            }
        }

        if self.has::<Brush>("border_brush") {
            if let Some(border_brush) = self.theme.brush("border-color", &selector) {
                self.set::<Brush>("border_brush", border_brush);
            }
        }

        if self.has::<f64>("border_radius") {
            if let Some(radius) = self.theme.uint("border-radius", &selector) {
                self.set::<f64>("border_radius", f64::from(radius));
            }
        }

        if self.has::<f32>("opacity") {
            if let Some(opacity) = self.theme.float("opacity", &selector) {
                self.set::<f32>("opacity", opacity);
            }
        }

        if self.has::<Thickness>("border_width") {
            if let Some(border_width) = self.theme.uint("border-width", &selector) {
                self.set::<Thickness>("border_width", Thickness::from(border_width as f64));
            }
        }

        self.update_font_properties_by_theme(&selector);

        if let Some(mut padding) = self.try_clone::<Thickness>("padding") {
            if let Some(pad) = self.theme.uint("padding", &selector) {
                padding.set_thickness(pad as f64);
            }

            if let Some(left) = self.theme.uint("padding-left", &selector) {
                padding.set_left(left as f64);
            }

            if let Some(top) = self.theme.uint("padding-top", &selector) {
                padding.set_top(top as f64);
            }

            if let Some(right) = self.theme.uint("padding-right", &selector) {
                padding.set_right(right as f64);
            }

            if let Some(bottom) = self.theme.uint("padding-bottom", &selector) {
                padding.set_bottom(bottom as f64);
            }
            self.set::<Thickness>("padding", padding);
        }

        if let Some(mut constraint) = self.try_clone::<Constraint>("constraint") {
            if let Some(width) = self.theme.uint("width", &selector) {
                constraint.set_width(width as f64);
            }

            if let Some(height) = self.theme.uint("height", &selector) {
                constraint.set_height(height as f64);
            }

            if let Some(min_width) = self.theme.uint("min-width", &selector) {
                constraint.set_min_width(min_width as f64);
            }

            if let Some(min_height) = self.theme.uint("min-height", &selector) {
                constraint.set_min_height(min_height as f64);
            }

            if let Some(max_width) = self.theme.uint("max-width", &selector) {
                constraint.set_max_width(max_width as f64);
            }

            if let Some(max_height) = self.theme.uint("max-height", &selector) {
                constraint.set_max_height(max_height as f64);
            }

            self.set::<Constraint>("constraint", constraint);
        }

        if self.has::<f64>("spacing") {
            if let Some(spacing) = self.theme.uint("spacing", &selector) {
                self.set::<f64>("spacing", spacing.into());
            }
        }

        self.get_mut::<Selector>("selector").set_dirty(true);
    }

    pub fn update_font_properties_by_theme(&mut self, selector: &Selector) {
        if self.has::<f64>("font_size") {
            if let Some(size) = self.theme.uint("font-size", selector) {
                self.set::<f64>("font_size", f64::from(size));
            }
        }

        if self.has::<String>("font_family") {
            if let Some(font_family) = self.theme.string("font-family", selector) {
                self.set::<String>("font_family", font_family);
            }
        }

        if self.has::<Brush>("icon_brush") {
            if let Some(color) = self.theme.brush("icon-color", selector) {
                self.set::<Brush>("icon_brush", color);
            }
        }

        if self.has::<f64>("icon_size") {
            if let Some(size) = self.theme.uint("icon-size", selector) {
                self.set::<f64>("icon_size", f64::from(size));
            }
        }

        if self.has::<String>("icon_family") {
            if let Some(font_family) = self.theme.string("icon-family", selector) {
                self.set::<String>("icon_family", font_family);
            }
        }
    }
}
<commit_msg>Added get_name() fn<commit_after>use std::any::TypeId;

use crate::{
    css_engine::*,
    prelude::*,
    utils::{Brush, String16, Thickness},
};

use dces::prelude::{Component, Entity, EntityComponentManager};

/// The `WidgetContainer` wraps the entity of a widget and provides access to its properties, its children properties and its parent properties.
pub struct WidgetContainer<'a> {
    ecm: &'a mut EntityComponentManager<Tree, StringComponentStore>,
    current_node: Entity,
    theme: &'a ThemeValue,
}

impl<'a> WidgetContainer<'a> {
    /// Creates a new widget container for the given `entity`.
    pub fn new(
        root: Entity,
        ecm: &'a mut EntityComponentManager<Tree, StringComponentStore>,
        theme: &'a ThemeValue,
    ) -> Self {
        WidgetContainer {
            ecm,
            current_node: root,
            theme,
        }
    }

    /// Gets the entity of the widget.
    pub fn entity(&self) -> Entity {
        self.current_node
    }

    /// Gets the property.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn get
(&self, key: &str) -> &P
    where
        P: Clone + Component,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return property;
        }

       let name = self.get_name();

       panic!(
       "Widget with name: {} and entity: {} does not contain property width type_id {:?} for key: {}",
       name,
       self.current_node.0,
       TypeId::of::
(),
       key
    );
    }

    /// Gets a mutable reference of the property of type `P`.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn get_mut
(&mut self, key: &str) -> &mut P
    where
        P: Clone + Component,
    {
        if let Ok(property) = self
            .ecm
            .component_store_mut()
            .get_mut::
(key, self.current_node)
        {
            return property;
        }

        panic!(
            "Entity {} does not contain property type {:?}, with key: {}",
            self.current_node.0,
            TypeId::of::
(),
            key
        );
    }

    /// Clones the property. If the property does not exists for the widget the
    /// default of the property value will be returned,
    pub fn clone_or_default
(&self, key: &str) -> P
    where
        P: Clone + Component + Default,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return property.clone();
        }

        P::default()
    }

    /// Clones the property.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn clone
(&self, key: &str) -> P
    where
        P: Clone + Component,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return property.clone();
        }

        let name = self.get_name();

        panic!(
        "Widget with name: {} and entity: {} does not contain property width type_id {:?} for key: {}",
        name,
        self.current_node.0,
        TypeId::of::
(),
        key
    );
    }

    /// Clones the property of type `P` from the given widget entity. If the entity does
    /// not exists or it doesn't have a component of type `P` `None` will be returned.
    pub fn try_clone
(&self, key: &str) -> Option

    where
        P: Clone + Component,
    {
        if let Ok(property) = self.ecm.component_store().get::
(key, self.current_node) {
            return Some(property.clone());
        }

        None
    }

    /// Sets the property of type `P`.
    ///
    /// # Panics
    ///
    /// Panics if the widget does not contains the property.
    pub fn set
(&mut self, key: &str, value: P)
    where
        P: Component + Clone,
    {
        if let Ok(property) = self
            .ecm
            .component_store_mut()
            .get_mut::
(key, self.current_node)
        {
            *property = value;
            return;
        }

        let name = self.get_name();

        panic!(
            "Widget with name: {} and entity: {} does not contain property width type_id {:?} for key: {}",
            name,
            self.current_node.0,
            TypeId::of::
(),
            key
        );
    }

    /// Returns `true` if the widget has a property of type `P` otherwise `false`.
    pub fn has
(&self, key: &str) -> bool
    where
        P: Clone + Component,
    {
        self.ecm
            .component_store()
            .get::
(key, self.current_node)
            .is_ok()
    }

    /// Returns a reference of a property of type `P` from the given widget entity. If the entity does
    /// not exists or it doesn't have a component of type `P` `None` will be returned.
    pub fn try_get
(&self, key: &str) -> Option<&P> {
        self.ecm
            .component_store()
            .get::
(key, self.current_node)
            .ok()
    }

    /// Returns a mutable reference of a property of type `P` from the given widget entity. If the entity does
    /// not exists or it doesn't have a component of type `P` `None` will be returned.
    pub fn try_get_mut
(&mut self, key: &str) -> Option<&mut P> {
        self.ecm
            .component_store_mut()
            .get_mut::
(key, self.current_node)
            .ok()
    }

    /// Checks if the given value is equal to the given property.
    pub fn eq
(&self, key: &str, other: &P) -> bool {
        if let Some(value) = self.try_get::
(key) {
            return value.eq(other);
        }

        false
    }

    fn update_internal_theme_by_state(&mut self, force: bool, entity: &Entity) {
        for child in &(self.ecm.entity_store().children.clone())[&entity] {
            self.update_internal_theme_by_state(force, child);
        }

        self.current_node = *entity;

        if let Some(selector) = self.try_clone::<Selector>("selector") {
            let mut update = false;

            if let Some(focus) = self.try_clone::<bool>("focused") {
                if focus && !selector.pseudo_classes.contains("focus") {
                    add_selector_to_widget("focus", self);
                    update = true;
                } else if !focus && selector.pseudo_classes.contains("focus") {
                    remove_selector_from_widget("focus", self);
                    update = true;
                }
            }

            if let Some(selected) = self.try_clone::<bool>("selected") {
                if selected && !selector.pseudo_classes.contains("selected") {
                    add_selector_to_widget("selected", self);
                    update = true;
                } else if !selected && selector.pseudo_classes.contains("selected") {
                    remove_selector_from_widget("selected", self);
                    update = true;
                }
            }

            if let Some(pressed) = self.try_clone::<bool>("pressed") {
                if pressed && !selector.pseudo_classes.contains("active") {
                    add_selector_to_widget("active", self);
                    update = true;
                } else if !pressed && selector.pseudo_classes.contains("active") {
                    remove_selector_from_widget("active", self);
                    update = true;
                }
            }

            if let Some(enabled) = self.try_clone::<bool>("enabled") {
                if !enabled && !selector.pseudo_classes.contains("disabled") {
                    add_selector_to_widget("disabled", self);
                    update = true;
                } else if enabled && selector.pseudo_classes.contains("disabled") {
                    remove_selector_from_widget("disabled", self);
                    update = true;
                }
            }

            if let Some(text) = self.try_clone::<String16>("text") {
                if text.is_empty() && !selector.pseudo_classes.contains("empty") {
                    add_selector_to_widget("empty", self);
                    update = true;
                } else if !text.is_empty() && selector.pseudo_classes.contains("empty") {
                    remove_selector_from_widget("empty", self);
                    update = true;
                }
            }

            if let Some(expanded) = self.try_clone::<bool>("expanded") {
                if expanded && !selector.pseudo_classes.contains("expanded") {
                    add_selector_to_widget("expanded", self);
                    update = true;
                } else if !expanded && selector.pseudo_classes.contains("expanded") {
                    remove_selector_from_widget("expanded", self);
                    update = true;
                }
            }

            if update || force {
                self.update_properties_by_theme();
            }
        }
    }

    /// Updates the theme by the inner state e.g. `selected` or `pressed`.
    pub fn update_theme_by_state(&mut self, force: bool) {
        self.update_internal_theme_by_state(force, &(self.current_node.clone()));
    }

    /// Update all properties for the theme.
    pub fn update_properties_by_theme(&mut self) {
        if !self.has::<Selector>("selector") {
            return;
        }

        let selector = self.clone::<Selector>("selector");

        if !selector.dirty() {
            return;
        }

        if self.has::<Brush>("foreground") {
            if let Some(color) = self.theme.brush("color", &selector) {
                self.set::<Brush>("foreground", color);
            }
        }

        if self.has::<Brush>("background") {
            if let Some(background) = self.theme.brush("background", &selector) {
                self.set::<Brush>("background", background);
            }
        }

        if self.has::<Brush>("border_brush") {
            if let Some(border_brush) = self.theme.brush("border-color", &selector) {
                self.set::<Brush>("border_brush", border_brush);
            }
        }

        if self.has::<f64>("border_radius") {
            if let Some(radius) = self.theme.uint("border-radius", &selector) {
                self.set::<f64>("border_radius", f64::from(radius));
            }
        }

        if self.has::<f32>("opacity") {
            if let Some(opacity) = self.theme.float("opacity", &selector) {
                self.set::<f32>("opacity", opacity);
            }
        }

        if self.has::<Thickness>("border_width") {
            if let Some(border_width) = self.theme.uint("border-width", &selector) {
                self.set::<Thickness>("border_width", Thickness::from(border_width as f64));
            }
        }

        self.update_font_properties_by_theme(&selector);

        if let Some(mut padding) = self.try_clone::<Thickness>("padding") {
            if let Some(pad) = self.theme.uint("padding", &selector) {
                padding.set_thickness(pad as f64);
            }

            if let Some(left) = self.theme.uint("padding-left", &selector) {
                padding.set_left(left as f64);
            }

            if let Some(top) = self.theme.uint("padding-top", &selector) {
                padding.set_top(top as f64);
            }

            if let Some(right) = self.theme.uint("padding-right", &selector) {
                padding.set_right(right as f64);
            }

            if let Some(bottom) = self.theme.uint("padding-bottom", &selector) {
                padding.set_bottom(bottom as f64);
            }
            self.set::<Thickness>("padding", padding);
        }

        if let Some(mut constraint) = self.try_clone::<Constraint>("constraint") {
            if let Some(width) = self.theme.uint("width", &selector) {
                constraint.set_width(width as f64);
            }

            if let Some(height) = self.theme.uint("height", &selector) {
                constraint.set_height(height as f64);
            }

            if let Some(min_width) = self.theme.uint("min-width", &selector) {
                constraint.set_min_width(min_width as f64);
            }

            if let Some(min_height) = self.theme.uint("min-height", &selector) {
                constraint.set_min_height(min_height as f64);
            }

            if let Some(max_width) = self.theme.uint("max-width", &selector) {
                constraint.set_max_width(max_width as f64);
            }

            if let Some(max_height) = self.theme.uint("max-height", &selector) {
                constraint.set_max_height(max_height as f64);
            }

            self.set::<Constraint>("constraint", constraint);
        }

        if self.has::<f64>("spacing") {
            if let Some(spacing) = self.theme.uint("spacing", &selector) {
                self.set::<f64>("spacing", spacing.into());
            }
        }

        self.get_mut::<Selector>("selector").set_dirty(true);
    }

    pub fn update_font_properties_by_theme(&mut self, selector: &Selector) {
        if self.has::<f64>("font_size") {
            if let Some(size) = self.theme.uint("font-size", selector) {
                self.set::<f64>("font_size", f64::from(size));
            }
        }

        if self.has::<String>("font_family") {
            if let Some(font_family) = self.theme.string("font-family", selector) {
                self.set::<String>("font_family", font_family);
            }
        }

        if self.has::<Brush>("icon_brush") {
            if let Some(color) = self.theme.brush("icon-color", selector) {
                self.set::<Brush>("icon_brush", color);
            }
        }

        if self.has::<f64>("icon_size") {
            if let Some(size) = self.theme.uint("icon-size", selector) {
                self.set::<f64>("icon_size", f64::from(size));
            }
        }

        if self.has::<String>("icon_family") {
            if let Some(font_family) = self.theme.string("icon-family", selector) {
                self.set::<String>("icon_family", font_family);
            }
        }
    }

    fn get_name(&self) -> String {
        self
        .ecm
        .component_store()
        .get::<String>("name", self.current_node)
        .unwrap()
        .clone()
    }
}
<|endoftext|>


<commit_before>
use pass;
use ir;

/// An IR strength reduction pass.
pub struct ConstantFolding;

impl pass::Metadata for ConstantFolding
{
    fn id(&self) -> pass::Id { pass::Id(0x32fabb11) }
    fn name(&self) -> &'static str { "Constant folding" }
}

impl pass::Transform<ir::Value> for ConstantFolding
{
    fn run_value(&mut self, value: ir::Value) -> ir::Value {
        self::fold::value(value)
    }
}

// TODO: blamket impl for all passes
impl Into<pass::Info<ir::Value>> for Box<ConstantFolding>
{
    fn into(self) -> pass::Info<ir::Value> {
        pass::Info::Transform(self)
    }
}

pub mod fold
{
    use ir::{Value,Instruction};
    use ir::value::literal::{Literal,Integer};

    pub fn value(value: Value) -> Value {
        match value {
            Value::Instruction(i) => instruction(i),
            _ => value,
        }
    }

    pub fn instruction(inst: Instruction) -> Value {
        use ir::instruction::Binary;
        let inst_copy = inst.clone();

        match inst {
            Instruction::Add(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a+b),
            Instruction::Sub(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a-b),
            Instruction::Mul(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a*b),
            Instruction::Div(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a/b),
            Instruction::Shl(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a<<b),
            Instruction::Shr(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a>>b),
            _ => inst.into(),
        }
    }

    pub fn arithmetic_binop<FI>(inst: Instruction,
                                values: (Value,Value),
                                mut f_int: FI) -> Value
        where FI: FnMut(Integer,Integer) -> Integer {

        // make sure the values are constants
        let (lhs,rhs) = match values {
            (Value::Literal(a),Value::Literal(b)) => (a,b),
            _ => return inst.into(), // we can only fold constants
        };

        match (lhs,rhs) {
            (Literal::Integer(li),Literal::Integer(ri)) => {
                f_int(li,ri).into()
            },
            _ => inst.into(),
        }
    }
}
<commit_msg>temporary<commit_after>
use pass;
use ir;

/// An IR strength reduction pass.
pub struct ConstantFolding;

impl pass::Metadata for ConstantFolding
{
    fn id(&self) -> pass::Id { pass::Id(0x32fabb11) }
    fn name(&self) -> &'static str { "Constant folding" }
}

impl pass::Transform<ir::Value> for ConstantFolding
{
    fn run_value(&mut self, value: ir::Value) -> ir::Value {
        self::fold::value(value)
    }
}

// TODO: blamket impl for all passes
impl Into<pass::Info<ir::Value>> for Box<ConstantFolding>
{
    fn into(self) -> pass::Info<ir::Value> {
        pass::Info::Transform(self)
    }
}

pub mod fold
{
    use ir::{Value,Instruction};
    use ir::value::literal::{Literal,Integer};

    pub fn value(value: Value) -> Value {
        match value {
            Value::Instruction(i) => instruction(i),
            _ => value,
        }
    }

    pub fn instruction(inst: Instruction) -> Value {
        use ir::instruction::Binary;
        let inst_copy = inst.clone();

        match inst {
            Instruction::Add(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a+b),
            Instruction::Sub(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a-b),
            Instruction::Mul(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a*b),
            Instruction::Div(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a/b),
            Instruction::Shl(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a<<b),
            Instruction::Shr(i) => arithmetic_binop(inst_copy, i.operands(), |a,b| a>>b),
            _ => inst.into(),
        }
    }

    pub fn arithmetic_binop<FI>(inst: Instruction,
                                values: (Value,Value),
                                mut f_int: FI) -> Value
        where FI: FnMut(Integer,Integer) -> Integer {

        // make sure the values are constants
        let (lhs,rhs) = match values {
            (Value::Literal(a),Value::Literal(b)) => (a,b),
            _ => return inst.into(), // we can only fold constants
        };

        match (lhs,rhs) {
            (Literal::Integer(li),Literal::Integer(ri)) => {
                f_int(li,ri).into()
            },
            _ => inst.into(),
        }
    }
}

#[test]
fn test_binop_fold()
{
    use {lang,ir};
    use lang::Value;

    let one = ir::Value::i8(1);
    let eight = ir::Value::i8(8);
    let ty = one.ty();

    let cases = [
        // (inst, expected value)
        (ir::Instruction::add(ty,one,eight), ir::Value::i8(9)),
    ];

    for &(inst,expected) in cases.iter() {
        let folded = fold::instruction(inst);

        assert!(folded == expected);
    }

}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::path::Path;
use std::process::{Command, Output, Stdio};

use jobserver::Client;
use shell_escape::escape;

use util::{process_error, CargoResult, CargoResultExt, read2};

/// A builder object for an external process, similar to `std::process::Command`.
#[derive(Clone, Debug)]
pub struct ProcessBuilder {
    /// The program to execute.
    program: OsString,
    /// A list of arguments to pass to the program.
    args: Vec<OsString>,
    /// Any environment variables that should be set for the program.
    env: HashMap<String, Option<OsString>>,
    /// Which directory to run the program from.
    cwd: Option<OsString>,
    /// The `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    jobserver: Option<Client>,
}

impl fmt::Display for ProcessBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "`{}", self.program.to_string_lossy())?;

        for arg in &self.args {
            write!(f, " {}", escape(arg.to_string_lossy()))?;
        }

        write!(f, "`")
    }
}

impl ProcessBuilder {
    /// (chainable) Set the executable for the process.
    pub fn program<T: AsRef<OsStr>>(&mut self, program: T) -> &mut ProcessBuilder {
        self.program = program.as_ref().to_os_string();
        self
    }

    /// (chainable) Add an arg to the args list.
    pub fn arg<T: AsRef<OsStr>>(&mut self, arg: T) -> &mut ProcessBuilder {
        self.args.push(arg.as_ref().to_os_string());
        self
    }

    /// (chainable) Add many args to the args list.
    pub fn args<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args
            .extend(arguments.iter().map(|t| t.as_ref().to_os_string()));
        self
    }

    /// (chainable) Replace args with new args list
    pub fn args_replace<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args = arguments
            .iter()
            .map(|t| t.as_ref().to_os_string())
            .collect();
        self
    }

    /// (chainable) Set the current working directory of the process
    pub fn cwd<T: AsRef<OsStr>>(&mut self, path: T) -> &mut ProcessBuilder {
        self.cwd = Some(path.as_ref().to_os_string());
        self
    }

    /// (chainable) Set an environment variable for the process.
    pub fn env<T: AsRef<OsStr>>(&mut self, key: &str, val: T) -> &mut ProcessBuilder {
        self.env
            .insert(key.to_string(), Some(val.as_ref().to_os_string()));
        self
    }

    /// (chainable) Unset an environment variable for the process.
    pub fn env_remove(&mut self, key: &str) -> &mut ProcessBuilder {
        self.env.insert(key.to_string(), None);
        self
    }

    /// Get the executable name.
    pub fn get_program(&self) -> &OsString {
        &self.program
    }

    /// Get the program arguments
    pub fn get_args(&self) -> &[OsString] {
        &self.args
    }

    /// Get the current working directory for the process
    pub fn get_cwd(&self) -> Option<&Path> {
        self.cwd.as_ref().map(Path::new)
    }

    /// Get an environment variable as the process will see it (will inherit from environment
    /// unless explicitally unset).
    pub fn get_env(&self, var: &str) -> Option<OsString> {
        self.env
            .get(var)
            .cloned()
            .or_else(|| Some(env::var_os(var)))
            .and_then(|s| s)
    }

    /// Get all environment variables explicitly set or unset for the process (not inherited
    /// vars).
    pub fn get_envs(&self) -> &HashMap<String, Option<OsString>> {
        &self.env
    }

    /// Set the `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    pub fn inherit_jobserver(&mut self, jobserver: &Client) -> &mut Self {
        self.jobserver = Some(jobserver.clone());
        self
    }

    /// Run the process, waiting for completion, and mapping non-success exit codes to an error.
    pub fn exec(&self) -> CargoResult<()> {
        let mut command = self.build_command();
        let exit = command.status().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if exit.success() {
            Ok(())
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(exit),
                None,
            ).into())
        }
    }

    /// On unix, executes the process using the unix syscall `execvp`, which will block this
    /// process, and will only return if there is an error. On windows this is a synonym for
    /// `exec`.
    #[cfg(unix)]
    pub fn exec_replace(&self) -> CargoResult<()> {
        use std::os::unix::process::CommandExt;

        let mut command = self.build_command();
        let error = command.exec();
        Err(::util::CargoError::from(error)
            .context(process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            ))
            .into())
    }

    /// On unix, executes the process using the unix syscall `execvp`, which will block this
    /// process, and will only return if there is an error. On windows this is a synonym for
    /// `exec`.
    #[cfg(windows)]
    pub fn exec_replace(&self) -> CargoResult<()> {
        self.exec()
    }

    /// Execute the process, returning the stdio output, or an error if non-zero exit status.
    pub fn exec_with_output(&self) -> CargoResult<Output> {
        let mut command = self.build_command();

        let output = command.output().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if output.status.success() {
            Ok(output)
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(output.status),
                Some(&output),
            ).into())
        }
    }

    /// Execute a command, passing each line of stdout and stderr to the supplied callbacks, which
    /// can mutate the string data.
    ///
    /// If any invocations of these function return an error, it will be propagated.
    ///
    /// Optionally, output can be passed to errors using `print_output`
    pub fn exec_with_streaming(
        &self,
        on_stdout_line: &mut FnMut(&str) -> CargoResult<()>,
        on_stderr_line: &mut FnMut(&str) -> CargoResult<()>,
        print_output: bool,
    ) -> CargoResult<Output> {
        let mut stdout = Vec::new();
        let mut stderr = Vec::new();

        let mut cmd = self.build_command();
        cmd.stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .stdin(Stdio::null());

        let mut callback_error = None;
        let status = (|| {
            let mut child = cmd.spawn()?;
            let out = child.stdout.take().unwrap();
            let err = child.stderr.take().unwrap();
            read2(out, err, &mut |is_out, data, eof| {
                let idx = if eof {
                    data.len()
                } else {
                    match data.iter().rposition(|b| *b == b'\n') {
                        Some(i) => i + 1,
                        None => return,
                    }
                };
                let data = data.drain(..idx);
                let dst = if is_out { &mut stdout } else { &mut stderr };
                let start = dst.len();
                dst.extend(data);
                for line in String::from_utf8_lossy(&dst[start..]).lines() {
                    if callback_error.is_some() {
                        break;
                    }
                    let callback_result = if is_out {
                        on_stdout_line(line)
                    } else {
                        on_stderr_line(line)
                    };
                    if let Err(e) = callback_result {
                        callback_error = Some(e);
                    }
                }
            })?;
            child.wait()
        })()
            .chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;
        let output = Output {
            stdout,
            stderr,
            status,
        };

        {
            let to_print = if print_output { Some(&output) } else { None };
            if !output.status.success() {
                return Err(process_error(
                    &format!("process didn't exit successfully: {}", self),
                    Some(output.status),
                    to_print,
                ).into());
            } else if let Some(e) = callback_error {
                let cx = process_error(
                    &format!("failed to parse process output: {}", self),
                    Some(output.status),
                    to_print,
                );
                return Err(e.context(cx).into());
            }
        }

        Ok(output)
    }

    /// Converts ProcessBuilder into a `std::process::Command`, and handles the jobserver if
    /// present.
    pub fn build_command(&self) -> Command {
        let mut command = Command::new(&self.program);
        if let Some(cwd) = self.get_cwd() {
            command.current_dir(cwd);
        }
        for arg in &self.args {
            command.arg(arg);
        }
        for (k, v) in &self.env {
            match *v {
                Some(ref v) => {
                    command.env(k, v);
                }
                None => {
                    command.env_remove(k);
                }
            }
        }
        if let Some(ref c) = self.jobserver {
            c.configure(&mut command);
        }
        command
    }
}

/// A helper function to create a `ProcessBuilder`.
pub fn process<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
    ProcessBuilder {
        program: cmd.as_ref().to_os_string(),
        args: Vec::new(),
        cwd: None,
        env: HashMap::new(),
        jobserver: None,
    }
}
<commit_msg>Add empty ctrlc handler on Windows.<commit_after>use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::path::Path;
use std::process::{Command, Output, Stdio};

use jobserver::Client;
use shell_escape::escape;

use util::{process_error, CargoResult, CargoResultExt, read2};

/// A builder object for an external process, similar to `std::process::Command`.
#[derive(Clone, Debug)]
pub struct ProcessBuilder {
    /// The program to execute.
    program: OsString,
    /// A list of arguments to pass to the program.
    args: Vec<OsString>,
    /// Any environment variables that should be set for the program.
    env: HashMap<String, Option<OsString>>,
    /// Which directory to run the program from.
    cwd: Option<OsString>,
    /// The `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    jobserver: Option<Client>,
}

impl fmt::Display for ProcessBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "`{}", self.program.to_string_lossy())?;

        for arg in &self.args {
            write!(f, " {}", escape(arg.to_string_lossy()))?;
        }

        write!(f, "`")
    }
}

impl ProcessBuilder {
    /// (chainable) Set the executable for the process.
    pub fn program<T: AsRef<OsStr>>(&mut self, program: T) -> &mut ProcessBuilder {
        self.program = program.as_ref().to_os_string();
        self
    }

    /// (chainable) Add an arg to the args list.
    pub fn arg<T: AsRef<OsStr>>(&mut self, arg: T) -> &mut ProcessBuilder {
        self.args.push(arg.as_ref().to_os_string());
        self
    }

    /// (chainable) Add many args to the args list.
    pub fn args<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args
            .extend(arguments.iter().map(|t| t.as_ref().to_os_string()));
        self
    }

    /// (chainable) Replace args with new args list
    pub fn args_replace<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args = arguments
            .iter()
            .map(|t| t.as_ref().to_os_string())
            .collect();
        self
    }

    /// (chainable) Set the current working directory of the process
    pub fn cwd<T: AsRef<OsStr>>(&mut self, path: T) -> &mut ProcessBuilder {
        self.cwd = Some(path.as_ref().to_os_string());
        self
    }

    /// (chainable) Set an environment variable for the process.
    pub fn env<T: AsRef<OsStr>>(&mut self, key: &str, val: T) -> &mut ProcessBuilder {
        self.env
            .insert(key.to_string(), Some(val.as_ref().to_os_string()));
        self
    }

    /// (chainable) Unset an environment variable for the process.
    pub fn env_remove(&mut self, key: &str) -> &mut ProcessBuilder {
        self.env.insert(key.to_string(), None);
        self
    }

    /// Get the executable name.
    pub fn get_program(&self) -> &OsString {
        &self.program
    }

    /// Get the program arguments
    pub fn get_args(&self) -> &[OsString] {
        &self.args
    }

    /// Get the current working directory for the process
    pub fn get_cwd(&self) -> Option<&Path> {
        self.cwd.as_ref().map(Path::new)
    }

    /// Get an environment variable as the process will see it (will inherit from environment
    /// unless explicitally unset).
    pub fn get_env(&self, var: &str) -> Option<OsString> {
        self.env
            .get(var)
            .cloned()
            .or_else(|| Some(env::var_os(var)))
            .and_then(|s| s)
    }

    /// Get all environment variables explicitly set or unset for the process (not inherited
    /// vars).
    pub fn get_envs(&self) -> &HashMap<String, Option<OsString>> {
        &self.env
    }

    /// Set the `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    pub fn inherit_jobserver(&mut self, jobserver: &Client) -> &mut Self {
        self.jobserver = Some(jobserver.clone());
        self
    }

    /// Run the process, waiting for completion, and mapping non-success exit codes to an error.
    pub fn exec(&self) -> CargoResult<()> {
        let mut command = self.build_command();
        let exit = command.status().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if exit.success() {
            Ok(())
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(exit),
                None,
            ).into())
        }
    }

    /// Replaces the current process with the target process.
    ///
    /// On Unix, this executes the process using the unix syscall `execvp`, which will block
    /// this process, and will only return if there is an error.
    ///
    /// On Windows this isn't technically possible. Instead we emulate it to the best of our
    /// ability. One aspect we fix here is that we specify a handler for the ctrl-c handler.
    /// In doing so (and by effectively ignoring it) we should emulate proxying ctrl-c
    /// handling to the application at hand, which will either terminate or handle it itself.
    /// According to microsoft's documentation at:
    /// https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals
    /// the ctrl-c signal is sent to all processes attached to a terminal, which should
    /// include our child process. If the child terminates then we'll reap them in Cargo
    /// pretty quickly, and if the child handles the signal then we won't terminate
    /// (and we shouldn't!) until the process itself later exits.
    pub fn exec_replace(&self) -> CargoResult<()> {
        imp::exec_replace(self)
    }

    /// Execute the process, returning the stdio output, or an error if non-zero exit status.
    pub fn exec_with_output(&self) -> CargoResult<Output> {
        let mut command = self.build_command();

        let output = command.output().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if output.status.success() {
            Ok(output)
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(output.status),
                Some(&output),
            ).into())
        }
    }

    /// Execute a command, passing each line of stdout and stderr to the supplied callbacks, which
    /// can mutate the string data.
    ///
    /// If any invocations of these function return an error, it will be propagated.
    ///
    /// Optionally, output can be passed to errors using `print_output`
    pub fn exec_with_streaming(
        &self,
        on_stdout_line: &mut FnMut(&str) -> CargoResult<()>,
        on_stderr_line: &mut FnMut(&str) -> CargoResult<()>,
        print_output: bool,
    ) -> CargoResult<Output> {
        let mut stdout = Vec::new();
        let mut stderr = Vec::new();

        let mut cmd = self.build_command();
        cmd.stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .stdin(Stdio::null());

        let mut callback_error = None;
        let status = (|| {
            let mut child = cmd.spawn()?;
            let out = child.stdout.take().unwrap();
            let err = child.stderr.take().unwrap();
            read2(out, err, &mut |is_out, data, eof| {
                let idx = if eof {
                    data.len()
                } else {
                    match data.iter().rposition(|b| *b == b'\n') {
                        Some(i) => i + 1,
                        None => return,
                    }
                };
                let data = data.drain(..idx);
                let dst = if is_out { &mut stdout } else { &mut stderr };
                let start = dst.len();
                dst.extend(data);
                for line in String::from_utf8_lossy(&dst[start..]).lines() {
                    if callback_error.is_some() {
                        break;
                    }
                    let callback_result = if is_out {
                        on_stdout_line(line)
                    } else {
                        on_stderr_line(line)
                    };
                    if let Err(e) = callback_result {
                        callback_error = Some(e);
                    }
                }
            })?;
            child.wait()
        })()
            .chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;
        let output = Output {
            stdout,
            stderr,
            status,
        };

        {
            let to_print = if print_output { Some(&output) } else { None };
            if !output.status.success() {
                return Err(process_error(
                    &format!("process didn't exit successfully: {}", self),
                    Some(output.status),
                    to_print,
                ).into());
            } else if let Some(e) = callback_error {
                let cx = process_error(
                    &format!("failed to parse process output: {}", self),
                    Some(output.status),
                    to_print,
                );
                return Err(e.context(cx).into());
            }
        }

        Ok(output)
    }

    /// Converts ProcessBuilder into a `std::process::Command`, and handles the jobserver if
    /// present.
    pub fn build_command(&self) -> Command {
        let mut command = Command::new(&self.program);
        if let Some(cwd) = self.get_cwd() {
            command.current_dir(cwd);
        }
        for arg in &self.args {
            command.arg(arg);
        }
        for (k, v) in &self.env {
            match *v {
                Some(ref v) => {
                    command.env(k, v);
                }
                None => {
                    command.env_remove(k);
                }
            }
        }
        if let Some(ref c) = self.jobserver {
            c.configure(&mut command);
        }
        command
    }
}

/// A helper function to create a `ProcessBuilder`.
pub fn process<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
    ProcessBuilder {
        program: cmd.as_ref().to_os_string(),
        args: Vec::new(),
        cwd: None,
        env: HashMap::new(),
        jobserver: None,
    }
}

#[cfg(unix)]
mod imp {
    use CargoResult;
    use std::os::unix::process::CommandExt;
    use util::{process_error, ProcessBuilder};

    pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> {
        let mut command = process_builder.build_command();
        let error = command.exec();
        Err(::util::CargoError::from(error)
            .context(process_error(
                &format!("could not execute process {}", process_builder),
                None,
                None,
            ))
            .into())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;

    use CargoResult;
    use util::{process_error, ProcessBuilder};
    use self::winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
    use self::winapi::um::consoleapi::SetConsoleCtrlHandler;

    unsafe extern "system" fn ctrlc_handler(_: DWORD) -> BOOL {
        // Do nothing. Let the child process handle it.
        TRUE
    }

    pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> {
        unsafe {
            if SetConsoleCtrlHandler(Some(ctrlc_handler), TRUE) == FALSE {
                return Err(process_error(
                    "Could not set Ctrl-C handler.",
                    None,
                    None).into());
            }
        }

        // Just exec the process as normal.
        process_builder.exec()
    }
}
<|endoftext|>


<commit_before>use std::os;
use std::path::Path;
use std::io;
use std::io::process::{Process,ProcessConfig,ProcessOutput,InheritFd};
use collections::HashMap;
use core::errors::{ToResult,CargoResult,CargoError};

#[deriving(Clone,Eq)]
pub struct ProcessBuilder {
    program: ~str,
    args: Vec<~str>,
    path: Vec<~str>,
    env: HashMap<~str, ~str>,
    cwd: Path
}

// TODO: Upstream a Windows/Posix branch to Rust proper
static PATH_SEP : &'static str = ":";

impl ProcessBuilder {
    pub fn args(mut self, arguments: &[~str]) -> ProcessBuilder {
        self.args = Vec::from_slice(arguments);
        self
    }

    pub fn extra_path(mut self, path: Path) -> ProcessBuilder {
        // For now, just convert to a string, but we should do something better
        self.path.push(format!("{}", path.display()));
        self
    }

    pub fn cwd(mut self, path: Path) -> ProcessBuilder {
        self.cwd = path;
        self
    }

    // TODO: should InheritFd be hardcoded?
    pub fn exec(&self) -> io::IoResult<()> {
        let mut config = try!(self.build_config());
        let env = self.build_env();

        // Set where the output goes
        config.env = Some(env.as_slice());
        config.stdout = InheritFd(1);
        config.stderr = InheritFd(2);

        let mut process = try!(Process::configure(config));
        let exit = process.wait();

        if exit.success() {
            Ok(())
        }
        else {
            Err(io::IoError {
                kind: io::OtherIoError,
                desc: "process did not exit successfully",
                detail: None
            })
        }
    }

    // TODO: Match exec()
    pub fn exec_with_output(&self) -> CargoResult<ProcessOutput> {
        let mut config = ProcessConfig::new();

        config.program = self.program.as_slice();
        config.args = self.args.as_slice();
        config.cwd = Some(&self.cwd);

        let os_path = try!(os::getenv("PATH").to_result(|_|
            CargoError::described("Could not find the PATH environment variable")));

        let path = os_path + PATH_SEP + self.path.connect(PATH_SEP);

        let path = [("PATH".to_owned(), path)];
        config.env = Some(path.as_slice());

        Process::configure(config).map(|mut ok| ok.wait_with_output()).to_result(|_|
            CargoError::described("Could not spawn process"))
    }

    fn build_config<'a>(&'a self) -> io::IoResult<ProcessConfig<'a>> {
        let mut config = ProcessConfig::new();

        config.program = self.program.as_slice();
        config.args = self.args.as_slice();
        config.cwd = Some(&self.cwd);

        Ok(config)
    }

    fn build_env(&self) -> ~[(~str, ~str)] {
        let mut ret = Vec::new();

        for (key, val) in self.env.iter() {
            // Skip path
            if key.as_slice() != "PATH" {
                ret.push((key.clone(), val.clone()));
            }
        }

        match self.build_path() {
            Some(path) => ret.push(("PATH".to_owned(), path)),
            _ => ()
        }

        ret.as_slice().to_owned()
    }

    fn build_path(&self) -> Option<~str> {
        let path = self.path.connect(PATH_SEP);

        match self.env.find_equiv(&("PATH")) {
            Some(existing) => {
                if self.path.is_empty() {
                    Some(existing.to_owned())
                }
                else {
                    Some(existing.as_slice() + PATH_SEP + path)
                }
            }
            None => {
                if self.path.is_empty() {
                    None
                }
                else {
                    Some(path)
                }
            }
        }
    }
}

pub fn process(cmd: &str) -> ProcessBuilder {
    ProcessBuilder {
        program: cmd.to_owned(),
        args: vec!(),
        path: vec!(),
        cwd: os::getcwd(),
        env: system_env()
    }
}

fn system_env() -> HashMap<~str, ~str> {
    let mut ret = HashMap::new();

    for &(ref key, ref val) in os::env().iter() {
        ret.insert(key.clone(), val.clone());
    }

    ret
}
<commit_msg>Cleanup ProcessBuilder<commit_after>use std::os;
use std::path::Path;
use std::io;
use std::io::process::{Process,ProcessConfig,ProcessOutput,InheritFd};
use collections::HashMap;

#[deriving(Clone,Eq)]
pub struct ProcessBuilder {
    program: ~str,
    args: Vec<~str>,
    path: Vec<~str>,
    env: HashMap<~str, ~str>,
    cwd: Path
}

// TODO: Upstream a Windows/Posix branch to Rust proper
static PATH_SEP : &'static str = ":";

impl ProcessBuilder {
    pub fn args(mut self, arguments: &[~str]) -> ProcessBuilder {
        self.args = Vec::from_slice(arguments);
        self
    }

    pub fn extra_path(mut self, path: Path) -> ProcessBuilder {
        // For now, just convert to a string, but we should do something better
        self.path.push(format!("{}", path.display()));
        self
    }

    pub fn cwd(mut self, path: Path) -> ProcessBuilder {
        self.cwd = path;
        self
    }

    // TODO: should InheritFd be hardcoded?
    pub fn exec(&self) -> io::IoResult<()> {
        let mut config = try!(self.build_config());
        let env = self.build_env();

        // Set where the output goes
        config.env = Some(env.as_slice());
        config.stdout = InheritFd(1);
        config.stderr = InheritFd(2);

        let mut process = try!(Process::configure(config));
        let exit = process.wait();

        if exit.success() {
            Ok(())
        }
        else {
            Err(io::IoError {
                kind: io::OtherIoError,
                desc: "process did not exit successfully",
                detail: None
            })
        }
    }

    pub fn exec_with_output(&self) -> io::IoResult<ProcessOutput> {
        let mut config = try!(self.build_config());
        let env = self.build_env();

        config.env = Some(env.as_slice());

        Process::configure(config).map(|mut ok| ok.wait_with_output())
    }

    fn build_config<'a>(&'a self) -> io::IoResult<ProcessConfig<'a>> {
        let mut config = ProcessConfig::new();

        config.program = self.program.as_slice();
        config.args = self.args.as_slice();
        config.cwd = Some(&self.cwd);

        Ok(config)
    }

    fn build_env(&self) -> ~[(~str, ~str)] {
        let mut ret = Vec::new();

        for (key, val) in self.env.iter() {
            // Skip path
            if key.as_slice() != "PATH" {
                ret.push((key.clone(), val.clone()));
            }
        }

        match self.build_path() {
            Some(path) => ret.push(("PATH".to_owned(), path)),
            _ => ()
        }

        ret.as_slice().to_owned()
    }

    fn build_path(&self) -> Option<~str> {
        let path = self.path.connect(PATH_SEP);

        match self.env.find_equiv(&("PATH")) {
            Some(existing) => {
                if self.path.is_empty() {
                    Some(existing.to_owned())
                }
                else {
                    Some(existing.as_slice() + PATH_SEP + path)
                }
            }
            None => {
                if self.path.is_empty() {
                    None
                }
                else {
                    Some(path)
                }
            }
        }
    }
}

pub fn process(cmd: &str) -> ProcessBuilder {
    ProcessBuilder {
        program: cmd.to_owned(),
        args: vec!(),
        path: vec!(),
        cwd: os::getcwd(),
        env: system_env()
    }
}

fn system_env() -> HashMap<~str, ~str> {
    let mut ret = HashMap::new();

    for &(ref key, ref val) in os::env().iter() {
        ret.insert(key.clone(), val.clone());
    }

    ret
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate coreutils;
extern crate extra;

use std::env;
use std::fs;
use std::path::Path;
use std::io::{stdout, stderr, Stderr, Write};
use std::os::unix::fs::MetadataExt;
use std::process::exit;

use coreutils::{ArgParser, to_human_readable_string};
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{ls} */ r#"
NAME
    ls - list directory contents

SYNOPSIS
    ls [ -h | --help | -l ][FILE]...

DESCRIPTION
    List information about the FILE(s), or the current directory

OPTIONS
    -h
    --human-readable
        with -l, print human readable sizes
    --help
        display this help and exit
    -l
        use a long listing format
"#; /* @MANEND */

fn list_dir(path: &str, parser: &ArgParser, string: &mut String, stderr: &mut Stderr) {
    let metadata = fs::metadata(path).try(stderr);
    if metadata.is_dir() {
        let read_dir = Path::new(path).read_dir().try(stderr);

        let mut entries: Vec<String> = read_dir.filter_map(|x| x.ok()).map(|dir| {
                let mut file_name = dir.file_name().to_string_lossy().into_owned();
                if dir.file_type().try(stderr).is_dir() {
                    file_name.push('/');
                }
                file_name
            })
            .collect();

        entries.sort();

        for entry in entries.iter() {
            if parser.found(&'l') || parser.found("long-format") {
                let mut entry_path = path.to_owned();
                if !entry_path.ends_with('/') {
                    entry_path.push('/');
                }
                entry_path.push_str(&entry);

                let metadata = fs::metadata(entry_path).try(stderr);
                string.push_str(&format!("{:>7o} {:>5} {:>5} ",
                                         metadata.mode(),
                                         metadata.uid(),
                                         metadata.gid()));
                if parser.found(&'h') || parser.found("human-readable") {
                    string.push_str(&format!("{:>6} ", to_human_readable_string(metadata.size())));
                } else {
                    string.push_str(&format!("{:>8} ", metadata.size()));
                }
            }
            string.push_str(entry);
            string.push('\n');
        }
    } else {
        if parser.found(&'l') || parser.found("long-format") {
            string.push_str(&format!("{:>7o} {:>5} {:>5} ",
                                     metadata.mode(),
                                     metadata.uid(),
                                     metadata.gid()));
            if parser.found(&'h') || parser.found("human-readable") {
                string.push_str(&format!("{:>6} ", to_human_readable_string(metadata.size())));
            } else {
                string.push_str(&format!("{:>8} ", metadata.size()));
            }
        }
        string.push_str(path);
        string.push('\n');
    }
}

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    let mut parser = ArgParser::new(3)
        .add_flag("l", "long-format")
        .add_flag("h", "human-readable")
        .add_flag("", "help");
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    }

    let mut string = String::new();
    if parser.args.is_empty() {
        list_dir(".", &parser, &mut string, &mut stderr);
    } else {
        for dir in parser.args.iter() {
            list_dir(&dir, &parser, &mut string, &mut stderr);
        }
    }
    stdout.write(string.as_bytes()).try(&mut stderr);
}

#[test]
fn test_human_readable() {
    assert_eq!(to_human_readable_string(0), "0");
    assert_eq!(to_human_readable_string(1023), "1023");
    assert_eq!(to_human_readable_string(1024), "1.0K");
    assert_eq!(to_human_readable_string(1024 + 100), "1.1K");
    assert_eq!(to_human_readable_string(1024u64.pow(2) * 2), "2.0M");
    assert_eq!(to_human_readable_string(1024u64.pow(3) * 3), "3.0G");
    assert_eq!(to_human_readable_string(1024u64.pow(4) * 4), "4.0T");
    assert_eq!(to_human_readable_string(1024u64.pow(5) * 5), "5.0P");
    assert_eq!(to_human_readable_string(1024u64.pow(6) * 6), "6.0E");
}
<commit_msg>add -R(recursive) option support and output a human-readable file mode like -rwxr-xr-x<commit_after>#![deny(warnings)]

extern crate coreutils;
extern crate extra;

use std::env;
use std::fs;
use std::fs::{Metadata, FileType};
use std::path::Path;
use std::io::{stdout, StdoutLock, stderr, Stderr, Write};
use std::os::unix::fs::MetadataExt;

use std::process::exit;

use coreutils::{ArgParser, to_human_readable_string};
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{ls} */ r#"
NAME
    ls - list directory contents

SYNOPSIS
    ls [ -h | --help | -l ][FILE]...

DESCRIPTION
    List information about the FILE(s), or the current directory

OPTIONS
    -h
    --human-readable
        with -l, print human readable sizes
    --help
        display this help and exit
    -l
        use a long listing format
    -R, --recursive
        list subdirectories recursively
"#; /* @MANEND */

fn mode_to_human_readable(file_type: &FileType, mode: u32) -> String {

    let mut result = String::from("");
    //TODO is a symlink? => push 'l'
    if file_type.is_dir() {
        result.push('d');
    }else{
        result.push('-');
    }

    let mode_str = format!("{:>6o}", mode);
    let mode_chars = mode_str[3..].chars();
    for i in mode_chars {
        match i {
            '7' => result.push_str("rwx"),
            '6' => result.push_str("rw-"),
            '5' => result.push_str("r-x"),
            '4' => result.push_str("r--"),
            '3' => result.push_str("-wx"),
            '2' => result.push_str("-w-"),
            '1' => result.push_str("--x"),
            _   => result.push_str("---")
        }
    }

    return result;
}

fn print_item(item_path: &str, metadata: &Metadata, parser: &ArgParser, stdout: &mut StdoutLock, stderr: &mut Stderr){
    if parser.found(&'l') || parser.found("long-format") {
    stdout.write(&format!("{} {:>5} {:>5} ",
            mode_to_human_readable(&(metadata.file_type()), metadata.mode()),
            metadata.uid(),
            metadata.gid()).as_bytes()).try(stderr);
        if parser.found(&'h') || parser.found("human-readable") {
            stdout.write(&format!("{:>6} ",
                    to_human_readable_string(metadata.size())).as_bytes()).try(stderr);
        } else {
            stdout.write(&format!("{:>8} ", metadata.size()).as_bytes()).try(stderr);
        }
    }
    if item_path.starts_with("./") {
        stdout.write(&item_path[2..].as_bytes()).try(stderr);
    }else{
        stdout.write(item_path.as_bytes()).try(stderr);
    }
    stdout.write("\n".as_bytes()).try(stderr);
    stdout.flush().try(stderr);
}

fn list_dir(path: &str, parser: &ArgParser, stdout: &mut StdoutLock, stderr: &mut Stderr) {
    let metadata = fs::metadata(path).try(stderr);
    if metadata.is_dir() {
        let read_dir = Path::new(path).read_dir().try(stderr);

        let mut entries: Vec<String> = read_dir.filter_map(|x| x.ok()).map(|dir| {
            let file_name = dir.file_name().to_string_lossy().into_owned();
            file_name
        }).collect();

        entries.sort();

        for entry in entries.iter() {
            let mut entry_path = path.to_owned();
            if !entry_path.ends_with('/') {
                entry_path.push('/');
            }
            entry_path.push_str(&entry);
            let metadata = fs::metadata(&entry_path).try(stderr);
            print_item(&entry_path, &metadata, &parser, stdout, stderr);
            if (parser.found(&'R') || parser.found("recursive")) && metadata.is_dir() {
                list_dir(&entry_path, parser, stdout, stderr);
            }
        }
    } else {
        print_item(&path, &metadata, &parser, stdout, stderr);
    }
}

fn main() {
    let stdout = stdout();
    let mut stdout = stdout.lock();
    let mut stderr = stderr();

    let mut parser = ArgParser::new(4)
        .add_flag("l", "long-format")
        .add_flag("h", "human-readable")
        .add_flag("R", "recursive")
        .add_flag("", "help");
    parser.parse(env::args());

    if parser.found("help") {
        stdout.write(MAN_PAGE.as_bytes()).try(&mut stderr);
        stdout.flush().try(&mut stderr);
        exit(0);
    }

    if parser.args.is_empty() {
        list_dir(".", &parser, &mut stdout, &mut stderr);
    } else {
        for dir in parser.args.iter() {
            list_dir(&dir, &parser, &mut stdout, &mut stderr);
        }
    }
}

#[test]
fn test_human_readable() {
    assert_eq!(to_human_readable_string(0), "0");
    assert_eq!(to_human_readable_string(1023), "1023");
    assert_eq!(to_human_readable_string(1024), "1.0K");
    assert_eq!(to_human_readable_string(1024 + 100), "1.1K");
    assert_eq!(to_human_readable_string(1024u64.pow(2) * 2), "2.0M");
    assert_eq!(to_human_readable_string(1024u64.pow(3) * 3), "3.0G");
    assert_eq!(to_human_readable_string(1024u64.pow(4) * 4), "4.0T");
    assert_eq!(to_human_readable_string(1024u64.pow(5) * 5), "5.0P");
    assert_eq!(to_human_readable_string(1024u64.pow(6) * 6), "6.0E");
}
<|endoftext|>


<commit_before>extern crate cgmath;
extern crate png;

use std::collections::HashMap;
use std::default::Default;
use time;

use cgmath::{Matrix4, Point3, Vector3};

#[cfg(target_os = "android")]
use egl_context::EglContext;
use gl;
use gl::Texture;
use mesh;
use program::{Buffers, Program};
use world::{Chunk, Point2, World};
#[cfg(target_os = "linux")]
use x11::{PollEventsIterator, XWindow};

#[cfg(target_os = "android")]
pub struct EngineImpl {
  pub egl_context: Option<Box<EglContext>>,
  pub program: Option<Program>,
}

#[cfg(target_os = "android")]
impl Default for EngineImpl {
  fn default() -> EngineImpl {
    EngineImpl {
      egl_context: None,
      program: None,
    }
  }
}

#[cfg(target_os = "linux")]
pub struct EngineImpl {
  pub window: XWindow,
  pub program: Program,
}

/// Field of view.
pub struct Fov {
  angle: f32,  // in degrees.
}

impl Fov {
  fn inc_angle(&mut self, degrees: f32) {
    self.angle += degrees;
    if self.angle > 360.0 {
      self.angle -= 360.0;
    }
  }
}

pub struct Engine {
  engine_impl: EngineImpl,
  animating: bool,
  fov: Fov,
  /// GL projection matrix
  projection_matrix: Matrix4<f32>,
  /// Texture atlas.
  texture: Texture,
  world: World,
  buffers: HashMap<Chunk, Buffers>,
}

impl Engine {
  #[cfg(target_os = "android")]
  pub fn new() -> Engine {
    Engine {
      engine_impl: Default::default(),
      animating: false,
      fov: Fov {
        angle: 0.0,
      },
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: World::new(&Point2::new(0.0, 0.0), FAR_PLANE),
      buffers: HashMap::new(),
    }
  }

  #[cfg(target_os = "linux")]
  pub fn new(window: XWindow, program: Program) -> Engine {
    Engine {
      engine_impl: EngineImpl {
        window: window,
        program: program,
      },
      animating: false,
      fov: Fov {
        angle: 0.0,
      },
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: World::new(&Point2::new(0.0, 0.0), FAR_PLANE),
      buffers: HashMap::new(),
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "android")]
  pub fn init(&mut self, egl_context: Box<EglContext>, texture_atlas_bytes: &[u8]) {
    self.engine_impl.egl_context = Some(egl_context);

    self.engine_impl.program = match Program::new() {
      Ok(p) => Some(p),
      Err(e) => panic!("Program failed: {:?}", e),
    };

    self.common_init(texture_atlas_bytes);

    if let Some(ref p) = self.engine_impl.program {
      p.set_texture_unit(0);
    }

    // Silly gymnastics to satisfy the borrow checker.
    let wh = if let Some(ref ec) = self.engine_impl.egl_context {
      Some((ec.width, ec.height))
    } else {
      None
    };
    if let Some((w, h)) = wh {
      self.set_viewport(w, h);
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "linux")]
  pub fn init(&mut self, texture_atlas_bytes: &[u8]) {
    self.common_init(texture_atlas_bytes);
    self.engine_impl.program.set_texture_unit(0);
  }

  fn common_init(&mut self, texture_atlas_bytes: &[u8]) {
    // Set the background clear color to sky blue.
    gl::clear_color(0.5, 0.69, 1.0, 1.0);

    // Enable reverse face culling.
    gl::enable(gl::CULL_FACE);
    // Enable depth test.
    gl::enable(gl::DEPTH_TEST);
    gl::depth_func(gl::LEQUAL);

    // Set up textures.
    self.texture = Engine::load_texture_atlas(texture_atlas_bytes);
    gl::active_texture(gl::TEXTURE0);
    gl::bind_texture_2d(self.texture);

    self.load_meshes();
  }

  #[cfg(target_os = "android")]
  fn load_meshes(&mut self) {
    let start_s = time::precise_time_s();
    log!("*** Loading meshes...");

    if let Some(ref mut p) = self.engine_impl.program {
      for (c, bs) in self.world.chunk_blocks() {
        let vertices = mesh::create_mesh_vertices(bs, &self.world);
        let buffers = p.upload_vertices(&vertices);
        let c = (*c).clone();
        self.buffers.insert(c, buffers);
      }
    }

    let spent_ms = (time::precise_time_s() - start_s) * 1000.0;
    log!("*** Loaded meshes: {:.3}ms, {} chunks", spent_ms, self.buffers.len());
  }

  #[cfg(target_os = "linux")]
  fn load_meshes(&mut self) {
    let start_s = time::precise_time_s();
    log!("*** Loading meshes...");

    let p = &self.engine_impl.program;
    for (c, bs) in self.world.chunk_blocks() {
      let vertices = mesh::create_mesh_vertices(bs, &self.world);
      let buffers = p.upload_vertices(&vertices);
      let c = (*c).clone();
      self.buffers.insert(c, buffers);
    }

    let spent_ms = (time::precise_time_s() - start_s) * 1000.0;
    log!("*** Loaded meshes: {:.3}ms, {} chunks", spent_ms, self.buffers.len());
  }

  pub fn set_viewport(&mut self, w: i32, h: i32) {
    gl::viewport(0, 0, w, h);
    self.projection_matrix = projection_matrix(w, h);
  }

  /// Load texture atlas from assets folder.
  fn load_texture_atlas(texture_atlas_bytes: &[u8]) -> Texture {
    let image = png::load_png_from_memory(texture_atlas_bytes)
      .unwrap_or_else(|s| panic!("load_png_from_memory() failed: {}", s));

    let pixels = match image.pixels {
      png::PixelsByColorType::RGBA8(v) => v,
      _ => {
        let color_type = match image.pixels {
            png::PixelsByColorType::K8(_) => "K8",
            png::PixelsByColorType::KA8(_) => "KA8",
            png::PixelsByColorType::RGB8(_) => "RGB8",
            png::PixelsByColorType::RGBA8(_) => panic!("Should not happen"),
        };
        panic!("Only RGBA8 image format supported, was: {}", color_type);
      }
    };

    let texture = gl::gen_texture();
    gl::bind_texture_2d(texture);
    gl::texture_2d_param(gl::TEXTURE_MIN_FILTER, gl::NEAREST_MIPMAP_LINEAR);
    gl::texture_2d_param(gl::TEXTURE_MAG_FILTER, gl::NEAREST);
    gl::texture_2d_param(gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE);
    gl::texture_2d_param(gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE);
    gl::texture_2d_image_rgba(image.width as i32, image.height as i32, &pixels);
    gl::generate_mipmap_2d();
    gl::bind_texture_2d(0);

    texture
  }

  /// Draw a frame.
  #[cfg(target_os = "android")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    match self.engine_impl.egl_context {
      None => return,  // No display.
      Some(ref egl_context) => {
        gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

        match self.engine_impl.program {
          Some(ref p) => {
            if let Some(e) = self.world.eye() {
              // Compute the composite mvp_matrix and send it to program.  Model matrix
              // is always identity so instead of MVP = P * V * M just do MVP = P * V.
              let mvp_matrix = self.projection_matrix * view_matrix(&e, self.fov.angle);
              p.set_mvp_matrix(mvp_matrix);

              // Finally, draw the cube mesh.
              // TODO: Draw only meshes for only visible chunks within FOV.
              for bs in self.buffers.values() {
                p.bind_buffers(bs);
                gl::draw_elements_triangles_u16(bs.index_count);
                p.unbind_buffers();
              }
            }
          },
          None => panic!("Missing program, should never happen"),
        }

        egl_context.swap_buffers();
      }
    }
  }

  /// Draw a frame.
  #[cfg(target_os = "linux")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

    let p = &self.engine_impl.program;

    if let Some(e) = self.world.eye() {
      // Compute the composite mvp_matrix and send it to program.  Model matrix
      // is always identity so instead of MVP = P * V * M just do MVP = P * V.
      let mvp_matrix = self.projection_matrix * view_matrix(&e, self.fov.angle);
      p.set_mvp_matrix(mvp_matrix);

      // Finally, draw the cube meshes for all chunks.
      // TODO: Draw only meshes for only visible chunks within FOV.
      for bs in self.buffers.values() {
        p.bind_buffers(bs);
        gl::draw_elements_triangles_u16(bs.index_count);
        p.unbind_buffers();
      }
    }

    self.engine_impl.window.swap_buffers();
    self.engine_impl.window.flush();
  }

  /// Update for time passed and draw a frame.
  pub fn update_draw(&mut self) {
    if self.animating {
      // Done processing events; draw next animation frame.
      // Do a complete rotation every 10 seconds, assuming 60 FPS.
      self.fov.inc_angle(360.0 / 600.0);

      // Drawing is throttled to the screen update rate, so there is no need to do timing here.
      self.draw();
    }
  }

  /// Terminate the engine.
  #[cfg(target_os = "android")]
  pub fn term(&mut self) {
    self.animating = false;
    // Drop the program and the EGL context.
    self.engine_impl = Default::default();
    log!("*** Renderer terminated");
  }

  /// Called when window gains input focus.
  pub fn gained_focus(&mut self) {
    self.animating = true;
  }

  /// Called when window loses input focus.
  pub fn lost_focus(&mut self) {
    self.animating = false;
  }

  #[cfg(target_os = "linux")]
  pub fn is_closed(&self) -> bool {
    self.engine_impl.window.is_closed()
  }

  #[cfg(target_os = "linux")]
  pub fn poll_events(&self) -> PollEventsIterator {
    self.engine_impl.window.poll_events()
  }
}

/// A view matrix, eye is at (p.x, p.y + 2.12, p.z), rotating in horizontal plane counter-clockwise
/// and looking at (p.x + sin α, p.y + 2.12, p.z + cos α).
fn view_matrix(p: &Point3<i32>, angle: f32) -> Matrix4<f32> {
  let y = p.y as f32 + 2.12;  // 0.5 for half block under feet + 1.62 up to eye height.
  let (s, c) = angle.to_radians().sin_cos();

  let eye = Point3::new(p.x as f32, y, p.z as f32);
  let center = Point3::new(p.x as f32 + s, y, p.x as f32 + c);
  let up = Vector3::new(0.0, 1.0, 0.0);
  Matrix4::look_at(&eye, &center, &up)
}

const NEAR_PLANE: f32 = 0.1;
const FAR_PLANE: f32 = 60.0;
const FIELD_OF_VIEW_DEGREES: f32 = 70.0;

/// Perspective projection matrix as frustum matrix.
fn projection_matrix(width: i32, height: i32) -> Matrix4<f32> {
  let inverse_aspect = height as f32 / width as f32;
  let field_of_view = FIELD_OF_VIEW_DEGREES.to_radians();

  let right = NEAR_PLANE * (field_of_view / 2.0).tan();
  let left = -right;
  let top = right * inverse_aspect;
  let bottom = -top;
  let near = NEAR_PLANE;
  let far = FAR_PLANE;
  cgmath::frustum(left, right, bottom, top, near, far)
}
<commit_msg>Fix the center position to use p.z as it should.<commit_after>extern crate cgmath;
extern crate png;

use std::collections::HashMap;
use std::default::Default;
use time;

use cgmath::{Matrix4, Point3, Vector3};

#[cfg(target_os = "android")]
use egl_context::EglContext;
use gl;
use gl::Texture;
use mesh;
use program::{Buffers, Program};
use world::{Chunk, Point2, World};
#[cfg(target_os = "linux")]
use x11::{PollEventsIterator, XWindow};

#[cfg(target_os = "android")]
pub struct EngineImpl {
  pub egl_context: Option<Box<EglContext>>,
  pub program: Option<Program>,
}

#[cfg(target_os = "android")]
impl Default for EngineImpl {
  fn default() -> EngineImpl {
    EngineImpl {
      egl_context: None,
      program: None,
    }
  }
}

#[cfg(target_os = "linux")]
pub struct EngineImpl {
  pub window: XWindow,
  pub program: Program,
}

/// Field of view.
pub struct Fov {
  angle: f32,  // in degrees.
}

impl Fov {
  fn inc_angle(&mut self, degrees: f32) {
    self.angle += degrees;
    if self.angle > 360.0 {
      self.angle -= 360.0;
    }
  }
}

pub struct Engine {
  engine_impl: EngineImpl,
  animating: bool,
  fov: Fov,
  /// GL projection matrix
  projection_matrix: Matrix4<f32>,
  /// Texture atlas.
  texture: Texture,
  world: World,
  buffers: HashMap<Chunk, Buffers>,
}

impl Engine {
  #[cfg(target_os = "android")]
  pub fn new() -> Engine {
    Engine {
      engine_impl: Default::default(),
      animating: false,
      fov: Fov {
        angle: 0.0,
      },
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: World::new(&Point2::new(0.0, 0.0), FAR_PLANE),
      buffers: HashMap::new(),
    }
  }

  #[cfg(target_os = "linux")]
  pub fn new(window: XWindow, program: Program) -> Engine {
    Engine {
      engine_impl: EngineImpl {
        window: window,
        program: program,
      },
      animating: false,
      fov: Fov {
        angle: 0.0,
      },
      projection_matrix: Matrix4::identity(),
      texture: Default::default(),
      world: World::new(&Point2::new(0.0, 0.0), FAR_PLANE),
      buffers: HashMap::new(),
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "android")]
  pub fn init(&mut self, egl_context: Box<EglContext>, texture_atlas_bytes: &[u8]) {
    self.engine_impl.egl_context = Some(egl_context);

    self.engine_impl.program = match Program::new() {
      Ok(p) => Some(p),
      Err(e) => panic!("Program failed: {:?}", e),
    };

    self.common_init(texture_atlas_bytes);

    if let Some(ref p) = self.engine_impl.program {
      p.set_texture_unit(0);
    }

    // Silly gymnastics to satisfy the borrow checker.
    let wh = if let Some(ref ec) = self.engine_impl.egl_context {
      Some((ec.width, ec.height))
    } else {
      None
    };
    if let Some((w, h)) = wh {
      self.set_viewport(w, h);
    }
  }

  /// Initialize the engine.
  #[cfg(target_os = "linux")]
  pub fn init(&mut self, texture_atlas_bytes: &[u8]) {
    self.common_init(texture_atlas_bytes);
    self.engine_impl.program.set_texture_unit(0);
  }

  fn common_init(&mut self, texture_atlas_bytes: &[u8]) {
    // Set the background clear color to sky blue.
    gl::clear_color(0.5, 0.69, 1.0, 1.0);

    // Enable reverse face culling.
    gl::enable(gl::CULL_FACE);
    // Enable depth test.
    gl::enable(gl::DEPTH_TEST);
    gl::depth_func(gl::LEQUAL);

    // Set up textures.
    self.texture = Engine::load_texture_atlas(texture_atlas_bytes);
    gl::active_texture(gl::TEXTURE0);
    gl::bind_texture_2d(self.texture);

    self.load_meshes();
  }

  #[cfg(target_os = "android")]
  fn load_meshes(&mut self) {
    let start_s = time::precise_time_s();
    log!("*** Loading meshes...");

    if let Some(ref mut p) = self.engine_impl.program {
      for (c, bs) in self.world.chunk_blocks() {
        let vertices = mesh::create_mesh_vertices(bs, &self.world);
        let buffers = p.upload_vertices(&vertices);
        let c = (*c).clone();
        self.buffers.insert(c, buffers);
      }
    }

    let spent_ms = (time::precise_time_s() - start_s) * 1000.0;
    log!("*** Loaded meshes: {:.3}ms, {} chunks", spent_ms, self.buffers.len());
  }

  #[cfg(target_os = "linux")]
  fn load_meshes(&mut self) {
    let start_s = time::precise_time_s();
    log!("*** Loading meshes...");

    let p = &self.engine_impl.program;
    for (c, bs) in self.world.chunk_blocks() {
      let vertices = mesh::create_mesh_vertices(bs, &self.world);
      let buffers = p.upload_vertices(&vertices);
      let c = (*c).clone();
      self.buffers.insert(c, buffers);
    }

    let spent_ms = (time::precise_time_s() - start_s) * 1000.0;
    log!("*** Loaded meshes: {:.3}ms, {} chunks", spent_ms, self.buffers.len());
  }

  pub fn set_viewport(&mut self, w: i32, h: i32) {
    gl::viewport(0, 0, w, h);
    self.projection_matrix = projection_matrix(w, h);
  }

  /// Load texture atlas from assets folder.
  fn load_texture_atlas(texture_atlas_bytes: &[u8]) -> Texture {
    let image = png::load_png_from_memory(texture_atlas_bytes)
      .unwrap_or_else(|s| panic!("load_png_from_memory() failed: {}", s));

    let pixels = match image.pixels {
      png::PixelsByColorType::RGBA8(v) => v,
      _ => {
        let color_type = match image.pixels {
            png::PixelsByColorType::K8(_) => "K8",
            png::PixelsByColorType::KA8(_) => "KA8",
            png::PixelsByColorType::RGB8(_) => "RGB8",
            png::PixelsByColorType::RGBA8(_) => panic!("Should not happen"),
        };
        panic!("Only RGBA8 image format supported, was: {}", color_type);
      }
    };

    let texture = gl::gen_texture();
    gl::bind_texture_2d(texture);
    gl::texture_2d_param(gl::TEXTURE_MIN_FILTER, gl::NEAREST_MIPMAP_LINEAR);
    gl::texture_2d_param(gl::TEXTURE_MAG_FILTER, gl::NEAREST);
    gl::texture_2d_param(gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE);
    gl::texture_2d_param(gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE);
    gl::texture_2d_image_rgba(image.width as i32, image.height as i32, &pixels);
    gl::generate_mipmap_2d();
    gl::bind_texture_2d(0);

    texture
  }

  /// Draw a frame.
  #[cfg(target_os = "android")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    match self.engine_impl.egl_context {
      None => return,  // No display.
      Some(ref egl_context) => {
        gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

        match self.engine_impl.program {
          Some(ref p) => {
            if let Some(e) = self.world.eye() {
              // Compute the composite mvp_matrix and send it to program.  Model matrix
              // is always identity so instead of MVP = P * V * M just do MVP = P * V.
              let mvp_matrix = self.projection_matrix * view_matrix(&e, self.fov.angle);
              p.set_mvp_matrix(mvp_matrix);

              // Finally, draw the cube mesh.
              // TODO: Draw only meshes for only visible chunks within FOV.
              for bs in self.buffers.values() {
                p.bind_buffers(bs);
                gl::draw_elements_triangles_u16(bs.index_count);
                p.unbind_buffers();
              }
            }
          },
          None => panic!("Missing program, should never happen"),
        }

        egl_context.swap_buffers();
      }
    }
  }

  /// Draw a frame.
  #[cfg(target_os = "linux")]
  pub fn draw(&mut self) {
    if !self.animating {
      return;
    }

    gl::clear(gl::DEPTH_BUFFER_BIT | gl::COLOR_BUFFER_BIT);

    let p = &self.engine_impl.program;

    if let Some(e) = self.world.eye() {
      // Compute the composite mvp_matrix and send it to program.  Model matrix
      // is always identity so instead of MVP = P * V * M just do MVP = P * V.
      let mvp_matrix = self.projection_matrix * view_matrix(&e, self.fov.angle);
      p.set_mvp_matrix(mvp_matrix);

      // Finally, draw the cube meshes for all chunks.
      // TODO: Draw only meshes for only visible chunks within FOV.
      for bs in self.buffers.values() {
        p.bind_buffers(bs);
        gl::draw_elements_triangles_u16(bs.index_count);
        p.unbind_buffers();
      }
    }

    self.engine_impl.window.swap_buffers();
    self.engine_impl.window.flush();
  }

  /// Update for time passed and draw a frame.
  pub fn update_draw(&mut self) {
    if self.animating {
      // Done processing events; draw next animation frame.
      // Do a complete rotation every 10 seconds, assuming 60 FPS.
      self.fov.inc_angle(360.0 / 600.0);

      // Drawing is throttled to the screen update rate, so there is no need to do timing here.
      self.draw();
    }
  }

  /// Terminate the engine.
  #[cfg(target_os = "android")]
  pub fn term(&mut self) {
    self.animating = false;
    // Drop the program and the EGL context.
    self.engine_impl = Default::default();
    log!("*** Renderer terminated");
  }

  /// Called when window gains input focus.
  pub fn gained_focus(&mut self) {
    self.animating = true;
  }

  /// Called when window loses input focus.
  pub fn lost_focus(&mut self) {
    self.animating = false;
  }

  #[cfg(target_os = "linux")]
  pub fn is_closed(&self) -> bool {
    self.engine_impl.window.is_closed()
  }

  #[cfg(target_os = "linux")]
  pub fn poll_events(&self) -> PollEventsIterator {
    self.engine_impl.window.poll_events()
  }
}

/// A view matrix, eye is at (p.x, p.y + 2.12, p.z), rotating in horizontal plane counter-clockwise
/// and looking at (p.x + sin α, p.y + 2.12, p.z + cos α).
fn view_matrix(p: &Point3<i32>, angle: f32) -> Matrix4<f32> {
  let y = p.y as f32 + 2.12;  // 0.5 for half block under feet + 1.62 up to eye height.
  let (s, c) = angle.to_radians().sin_cos();

  let eye = Point3::new(p.x as f32, y, p.z as f32);
  let center = Point3::new(p.x as f32 + s, y, p.z as f32 + c);
  let up = Vector3::new(0.0, 1.0, 0.0);
  Matrix4::look_at(&eye, &center, &up)
}

const NEAR_PLANE: f32 = 0.1;
const FAR_PLANE: f32 = 60.0;
const FIELD_OF_VIEW_DEGREES: f32 = 70.0;

/// Perspective projection matrix as frustum matrix.
fn projection_matrix(width: i32, height: i32) -> Matrix4<f32> {
  let inverse_aspect = height as f32 / width as f32;
  let field_of_view = FIELD_OF_VIEW_DEGREES.to_radians();

  let right = NEAR_PLANE * (field_of_view / 2.0).tan();
  let left = -right;
  let top = right * inverse_aspect;
  let bottom = -top;
  let near = NEAR_PLANE;
  let far = FAR_PLANE;
  cgmath::frustum(left, right, bottom, top, near, far)
}
<|endoftext|>


<commit_before><commit_msg>Make the various window string methods generic on AsRef<str> to support owned strings or refs<commit_after><|endoftext|>