<commit_before>use std::{
    cell::{Cell, RefCell},
    sync::Arc,
};

use crate::prelude::*;

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

type SelectedItem = Option<Entity>;

#[derive(Debug, Copy, Clone)]
enum Action {
    CheckMouseUpOutside { x: f64, y: f64 },
}

/// The `ComboBoxItemState` handles the interaction an selection of a `ComboBoxItem`.
#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
    index: usize,
    selected_container: Entity,
    combo_box: Entity,
    // ugly work around for item builder context clone, todo make it better 😉
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
	self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
	let selected_index = ctx
	    .get_widget(self.combo_box)
	    .clone_or_default::<i32>("selected_index");
	let selected: bool = *ctx.widget().get("selected");

	if selected_index >= 0 && (selected_index as usize) == self.index && !selected {
	    self.request_selection_toggle.set(true);
	}

	if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
	    return;
	}
	self.request_selection_toggle.set(false);

	let entity = ctx.entity;

	// unselect previous selected item.
	if let Some(item) = ctx
	    .get_widget(self.combo_box)
	    .clone::<SelectedItem>("selected_item")
	{
	    ctx.get_widget(item).set("selected", false);
	    ctx.get_widget(item).update_theme_by_state(false);
	}

	ctx.widget().set("selected", true);
	ctx.widget().update_theme_by_state(false);
	ctx.get_widget(self.combo_box)
	    .set("selected_index", self.index as i32);
	ctx.get_widget(self.combo_box)
	    .set("selected_item", Some(entity));

	// Add selected content to combobox
	let index = self.index;
	let selected_container = self.selected_container;
	if let Some(builder) = &self.builder {
	    ctx.clear_children_of(selected_container);
	    let build_context = &mut ctx.build_context();
	    let selected_content = builder.borrow()(build_context, index);
	    build_context.append_child(selected_container, selected_content);
	}
    }
}

widget!(
    /// The `ComboBoxItem` describes an item inside of a `ComboBox`.
    ///
    /// **CSS element:** `combo_box_item``
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
	/// Sets or shares the background property.
	background: Brush,

	/// Sets or shares the border radius property.
	border_radius: f64,

	/// Sets or shares the border thickness property.
	border_width: Thickness,

	/// Sets or shares the border brush property.
	border_brush: Brush,

	/// Sets or shares the foreground property.
	foreground: Brush,

	/// Sets or share the font size property.
	font_size: f64,

	/// Sets or shares the font property.
	font: String,

	/// Sets or shares the padding property.
	padding: Thickness,

	/// Sets or shares the pressed property.
	pressed: bool,

	/// Sets or shares the selected property.
	selected: bool
    }
);

impl ComboBoxItem {
    fn selected_container(mut self, selected_container: impl Into<Entity>) -> Self {
	self.state_mut().selected_container = selected_container.into();
	self
    }

    fn index(mut self, index: usize) -> Self {
	self.state_mut().index = index;
	self
    }

    fn combo_box(mut self, combo_box: impl Into<Entity>) -> Self {
	self.state_mut().combo_box = combo_box.into();
	self
    }

    // Define the template build function for the selected content of the ComboBoxItems.
    fn items_builder(
	&mut self,
	builder: &Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>,
    ) {
	self.state_mut().builder = Some(builder.clone());
    }
}

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
	self.name("ComboBoxItem")
	    .min_width(64.0)
	    .height(24.0)
	    .selected(false)
	    .pressed(false)
	    .element("combo_box_item")
	    .padding(0.0)
	    .background("transparent")
	    .border_radius(0.0)
	    .border_width(0.0)
	    .border_brush("transparent")
	    .foreground(colors::LINK_WATER_COLOR)
	    .font_size(32.0)
	    .font("Roboto Regular")
	    .on_click(move |states, _| {
		states.get::<ComboBoxItemState>(id).toggle_selection();
		false
	    })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
	Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
	Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    popup: Entity,
    action: Option<Action>,
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
    count: usize,
    items_panel: Entity,
    selected_container: Entity,
}

impl ComboBoxState {
    fn action(&mut self, action: impl Into<Option<Action>>) {
	self.action = action.into();
    }

    // closes the popup on mouse up outside of the combobox and popup.
    fn close_popup(&mut self, ctx: &mut Context, x: f64, y: f64) {
	let combo_box_position = ctx.widget().clone::<Point>("position");
	let combo_box_bounds = ctx.widget().clone::<Rectangle>("bounds");

	let combo_box_global_bounds = Rectangle::new(
	    combo_box_position.x,
	    combo_box_position.y,
	    combo_box_bounds.width,
	    combo_box_bounds.height,
	);

	if !combo_box_global_bounds.contains((x, y)) {
	    ctx.widget().set("selected", false);
	    ctx.get_widget(self.popup)
		.set("visibility", Visibility::Collapsed);
	    ctx.get_widget(self.popup).update_theme_by_state(false);
	    ctx.widget().update_theme_by_state(false);
	}
    }
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
	let count = ctx.widget().clone_or_default::<usize>("count");
	let entity = ctx.entity;

	// build the combobox items
	if count != self.count {
	    if let Some(builder) = &self.builder {
		ctx.clear_children_of(self.items_panel);

		for i in 0..count {
		    let item = {
			let build_context = &mut ctx.build_context();
			let child = builder.borrow()(build_context, i);
			let mut item = ComboBoxItem::new()
			    .index(i)
			    .combo_box(entity)
			    .selected_container(self.selected_container);

			if let Some(builder) = &self.builder {
			    item.items_builder(builder);
			}
			let item = item.build(build_context);

			let mouse_behavior = MouseBehavior::new().build(build_context);
			build_context.register_shared_property::<Selector>(
			    "selector",
			    mouse_behavior,
			    item,
			);
			build_context.register_shared_property::<bool>(
			    "pressed",
			    mouse_behavior,
			    item,
			);
			build_context.append_child(item, mouse_behavior);

			build_context.register_shared_property::<Brush>("foreground", child, item);
			build_context.register_shared_property::<f32>("opacity", item, entity);
			build_context.register_shared_property::<f32>("opacity", child, entity);
			build_context.register_shared_property::<f64>("font_size", child, item);
			build_context.append_child(self.items_panel, item);
			build_context.append_child(mouse_behavior, child);

			item
		    };
		    ctx.get_widget(item).update_properties_by_theme();
		}
	    }

	    self.count = count;
	}
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context) {
	if self.action.is_none() || !(*ctx.widget().get::<bool>("selected")) {
	    return;
	}

	if let Some(action) = self.action {
	    match action {
		Action::CheckMouseUpOutside { x, y } => {
		    self.close_popup(ctx, x, y);
		}
	    }
	}
    }

    fn cleanup(&mut self, _: &mut Registry, ctx: &mut Context) {
	let _ = ctx.remove_child_from_overlay(self.popup);
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `combo_box`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
	/// Sets or shares the background property.
	background: Brush,

	/// Sets or shares the border radius property.
	border_radius: f64,

	/// Sets or shares the border thickness property.
	border_width: Thickness,

	/// Sets or shares the border brush property.
	border_brush: Brush,

	/// Sets or shares the foreground property.
	foreground: Brush,

	/// Sets or share the font size property.
	font_size: f64,

	/// Sets or shares the font property.
	font: String,

	/// Sets or shared the count.
	count: usize,

	/// Sets or shares the selected index. If the value is -1 no item is selected.
	selected_index: i32,

	/// The entity of the selected item.
	selected_item: SelectedItem,

	/// Sets or shares the padding property.
	padding: Thickness,

	/// Sets or shares the pressed property.
	pressed: bool,

	/// Sets or shares the flag if the drop down is open.
	selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static + Clone>(
	mut self,
	builder: F,
    ) -> Self {
	self.state_mut().builder = Some(Arc::new(RefCell::new(builder)));
	self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
	let selected_container = Container::new().attach(Grid::column(0)).build(ctx);

	let container = Container::new()
	    .id(CONTAINER)
	    .background(id)
	    .border_radius(id)
	    .border_width(id)
	    .border_brush(id)
	    .padding(id)
	    .child(
		Grid::new()
		    .columns(Columns::new().add("*").add(4.0).add(14.0))
		    .child(selected_container)
		    .child(
			FontIconBlock::new()
			    .attach(Grid::column(2))
			    .v_align("center")
			    .icon(material_icons_font_ttf::ARROW_DOWN_ICON)
			    .build(ctx),
		    )
		    .build(ctx),
	    )
	    .build(ctx);
	self.state_mut().selected_container = selected_container;

	let items_panel = Stack::new()
	    .v_align("start")
	    .id(ITEMS_PANEL)
	    .orientation("vertical")
	    .build(ctx);

	self.state_mut().items_panel = items_panel;
	let scroll_viewer = ScrollViewer::new()
	    .scroll_viewer_mode(("disabled", "auto"))
	    .child(items_panel)
	    .build(ctx);

	let popup = Popup::new()
	    .height(200.0)
	    .open(("selected", id))
	    .child(scroll_viewer)
	    .child(
		ScrollIndicator::new()
		    .padding(2.0)
		    .content_id(items_panel.0)
		    .scroll_offset(scroll_viewer)
		    .opacity(id)
		    .build(ctx),
	    )
	    .target(container.0)
	    .build(ctx);
	self.state_mut().popup = popup;

	let _ = ctx.append_child_to_overlay(popup);

	self.name("ComboBox")
	    .element("combo_box")
	    .height(32.0)
	    .min_width(80.0)
	    .selected(false)
	    .selected_index(-1)
	    .child(
		MouseBehavior::new()
		    .pressed(id)
		    .enabled(id)
		    .target(id.0)
		    .child(
			SelectionBehavior::new()
			    .selected(id)
			    .enabled(id)
			    .target(id.0)
			    .child(container)
			    .build(ctx),
		    )
		    .build(ctx),
	    )
	    .on_global_mouse_up(move |states, e| {
		states
		    .get_mut::<ComboBoxState>(id)
		    .action(Action::CheckMouseUpOutside { x: e.x, y: e.y })
	    })
    }
}
<commit_msg>widgets: combo_box.rs update<commit_after>use std::{
    cell::{Cell, RefCell},
    sync::Arc,
};

use crate::prelude::*;

use super::behaviors::{MouseBehavior, SelectionBehavior};

static CONTAINER: &'static str = "container";
static ITEMS_PANEL: &'static str = "items_panel";

type SelectedItem = Option<Entity>;

#[derive(Debug, Copy, Clone)]
enum Action {
    CheckMouseUpOutside { x: f64, y: f64 },
}

/// The `ComboBoxItemState` handles the interaction an selection of a `ComboBoxItem`.
#[derive(Default, AsAny)]
pub struct ComboBoxItemState {
    request_selection_toggle: Cell<bool>,
    index: usize,
    selected_container: Entity,
    combo_box: Entity,
    // ugly work around for item builder context clone, todo make it better 😉
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
}

impl ComboBoxItemState {
    fn toggle_selection(&self) {
        self.request_selection_toggle.set(true);
    }
}

impl State for ComboBoxItemState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        let selected_index = ctx
            .get_widget(self.combo_box)
            .clone_or_default::<i32>("selected_index");
        let selected: bool = *ctx.widget().get("selected");

        if selected_index >= 0 && (selected_index as usize) == self.index && !selected {
            self.request_selection_toggle.set(true);
        }

        if !ctx.widget().get::<bool>("enabled") || !self.request_selection_toggle.get() {
            return;
        }
        self.request_selection_toggle.set(false);

        let entity = ctx.entity;

        // unselect previous selected item.
        if let Some(item) = ctx
            .get_widget(self.combo_box)
            .clone::<SelectedItem>("selected_item")
        {
            ctx.get_widget(item).set("selected", false);
            ctx.get_widget(item).update_theme_by_state(false);
        }

        ctx.widget().set("selected", true);
        ctx.widget().update_theme_by_state(false);
        ctx.get_widget(self.combo_box)
            .set("selected_index", self.index as i32);
        ctx.get_widget(self.combo_box)
            .set("selected_item", Some(entity));

        // Add selected content to combobox
        let index = self.index;
        let selected_container = self.selected_container;
        if let Some(builder) = &self.builder {
            ctx.clear_children_of(selected_container);
            let build_context = &mut ctx.build_context();
            let selected_content = builder.borrow()(build_context, index);
            build_context.append_child(selected_container, selected_content);
        }
    }
}

widget!(
    /// The `ComboBoxItem` describes an item inside of a `ComboBox`.
    ///
    /// **CSS element:** `combo_box_item``
    ComboBoxItem<ComboBoxItemState>: MouseHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the selected property.
        selected: bool
    }
);

impl ComboBoxItem {
    fn selected_container(mut self, selected_container: impl Into<Entity>) -> Self {
        self.state_mut().selected_container = selected_container.into();
        self
    }

    fn index(mut self, index: usize) -> Self {
        self.state_mut().index = index;
        self
    }

    fn combo_box(mut self, combo_box: impl Into<Entity>) -> Self {
        self.state_mut().combo_box = combo_box.into();
        self
    }

    // Define the template build function for the selected content of the ComboBoxItems.
    fn items_builder(
        &mut self,
        builder: &Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>,
    ) {
        self.state_mut().builder = Some(builder.clone());
    }
}

impl Template for ComboBoxItem {
    fn template(self, id: Entity, _: &mut BuildContext) -> Self {
        self.name("ComboBoxItem")
            .min_width(64.0)
            .height(24.0)
            .selected(false)
            .pressed(false)
            .element("combo_box_item")
            .padding(0.0)
            .background("transparent")
            .border_radius(0.0)
            .border_width(0.0)
            .border_brush("transparent")
            .foreground(colors::LINK_WATER_COLOR)
            .font_size(32.0)
            .font("Roboto Regular")
            .on_click(move |states, _| {
                states.get::<ComboBoxItemState>(id).toggle_selection();
                false
            })
    }

    fn render_object(&self) -> Box<dyn RenderObject> {
        Box::new(RectangleRenderObject)
    }

    fn layout(&self) -> Box<dyn Layout> {
        Box::new(PaddingLayout::new())
    }
}

/// The `ComboBoxState` is used to manipulate the position of the thumb of the slider widget.
#[derive(Default, AsAny)]
pub struct ComboBoxState {
    popup: Entity,
    action: Option<Action>,
    builder: Option<Arc<RefCell<dyn Fn(&mut BuildContext, usize) -> Entity + 'static>>>,
    count: usize,
    items_panel: Entity,
    selected_container: Entity,
}

impl ComboBoxState {
    fn action(&mut self, action: impl Into<Option<Action>>) {
        self.action = action.into();
    }

    // closes the popup on mouse up outside of the combobox and popup.
    fn close_popup(&mut self, ctx: &mut Context, x: f64, y: f64) {
        let combo_box_position = ctx.widget().clone::<Point>("position");
        let combo_box_bounds = ctx.widget().clone::<Rectangle>("bounds");

        let combo_box_global_bounds = Rectangle::new(
            combo_box_position.x,
            combo_box_position.y,
            combo_box_bounds.width,
            combo_box_bounds.height,
        );

        if !combo_box_global_bounds.contains((x, y)) {
            ctx.widget().set("selected", false);
            ctx.get_widget(self.popup)
                .set("visibility", Visibility::Collapsed);
            ctx.get_widget(self.popup).update_theme_by_state(false);
            ctx.widget().update_theme_by_state(false);
        }
    }
}

impl State for ComboBoxState {
    fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
        let count = ctx.widget().clone_or_default::<usize>("count");
        let entity = ctx.entity;

        // build the combobox items
        if count != self.count {
            if let Some(builder) = &self.builder {
                ctx.clear_children_of(self.items_panel);

                for i in 0..count {
                    let item = {
                        let build_context = &mut ctx.build_context();
                        let child = builder.borrow()(build_context, i);
                        let mut item = ComboBoxItem::new()
                            .index(i)
                            .combo_box(entity)
                            .selected_container(self.selected_container);

                        if let Some(builder) = &self.builder {
                            item.items_builder(builder);
                        }
                        let item = item.build(build_context);

                        let mouse_behavior = MouseBehavior::new().build(build_context);
                        build_context.register_shared_property::<Selector>(
                            "selector",
                            mouse_behavior,
                            item,
                        );
                        build_context.register_shared_property::<bool>(
                            "pressed",
                            mouse_behavior,
                            item,
                        );
                        build_context.append_child(item, mouse_behavior);

                        build_context.register_shared_property::<Brush>("foreground", child, item);
                        build_context.register_shared_property::<f32>("opacity", item, entity);
                        build_context.register_shared_property::<f32>("opacity", child, entity);
                        build_context.register_shared_property::<f64>("font_size", child, item);
                        build_context.append_child(self.items_panel, item);
                        build_context.append_child(mouse_behavior, child);

                        item
                    };
                    ctx.get_widget(item).update_properties_by_theme();
                }
            }

            self.count = count;
        }
    }

    fn update_post_layout(&mut self, _: &mut Registry, ctx: &mut Context) {
        if self.action.is_none() || !(*ctx.widget().get::<bool>("selected")) {
            return;
        }

        if let Some(action) = self.action {
            match action {
                Action::CheckMouseUpOutside { x, y } => {
                    self.close_popup(ctx, x, y);
                }
            }
        }
    }

    fn cleanup(&mut self, _: &mut Registry, ctx: &mut Context) {
        let _ = ctx.remove_child_from_overlay(self.popup);
    }
}

// todo use code of list view item, by create combobox item insert entity of popup container

widget!(
    /// The `ComboBox` represents an selection widget with a drop-down list.
    ///
    /// **CSS element:** `combo_box`
    ComboBox<ComboBoxState>: MouseHandler, ChangedHandler {
        /// Sets or shares the background property.
        background: Brush,

        /// Sets or shares the border radius property.
        border_radius: f64,

        /// Sets or shares the border thickness property.
        border_width: Thickness,

        /// Sets or shares the border brush property.
        border_brush: Brush,

        /// Sets or shares the foreground property.
        foreground: Brush,

        /// Sets or share the font size property.
        font_size: f64,

        /// Sets or shares the font property.
        font: String,

        /// Sets or shared the count.
        count: usize,

        /// Sets or shares the selected index. If the value is -1 no item is selected.
        selected_index: i32,

        /// The entity of the selected item.
        selected_item: SelectedItem,

        /// Sets or shares the padding property.
        padding: Thickness,

        /// Sets or shares the pressed property.
        pressed: bool,

        /// Sets or shares the flag if the drop down is open.
        selected: bool
    }
);

impl ComboBox {
    /// Define the template build function for the content of the ComboBoxItems.
    pub fn items_builder<F: Fn(&mut BuildContext, usize) -> Entity + 'static + Clone>(
        mut self,
        builder: F,
    ) -> Self {
        self.state_mut().builder = Some(Arc::new(RefCell::new(builder)));
        self
    }
}

impl Template for ComboBox {
    fn template(mut self, id: Entity, ctx: &mut BuildContext) -> Self {
        let selected_container = Container::new().attach(Grid::column(0)).build(ctx);

        let container = Container::new()
            .id(CONTAINER)
            .background(id)
            .border_radius(id)
            .border_width(id)
            .border_brush(id)
            .padding(id)
            .child(
                Grid::new()
                    .columns(Columns::new().add("*").add(4.0).add(14.0))
                    .child(selected_container)
                    .child(
                        FontIconBlock::new()
                            .attach(Grid::column(2))
                            .v_align("center")
                            .icon(material_icons_font_ttf::MD_ARROW_DROP_DOWN_CIRCLE)
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .build(ctx);
        self.state_mut().selected_container = selected_container;

        let items_panel = Stack::new()
            .v_align("start")
            .id(ITEMS_PANEL)
            .orientation("vertical")
            .build(ctx);

        self.state_mut().items_panel = items_panel;
        let scroll_viewer = ScrollViewer::new()
            .scroll_viewer_mode(("disabled", "auto"))
            .child(items_panel)
            .build(ctx);

        let popup = Popup::new()
            .height(200.0)
            .open(("selected", id))
            .child(scroll_viewer)
            .child(
                ScrollIndicator::new()
                    .padding(2.0)
                    .content_id(items_panel.0)
                    .scroll_offset(scroll_viewer)
                    .opacity(id)
                    .build(ctx),
            )
            .target(container.0)
            .build(ctx);
        self.state_mut().popup = popup;

        let _ = ctx.append_child_to_overlay(popup);

        self.name("ComboBox")
            .element("combo_box")
            .height(32.0)
            .min_width(80.0)
            .selected(false)
            .selected_index(-1)
            .child(
                MouseBehavior::new()
                    .pressed(id)
                    .enabled(id)
                    .target(id.0)
                    .child(
                        SelectionBehavior::new()
                            .selected(id)
                            .enabled(id)
                            .target(id.0)
                            .child(container)
                            .build(ctx),
                    )
                    .build(ctx),
            )
            .on_global_mouse_up(move |states, e| {
                states
                    .get_mut::<ComboBoxState>(id)
                    .action(Action::CheckMouseUpOutside { x: e.x, y: e.y })
            })
    }
}
<|endoftext|>


<commit_before>use gfx;
use shaders::Projection;

gfx_defines! {
    constant TileMapData {
        data: [f32; 4] = "data",
    }

    constant TilemapSettings {
        world_size: [f32; 2] = "u_WorldSize",
        tilesheet_size: [f32; 2] = "u_TilesheetSize",
    }

    vertex VertexData {
        pos: [f32; 3] = "a_Pos",
        buf_pos: [f32; 2] = "a_BufPos",
    }

    pipeline pipe {
        vbuf: gfx::VertexBuffer<VertexData> = (),
        projection_cb: gfx::ConstantBuffer<Projection> = "b_VsLocals",
        tilemap: gfx::ConstantBuffer<TileMapData> = "b_TileMap",
        tilemap_cb: gfx::ConstantBuffer<TilemapSettings> = "b_PsLocals",
        tilesheet: gfx::TextureSampler<[f32; 4]> = "t_TileSheet",
        out_color: gfx::RenderTarget<gfx::format::Rgba8> = "Target0",
        out_depth: gfx::DepthTarget<gfx::format::DepthStencil> = gfx::preset::depth::LESS_EQUAL_WRITE,
    }
}
<commit_msg>Reindent terrain/gfx_macros.rs<commit_after>use gfx;
use shaders::Projection;

gfx_defines! {
  constant TileMapData {
      data: [f32; 4] = "data",
  }

  constant TilemapSettings {
      world_size: [f32; 2] = "u_WorldSize",
      tilesheet_size: [f32; 2] = "u_TilesheetSize",
  }

  vertex VertexData {
      pos: [f32; 3] = "a_Pos",
      buf_pos: [f32; 2] = "a_BufPos",
  }

  pipeline pipe {
      vbuf: gfx::VertexBuffer<VertexData> = (),
      projection_cb: gfx::ConstantBuffer<Projection> = "b_VsLocals",
      tilemap: gfx::ConstantBuffer<TileMapData> = "b_TileMap",
      tilemap_cb: gfx::ConstantBuffer<TilemapSettings> = "b_PsLocals",
      tilesheet: gfx::TextureSampler<[f32; 4]> = "t_TileSheet",
      out_color: gfx::RenderTarget<gfx::format::Rgba8> = "Target0",
      out_depth: gfx::DepthTarget<gfx::format::DepthStencil> = gfx::preset::depth::LESS_EQUAL_WRITE,
  }
}
<|endoftext|>


<commit_before>use std::iter::IntoIterator;
use std::str::FromStr;

use crate::error::Result;
use crate::map::Map;
use crate::source::AsyncSource;
use crate::{config::Config, path::Expression, source::Source, value::Value};

/// A configuration builder
///
/// It registers ordered sources of configuration to later build consistent [`Config`] from them.
/// Configuration sources it defines are defaults, [`Source`]s and overrides.
///
/// Defaults are alaways loaded first and can be overwritten by any of two other sources.
/// Overrides are always loaded last, thus cannot be overridden.
/// Both can be only set explicitly key by key in code
/// using [`set_default`](Self::set_default) or [`set_override`](Self::set_override).
///
/// An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources
/// like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing
/// a trait for a struct.
///
/// Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config.
/// It happens on demand when [`build`](Self::build) (or its alternative) is called.
/// Therefore all errors, related to any of the [`Source`] will only show up then.
///
/// # Sync and async builder
///
/// [`ConfigBuilder`] uses type parameter to keep track of builder state.
///
/// In [`DefaultState`] builder only supports [`Source`]s
///
/// In [`AsyncState`] it supports both [`Source`]s and [`AsyncSource`]s at the price of building using `async fn`.
///
/// # Examples
///
/// ```rust
/// # use config::*;
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder()
///     .set_default("default", "1")?
///     .add_source(File::new("config/settings", FileFormat::Json))
/// //  .add_async_source(...)
///     .set_override("override", "1")?;
///
/// match builder.build() {
///     Ok(config) => {
///         // use your config
///     },
///     Err(e) => {
///         // something went wrong
///     }
/// }
/// # Ok(())
/// # }
/// ```
///
/// If any [`AsyncSource`] is used, the builder will transition to [`AsyncState`].
/// In such case, it is required to _await_ calls to [`build`](Self::build) and its non-consuming sibling.
///
/// Calls can be not chained as well
/// ```rust
/// # use std::error::Error;
/// # use config::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder();
/// builder = builder.set_default("default", "1")?;
/// builder = builder.add_source(File::new("config/settings", FileFormat::Json));
/// builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json));
/// builder = builder.set_override("override", "1")?;
/// # Ok(())
/// # }
/// ```
///
/// Calling [`Config::builder`](Config::builder) yields builder in the default state.
/// If having an asynchronous state as the initial state is desired, _turbofish_ notation needs to be used.
/// ```rust
/// # use config::{*, builder::AsyncState};
/// let mut builder = ConfigBuilder::<AsyncState>::default();
/// ```
///
/// If for some reason acquiring builder in default state is required without calling [`Config::builder`](Config::builder)
/// it can also be achieved.
/// ```rust
/// # use config::{*, builder::DefaultState};
/// let mut builder = ConfigBuilder::<DefaultState>::default();
/// ```
#[derive(Debug, Clone, Default)]
pub struct ConfigBuilder<St: BuilderState> {
    defaults: Map<Expression, Value>,
    overrides: Map<Expression, Value>,
    state: St,
}

/// Represents [`ConfigBuilder`] state.
pub trait BuilderState {}

/// Represents data specific to builder in default, sychronous state, without support for async.
#[derive(Debug, Default)]
pub struct DefaultState {
    sources: Vec<Box<dyn Source + Send + Sync>>,
}

/// The asynchronous configuration builder.
///
/// Similar to a [`ConfigBuilder`] it maintains a set of defaults, a set of sources, and overrides.
///
/// Defaults do not override anything, sources override defaults, and overrides override anything else.
/// Within those three groups order of adding them at call site matters - entities added later take precedence.
///
/// For more detailed description and examples see [`ConfigBuilder`].
/// [`AsyncConfigBuilder`] is just an extension of it that takes async functions into account.
///
/// To obtain a [`Config`] call [`build`](AsyncConfigBuilder::build) or [`build_cloned`](AsyncConfigBuilder::build_cloned)
///
/// # Example
/// Since this library does not implement any [`AsyncSource`] an example in rustdocs cannot be given.
/// Detailed explanation about why such a source is not implemented is in [`AsyncSource`]'s documentation.
///
/// Refer to [`ConfigBuilder`] for similar API sample usage or to the examples folder of the crate, where such a source is implemented.
#[derive(Debug, Clone, Default)]
pub struct AsyncConfigBuilder {}

/// Represents data specific to builder in asychronous state, with support for async.
#[derive(Debug, Default)]
pub struct AsyncState {
    sources: Vec<SourceType>,
}

#[derive(Debug, Clone)]
enum SourceType {
    Sync(Box<dyn Source + Send + Sync>),
    Async(Box<dyn AsyncSource + Send + Sync>),
}

impl BuilderState for DefaultState {}
impl BuilderState for AsyncState {}

impl<St: BuilderState> ConfigBuilder<St> {
    // operations allowed in any state

    /// Set a default `value` at `key`
    ///
    /// This value can be overwritten by any [`Source`], [`AsyncSource`] or override.
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.defaults
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }

    /// Set an override
    ///
    /// This function sets an overwrite value. It will not be altered by any default, [`Source`] nor [`AsyncSource`]
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.overrides
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }
}

impl ConfigBuilder<DefaultState> {
    // operations allowed in sync state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> Self
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(Box::new(source));
        self
    }

    /// Registers new [`AsyncSource`] in this builder and forces transition to [`AsyncState`].
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        let async_state = ConfigBuilder {
            state: AsyncState {
                sources: self
                    .state
                    .sources
                    .into_iter()
                    .map(|s| SourceType::Sync(s))
                    .collect(),
            },
            defaults: self.defaults,
            overrides: self.overrides,
        };

        async_state.add_async_source(source)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
    }

    fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[Box<dyn Source + Send + Sync>],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults.into_iter() {
            key.set(&mut cache, val);
        }

        // Add sources
        sources.collect_to(&mut cache)?;

        // Add overrides
        for (key, val) in overrides.into_iter() {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}

impl ConfigBuilder<AsyncState> {
    // operations allowed in async state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> ConfigBuilder<AsyncState>
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Sync(Box::new(source)));
        self
    }

    /// Registers new [`AsyncSource`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_async_source<T>(mut self, source: T) -> ConfigBuilder<AsyncState>
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Async(Box::new(source)));
        self
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources).await
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
        .await
    }

    async fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[SourceType],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults.into_iter() {
            key.set(&mut cache, val);
        }

        for source in sources.iter() {
            match source {
                SourceType::Sync(source) => source.collect_to(&mut cache)?,
                SourceType::Async(source) => source.collect_to(&mut cache).await?,
            }
        }

        // Add overrides
        for (key, val) in overrides.into_iter() {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}
<commit_msg>use 'Self' to refer to own type<commit_after>use std::iter::IntoIterator;
use std::str::FromStr;

use crate::error::Result;
use crate::map::Map;
use crate::source::AsyncSource;
use crate::{config::Config, path::Expression, source::Source, value::Value};

/// A configuration builder
///
/// It registers ordered sources of configuration to later build consistent [`Config`] from them.
/// Configuration sources it defines are defaults, [`Source`]s and overrides.
///
/// Defaults are alaways loaded first and can be overwritten by any of two other sources.
/// Overrides are always loaded last, thus cannot be overridden.
/// Both can be only set explicitly key by key in code
/// using [`set_default`](Self::set_default) or [`set_override`](Self::set_override).
///
/// An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources
/// like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing
/// a trait for a struct.
///
/// Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config.
/// It happens on demand when [`build`](Self::build) (or its alternative) is called.
/// Therefore all errors, related to any of the [`Source`] will only show up then.
///
/// # Sync and async builder
///
/// [`ConfigBuilder`] uses type parameter to keep track of builder state.
///
/// In [`DefaultState`] builder only supports [`Source`]s
///
/// In [`AsyncState`] it supports both [`Source`]s and [`AsyncSource`]s at the price of building using `async fn`.
///
/// # Examples
///
/// ```rust
/// # use config::*;
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder()
///     .set_default("default", "1")?
///     .add_source(File::new("config/settings", FileFormat::Json))
/// //  .add_async_source(...)
///     .set_override("override", "1")?;
///
/// match builder.build() {
///     Ok(config) => {
///         // use your config
///     },
///     Err(e) => {
///         // something went wrong
///     }
/// }
/// # Ok(())
/// # }
/// ```
///
/// If any [`AsyncSource`] is used, the builder will transition to [`AsyncState`].
/// In such case, it is required to _await_ calls to [`build`](Self::build) and its non-consuming sibling.
///
/// Calls can be not chained as well
/// ```rust
/// # use std::error::Error;
/// # use config::*;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// let mut builder = Config::builder();
/// builder = builder.set_default("default", "1")?;
/// builder = builder.add_source(File::new("config/settings", FileFormat::Json));
/// builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json));
/// builder = builder.set_override("override", "1")?;
/// # Ok(())
/// # }
/// ```
///
/// Calling [`Config::builder`](Config::builder) yields builder in the default state.
/// If having an asynchronous state as the initial state is desired, _turbofish_ notation needs to be used.
/// ```rust
/// # use config::{*, builder::AsyncState};
/// let mut builder = ConfigBuilder::<AsyncState>::default();
/// ```
///
/// If for some reason acquiring builder in default state is required without calling [`Config::builder`](Config::builder)
/// it can also be achieved.
/// ```rust
/// # use config::{*, builder::DefaultState};
/// let mut builder = ConfigBuilder::<DefaultState>::default();
/// ```
#[derive(Debug, Clone, Default)]
pub struct ConfigBuilder<St: BuilderState> {
    defaults: Map<Expression, Value>,
    overrides: Map<Expression, Value>,
    state: St,
}

/// Represents [`ConfigBuilder`] state.
pub trait BuilderState {}

/// Represents data specific to builder in default, sychronous state, without support for async.
#[derive(Debug, Default)]
pub struct DefaultState {
    sources: Vec<Box<dyn Source + Send + Sync>>,
}

/// The asynchronous configuration builder.
///
/// Similar to a [`ConfigBuilder`] it maintains a set of defaults, a set of sources, and overrides.
///
/// Defaults do not override anything, sources override defaults, and overrides override anything else.
/// Within those three groups order of adding them at call site matters - entities added later take precedence.
///
/// For more detailed description and examples see [`ConfigBuilder`].
/// [`AsyncConfigBuilder`] is just an extension of it that takes async functions into account.
///
/// To obtain a [`Config`] call [`build`](AsyncConfigBuilder::build) or [`build_cloned`](AsyncConfigBuilder::build_cloned)
///
/// # Example
/// Since this library does not implement any [`AsyncSource`] an example in rustdocs cannot be given.
/// Detailed explanation about why such a source is not implemented is in [`AsyncSource`]'s documentation.
///
/// Refer to [`ConfigBuilder`] for similar API sample usage or to the examples folder of the crate, where such a source is implemented.
#[derive(Debug, Clone, Default)]
pub struct AsyncConfigBuilder {}

/// Represents data specific to builder in asychronous state, with support for async.
#[derive(Debug, Default)]
pub struct AsyncState {
    sources: Vec<SourceType>,
}

#[derive(Debug, Clone)]
enum SourceType {
    Sync(Box<dyn Source + Send + Sync>),
    Async(Box<dyn AsyncSource + Send + Sync>),
}

impl BuilderState for DefaultState {}
impl BuilderState for AsyncState {}

impl<St: BuilderState> ConfigBuilder<St> {
    // operations allowed in any state

    /// Set a default `value` at `key`
    ///
    /// This value can be overwritten by any [`Source`], [`AsyncSource`] or override.
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_default<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.defaults
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }

    /// Set an override
    ///
    /// This function sets an overwrite value. It will not be altered by any default, [`Source`] nor [`AsyncSource`]
    ///
    /// # Errors
    ///
    /// Fails if `Expression::from_str(key)` fails.
    pub fn set_override<S, T>(mut self, key: S, value: T) -> Result<ConfigBuilder<St>>
    where
        S: AsRef<str>,
        T: Into<Value>,
    {
        self.overrides
            .insert(Expression::from_str(key.as_ref())?, value.into());
        Ok(self)
    }
}

impl ConfigBuilder<DefaultState> {
    // operations allowed in sync state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> Self
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(Box::new(source));
        self
    }

    /// Registers new [`AsyncSource`] in this builder and forces transition to [`AsyncState`].
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    pub fn add_async_source<T>(self, source: T) -> ConfigBuilder<AsyncState>
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        let async_state = ConfigBuilder {
            state: AsyncState {
                sources: self
                    .state
                    .sources
                    .into_iter()
                    .map(|s| SourceType::Sync(s))
                    .collect(),
            },
            defaults: self.defaults,
            overrides: self.overrides,
        };

        async_state.add_async_source(source)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources)
    }

    /// Reads all registered [`Source`]s.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
    }

    fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[Box<dyn Source + Send + Sync>],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults.into_iter() {
            key.set(&mut cache, val);
        }

        // Add sources
        sources.collect_to(&mut cache)?;

        // Add overrides
        for (key, val) in overrides.into_iter() {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}

impl ConfigBuilder<AsyncState> {
    // operations allowed in async state

    /// Registers new [`Source`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_source<T>(mut self, source: T) -> Self
    where
        T: Source + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Sync(Box::new(source)));
        self
    }

    /// Registers new [`AsyncSource`] in this builder.
    ///
    /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use.
    #[must_use]
    pub fn add_async_source<T>(mut self, source: T) -> Self
    where
        T: AsyncSource + Send + Sync + 'static,
    {
        self.state.sources.push(SourceType::Async(Box::new(source)));
        self
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// This is the method that invokes all I/O operations.
    /// For a non consuming alternative see [`build_cloned`](Self::build_cloned)
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build(self) -> Result<Config> {
        Self::build_internal(self.defaults, self.overrides, &self.state.sources).await
    }

    /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides.
    ///
    /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse.
    /// Internally it clones data to achieve it.
    ///
    /// # Errors
    /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons,
    /// this method returns error.
    pub async fn build_cloned(&self) -> Result<Config> {
        Self::build_internal(
            self.defaults.clone(),
            self.overrides.clone(),
            &self.state.sources,
        )
        .await
    }

    async fn build_internal(
        defaults: Map<Expression, Value>,
        overrides: Map<Expression, Value>,
        sources: &[SourceType],
    ) -> Result<Config> {
        let mut cache: Value = Map::<String, Value>::new().into();

        // Add defaults
        for (key, val) in defaults.into_iter() {
            key.set(&mut cache, val);
        }

        for source in sources.iter() {
            match source {
                SourceType::Sync(source) => source.collect_to(&mut cache)?,
                SourceType::Async(source) => source.collect_to(&mut cache).await?,
            }
        }

        // Add overrides
        for (key, val) in overrides.into_iter() {
            key.set(&mut cache, val);
        }

        Ok(Config::new(cache))
    }
}
<|endoftext|>


<commit_before>//! Error conditions.

use std::fmt::{self, Display, Formatter};

/// An error encountered when trying to parse an invalid ID string.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error {
    /// The ID's localpart contains invalid characters.
    ///
    /// Only relevant for user IDs.
    InvalidCharacters,
    /// The key version contains outside of [a-zA-Z0-9_].
    InvalidKeyVersion,
    /// The localpart of the ID string is not valid (because it is empty).
    InvalidLocalPart,
    /// The server name part of the the ID string is not a valid server name.
    InvalidServerName,
    /// The ID exceeds 255 bytes (or 32 codepoints for a room version ID.)
    MaximumLengthExceeded,
    /// The ID is less than 4 characters (or is an empty room version ID.)
    MinimumLengthNotSatisfied,
    /// The ID is missing the colon delimiter between localpart and server name.
    MissingDelimiter,
    /// The ID is missing the colon delimiter between key algorithm and device ID.
    MissingDeviceKeyDelimiter,
    /// The ID is missing the colon delimiter between key algorithm and version.
    MissingServerKeyDelimiter,
    /// The ID is missing the leading sigil.
    MissingSigil,
    /// The key algorithm is not recognized.
    UnknownKeyAlgorithm,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let message = match self {
            Error::InvalidCharacters => "localpart contains invalid characters",
            Error::InvalidKeyVersion => "key id version contains invalid characters",
            Error::InvalidLocalPart => "localpart is empty",
            Error::InvalidServerName => "server name is not a valid IP address or domain name",
            Error::MaximumLengthExceeded => "ID exceeds 255 bytes",
            Error::MinimumLengthNotSatisfied => "ID must be at least 4 characters",
            Error::MissingDelimiter => "colon is required between localpart and server name",
            Error::MissingDeviceKeyDelimiter => "colon is required between algorithm and device ID",
            Error::MissingServerKeyDelimiter => "colon is required between algorithm and version",
            Error::MissingSigil => "leading sigil is missing",
            Error::UnknownKeyAlgorithm => "unknown key algorithm specified",
        };

        write!(f, "{}", message)
    }
}

impl std::error::Error for Error {}
<commit_msg>identifiers-validation: Fix docs<commit_after>//! Error conditions.

use std::fmt::{self, Display, Formatter};

/// An error encountered when trying to parse an invalid ID string.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error {
    /// The ID's localpart contains invalid characters.
    ///
    /// Only relevant for user IDs.
    InvalidCharacters,
    /// The key version contains outside of [a-zA-Z0-9_].
    InvalidKeyVersion,
    /// The localpart of the ID string is not valid (because it is empty).
    InvalidLocalPart,
    /// The server name part of the the ID string is not a valid server name.
    InvalidServerName,
    /// The ID exceeds 255 bytes (or 32 codepoints for a room version ID).
    MaximumLengthExceeded,
    /// The ID is less than 4 characters (or is an empty room version ID).
    MinimumLengthNotSatisfied,
    /// The ID is missing the colon delimiter between localpart and server name.
    MissingDelimiter,
    /// The ID is missing the colon delimiter between key algorithm and device ID.
    MissingDeviceKeyDelimiter,
    /// The ID is missing the colon delimiter between key algorithm and version.
    MissingServerKeyDelimiter,
    /// The ID is missing the leading sigil.
    MissingSigil,
    /// The key algorithm is not recognized.
    UnknownKeyAlgorithm,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let message = match self {
            Error::InvalidCharacters => "localpart contains invalid characters",
            Error::InvalidKeyVersion => "key id version contains invalid characters",
            Error::InvalidLocalPart => "localpart is empty",
            Error::InvalidServerName => "server name is not a valid IP address or domain name",
            Error::MaximumLengthExceeded => "ID exceeds 255 bytes",
            Error::MinimumLengthNotSatisfied => "ID must be at least 4 characters",
            Error::MissingDelimiter => "colon is required between localpart and server name",
            Error::MissingDeviceKeyDelimiter => "colon is required between algorithm and device ID",
            Error::MissingServerKeyDelimiter => "colon is required between algorithm and version",
            Error::MissingSigil => "leading sigil is missing",
            Error::UnknownKeyAlgorithm => "unknown key algorithm specified",
        };

        write!(f, "{}", message)
    }
}

impl std::error::Error for Error {}
<|endoftext|>


<commit_before>use rust2html::htmlwriter::HtmlWriter;
use std::str;
use std::io::{File, UserDir};
use std::io::fs::{mkdir_recursive,copy,walk_dir};
use rust2html::RF_Options;
use collections::hashmap::HashMap;
use collections::hashmap::HashSet;
use rust2html;
use std::cmp;

// create index page. Show subdirectories and files matching extentions.
// each subdirectory links to the subdir.html
// each listed file links to its 'file.html'.

pub fn write_index_html(source_dir: &Path,extentions:&[~str], options:&RF_Options) {

	//println!("output dir={}",options.output_dir.as_str().unwrap_or(""));
	println!("Generating index page:-");

	// the index page shows: all source in root dir,
	// all dirs containing source,
	// direct link to any 'mod.rs' or 'lib.rs'

	// hmm, why dont we just make a treeview, ffs.
	let mut source_dirs= HashSet::<(Path,~str)>::new();
	let mut source_files=HashSet::<(Path,~str)>::new();
	match walk_dir(source_dir) {
		Ok(mut dirs)=> {
//			write_index_html_sub(source_dir, dirs, extentions);
//			println!("dir contents {:?} {:?}", source_dir, dirs);
			for path in dirs {
				match path.as_str() {
					Some( ref s)=> {
						let ext=path.extension_str().unwrap_or(&"");;
						let filename=path.filename_str().unwrap_or(&"");
						let dirname=path.dirname_str().unwrap_or(&"");
						if (ext=="rs") {
							source_dirs.insert((path.clone(), dirname.to_owned()));
							source_files.insert((path.clone(), filename.to_owned()));
						}
					}
					None=>{},
				}
			}
		},
		_=>{}
	};

	let mut doc=HtmlWriter::new();
	let index_path = options.output_dir.join(Path::new("index.html"));
	rust2html::write_head(&mut doc, &index_path, options);

	doc.begin_tag_check("body");
	doc.begin_tag("div");
	doc.begin_tag_check("maintext");

	doc.writeln("Index of " + source_dir.as_str().unwrap_or(""));
	doc.writeln(rust2html::get_git_branch_info());
	doc.writeln("");

	// write as grid, todo abstract it ffs.
	// TODO we're writing HTML, ffs, its got tables and all sorts..
	// TODO: Grid Layout - seperate path/filename to make squarer items.

	let mut max_line_len:uint=0;
	for &(ref sp,ref sf) in source_files.iter() {
		max_line_len =cmp::max(max_line_len, sf.len())
	}
	let desired_width=100;
	let num_cols = desired_width / max_line_len;

	let mut column=0;
	for &(ref sp,ref sf) in source_files.iter() {
		doc.begin_tag_link( sp.as_str().unwrap_or("") + ".html");
		let sp_str=sp.as_str().unwrap_or("");
		doc.write(sp_str);
		doc.end_tag();
		let mut i=sp_str.len();
		while i<max_line_len { doc.write(" "); i+=1;}
		column+=1;
		if column>=num_cols {
			doc.write_tag("br");
			column=0;
		}
	}

    doc.end_tag();
    doc.end_tag();
    doc.end_tag();

	rust2html::file_write_bytes_as(&index_path, doc.as_bytes());
}

/*
pub fn write_index_html_sub(source_dir:&Path, contents:[&Path]{
	doc = htmlwrite::HtmlWriter::new();
	::rust2html::write_head(doc, source_dir+"/index.html");

}*/
<commit_msg>improved index page<commit_after>use rust2html::htmlwriter::HtmlWriter;
use std::str;
use std::io::{File, UserDir};
use std::io::fs::{mkdir_recursive,copy,walk_dir};
use rust2html::RF_Options;
use collections::hashmap::HashMap;
use collections::hashmap::HashSet;
use rust2html;
use std::cmp;
use std::vec::Vec;

// create index page. Show subdirectories and files matching extentions.
// each subdirectory links to the subdir.html
// each listed file links to its 'file.html'.

pub fn write_index_html(source_dir: &Path,extentions:&[~str], options:&RF_Options) {

	//println!("output dir={}",options.output_dir.as_str().unwrap_or(""));
	println!("Generating index page:-");

	// Display all files in the directory tree,
	// under the containing directory paths.
	let mut files_per_dir=HashMap::<~str,Vec<(~str,~str)> >::new();
	match walk_dir(source_dir) {
		Ok(mut dirs)=> {
//			write_index_html_sub(source_dir, dirs, extentions);
//			println!("dir contents {:?} {:?}", source_dir, dirs);
			for path in dirs {
				match path.as_str() {
					Some( ref s)=> {
						let ext=path.extension_str().unwrap_or(&"");;
						let filename=path.filename_str().unwrap_or(&"");
						let dirname=path.dirname_str().unwrap_or(&"");
						if (ext=="rs") {
							let link_target= path.as_str().unwrap_or("").to_owned()+".html";
							if Path::new(link_target.as_slice()).exists() {
								let bucket=files_per_dir.find_or_insert(
									dirname.to_owned(),
									Vec::<(~str,~str)>::new()
								);
// does the file we link to actually exist?
								bucket.push( (filename.to_owned(), link_target)  );
							}

						}
					}
					None=>{},
				}
			}
		},
		_=>{}
	};

	let mut doc=HtmlWriter::new();
	let index_path = options.output_dir.join(Path::new("index.html"));
	rust2html::write_head(&mut doc, &index_path, options);

	doc.begin_tag_check("body");
	doc.begin_tag("div");
	doc.begin_tag_check("maintext");

	doc.writeln("Index of " + source_dir.as_str().unwrap_or(""));
	doc.begin_tag("c40").writeln(rust2html::get_git_branch_info()).end_tag();

	for (dir, files) in files_per_dir.iter() {
		doc.writeln("");
		doc.begin_tag("c30"); doc.writeln(*dir); doc.end_tag();

		write_grid_of_text_links(&mut doc,files);
	}

	// write as grid, todo abstract it ffs.
	// TODO we're writing HTML, ffs, its got tables and all sorts..
	// TODO: Grid Layout - seperate path/filename to make squarer items.

//	write_grid_of_text_links(&mut doc,&links);

	doc.end_tag();
    doc.end_tag();
    doc.end_tag();

	rust2html::file_write_bytes_as(&index_path, doc.as_bytes());
}

fn write_grid_of_text_links(doc:&mut HtmlWriter, links:&Vec<(~str,~str)>) {
	let mut max_line_len:uint=1;
	for &(ref name,ref link) in links.iter() {
		max_line_len =cmp::max(max_line_len, name.len()+1)
	}
	let desired_width=120;
	let num_cols = desired_width / max_line_len;

	let mut column=0;
	for &(ref name,ref link) in links.iter() {
		doc.begin_tag_link(*link);
		doc.write(*name);
		doc.end_tag();
		let mut i=name.len();
		while i<max_line_len { doc.write(" "); i+=1;}
		column+=1;
		if column>=num_cols {
			doc.write_tag("br");
			column=0;
		}
	}
	if column!=0 {
		doc.write_tag("br");
		column=0;
	}

}

/*
pub fn write_index_html_sub(source_dir:&Path, contents:[&Path]{
	doc = htmlwrite::HtmlWriter::new();
	::rust2html::write_head(doc, source_dir+"/index.html");

}*/
<|endoftext|>


<commit_before>//! Fixes the call target for all call sites in the SSA

use frontend::radeco_containers::*;
use middle::ir;
use middle::ssa::cfg_traits::*;
use middle::ssa::ssa_traits::*;
use middle::ssa::ssastorage::SSAStorage;

use petgraph::prelude::*;

use std::collections::HashMap;

pub fn go(rmod: &mut RadecoModule) -> () {
    for rfun in rmod.functions.values_mut() {
        go_fn(rfun, &rmod.callgraph)
    }
}

fn go_fn(rfun: &mut RadecoFunction, callgraph: &CallGraph) -> () {
    let fn_addr = rfun.offset;
    let call_site_addr_to_target_addr: HashMap<u64, u64> = callgraph
        .callees(rfun.cgid())
        .map(|(cs_a, tgt_idx)| (cs_a, callgraph[tgt_idx]))
        .collect();
    let ssa = rfun.ssa_mut();
    for node in ssa.inorder_walk() {
        if let Ok(NodeType::Op(ir::MOpcode::OpCall)) = ssa.node_data(node).map(|x| x.nt) {
            fix_call_site(ssa, node, &call_site_addr_to_target_addr).unwrap_or_else(|| {
                radeco_err!(
                    "failed to fix call site {:?} in function at {:#X}",
                    node,
                    fn_addr
                )
            });
        }
    }
}

fn fix_call_site(
    ssa: &mut SSAStorage,
    call_node: <SSAStorage as SSA>::ValueRef,
    fn_call_map: &HashMap<u64, u64>,
) -> Option<()> {
    let call_site_addr = ssa.address(call_node)?.address;
    if let Some(&call_target_addr) = fn_call_map.get(&call_site_addr) {
        let old_opcall_tgt_node = ssa.sparse_operands_of(call_node)
            .iter()
            .find(|x| x.0 == 0)?
            .1;
        let new_opcall_tgt_node = ssa.insert_const(call_target_addr)?;
        ssa.op_unuse(call_node, old_opcall_tgt_node);
        ssa.op_use(call_node, 0, new_opcall_tgt_node);
    } else {
        radeco_trace!(
            "call site at {:#X} isn't in call graph; perhaps the call is indirect?",
            call_site_addr
        );
    }
    Some(())
}

<commit_msg>Add documentation for `fix_ssa_opcalls::go`<commit_after>//! Fixes the call target for all call sites in the SSA

use frontend::radeco_containers::*;
use middle::ir;
use middle::ssa::cfg_traits::*;
use middle::ssa::ssa_traits::*;
use middle::ssa::ssastorage::SSAStorage;

use petgraph::prelude::*;

use std::collections::HashMap;

/// For every [`OpCall`] SSA node in every function, try to find that call
/// site's corresponding edge in [the callgraph] and replace the "target"
/// operand of the SSA node with a constant value for the address of the actual
/// call target.
///
/// [`OpCall`]: ir::MOpcode::OpCall
/// [the callgraph]: RadecoModule::callgraph
pub fn go(rmod: &mut RadecoModule) -> () {
    for rfun in rmod.functions.values_mut() {
        go_fn(rfun, &rmod.callgraph);
    }
}

fn go_fn(rfun: &mut RadecoFunction, callgraph: &CallGraph) -> () {
    let fn_addr = rfun.offset;
    let call_site_addr_to_target_addr: HashMap<u64, u64> = callgraph
        .callees(rfun.cgid())
        .map(|(cs_a, tgt_idx)| (cs_a, callgraph[tgt_idx]))
        .collect();
    let ssa = rfun.ssa_mut();
    for node in ssa.inorder_walk() {
        if let Ok(NodeType::Op(ir::MOpcode::OpCall)) = ssa.node_data(node).map(|x| x.nt) {
            fix_call_site(ssa, node, &call_site_addr_to_target_addr).unwrap_or_else(|| {
                radeco_err!(
                    "failed to fix call site {:?} in function at {:#X}",
                    node,
                    fn_addr
                )
            });
        }
    }
}

fn fix_call_site(
    ssa: &mut SSAStorage,
    call_node: <SSAStorage as SSA>::ValueRef,
    fn_call_map: &HashMap<u64, u64>,
) -> Option<()> {
    let call_site_addr = ssa.address(call_node)?.address;
    if let Some(&call_target_addr) = fn_call_map.get(&call_site_addr) {
        let old_opcall_tgt_node = ssa.sparse_operands_of(call_node)
            .iter()
            .find(|x| x.0 == 0)?
            .1;
        let new_opcall_tgt_node = ssa.insert_const(call_target_addr)?;
        ssa.op_unuse(call_node, old_opcall_tgt_node);
        ssa.op_use(call_node, 0, new_opcall_tgt_node);
    } else {
        radeco_trace!(
            "call site at {:#X} isn't in call graph; perhaps the call is indirect?",
            call_site_addr
        );
    }
    Some(())
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::sync::mpsc::channel;

use adapter::ChatAdapter;
use handler::MessageHandler;

/// The Chatbot is the central data structure of the chatbot platform. It contains a `run` method
/// which listens for messages from adapters and routes them to handlers. Any program which uses
/// chatbot will need to minimally create a Chatbot, add an adapter, add a handler, and call Chatbot
/// [`run`](chatbot/struct.Chatbot.html#method.run).
pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>,
    addresser: Regex
}

impl Chatbot {
    /// Create a new chatbot instance.
    ///
    /// The name provided here will be used for message filtering and in handlers for whatever they
    /// want. You'll want to make your binding mutable so you can call `add_adapter` and
    /// `add_handler`.
    pub fn new(name: &str) -> Chatbot {
        let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", name);
        let addresser = Regex::new(addresser_str.as_ref());

        Chatbot {
            name: name.to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new(),
            addresser: addresser.unwrap()
        }
    }

    /// Return the name provided in initialization
    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    /// Return the regular expression of what the bot will be addressed by, if present
    pub fn get_addresser(&self) -> Option<&Regex> {
        self.addresser.as_ref()
    }

    /// Add a ChatAdapter to the bot
    ///
    /// Add as many adapters as you like. The IncomingMessages sent by adapters are made available
    /// to all handlers regardless of how many adapters exist. The IncomingMessage.reply method
    /// makes sure the response is sent back to the adapter from whence the message came.
    pub fn add_adapter<T>(&mut self, adapter: T)
        where T: ChatAdapter + 'static
    {
        println!("Adding adapter {}", adapter.get_name());
        self.adapters.push(Box::new(adapter))
    }

    /// Add a MessageHandler to the bot
    ///
    /// The more handlers you have the more useful your bot becomes (for potentially loose
    /// definitions of useful :P). Check out the handler! macro for making simple handlers and also
    /// see the `MessageHandler` implementors section for a list of built in handlers.
    pub fn add_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.handlers.push(Box::new(handler))
    }

    /// Start processing messages
    ///
    /// Call process_events on all of the adapters and `recv` on the `IncomingMessage` channel.
    /// Distribute IncomingMessages to list of handlers.
    pub fn run(&self) {

        assert!(self.adapters.len() > 0);
        assert!(self.handlers.len() > 0);

        println!("Chatbot: {} adapters", self.adapters.len());
        println!("Chatbot: {} handlers", self.handlers.len());

        let (incoming_tx, incoming_rx) = channel();

        for adapter in &self.adapters {
            adapter.process_events(self, incoming_tx.clone());
        }

        loop {
            // Get message from adapter
            let msg = match incoming_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break
            };

            // Distribute to handlers
            for handler in &self.handlers {
                let msg_str = msg.get_contents();

                if handler.can_handle(msg_str) {
                    // When addresser is present, ignore handlers that don't directly call the
                    // bot's name
                    if let Some(ref addresser) = self.addresser {
                        if !addresser.is_match(msg_str) {
                            continue;
                        }
                    }

                    match handler.handle(&msg) {
                        Err(e) => {
                            println!("Error in handler `{}`", handler.name());
                            println!("{:?}", e);
                            println!("The incoming message was {}", msg_str);

                            // TODO remove handler?
                        },
                        _ => ()
                    }
                }
            }
        }

        println!("chatbot shutting down");
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;

    #[test]
    fn test_create_chatbot() {
        let name = "testbot";
        let bot = Chatbot::new("testbot");
        assert_eq!(bot.get_name(), name);
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new("testbot");
        let cli = CliAdapter::new();
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new("testbot");
        let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
            Some(msg.to_owned())
        });
        bot.add_handler(echo);
    }

    #[test]
    fn test_chatbot_address_by_name() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
    }

    #[test]
    fn test_chatbot_address_matches() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
        let addresser = bot.get_addresser().unwrap();

        assert!(!addresser.is_match("testbotping"),
                "Shouldn't match bot name without a break (space, colon, comma)");
        assert!(!addresser.is_match("@testbotping"),
                "Shouldn't match bot name with '@' sign minus a break (space, colon, comma)");

        // Space separation
        assert!(addresser.is_match("testbot ping"), "Should match bot name with space");
        assert!(addresser.is_match("@testbot ping"),
                "Should match bot name with '@' sign plus '@' space");

        // Colon separation
        assert!(addresser.is_match("testbot:ping"), "Should match bot name with colon");
        assert!(addresser.is_match("testbot: ping"), "Should match bot name with colon and space");
        assert!(addresser.is_match("@testbot:ping"),
                "Should match bot name with '@' sign plus colon");
        assert!(addresser.is_match("@testbot: ping"),
                "Should match bot name with '@' sign plus colon and space");

        // Comma separation
        assert!(addresser.is_match("testbot,ping"), "Should match bot name with comma");
        assert!(addresser.is_match("testbot, ping"), "Should match bot name with comma and space");
        assert!(addresser.is_match("@testbot,ping"),
                "Should match bot name with '@' sign plus comma");
        assert!(addresser.is_match("@testbot, ping"),
                "Should match bot name with '@' sign plus comma and space");
    }
}
<commit_msg>Change get_addresser to reflect type change<commit_after>use regex::Regex;
use std::sync::mpsc::channel;

use adapter::ChatAdapter;
use handler::MessageHandler;

/// The Chatbot is the central data structure of the chatbot platform. It contains a `run` method
/// which listens for messages from adapters and routes them to handlers. Any program which uses
/// chatbot will need to minimally create a Chatbot, add an adapter, add a handler, and call Chatbot
/// [`run`](chatbot/struct.Chatbot.html#method.run).
pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>,
    addresser: Regex
}

impl Chatbot {
    /// Create a new chatbot instance.
    ///
    /// The name provided here will be used for message filtering and in handlers for whatever they
    /// want. You'll want to make your binding mutable so you can call `add_adapter` and
    /// `add_handler`.
    pub fn new(name: &str) -> Chatbot {
        let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", name);
        let addresser = Regex::new(addresser_str.as_ref());

        Chatbot {
            name: name.to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new(),
            addresser: addresser.unwrap()
        }
    }

    /// Return the name provided in initialization
    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    /// Return the regular expression of what the bot will be addressed by
    pub fn get_addresser(&self) -> &Regex {
        &self.addresser
    }

    /// Add a ChatAdapter to the bot
    ///
    /// Add as many adapters as you like. The IncomingMessages sent by adapters are made available
    /// to all handlers regardless of how many adapters exist. The IncomingMessage.reply method
    /// makes sure the response is sent back to the adapter from whence the message came.
    pub fn add_adapter<T>(&mut self, adapter: T)
        where T: ChatAdapter + 'static
    {
        println!("Adding adapter {}", adapter.get_name());
        self.adapters.push(Box::new(adapter))
    }

    /// Add a MessageHandler to the bot
    ///
    /// The more handlers you have the more useful your bot becomes (for potentially loose
    /// definitions of useful :P). Check out the handler! macro for making simple handlers and also
    /// see the `MessageHandler` implementors section for a list of built in handlers.
    pub fn add_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.handlers.push(Box::new(handler))
    }

    /// Start processing messages
    ///
    /// Call process_events on all of the adapters and `recv` on the `IncomingMessage` channel.
    /// Distribute IncomingMessages to list of handlers.
    pub fn run(&self) {

        assert!(self.adapters.len() > 0);
        assert!(self.handlers.len() > 0);

        println!("Chatbot: {} adapters", self.adapters.len());
        println!("Chatbot: {} handlers", self.handlers.len());

        let (incoming_tx, incoming_rx) = channel();

        for adapter in &self.adapters {
            adapter.process_events(self, incoming_tx.clone());
        }

        loop {
            // Get message from adapter
            let msg = match incoming_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break
            };

            // Distribute to handlers
            for handler in &self.handlers {
                let msg_str = msg.get_contents();

                if handler.can_handle(msg_str) {
                    // When addresser is present, ignore handlers that don't directly call the
                    // bot's name
                    if let Some(ref addresser) = self.addresser {
                        if !addresser.is_match(msg_str) {
                            continue;
                        }
                    }

                    match handler.handle(&msg) {
                        Err(e) => {
                            println!("Error in handler `{}`", handler.name());
                            println!("{:?}", e);
                            println!("The incoming message was {}", msg_str);

                            // TODO remove handler?
                        },
                        _ => ()
                    }
                }
            }
        }

        println!("chatbot shutting down");
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;

    #[test]
    fn test_create_chatbot() {
        let name = "testbot";
        let bot = Chatbot::new("testbot");
        assert_eq!(bot.get_name(), name);
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new("testbot");
        let cli = CliAdapter::new();
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new("testbot");
        let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
            Some(msg.to_owned())
        });
        bot.add_handler(echo);
    }

    #[test]
    fn test_chatbot_address_by_name() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
    }

    #[test]
    fn test_chatbot_address_matches() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
        let addresser = bot.get_addresser().unwrap();

        assert!(!addresser.is_match("testbotping"),
                "Shouldn't match bot name without a break (space, colon, comma)");
        assert!(!addresser.is_match("@testbotping"),
                "Shouldn't match bot name with '@' sign minus a break (space, colon, comma)");

        // Space separation
        assert!(addresser.is_match("testbot ping"), "Should match bot name with space");
        assert!(addresser.is_match("@testbot ping"),
                "Should match bot name with '@' sign plus '@' space");

        // Colon separation
        assert!(addresser.is_match("testbot:ping"), "Should match bot name with colon");
        assert!(addresser.is_match("testbot: ping"), "Should match bot name with colon and space");
        assert!(addresser.is_match("@testbot:ping"),
                "Should match bot name with '@' sign plus colon");
        assert!(addresser.is_match("@testbot: ping"),
                "Should match bot name with '@' sign plus colon and space");

        // Comma separation
        assert!(addresser.is_match("testbot,ping"), "Should match bot name with comma");
        assert!(addresser.is_match("testbot, ping"), "Should match bot name with comma and space");
        assert!(addresser.is_match("@testbot,ping"),
                "Should match bot name with '@' sign plus comma");
        assert!(addresser.is_match("@testbot, ping"),
                "Should match bot name with '@' sign plus comma and space");
    }
}
<|endoftext|>


<commit_before>use adapter::ChatAdapter;
use handler::MessageHandler;
use std::collections::HashMap;
use std::sync::mpsc::Select;

pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>
}

impl Chatbot {
    pub fn new() -> Chatbot {
        Chatbot {
            name: "computer".to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new()
        }
    }

    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    pub fn add_adapter<T: ChatAdapter + 'static>(&mut self, adapter: Box<T>) {
        println!("Adding adapter {}", adapter.get_name());
        self.adapters.push(adapter)
    }

    pub fn add_handler<T: MessageHandler + 'static>(&mut self, handler: Box<T>) {
        println!("Adding handler {}", handler.name());
        self.handlers.push(handler)
    }

    pub fn run(&self) {

        // TODO this could be cleaned up if more information could be stored on
        // the adapter.
        println!("Chatbot: starting {} adapters", self.adapters.len());

        let sel = Select::new();

        let receivers = self.adapters.iter().map(|adapter| {
            adapter.process_events()
        }).collect::<Vec<_>>();

        let mut handles = HashMap::new();
        for rx in &receivers {
            let handle = sel.handle(&rx);
            let id = handle.id();
            handles.insert(id, handle);
            let mut h = handles.get_mut(&id).unwrap();
            unsafe { (*h).add() };
        }

        println!("Have {} receivers", receivers.len());

        println!("Chatbot: entering main loop");

        loop {
            let id = sel.wait();
            let handle = handles.get_mut(&id).unwrap();

            let msg = match handle.recv() {
                Ok(msg) => msg,
                Err(e) => panic!("Chatbot main channel error {}", e)
            };

            println!("Got message: {}", msg.get_contents());
            for handler in &self.handlers {
                if let Err(e) = handler.handle(&msg) {
                    // TODO check error variant and release adapters as needed.
                    println!("Error in handler.handle: {:?}", e);
                    break;
                }
            }
        }

        // TODO there's a crash when this falls through
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;
    use handler::EchoHandler;

    #[test]
    fn test_create_chatbot() {
        let bot = Chatbot::new();
        assert_eq!(bot.get_name(), "computer");
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new();
        let cli = Box::new(CliAdapter::new());
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new();
        let handler = Box::new(EchoHandler::new());
        bot.add_handler(handler);
    }
}
<commit_msg>Remove unsafe Select usage from chatbot.rs<commit_after>use adapter::ChatAdapter;
use handler::MessageHandler;

pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>
}

impl Chatbot {
    pub fn new() -> Chatbot {
        Chatbot {
            name: "computer".to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new()
        }
    }

    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    pub fn add_adapter<T: ChatAdapter + 'static>(&mut self, adapter: Box<T>) {
        println!("Adding adapter {}", adapter.get_name());
        // Temporarily limit the number of concurrent adapters to 1 until
        // switching to mio or similar
        assert_eq!(self.adapters.len(), 0);
        self.adapters.push(adapter)
    }

    pub fn add_handler<T: MessageHandler + 'static>(&mut self, handler: Box<T>) {
        println!("Adding handler {}", handler.name());
        self.handlers.push(handler)
    }

    pub fn run(&self) {

        println!("Chatbot: starting {} adapters", self.adapters.len());

        let ref adapter = self.adapters[0];
        let adapter_rx = adapter.process_events();

        println!("Have {} receivers", self.adapters.len());
        println!("Chatbot: entering main loop");

        loop {
            // Get message from adapter
            let msg = match adapter_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break
            };

            // Distribute to handlers
            for handler in &self.handlers {
                if let Err(e) = handler.handle(&msg) {
                    // TODO check error variant and release adapters as needed.
                    println!("Error in handler.handle: {:?}", e);
                    break;
                }
            }
        }

        println!("chatbot shutting down");
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;
    use handler::EchoHandler;

    #[test]
    fn test_create_chatbot() {
        let bot = Chatbot::new();
        assert_eq!(bot.get_name(), "computer");
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new();
        let cli = Box::new(CliAdapter::new());
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new();
        let handler = Box::new(EchoHandler::new());
        bot.add_handler(handler);
    }
}
<|endoftext|>


<commit_before>#[link(name="echo", vers="1.0.0", author="Derek Chiang")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::stderr;
use std::uint;
use extra::getopts::groups;

static VERSION: &'static str = "1.0.0";

fn print_char(c: char) {
    print!("{}", c);
}

fn to_char(bytes: &[u8], base: uint) -> char {
    uint::parse_bytes(bytes, base).unwrap() as u8 as char
}

fn isxdigit(c: u8) -> bool {
    match c as char {
        '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
        '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => true,
        _ => false
    }
}

fn isodigit(c: u8) -> bool {
    match c as char {
        '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => true,
        _ => false
    }
}

fn convert_str(string: &str, index: uint, base: uint) -> (char, int) {
    let (max_digits, is_legal_digit) = match base {
        8u => (3, isodigit),
        16u => (2, isxdigit),
        _ => fail!(),
    };

    let mut bytes: ~[u8] = ~[];
    for offset in range(0, max_digits) {
        let c = string[index + offset as uint];
        if is_legal_digit(c) {
            bytes.push(c as u8);
        } else {
            if bytes.len() > 0 {
                return (to_char(bytes, base), offset);
            } else {
                return (' ', offset);
            }
        }
    }
    return (to_char(bytes, base), max_digits)
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        groups::optflag("n", "", "do not output the trailing newline"),
        groups::optflag("e", "", "enable interpretation of backslash escapes"),
        groups::optflag("E", "", "disable interpretation of backslash escapes (default)"),
        groups::optflag("h", "help", "display this help and exit"),
        groups::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("echo " + VERSION + " - display a line of text");
        println("");
        println("Usage:");
        println!("  {0:s} [SHORT-OPTION]... [STRING]...", program);
        println!("  {0:s} LONG-OPTION", program);
        println("");
        print(groups::usage("Echo the STRING(s) to standard output.", opts));
        print("\nIf -e is in effect, the following sequences are recognized:\n\
\n\
  \\\\      backslash\n\
  \\a      alert (BEL)\n\
  \\b      backspace\n\
  \\c      produce no further output\n\
  \\e      escape\n\
  \\f      form feed\n\
  \\n      new line\n\
  \\r      carriage return\n\
  \\t      horizontal tab\n\
  \\v      vertical tab\n\
  \\0NNN   byte with octal value NNN (1 to 3 digits)\n\
  \\xHH    byte with hexadecimal value HH (1 to 2 digits)\n\
");
        return;
    }

    if matches.opt_present("version") {
        return println("echo version: " + VERSION);
    }

    if !matches.free.is_empty() {
        let string = matches.free.connect(" ");
        if matches.opt_present("e") {
            let mut prev_was_slash = false;
            let mut iter = string.chars().enumerate();
            loop {
                match iter.next() {
                    Some((index, c)) => {
                        if !prev_was_slash {
                            if c != '\\' {
                                print_char(c);
                            } else {
                                prev_was_slash = true;
                            }
                        } else {
                            prev_was_slash = false;
                            match c {
                                '\\' => print_char('\\'),
                                'a' => print_char('\x07'),
                                'b' => print_char('\x08'),
                                'c' => break,
                                'e' => print_char('\x1B'),
                                'f' => print_char('\x0C'),
                                'n' => print_char('\n'),
                                'r' => print_char('\r'),
                                't' => print_char('\t'),
                                'v' => print_char('\x0B'),
                                'x' => {
                                    let (c, num_char_used) = convert_str(string, index + 1, 16u);
                                    if num_char_used == 0 {
                                        print_char('\\');
                                        print_char('x');
                                    } else {
                                        print_char(c);
                                        for _ in range(0, num_char_used) {
                                            iter.next(); // consume used characters
                                        }
                                    }
                                },
                                '0' => {
                                    let (c, num_char_used) = convert_str(string, index + 1, 8u);
                                    if num_char_used == 0 {
                                        print_char('\\');
                                        print_char('0');
                                    } else {
                                        print_char(c);
                                        for _ in range(0, num_char_used) {
                                            iter.next(); // consume used characters
                                        }
                                    }
                                }
                                _ => {
                                    print_char('\\');
                                    print_char(c);
                                }
                            }
                        }
                    }
                    None => break
                }
            }
        } else {
            print(string);
        }
    }

    if !matches.opt_present("n") {
        println("")
    }
}
<commit_msg>rust println supports multi-line ...<commit_after>#[link(name="echo", vers="1.0.0", author="Derek Chiang")];

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern mod extra;

use std::os;
use std::io::stderr;
use std::uint;
use extra::getopts::groups;

static VERSION: &'static str = "1.0.0";

fn print_char(c: char) {
    print!("{}", c);
}

fn to_char(bytes: &[u8], base: uint) -> char {
    uint::parse_bytes(bytes, base).unwrap() as u8 as char
}

fn isxdigit(c: u8) -> bool {
    match c as char {
        '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
        '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' => true,
        _ => false
    }
}

fn isodigit(c: u8) -> bool {
    match c as char {
        '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' => true,
        _ => false
    }
}

fn convert_str(string: &str, index: uint, base: uint) -> (char, int) {
    let (max_digits, is_legal_digit) = match base {
        8u => (3, isodigit),
        16u => (2, isxdigit),
        _ => fail!(),
    };

    let mut bytes: ~[u8] = ~[];
    for offset in range(0, max_digits) {
        let c = string[index + offset as uint];
        if is_legal_digit(c) {
            bytes.push(c as u8);
        } else {
            if bytes.len() > 0 {
                return (to_char(bytes, base), offset);
            } else {
                return (' ', offset);
            }
        }
    }
    return (to_char(bytes, base), max_digits)
}

fn main() {
    let args = os::args();
    let program = args[0].clone();
    let opts = ~[
        groups::optflag("n", "", "do not output the trailing newline"),
        groups::optflag("e", "", "enable interpretation of backslash escapes"),
        groups::optflag("E", "", "disable interpretation of backslash escapes (default)"),
        groups::optflag("h", "help", "display this help and exit"),
        groups::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match groups::getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            writeln!(&mut stderr() as &mut Writer,
                "Invalid options\n{}", f.to_err_msg());
            os::set_exit_status(1);
            return
        }
    };

    if matches.opt_present("help") {
        println("echo " + VERSION + " - display a line of text");
        println("");
        println("Usage:");
        println!("  {0:s} [SHORT-OPTION]... [STRING]...", program);
        println!("  {0:s} LONG-OPTION", program);
        println("");
        println(groups::usage("Echo the STRING(s) to standard output.", opts));
        println("If -e is in effect, the following sequences are recognized:

\\\\      backslash
\\a      alert (BEL)
\\b      backspace
\\c      produce no further output
\\e      escape
\\f      form feed
\\n      new line
\\r      carriage return
\\t      horizontal tab
\\v      vertical tab
\\0NNN   byte with octal value NNN (1 to 3 digits)
\\xHH    byte with hexadecimal value HH (1 to 2 digits)");
        return;
    }

    if matches.opt_present("version") {
        return println("echo version: " + VERSION);
    }

    if !matches.free.is_empty() {
        let string = matches.free.connect(" ");
        if matches.opt_present("e") {
            let mut prev_was_slash = false;
            let mut iter = string.chars().enumerate();
            loop {
                match iter.next() {
                    Some((index, c)) => {
                        if !prev_was_slash {
                            if c != '\\' {
                                print_char(c);
                            } else {
                                prev_was_slash = true;
                            }
                        } else {
                            prev_was_slash = false;
                            match c {
                                '\\' => print_char('\\'),
                                'a' => print_char('\x07'),
                                'b' => print_char('\x08'),
                                'c' => break,
                                'e' => print_char('\x1B'),
                                'f' => print_char('\x0C'),
                                'n' => print_char('\n'),
                                'r' => print_char('\r'),
                                't' => print_char('\t'),
                                'v' => print_char('\x0B'),
                                'x' => {
                                    let (c, num_char_used) = convert_str(string, index + 1, 16u);
                                    if num_char_used == 0 {
                                        print_char('\\');
                                        print_char('x');
                                    } else {
                                        print_char(c);
                                        for _ in range(0, num_char_used) {
                                            iter.next(); // consume used characters
                                        }
                                    }
                                },
                                '0' => {
                                    let (c, num_char_used) = convert_str(string, index + 1, 8u);
                                    if num_char_used == 0 {
                                        print_char('\\');
                                        print_char('0');
                                    } else {
                                        print_char(c);
                                        for _ in range(0, num_char_used) {
                                            iter.next(); // consume used characters
                                        }
                                    }
                                }
                                _ => {
                                    print_char('\\');
                                    print_char(c);
                                }
                            }
                        }
                    }
                    None => break
                }
            }
        } else {
            print(string);
        }
    }

    if !matches.opt_present("n") {
        println("")
    }
}
<|endoftext|>