pistoncore-event 0.0.3

A library for flexible generic event threading
//! Trait for generic events

use std::borrow::ToOwned;
use std::any::{ Any, TypeId };
use std::hash::{ hash, SipHasher };

use input::{ Button, Input, Motion };
use {
    Event,
    FocusEvent,
    PressEvent,
    ReleaseEvent,
    MouseCursorEvent,
    MouseRelativeEvent,
    MouseScrollEvent,
    TextEvent,
    ResizeEvent,
    UpdateEvent,
    RenderEvent,
    IdleEvent,
    UpdateArgs,
    RenderArgs,
    IdleArgs,
};

/// Implemented by all events
pub trait GenericEvent {
    /// The id of this event, usually `TypeOf::of::<Box<EventTrait>>().hash()`
    fn event_id(&self) -> u64;
    /// Calls closure with table
    fn with_args<'a, F, U>(&'a self, f: F) -> U
        where F: FnMut(&Any) -> U
    ;
    /// Converts from table to `Self`
    fn from_args(event_id: u64, any: &Any) -> Option<Self>;
}

impl GenericEvent for Input {
    fn event_id(&self) -> u64 {
        match self {
            &Input::Focus(_) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<FocusEvent>>())
            }
            &Input::Press(_) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<PressEvent>>())
            }
            &Input::Release(_) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<ReleaseEvent>>())
            }
            &Input::Move(Motion::MouseCursor(_, _)) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<MouseCursorEvent>>())
            }
            &Input::Move(Motion::MouseRelative(_, _)) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<MouseRelativeEvent>>())
            }
            &Input::Move(Motion::MouseScroll(_, _)) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<MouseScrollEvent>>())
            }
            &Input::Text(_) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<TextEvent>>())
            }
            &Input::Resize(_, _) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<ResizeEvent>>())
            }
        }
    }

    fn with_args<'a, F, U>(&'a self, mut f: F) -> U
        where F: FnMut(&Any) -> U
    {
        match self {
            &Input::Focus(focused) => {
                f(&focused as &Any)
            }
            &Input::Press(button) => {
                f(&button as &Any)
            }
            &Input::Release(button) => {
                f(&button as &Any)
            }
            &Input::Move(Motion::MouseCursor(x, y)) => {
                f(&(x, y) as &Any)
            }
            &Input::Move(Motion::MouseRelative(x, y)) => {
                f(&(x, y) as &Any)
            }
            &Input::Move(Motion::MouseScroll(x, y)) => {
                f(&(x, y) as &Any)
            }
            &Input::Text(ref text) => {
                f(text as &Any)
            }
            &Input::Resize(w, h) => {
                f(&(w, h) as &Any)
            }
        }
    }

    fn from_args(event_id: u64, any: &Any) -> Option<Self> {
        let focus_id = hash::<_, SipHasher>(&TypeId::of::<Box<FocusEvent>>());
        let mouse_cursor_id = hash::<_, SipHasher>(&TypeId::of::<Box<MouseCursorEvent>>());
        let mouse_relative_id = hash::<_, SipHasher>(&TypeId::of::<Box<MouseRelativeEvent>>());
        let mouse_scroll_id = hash::<_, SipHasher>(&TypeId::of::<Box<MouseScrollEvent>>());
        let press_id = hash::<_, SipHasher>(&TypeId::of::<Box<PressEvent>>());
        let release_id = hash::<_, SipHasher>(&TypeId::of::<Box<ReleaseEvent>>());
        let resize_id = hash::<_, SipHasher>(&TypeId::of::<Box<ResizeEvent>>());
        let text_id = hash::<_, SipHasher>(&TypeId::of::<Box<TextEvent>>());

        match event_id {
            x if x == focus_id => {
                if let Some(&focused) = any.downcast_ref::<bool>() {
                    Some(Input::Focus(focused))
                } else {
                    panic!("Expected bool")
                }
            }
            x if x == mouse_cursor_id => {
                if let Some(&(x, y)) = any.downcast_ref::<(f64, f64)>() {
                    Some(Input::Move(Motion::MouseCursor(x, y)))
                } else {
                    panic!("Expected (f64, f64)")
                }
            }
            x if x == mouse_relative_id => {
                if let Some(&(x, y)) = any.downcast_ref::<(f64, f64)>() {
                    Some(Input::Move(Motion::MouseRelative(x, y)))
                } else {
                    panic!("Expected (f64, f64)")
                }
            }
            x if x == mouse_scroll_id => {
                if let Some(&(x, y)) = any.downcast_ref::<(f64, f64)>() {
                    Some(Input::Move(Motion::MouseScroll(x, y)))
                } else {
                    panic!("Expected (f64, f64)")
                }
            }
            x if x == press_id => {
                if let Some(&button) = any.downcast_ref::<Button>() {
                    Some(Input::Press(button))
                } else {
                    panic!("Expected Button")
                }
            }
            x if x == release_id => {
                if let Some(&button) = any.downcast_ref::<Button>() {
                    Some(Input::Release(button))
                } else {
                    panic!("Expected Button")
                }
            }
            x if x == resize_id => {
                if let Some(&(w, h)) = any.downcast_ref::<(u32, u32)>() {
                    Some(Input::Resize(w as u32, h as u32))
                } else {
                    panic!("Expected (u32, u32))")
                }
            }
            x if x == text_id => {
                if let Some(text) = any.downcast_ref::<String>() {
                    Some(Input::Text(text.to_owned()))
                } else {
                    panic!("Expected &str")
                }
            }
            _ => { return None; }
        }
    }
}

impl<I: GenericEvent> GenericEvent for Event<I> {
    fn event_id(&self) -> u64 {
        match self {
            &Event::Update(_) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<UpdateEvent>>())
            }
            &Event::Render(_) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<RenderEvent>>())
            }
            &Event::Idle(_) => {
                hash::<_, SipHasher>(&TypeId::of::<Box<IdleEvent>>())
            }
            &Event::Input(ref input) => {
                input.event_id()
            }
        }
    }

    fn with_args<'a, F, U>(&'a self, mut f: F) -> U
        where F: FnMut(&Any) -> U
    {
        match self {
            &Event::Update(ref args) => {
                f(args as &Any)
            }
            &Event::Render(ref args) => {
                f(args as &Any)
            }
            &Event::Idle(ref args) => {
                f(args as &Any)
            }
            &Event::Input(ref input) => {
                input.with_args(f)
            }
        }
    }

    fn from_args(event_id: u64, any: &Any) -> Option<Self> {
        let update_id = hash::<_, SipHasher>(&TypeId::of::<Box<UpdateEvent>>());
        let render_id = hash::<_, SipHasher>(&TypeId::of::<Box<RenderEvent>>());
        let idle_id = hash::<_, SipHasher>(&TypeId::of::<Box<IdleEvent>>());

        match event_id {
            x if x == update_id => {
                if let Some(&args) = any.downcast_ref::<UpdateArgs>() {
                    Some(Event::Update(args))
                } else {
                    panic!("Expected UpdateArgs")
                }
            }
            x if x == render_id => {
                if let Some(&args) = any.downcast_ref::<RenderArgs>() {
                    Some(Event::Render(args))
                } else {
                    panic!("Expected RenderArgs")
                }
            }
            x if x == idle_id => {
                if let Some(&args) = any.downcast_ref::<IdleArgs>() {
                    Some(Event::Idle(args))
                } else {
                    panic!("Expected IdleArgs")
                }
            }
            _ => {
                let input: Option<I> =
                    GenericEvent::from_args(event_id, any);
                match input {
                    Some(x) => Some(Event::Input(x)),
                    None => None
                }
            }
        }
    }
}