<commit_before>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate libc;
extern crate x11;

use std::ffi::CString;
use std::os::raw::{c_int, c_char};
use std::ptr;
use std::convert::From;

use x11::xlib;
use x11::xlib::{BadAccess, Display, Window, XErrorEvent, XOpenDisplay, XDefaultRootWindow,
                XSetErrorHandler};

// Error handler used during setup, which simply checks for the BadAccess error
// which indicates that another WM is already running.
unsafe extern "C" fn error_handler_init(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    if (*err).error_code == BadAccess {
        panic!("Another WM is already running");
    }
    0
}

// Actual error handler used during normal operation.
unsafe extern "C" fn error_handler(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    let buffer_size: usize = 1024;
    let mut buffer = Vec::<u8>::with_capacity(buffer_size);
    // XXX the docs say this returns the error text in the 'current locale'. We're
    // being extremely naughty and assuming this is UTF-8.
    // We're also assuming the return value of XGetErrorText is the actual length
    // of the string.
    let len: c_int = xlib::XGetErrorText(disp,
                                         (*err).error_code as i32,
                                         buffer.as_mut_ptr() as *mut c_char,
                                         buffer_size as i32);
    buffer.truncate(len as usize);
    let error_text = CString::new(buffer).unwrap().into_string().unwrap();
    error!("Received X error: request={}, error_code=({}, {}), resource_id={}",
           (*err).request_code,
           (*err).error_code,
           error_text,
           (*err).resourceid);
    0
}

fn xevent_to_str(event: &xlib::XEvent) -> &str {
    match event.get_type() {
        2 => "KeyPress",
        3 => "KeyRelease",
        4 => "ButtonPress",
        5 => "ButtonRelease",
        6 => "MotionNotify",
        7 => "EnterNotify",
        8 => "LeaveNotify",
        9 => "FocusIn",
        10 => "FocusOut",
        11 => "KeymapNotify",
        12 => "Expose",
        13 => "GraphicsExpose",
        14 => "NoExpose",
        15 => "VisibilityNotify",
        16 => "CreateNotify",
        17 => "DestroyNotify",
        18 => "UnmapNotify",
        19 => "MapNotify",
        20 => "MapRequest",
        21 => "ReparentNotify",
        22 => "ConfigureNotify",
        23 => "ConfigureRequest",
        24 => "GravityNotify",
        25 => "ResizeRequest",
        26 => "CirculateNotify",
        27 => "CirculateRequest",
        28 => "PropertyNotify",
        29 => "SelectionClear",
        30 => "SelectionRequest",
        31 => "SelectionNotify",
        32 => "ColormapNotify",
        33 => "ClientMessage",
        34 => "MappingNotify",
        35 => "GenericEvent",
        36 => "LASTEvent",
        _ => {
            error!("Unknown XEvent type: {}", event.get_type());
            "Unknown"
        }
    }
}

unsafe fn layout(disp: *mut Display, attrs: &xlib::XWindowAttributes, stack: &Vec<Window>) {
    if stack.len() == 0 {
        return;
    }

    let height = attrs.height / stack.len() as i32;

    for (i, window) in stack.iter().enumerate() {
        let i = i as i32;
        let mut changes: xlib::XWindowChanges = std::mem::zeroed();
        changes.x = 0;
        changes.y = i * height;
        changes.width = attrs.width;
        changes.height = height;
        let flags = xlib::CWX | xlib::CWY | xlib::CWWidth | xlib::CWHeight;
        xlib::XConfigureWindow(disp,
                               *window,
                               flags as u32,
                               &mut changes);
    }
}

fn main() {
    env_logger::init().unwrap();

    unsafe {
        let disp: *mut Display = XOpenDisplay(ptr::null_mut());
        assert!(!disp.is_null());
        let root: Window = XDefaultRootWindow(disp);
        assert!(root != 0);

        XSetErrorHandler(Some(error_handler_init));
        xlib::XSelectInput(disp,
                           root,
                           xlib::SubstructureNotifyMask | xlib::SubstructureRedirectMask);
        xlib::XSync(disp, 0);

        // If we get this far, then our panicing error handler didn't complain!
        info!("We are now the WM");

        XSetErrorHandler(Some(error_handler));

        let mut attrs: xlib::XWindowAttributes = std::mem::zeroed();
        xlib::XGetWindowAttributes(disp, root, &mut attrs);
        info!("Root window has geometry: {}x{}", attrs.width, attrs.height);

        let mut stack: Vec<Window> = Vec::new();

        loop {
            info!("Getting event...");
            let mut event = xlib::XEvent { pad: [0; 24] };
            xlib::XNextEvent(disp, &mut event);
            info!("Received event: {}", xevent_to_str(&event));

            match event.get_type() {
                xlib::MapRequest => {
                    let event = xlib::XMapRequestEvent::from(event);

                    stack.push(event.window);

                    layout(disp, &attrs, &stack);

                    xlib::XMapWindow(disp, event.window);
                }

                xlib::DestroyNotify => {
                    let event = xlib::XDestroyWindowEvent::from(event);

                    stack.iter().position(|&w| w == event.window).map(|index| stack.remove(index));

                    layout(disp, &attrs, &stack);
                }
                _ => {}
            }

            println!("Stack: {:?}", stack);
        }

    };
    println!("Hello, world!");
}
<commit_msg>Create a Layout trait.<commit_after>#[macro_use]
extern crate log;
extern crate env_logger;
extern crate libc;
extern crate x11;

use std::ffi::CString;
use std::os::raw::{c_int, c_char};
use std::ptr;
use std::convert::From;

use x11::xlib;
use x11::xlib::{BadAccess, Display, Window, XErrorEvent, XOpenDisplay, XDefaultRootWindow,
                XSetErrorHandler};

#[derive(Debug)]
struct Win {
    display: *mut Display,
    xwindow: Window,
}

impl Win {
    // Does this really need to be &mut self? It feels light it ought to be as it is actually
    // modifying the underlying window, even if we're not actually modifying the value as far as
    // Rust is concerned.
    fn position(&mut self, x: i32, y: i32, width: i32, height: i32) -> Result<(), ()> {
        let mut changes = xlib::XWindowChanges {
            x: x,
            y: y,
            width: width,
            height: height,

            // Ignored:
            border_width: 0,
            sibling: 0,
            stack_mode: 0,
        };
        let flags = (xlib::CWX | xlib::CWY | xlib::CWWidth | xlib::CWHeight) as u32;

        unsafe {
            xlib::XConfigureWindow(self.display, self.xwindow, flags, &mut changes);
        };

        Ok(())
    }
}

trait Layout {
    fn layout(&self, width: i32, height: i32, stack: &mut [Win]) -> Result<(), ()>;
}

struct TiledLayout;

impl Layout for TiledLayout {
    fn layout(&self, width: i32, height: i32, stack: &mut [Win]) -> Result<(), ()> {
        if stack.len() == 0 {
            return Ok(());
        }

        let tile_height = height / stack.len() as i32;

        for (i, window) in stack.iter_mut().enumerate() {
            window.position(0, i as i32 * tile_height, width, tile_height)?;
        }

        Ok(())
    }
}

// Error handler used during setup, which simply checks for the BadAccess error
// which indicates that another WM is already running.
unsafe extern "C" fn error_handler_init(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    if (*err).error_code == BadAccess {
        panic!("Another WM is already running");
    }
    0
}

// Actual error handler used during normal operation.
unsafe extern "C" fn error_handler(disp: *mut Display, err: *mut XErrorEvent) -> c_int {
    let buffer_size: usize = 1024;
    let mut buffer = Vec::<u8>::with_capacity(buffer_size);
    // XXX the docs say this returns the error text in the 'current locale'. We're
    // being extremely naughty and assuming this is UTF-8.
    // We're also assuming the return value of XGetErrorText is the actual length
    // of the string.
    let len: c_int = xlib::XGetErrorText(disp,
                                         (*err).error_code as i32,
                                         buffer.as_mut_ptr() as *mut c_char,
                                         buffer_size as i32);
    buffer.truncate(len as usize);
    let error_text = CString::new(buffer).unwrap().into_string().unwrap();
    error!("Received X error: request={}, error_code=({}, {}), resource_id={}",
           (*err).request_code,
           (*err).error_code,
           error_text,
           (*err).resourceid);
    0
}

fn xevent_to_str(event: &xlib::XEvent) -> &str {
    match event.get_type() {
        2 => "KeyPress",
        3 => "KeyRelease",
        4 => "ButtonPress",
        5 => "ButtonRelease",
        6 => "MotionNotify",
        7 => "EnterNotify",
        8 => "LeaveNotify",
        9 => "FocusIn",
        10 => "FocusOut",
        11 => "KeymapNotify",
        12 => "Expose",
        13 => "GraphicsExpose",
        14 => "NoExpose",
        15 => "VisibilityNotify",
        16 => "CreateNotify",
        17 => "DestroyNotify",
        18 => "UnmapNotify",
        19 => "MapNotify",
        20 => "MapRequest",
        21 => "ReparentNotify",
        22 => "ConfigureNotify",
        23 => "ConfigureRequest",
        24 => "GravityNotify",
        25 => "ResizeRequest",
        26 => "CirculateNotify",
        27 => "CirculateRequest",
        28 => "PropertyNotify",
        29 => "SelectionClear",
        30 => "SelectionRequest",
        31 => "SelectionNotify",
        32 => "ColormapNotify",
        33 => "ClientMessage",
        34 => "MappingNotify",
        35 => "GenericEvent",
        36 => "LASTEvent",
        _ => {
            error!("Unknown XEvent type: {}", event.get_type());
            "Unknown"
        }
    }
}

fn main() {
    env_logger::init().unwrap();

    unsafe {
        let disp: *mut Display = XOpenDisplay(ptr::null_mut());
        assert!(!disp.is_null());
        let root: Window = XDefaultRootWindow(disp);
        assert!(root != 0);

        XSetErrorHandler(Some(error_handler_init));
        xlib::XSelectInput(disp,
                           root,
                           xlib::SubstructureNotifyMask | xlib::SubstructureRedirectMask);
        xlib::XSync(disp, 0);

        // If we get this far, then our panicing error handler didn't complain!
        info!("We are now the WM");

        XSetErrorHandler(Some(error_handler));

        let mut attrs: xlib::XWindowAttributes = std::mem::zeroed();
        xlib::XGetWindowAttributes(disp, root, &mut attrs);
        info!("Root window has geometry: {}x{}", attrs.width, attrs.height);

        let mut stack: Vec<Win> = Vec::new();

        let layout = TiledLayout {};

        loop {
            info!("Getting event...");
            let mut event = xlib::XEvent { pad: [0; 24] };
            xlib::XNextEvent(disp, &mut event);
            info!("Received event: {}", xevent_to_str(&event));

            match event.get_type() {
                xlib::MapRequest => {
                    let event = xlib::XMapRequestEvent::from(event);

                    stack.push(Win {
                                   display: disp,
                                   xwindow: event.window,
                               });

                    layout.layout(attrs.width, attrs.height, stack.as_mut());

                    // TODO: Make this into a method on Win?
                    xlib::XMapWindow(disp, event.window);
                }

                xlib::DestroyNotify => {
                    let event = xlib::XDestroyWindowEvent::from(event);

                    stack.iter().position(|ref w| w.xwindow == event.window).map(|index| stack.remove(index));

                    layout.layout(attrs.width, attrs.height, stack.as_mut());
                }
                _ => {}
            }

            println!("Stack: {:?}", stack);
        }

    };
    println!("Hello, world!");
}
<|endoftext|>


<commit_before>#[allow(dead_code)]
#[path = "../libm/src/math/mod.rs"]
mod libm;

macro_rules! no_mangle {
    ($(fn $fun:ident($($iid:ident : $ity:ty),+) -> $oty:ty;)+) => {
        intrinsics! {
            $(
                pub extern "C" fn $fun($($iid: $ity),+) -> $oty {
                    self::libm::$fun($($iid),+)
                }
            )+
        }
    }
}

// only for the wasm32-unknown-unknown target
#[cfg(any(all(target_arch = "wasm32", target_os = "unknown"), target_env = "sgx"))]
no_mangle! {
    fn acos(x: f64) -> f64;
    fn asin(x: f64) -> f64;
    fn atan(x: f64) -> f64;
    fn atan2(x: f64, y: f64) -> f64;
    fn cbrt(x: f64) -> f64;
    fn cosh(x: f64) -> f64;
    fn expm1(x: f64) -> f64;
    fn hypot(x: f64, y: f64) -> f64;
    fn log1p(x: f64) -> f64;
    fn sinh(x: f64) -> f64;
    fn tan(x: f64) -> f64;
    fn tanh(x: f64) -> f64;
    fn cos(x: f64) -> f64;
    fn cosf(x: f32) -> f32;
    fn exp(x: f64) -> f64;
    fn expf(x: f32) -> f32;
    fn log2(x: f64) -> f64;
    fn log2f(x: f32) -> f32;
    fn log10(x: f64) -> f64;
    fn log10f(x: f32) -> f32;
    fn log(x: f64) -> f64;
    fn logf(x: f32) -> f32;
    fn round(x: f64) -> f64;
    fn roundf(x: f32) -> f32;
    fn sin(x: f64) -> f64;
    fn sinf(x: f32) -> f32;
    fn pow(x: f64, y: f64) -> f64;
    fn powf(x: f32, y: f32) -> f32;
    fn exp2(x: f64) -> f64;
    fn exp2f(x: f32) -> f32;
    fn fmod(x: f64, y: f64) -> f64;
    fn fmodf(x: f32, y: f32) -> f32;
    fn fma(x: f64, y: f64, z: f64) -> f64;
    fn fmaf(x: f32, y: f32, z: f32) -> f32;
    fn acosf(n: f32) -> f32;
    fn asinf(n: f32) -> f32;
    fn atan2f(a: f32, b: f32) -> f32;
    fn atanf(n: f32) -> f32;
    fn cbrtf(n: f32) -> f32;
    fn coshf(n: f32) -> f32;
    fn expm1f(n: f32) -> f32;
    fn fdimf(a: f32, b: f32) -> f32;
    fn hypotf(x: f32, y: f32) -> f32;
    fn log1pf(n: f32) -> f32;
    fn sinhf(n: f32) -> f32;
    fn tanf(n: f32) -> f32;
    fn tanhf(n: f32) -> f32;
}

// only for the thumb*-none-eabi* targets
#[cfg(all(target_arch = "arm", target_os = "none"))]
no_mangle! {
    // `f64 % f64`
    fn fmod(x: f64, y: f64) -> f64;
    // `f32 % f32`
    fn fmodf(x: f32, y: f32) -> f32;
}
<commit_msg>expose ceil, floor and trunc on sgx-target<commit_after>#[allow(dead_code)]
#[path = "../libm/src/math/mod.rs"]
mod libm;

macro_rules! no_mangle {
    ($(fn $fun:ident($($iid:ident : $ity:ty),+) -> $oty:ty;)+) => {
        intrinsics! {
            $(
                pub extern "C" fn $fun($($iid: $ity),+) -> $oty {
                    self::libm::$fun($($iid),+)
                }
            )+
        }
    }
}

// only for the wasm32-unknown-unknown target
#[cfg(any(all(target_arch = "wasm32", target_os = "unknown"), target_env = "sgx"))]
no_mangle! {
    fn acos(x: f64) -> f64;
    fn asin(x: f64) -> f64;
    fn atan(x: f64) -> f64;
    fn atan2(x: f64, y: f64) -> f64;
    fn cbrt(x: f64) -> f64;
    fn cosh(x: f64) -> f64;
    fn expm1(x: f64) -> f64;
    fn hypot(x: f64, y: f64) -> f64;
    fn log1p(x: f64) -> f64;
    fn sinh(x: f64) -> f64;
    fn tan(x: f64) -> f64;
    fn tanh(x: f64) -> f64;
    fn cos(x: f64) -> f64;
    fn cosf(x: f32) -> f32;
    fn exp(x: f64) -> f64;
    fn expf(x: f32) -> f32;
    fn log2(x: f64) -> f64;
    fn log2f(x: f32) -> f32;
    fn log10(x: f64) -> f64;
    fn log10f(x: f32) -> f32;
    fn log(x: f64) -> f64;
    fn logf(x: f32) -> f32;
    fn round(x: f64) -> f64;
    fn roundf(x: f32) -> f32;
    fn sin(x: f64) -> f64;
    fn sinf(x: f32) -> f32;
    fn pow(x: f64, y: f64) -> f64;
    fn powf(x: f32, y: f32) -> f32;
    fn exp2(x: f64) -> f64;
    fn exp2f(x: f32) -> f32;
    fn fmod(x: f64, y: f64) -> f64;
    fn fmodf(x: f32, y: f32) -> f32;
    fn fma(x: f64, y: f64, z: f64) -> f64;
    fn fmaf(x: f32, y: f32, z: f32) -> f32;
    fn acosf(n: f32) -> f32;
    fn asinf(n: f32) -> f32;
    fn atan2f(a: f32, b: f32) -> f32;
    fn atanf(n: f32) -> f32;
    fn cbrtf(n: f32) -> f32;
    fn coshf(n: f32) -> f32;
    fn expm1f(n: f32) -> f32;
    fn fdimf(a: f32, b: f32) -> f32;
    fn hypotf(x: f32, y: f32) -> f32;
    fn log1pf(n: f32) -> f32;
    fn sinhf(n: f32) -> f32;
    fn tanf(n: f32) -> f32;
    fn tanhf(n: f32) -> f32;
}

#[cfg(target_env = "sgx")]
no_mangle! {
    fn ceil(x: f64) -> f64;
    fn ceilf(x: f32) -> f32;
    fn floor(x: f64) -> f64;
    fn floorf(x: f32) -> f32;
    fn trunc(x: f64) -> f64;
    fn truncf(x: f32) -> f32;
}

// only for the thumb*-none-eabi* targets
#[cfg(all(target_arch = "arm", target_os = "none"))]
no_mangle! {
    // `f64 % f64`
    fn fmod(x: f64, y: f64) -> f64;
    // `f32 % f32`
    fn fmodf(x: f32, y: f32) -> f32;
}
<|endoftext|>


<commit_before>use std::f32::{NEG_INFINITY, INFINITY};
use std::cmp::Ordering;
use std::collections::HashMap;

use identity::{Team, JobDescription, Agent};
use life::{Commit, WorldState};
use motion::CENTER_OF_THE_WORLD;
use space::Pinfield;

pub fn orientation(team: Team) -> f32 {
    match team {
        Team::Orange => 1.0,
        Team::Blue => -1.0
    }
}

pub fn figurine_valuation(agent: Agent) -> f32 {
    let value = match agent.job_description {
        // en.wikipedia.org/wiki/
        // Chess_piece_relative_value#Hans_Berliner.27s_system
        JobDescription::Servant => 1.0,
        JobDescription::Pony => 3.2,
        JobDescription::Scholar => 3.3,
        JobDescription::Cop => 5.1,
        JobDescription::Princess => 8.8,
        JobDescription::Figurehead => 20000.0
    };
    orientation(agent.team) * value
}

pub fn score(world: WorldState) -> f32 {
    let mut valuation = 0.0;

    for team in Team::league().into_iter() {
        for agent in Agent::dramatis_personae(team).into_iter() {
            valuation += world.agent_to_pinfield_ref(
                agent).pincount() as f32 * figurine_valuation(agent);
        }
        // breadth of scholarship bonus
        if world.agent_to_pinfield_ref(
            Agent { team: team,
                    job_description: JobDescription::Scholar }
            ).to_locales().len() >= 2 {
                valuation += orientation(team) * 0.5
        }
    }

    // ponies and servants want to be in the center of the world's
    // action, maybe??
    let center = Pinfield(CENTER_OF_THE_WORLD);
    // cast to signed to avoid overflow
    let orange_centerism: i8 = world.orange_servants.union(
        world.orange_ponies).intersection(center).pincount() as i8;
    let blue_centerism: i8 = world.blue_servants.union(
        world.blue_ponies).intersection(center).pincount() as i8;
    valuation += (orange_centerism - blue_centerism) as f32 * 0.1;

    valuation
}

fn mmv_lva_heuristic(commit: &Commit) -> f32 {
    // https://chessprogramming.wikispaces.com/MVV-LVA
    match commit.hospitalization {
        Some(patient) => (figurine_valuation(patient) -
                          figurine_valuation(commit.patch.star)),
        None => 0.0
    }
}

fn order_moves(commits: &mut Vec<Commit>) {
    commits.sort_by(|a, b| mmv_lva_heuristic(
        &b).partial_cmp(&mmv_lva_heuristic(&a)).unwrap_or(Ordering::Equal));
}

pub fn negamax_search(world: WorldState, depth: u8) -> (Option<Commit>, f32) {
    let team = world.to_move;
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    }
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let (_after, mut value) = negamax_search(premonition.tree, depth-1);
        value = -value;
        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
    }
    (optimand, optimum)
}

// _very_ tempted to switch to developing on Nightly just so we can
// ungate non_ascii_idents and call this α_β_negamax_search
pub fn alpha_beta_negamax_search(
    world: WorldState, depth: u8,
    alpha: f32, beta: f32,
    deja_vu_table: &mut HashMap<WorldState, f32>) -> (Option<Commit>, f32)
{

    // RESEARCH: I don't really care that much right now, but can you
    // mutate (reassign) an argument name, and if so, what is the syntax?
    let mut experienced_alpha = alpha;
    let team = world.to_move;
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    };
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let mut value: f32;
        let mut cached: bool;
        {
            let cached_value_maybe = deja_vu_table.get(&premonition.tree);
            match cached_value_maybe {
                Some(&cached_value) => {
                    cached = true;
                    value = cached_value;
                },
                None => {
                    cached = false;
                    // XXX fake assignment to work around the compiler's
                    // "possibly uninitialized value" rules
                    value = NEG_INFINITY;
                }
            };
        }

        if !cached {
            let (_, acquired_value) = alpha_beta_negamax_search(
                premonition.tree, depth-1, -beta, -experienced_alpha,
                deja_vu_table);
            value = -acquired_value;
            deja_vu_table.insert(premonition.tree, value);
        }

        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
        if value > experienced_alpha {
            experienced_alpha = value;
        }
        if experienced_alpha >= beta {
            break;
        }
    }

    (optimand, optimum)
}

// The vision here is that for the turn I'm immediately going to take,
// I want a report of all possible moves ranked by negamax-computed
// optimality, but that we don't want to bother with all that bookkeeping
// for subsequent levels of the game tree; minimax is expensive enough
// already!!
pub fn kickoff(world: WorldState, depth: u8) -> Vec<(Commit, f32)> {
    // when we get non-ASCII identifiers: `déjà_vu_table`
    let mut deja_vu_table: HashMap<WorldState, f32> = HashMap::new();
    let mut premonitions = world.lookahead();
    order_moves(&mut premonitions);
    let mut forecasts = Vec::new();
    for premonition in premonitions.into_iter() {
        let (_grandchild, mut value) = alpha_beta_negamax_search(
            premonition.tree, depth-1, NEG_INFINITY, INFINITY,
            &mut deja_vu_table
        );
        value = -value;
        forecasts.push((premonition, value));
    }
    // The circumlocution (thanks to
    // https://www.reddit.com/r/rust/comments/29kia3/no_ord_for_f32/ for
    // the suggestion) is because Rust is sanctimonious about IEEE 754
    // floats not being totally ordered
    forecasts.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
    forecasts
}

#[cfg(test)]
mod test {
    use time;

    use super::{negamax_search, kickoff, score};
    use space::Locale;
    use life::WorldState;

    #[test]
    fn concerning_fairness_of_the_initial_position() {
        assert_eq!(0.0, score(WorldState::new()));
    }

    #[test]
    fn experimentally_about_kickoff() {
        let mut world = WorldState::new_except_empty();
        // SCENARIO: let's imagine Orange (to move) has separate attacks against
        // Blue's pony and servant, against which Blue has no defense but
        // to run away. We predict that Orange will take the pony, and
        // then Blue will move the servant out of the way.

        // scholar endangers pony
        world.blue_ponies = world.blue_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        world.orange_scholars = world.orange_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endangers servant
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        world.orange_ponies = world.orange_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Blue has another servant sitting nowhere interesting
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let depth = 2;
        let start = time::get_time();
        let advisory = kickoff(world, depth);
        let end = time:: get_time();

        // (you can see this if you run the tests with `-- --nocapture`)
        println!("negamax kickoff: evaluating {} possible choices to \
                  depth {} took {:?}", advisory.len(), depth, end-start);
        for item in advisory.iter() {
            println!("{:?}", item);
        }

        // taking the pony is the right thing to do
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);

        // And, furthermore, the answer should be the same if we face the
        // same situation with the colors reversed
        //
        // XXX this would be tidier and less copy-pastey if I had more
        // general figurine-placing functions that were three rather than
        // two levels of abstraction above twiddling bits on an unsigned
        // int ... oh, well
        let mut negaworld = WorldState::new_except_empty();

        // scholar endangers pony
        negaworld.orange_ponies = negaworld.orange_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        negaworld.blue_scholars = negaworld.blue_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endanger servant
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        negaworld.blue_ponies = negaworld.blue_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Orange has another servant sitting nowhere interesting
        negaworld.orange_servants = world.orange_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let negadvisory = kickoff(negaworld, depth);

        // taking the pony is still the right thing to do, even in the
        // negaworld
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);
    }

}
<commit_msg>Some benchmarks. You're using a nightly build, right? ;)<commit_after>use std::f32::{NEG_INFINITY, INFINITY};
use std::cmp::Ordering;
use std::collections::HashMap;

use identity::{Team, JobDescription, Agent};
use life::{Commit, WorldState};
use motion::CENTER_OF_THE_WORLD;
use space::Pinfield;

pub fn orientation(team: Team) -> f32 {
    match team {
        Team::Orange => 1.0,
        Team::Blue => -1.0
    }
}

pub fn figurine_valuation(agent: Agent) -> f32 {
    let value = match agent.job_description {
        // en.wikipedia.org/wiki/
        // Chess_piece_relative_value#Hans_Berliner.27s_system
        JobDescription::Servant => 1.0,
        JobDescription::Pony => 3.2,
        JobDescription::Scholar => 3.3,
        JobDescription::Cop => 5.1,
        JobDescription::Princess => 8.8,
        JobDescription::Figurehead => 20000.0
    };
    orientation(agent.team) * value
}

pub fn score(world: WorldState) -> f32 {
    let mut valuation = 0.0;

    for team in Team::league().into_iter() {
        for agent in Agent::dramatis_personae(team).into_iter() {
            valuation += world.agent_to_pinfield_ref(
                agent).pincount() as f32 * figurine_valuation(agent);
        }
        // breadth of scholarship bonus
        if world.agent_to_pinfield_ref(
            Agent { team: team,
                    job_description: JobDescription::Scholar }
            ).to_locales().len() >= 2 {
                valuation += orientation(team) * 0.5
        }
    }

    // ponies and servants want to be in the center of the world's
    // action, maybe??
    let center = Pinfield(CENTER_OF_THE_WORLD);
    // cast to signed to avoid overflow
    let orange_centerism: i8 = world.orange_servants.union(
        world.orange_ponies).intersection(center).pincount() as i8;
    let blue_centerism: i8 = world.blue_servants.union(
        world.blue_ponies).intersection(center).pincount() as i8;
    valuation += (orange_centerism - blue_centerism) as f32 * 0.1;

    valuation
}

fn mmv_lva_heuristic(commit: &Commit) -> f32 {
    // https://chessprogramming.wikispaces.com/MVV-LVA
    match commit.hospitalization {
        Some(patient) => (figurine_valuation(patient) -
                          figurine_valuation(commit.patch.star)),
        None => 0.0
    }
}

fn order_moves(commits: &mut Vec<Commit>) {
    commits.sort_by(|a, b| mmv_lva_heuristic(
        &b).partial_cmp(&mmv_lva_heuristic(&a)).unwrap_or(Ordering::Equal));
}

pub fn negamax_search(world: WorldState, depth: u8) -> (Option<Commit>, f32) {
    let team = world.to_move;
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    }
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let (_after, mut value) = negamax_search(premonition.tree, depth-1);
        value = -value;
        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
    }
    (optimand, optimum)
}

// _very_ tempted to switch to developing on Nightly just so we can
// ungate non_ascii_idents and call this α_β_negamax_search
pub fn alpha_beta_negamax_search(
    world: WorldState, depth: u8,
    alpha: f32, beta: f32,
    deja_vu_table: &mut HashMap<WorldState, f32>) -> (Option<Commit>, f32)
{

    // RESEARCH: I don't really care that much right now, but can you
    // mutate (reassign) an argument name, and if so, what is the syntax?
    let mut experienced_alpha = alpha;
    let team = world.to_move;
    let mut premonitions = world.reckless_lookahead();
    order_moves(&mut premonitions);
    if depth == 0 || premonitions.is_empty() {
        return (None, orientation(team) * score(world))
    };
    let mut optimum = NEG_INFINITY;
    let mut optimand = None;
    for premonition in premonitions.into_iter() {
        let mut value: f32;
        let mut cached: bool;
        {
            let cached_value_maybe = deja_vu_table.get(&premonition.tree);
            match cached_value_maybe {
                Some(&cached_value) => {
                    cached = true;
                    value = cached_value;
                },
                None => {
                    cached = false;
                    // XXX fake assignment to work around the compiler's
                    // "possibly uninitialized value" rules
                    value = NEG_INFINITY;
                }
            };
        }

        if !cached {
            let (_, acquired_value) = alpha_beta_negamax_search(
                premonition.tree, depth-1, -beta, -experienced_alpha,
                deja_vu_table);
            value = -acquired_value;
            deja_vu_table.insert(premonition.tree, value);
        }

        if value > optimum {
            optimum = value;
            optimand = Some(premonition);
        }
        if value > experienced_alpha {
            experienced_alpha = value;
        }
        if experienced_alpha >= beta {
            break;
        }
    }

    (optimand, optimum)
}

// The vision here is that for the turn I'm immediately going to take,
// I want a report of all possible moves ranked by negamax-computed
// optimality, but that we don't want to bother with all that bookkeeping
// for subsequent levels of the game tree; minimax is expensive enough
// already!!
pub fn kickoff(world: WorldState, depth: u8) -> Vec<(Commit, f32)> {
    // when we get non-ASCII identifiers: `déjà_vu_table`
    let mut deja_vu_table: HashMap<WorldState, f32> = HashMap::new();
    let mut premonitions = world.lookahead();
    order_moves(&mut premonitions);
    let mut forecasts = Vec::new();
    for premonition in premonitions.into_iter() {
        let (_grandchild, mut value) = alpha_beta_negamax_search(
            premonition.tree, depth-1, NEG_INFINITY, INFINITY,
            &mut deja_vu_table
        );
        value = -value;
        forecasts.push((premonition, value));
    }
    // The circumlocution (thanks to
    // https://www.reddit.com/r/rust/comments/29kia3/no_ord_for_f32/ for
    // the suggestion) is because Rust is sanctimonious about IEEE 754
    // floats not being totally ordered
    forecasts.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
    forecasts
}

#[cfg(test)]
mod tests {
    extern crate test;
    use self::test::Bencher;
    use time;

    use super::{kickoff, score};
    use space::Locale;
    use life::WorldState;

    #[bench]
    fn benchmark_scoring(b: &mut Bencher) {
        b.iter(|| score(WorldState::new()));
    }

    #[bench]
    fn benchmark_kickoff_d1(b: &mut Bencher) {
        b.iter(|| kickoff(WorldState::new(), 1));
    }

    #[bench]
    fn benchmark_kickoff_d2(b: &mut Bencher) {
        b.iter(|| kickoff(WorldState::new(), 2));
    }

    #[bench]
    fn benchmark_kickoff_d3(b: &mut Bencher) {
        b.iter(|| kickoff(WorldState::new(), 3));
    }

    #[test]
    fn concerning_fairness_of_the_initial_position() {
        assert_eq!(0.0, score(WorldState::new()));
    }

    #[test]
    fn experimentally_about_kickoff() {
        let mut world = WorldState::new_except_empty();
        // SCENARIO: let's imagine Orange (to move) has separate attacks against
        // Blue's pony and servant, against which Blue has no defense but
        // to run away. We predict that Orange will take the pony, and
        // then Blue will move the servant out of the way.

        // scholar endangers pony
        world.blue_ponies = world.blue_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        world.orange_scholars = world.orange_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endangers servant
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        world.orange_ponies = world.orange_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Blue has another servant sitting nowhere interesting
        world.blue_servants = world.blue_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let depth = 2;
        let start = time::get_time();
        let advisory = kickoff(world, depth);
        let end = time:: get_time();

        // (you can see this if you run the tests with `-- --nocapture`)
        println!("negamax kickoff: evaluating {} possible choices to \
                  depth {} took {:?}", advisory.len(), depth, end-start);
        for item in advisory.iter() {
            println!("{:?}", item);
        }

        // taking the pony is the right thing to do
        assert_eq!(Locale { rank: 0, file: 0 }, advisory[0].0.patch.whither);

        // And, furthermore, the answer should be the same if we face the
        // same situation with the colors reversed
        //
        // XXX this would be tidier and less copy-pastey if I had more
        // general figurine-placing functions that were three rather than
        // two levels of abstraction above twiddling bits on an unsigned
        // int ... oh, well
        let mut negaworld = WorldState::new_except_empty();

        // scholar endangers pony
        negaworld.orange_ponies = negaworld.orange_ponies.alight(
            Locale { rank: 0, file: 0 }
        );
        negaworld.blue_scholars = negaworld.blue_scholars.alight(
            Locale { rank: 2, file: 2 }
        );

        // pony endanger servant
        negaworld.orange_servants = negaworld.orange_servants.alight(
            Locale { rank: 7, file: 1 }
        );
        negaworld.blue_ponies = negaworld.blue_ponies.alight(
            Locale { rank: 5, file: 2 }
        );

        // Orange has another servant sitting nowhere interesting
        negaworld.orange_servants = world.orange_servants.alight(
            Locale { rank: 3, file: 6 }
        );

        let negadvisory = kickoff(negaworld, depth);

        // taking the pony is still the right thing to do, even in the
        // negaworld
        assert_eq!(Locale { rank: 0, file: 0 }, negadvisory[0].0.patch.whither);
    }

}
<|endoftext|>


<commit_before>extern crate mon_artist;

use mon_artist::render::svg::{SvgRender};
use mon_artist::render::{RenderS};
use mon_artist::grid::{Grid, ParseError};

use std::convert::From;
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};

fn main() {
    let mut args = env::args();
    args.next(); // skip program name
    loop {
        let table = if let Some(arg) = args.next() { arg } else { break; };
        let in_file =  if let Some(arg) = args.next() { arg } else { break; };
        let out_file =  if let Some(arg) = args.next() { arg } else { break; };
        println!("processing {} to {}", in_file, out_file);
        process(&table, &in_file, &out_file).unwrap();
    }
    // describe_table();
}

#[allow(dead_code)]
fn describe_table() {
    use mon_artist::format::{Table, Neighbor};

    let t: Table = Default::default();
    let mut may_both_count = 0;
    let mut may_start_count = 0;
    let mut may_end_count = 0;
    let mut neither_count = 0;
    for (j, e) in t.entries().enumerate() {
        println!("{} {:?}", j+1, e);
        match (&e.incoming(), &e.outgoing()) {
            (&Neighbor::May(..), &Neighbor::May(..)) => may_both_count += 1,
            (&Neighbor::May(..), _) => may_start_count += 1,
            (_, &Neighbor::May(..)) => may_end_count += 1,
            _ => neither_count += 1,
        }
    }
    println!("");
    println!("both: {} may_start: {} may_end: {} neither: {}",
             may_both_count, may_start_count, may_end_count, neither_count);
}

#[derive(Debug)]
enum Error {
    IO(io::Error),
    Parse(ParseError),
}

impl From<io::Error> for Error { fn from(e: io::Error) -> Self { Error::IO(e) } }
impl From<ParseError> for Error { fn from(e: ParseError) -> Self { Error::Parse(e) } }

use mon_artist::format::Table;

fn get_table(table: &str) -> Table {
    match table {
        "default" => Table::default(),
        "demo"    => Table::demo(),
        _ => panic!("Unknown table name: {}", table),
    }
}

fn process(table: &str, in_file: &str, out_file: &str) -> Result<(), Error> {
    let mut input = File::open(in_file)?;
    let mut content = String::new();
    input.read_to_string(&mut content)?;
    let table = get_table(table);
    let s = content.parse::<Grid>()?.into_scene(&table);
    let r = SvgRender {
        x_scale: 8, y_scale: 13,
        font_family: "monospace".to_string(), font_size: 13,
        show_gridlines: false,
        infer_rect_elements: false,
        name: in_file.to_string(),
    };
    let svg = r.render_s(&s);
    let mut output = File::create(out_file)?;
    Ok(write!(&mut output, "{}", svg)?)
}
<commit_msg>Add handling of first cmd line arg to try to treat it as a file holding rules spec.<commit_after>extern crate mon_artist;

use mon_artist::render::svg::{SvgRender};
use mon_artist::render::{RenderS};
use mon_artist::grid::{Grid, ParseError};

use std::convert::From;
use std::env;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, Write};

fn main() {
    let mut args = env::args();
    args.next(); // skip program name
    loop {
        let table = if let Some(arg) = args.next() { arg } else { break; };
        let in_file =  if let Some(arg) = args.next() { arg } else { break; };
        let out_file =  if let Some(arg) = args.next() { arg } else { break; };
        println!("processing {} to {} via {}", in_file, out_file, table);
        process(&table, &in_file, &out_file).unwrap();
    }
    // describe_table();
}

#[allow(dead_code)]
fn describe_table() {
    use mon_artist::format::{Table, Neighbor};

    let t: Table = Default::default();
    let mut may_both_count = 0;
    let mut may_start_count = 0;
    let mut may_end_count = 0;
    let mut neither_count = 0;
    for (j, e) in t.entries().enumerate() {
        println!("{} {:?}", j+1, e);
        match (&e.incoming(), &e.outgoing()) {
            (&Neighbor::May(..), &Neighbor::May(..)) => may_both_count += 1,
            (&Neighbor::May(..), _) => may_start_count += 1,
            (_, &Neighbor::May(..)) => may_end_count += 1,
            _ => neither_count += 1,
        }
    }
    println!("");
    println!("both: {} may_start: {} may_end: {} neither: {}",
             may_both_count, may_start_count, may_end_count, neither_count);
}

#[derive(Debug)]
enum Error {
    IO(io::Error),
    Parse(ParseError),
}

impl From<io::Error> for Error { fn from(e: io::Error) -> Self { Error::IO(e) } }
impl From<ParseError> for Error { fn from(e: ParseError) -> Self { Error::Parse(e) } }

use mon_artist::format::Table;

fn get_table(table: &str) -> Table {
    match File::open(table) {
        Ok(input) => Table::from_lines(BufReader::new(input).lines()),
        Err(err) => match table {
            "default" => Table::default(),
            "demo"    => Table::demo(),
            _ => panic!("Unknown table name: {}, file err: {:?}", table, err),
        },
    }
}

fn process(table: &str, in_file: &str, out_file: &str) -> Result<(), Error> {
    let mut input = File::open(in_file)?;
    let mut content = String::new();
    input.read_to_string(&mut content)?;
    let table = get_table(table);
    let s = content.parse::<Grid>()?.into_scene(&table);
    let r = SvgRender {
        x_scale: 8, y_scale: 13,
        font_family: "monospace".to_string(), font_size: 13,
        show_gridlines: false,
        infer_rect_elements: false,
        name: in_file.to_string(),
    };
    let svg = r.render_s(&s);
    let mut output = File::create(out_file)?;
    Ok(write!(&mut output, "{}", svg)?)
}
<|endoftext|>


<commit_before>use crate::{
    command_line::{CommandLine, Entry},
    schema::{default_renderer, FieldValidator, KindValue, ValidatorContext, SCHEMA},
    types::{Hash, Model, Msg, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
    old_props: Option<NodeProperties>,
}

#[derive(Properties, PartialEq, Clone)]
pub struct NodeProperties {
    pub path: Vec<Selector>,
    pub model: Rc<Model>,
    #[prop_or_default]
    pub hash: Option<Hash>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub validators: &'static [FieldValidator],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        Self {
            input_node_ref: NodeRef::default(),
            old_props: None,
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
        let props = ctx.props();
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let selected = path == cursor;
        if selected {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let hash = props.hash.clone().unwrap_or_default();
        let node = props.model.file.lookup_hash(&hash).unwrap_or(&default_node);
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let oninput = props.oninput.clone();
        let selected = path == cursor;
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let path = path.clone();
            let entries: Vec<Entry> = props
                .validators
                .iter()
                .flat_map(|v| match v {
                    FieldValidator::Kind(k) => Some(Entry {
                        label: k.to_string(),
                        description: "".to_string(),
                        action: Msg::ReplaceNode(
                            path.clone(),
                            Node {
                                kind: k.to_string(),
                                value: "".to_string(),
                                children: BTreeMap::new(),
                            },
                            false,
                        ),
                        valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                    }),
                    FieldValidator::Literal(v) => None,
                })
                .collect();
            let onenter = {
                let path = path.clone();
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected }
              />
            }
        } else {
            let renderer = if props.model.rich_render {
                kind.map(|r| {
                    let KindValue::Struct { renderer, .. } = r.value;
                    renderer
                })
                .unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                model: props.model.clone(),
                path: path.clone(),
                node: node.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let KindValue::Struct { fields, .. } = k.value;
                        fields
                            .iter()
                            .map(|f| Entry {
                                label: f.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(path.to_vec(), f.name.to_string()),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect()
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {

                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ Callback::from(move |()| {
                                onupdatemodel0.emit(Msg::Parent);
                            }) }
                            enabled=true
                        />

                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let path = path.clone();
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {

              { inner }

        }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        let same = self.old_props == Some(ctx.props().clone());
        log::debug!("Node changed");
        log::debug!("same props: {:?}", same);
        if let Some(old_props) = &self.old_props {
            log::debug!("same path: {:?}", old_props.path == ctx.props().path);
            log::debug!("same model: {:?}", old_props.model == ctx.props().model);
            log::debug!("same hash: {:?}", old_props.hash == ctx.props().hash);
            log::debug!(
                "same updatemodel: {:?}",
                old_props.updatemodel == ctx.props().updatemodel
            );
            return old_props.path != ctx.props().path
                || old_props.model != ctx.props().model
                || old_props.hash != ctx.props().hash;
        }
        self.old_props = Some(ctx.props().clone());
        !same
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        input_node
            .cast::<HtmlInputElement>()
            .unwrap()
            .focus()
            .unwrap();
    }
}
<commit_msg>Memoize callbacks<commit_after>use crate::{
    command_line::{CommandLine, Entry},
    schema::{default_renderer, FieldValidator, KindValue, ValidatorContext, SCHEMA},
    types::{Hash, Model, Msg, Node, Selector},
};
use std::{collections::BTreeMap, rc::Rc};
use web_sys::HtmlInputElement;
use yew::prelude::*;

pub struct NodeComponent {
    input_node_ref: NodeRef,
    old_props: Option<NodeProperties>,
    // Memoize callbacks?
    ondelete: Callback<()>,
}

#[derive(Properties, PartialEq, Clone)]
pub struct NodeProperties {
    pub path: Vec<Selector>,
    pub model: Rc<Model>,
    #[prop_or_default]
    pub hash: Option<Hash>,
    #[prop_or_default]
    pub placeholder: String,
    // When a new value is typed in the text box.
    #[prop_or_default]
    pub oninput: Callback<String>,
    // When the node is selected (e.g. clicked).
    #[prop_or_default]
    pub onselect: Callback<Vec<Selector>>,
    #[prop_or_default]
    pub updatemodel: Callback<Msg>,
    #[prop_or_default]
    pub validators: &'static [FieldValidator],
}

pub enum NodeMsg {
    Click,
}

pub const FIELD_CLASSES: &[&str] = &[
    "bg-blue-300",
    "inline-block",
    "p-0.5",
    "px-1",
    "border-blue-800",
    "border",
    "rounded",
];
pub const KIND_CLASSES: &[&str] = &[
    "kind",
    "bg-yellow-400",
    "border-yellow-800",
    "border-4",
    "inline-block",
    "p-0.5",
    "px-1",
];

impl Component for NodeComponent {
    type Message = NodeMsg;
    type Properties = NodeProperties;

    fn create(ctx: &Context<Self>) -> Self {
        log::debug!("creating node");
        let updatemodel = ctx.props().updatemodel.clone();
        Self {
            input_node_ref: NodeRef::default(),
            old_props: None,
            ondelete: Callback::from(move |()| updatemodel.emit(Msg::Parent)),
        }
    }

    fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
        let props = ctx.props();
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let selected = path == cursor;
        if selected {
            self.focus_input();
        }
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let default_node = Node::default();
        let props = ctx.props();
        let hash = props.hash.clone().unwrap_or_default();
        let node = props.model.file.lookup_hash(&hash).unwrap_or(&default_node);
        let path = props.path.clone();
        let cursor = props.model.cursor.clone();
        let oninput = props.oninput.clone();
        let selected = path == cursor;
        let kind = SCHEMA.get_kind(&node.kind);
        let inner = if node.kind.is_empty() {
            let onupdatemodel = ctx.props().updatemodel.clone();
            let path = path.clone();
            let entries: Vec<Entry> = props
                .validators
                .iter()
                .flat_map(|v| match v {
                    FieldValidator::Kind(k) => Some(Entry {
                        label: k.to_string(),
                        description: "".to_string(),
                        action: Msg::ReplaceNode(
                            path.clone(),
                            Node {
                                kind: k.to_string(),
                                value: "".to_string(),
                                children: BTreeMap::new(),
                            },
                            false,
                        ),
                        valid_classes: KIND_CLASSES.iter().map(|v| v.to_string()).collect(),
                    }),
                    FieldValidator::Literal(v) => None,
                })
                .collect();
            let onenter = {
                let path = path.clone();
                let onupdatemodel = onupdatemodel.clone();
                Callback::from(move |()| {
                    onupdatemodel.emit(Msg::Parent);
                })
            };
            let onupdatemodel0 = onupdatemodel.clone();
            html! {
              <CommandLine
                input_node_ref={ self.input_node_ref.clone() }
                entries={ entries }
                value={ node.value.clone() }
                oninput={ Callback::from(move |v| {
                    onupdatemodel.emit(Msg::SetNodeValue(path.clone(), v));
                 }) }
                onselect={ ctx.props().updatemodel.clone() }
                ondelete={ Callback::from(move |()| {
                    onupdatemodel0.emit(Msg::DeleteItem);
                 }) }
                onenter={ onenter }
                enabled={ selected }
              />
            }
        } else {
            let renderer = if props.model.rich_render {
                kind.map(|r| {
                    let KindValue::Struct { renderer, .. } = r.value;
                    renderer
                })
                .unwrap_or(default_renderer)
            } else {
                default_renderer
            };
            let validator_context = ValidatorContext {
                model: props.model.clone(),
                path: path.clone(),
                node: node.clone(),
                onselect: props.onselect.clone(),
                updatemodel: props.updatemodel.clone(),
            };
            let content = renderer(&validator_context);
            let footer = if selected {
                let entries: Vec<Entry> = kind
                    .map(|k| {
                        let KindValue::Struct { fields, .. } = k.value;
                        fields
                            .iter()
                            .map(|f| Entry {
                                label: f.name.to_string(),
                                description: "".to_string(),
                                action: Msg::AddField(path.to_vec(), f.name.to_string()),
                                valid_classes: FIELD_CLASSES
                                    .iter()
                                    .map(|v| v.to_string())
                                    .collect(),
                            })
                            .collect()
                    })
                    .unwrap_or_default();
                // Make it look like an actual field.
                let onupdatemodel0 = ctx.props().updatemodel.clone();
                html! {

                        <CommandLine
                            input_node_ref={ self.input_node_ref.clone() }
                            entries={ entries }
                            value={ node.value.clone() }
                            onselect={ ctx.props().updatemodel.clone() }
                            ondelete={ self.ondelete.clone() }
                            enabled=true
                        />

                }
            } else {
                html! {}
            };
            html! {
              <>
                { content }
                { footer }
              </>
            }
        };
        let onselect = ctx.props().onselect.clone();
        let onclick = {
            let path = path.clone();
            ctx.link().callback(move |e: MouseEvent| {
                e.stop_propagation();
                onselect.emit(path.clone());
                NodeMsg::Click
            })
        };
        let mut classes = vec![
            "node".to_string(),
            "align-top".to_string(),
            "flex-auto".to_string(),
        ];
        if selected {
            classes.push("selected".to_string());
        }
        html! {

              { inner }

        }
    }

    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            NodeMsg::Click => {
                self.focus_input();
                true
            }
        }
    }

    fn changed(&mut self, ctx: &Context<Self>) -> bool {
        let same = self.old_props == Some(ctx.props().clone());
        log::debug!("Node changed");
        log::debug!("same props: {:?}", same);
        if let Some(old_props) = &self.old_props {
            log::debug!("same path: {:?}", old_props.path == ctx.props().path);
            log::debug!("same model: {:?}", old_props.model == ctx.props().model);
            log::debug!("same hash: {:?}", old_props.hash == ctx.props().hash);
            log::debug!(
                "same updatemodel: {:?}",
                old_props.updatemodel == ctx.props().updatemodel
            );
            return old_props.path != ctx.props().path
                || old_props.model != ctx.props().model
                || old_props.hash != ctx.props().hash;
        }
        self.old_props = Some(ctx.props().clone());
        !same
    }
}

impl NodeComponent {
    fn focus_input(&self) {
        log::info!("focusing input");
        let input_node = self.input_node_ref.clone();
        input_node
            .cast::<HtmlInputElement>()
            .unwrap()
            .focus()
            .unwrap();
    }
}
<|endoftext|>


<commit_before>use {Error, Result};
use reader::Reader;

/// The data attribute of a path.
///
/// http://www.w3.org/TR/SVG/paths.html#PathData
pub struct Data {
    commands: Vec<Command>,
}

/// A command used to draw a path.
#[derive(Debug)]
pub enum Command {
    /// [Establish][1] a new current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
    MoveTo(Positioning, Vec<f64>),

    /// [End][1] the current subpath.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
    ClosePath,

    /// [Draw][1] straight lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    LineTo(Positioning, Vec<f64>),

    /// [Draw][1] horizontal lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    HorizontalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] vertical lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    VerticalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    CurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve assuming the first control point to be
    /// the reflection of the second control point on the previous command
    /// relative to the current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    SmoothCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    QuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve assuming the control point to be the
    /// reflection of the control point on the previous command relative to the
    /// current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    SmoothQuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] an elliptical arc.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
    EllipticalArc(Positioning, Vec<f64>),
}

/// An attribute of a command indicating whether the coordinates of the command
/// are absolute or relative to the current position.
#[derive(Debug)]
pub enum Positioning {
    Absolute,
    Relative,
}

impl Data {
    /// Parse the data attribute of a path.
    #[inline]
    pub fn parse(text: &str) -> Result<Data> {
        Parser::new(text).process()
    }

    #[inline]
    pub fn iter(&self) -> ::std::slice::Iter<Command> {
        self.commands.iter()
    }
}

struct Parser<'s> {
    reader: Reader<'s>,
}

macro_rules! ok(
    ($result:expr) => (
        match $result {
            Ok(result) => result,
            Err(error) => return Err(error),
        }
    )
);

macro_rules! raise(
    ($parser:expr, $($arg:tt)*) => ({
        let (line, column) = $parser.reader.position();
        return Err(Error {
            line: line,
            column: column,
            message: format!($($arg)*),
        })
    });
);

impl<'s> Parser<'s> {
    #[inline]
    fn new(text: &'s str) -> Parser<'s> {
        Parser {
            reader: Reader::new(text),
        }
    }

    fn process(&mut self) -> Result<Data> {
        let mut commands = Vec::new();

        loop {
            self.reader.consume_whitespace();

            match ok!(self.read_command()) {
                Some(command) => commands.push(command),
                _ => break,
            }
        }

        Ok(Data {
            commands: commands,
        })
    }

    fn read_command(&mut self) -> Result<Option<Command>> {
        use self::Command::*;
        use self::Positioning::*;

        let name = match self.reader.next() {
            Some(name) => match name {
                'A'...'Z' | 'a'...'z' => name,
                _ => raise!(self, "expected a path command"),
            },
            _ => return Ok(None),
        };

        self.reader.consume_whitespace();

        let params = ok!(self.read_parameters());

        Ok(Some(match name {
            'M' => MoveTo(Absolute, params),
            'm' => MoveTo(Relative, params),

            'Z' | 'z' => ClosePath,

            'L' => LineTo(Absolute, params),
            'l' => LineTo(Relative, params),

            'H' => HorizontalLineTo(Absolute, params),
            'h' => HorizontalLineTo(Relative, params),

            'V' => VerticalLineTo(Absolute, params),
            'v' => VerticalLineTo(Relative, params),

            'C' => CurveTo(Absolute, params),
            'c' => CurveTo(Relative, params),

            'S' => SmoothCurveTo(Absolute, params),
            's' => SmoothCurveTo(Relative, params),

            'Q' => QuadraticBezierCurveTo(Absolute, params),
            'q' => QuadraticBezierCurveTo(Relative, params),

            'T' => SmoothQuadraticBezierCurveTo(Absolute, params),
            't' => SmoothQuadraticBezierCurveTo(Relative, params),

            'A' => EllipticalArc(Absolute, params),
            'a' => EllipticalArc(Relative, params),

            _ => raise!(self, "found an unknown path command '{}'", name),
        }))
    }

    fn read_parameters(&mut self) -> Result<Vec<f64>> {
        let mut params = Vec::new();

        loop {
            match ok!(self.read_number()) {
                Some(number) => params.push(number),
                _ => break,
            }

            self.reader.consume_whitespace();
            self.reader.consume_chars(",");
        }

        Ok(params)
    }

    pub fn read_number(&mut self) -> Result<Option<f64>> {
        self.reader.consume_whitespace();

        let number = {
            let number = self.reader.capture(|reader| {
                reader.consume_chars("-");
                reader.consume_digits();
                reader.consume_chars(".");
                reader.consume_digits();
            });

            if number.is_empty() {
                return Ok(None)
            }

            number.parse()
        };

        match number {
            Ok(number) => Ok(Some(number)),
            Err(_) => raise!(self, "failed to parse a number"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Data, Parser};
    use super::Command::*;
    use super::Positioning::*;

    #[test]
    fn parser() {
        let data = Data::parse("M1,2 l3,4").ok().unwrap();

        assert_eq!(data.commands.len(), 2);

        match data.commands[0] {
            MoveTo(Absolute, ref params) => assert_eq!(*params, vec![1.0, 2.0]),
            _ => assert!(false),
        }

        match data.commands[1] {
            LineTo(Relative, ref params) => assert_eq!(*params, vec![3.0, 4.0]),
            _ => assert!(false),
        }
    }

    #[test]
    fn parser_read_command() {
        macro_rules! run(
            ($text:expr) => ({
                let mut parser = Parser::new($text);
                parser.read_command().ok().unwrap().unwrap()
            });
        );

        macro_rules! test(
            ($text:expr, $command:ident, $positioning:ident, $params:expr) => (
                match run!($text) {
                    $command($positioning, params) => assert_eq!(params, $params),
                    _ => assert!(false),
                }
            );
            ($text:expr, $command:ident) => (
                match run!($text) {
                    $command => {},
                    _ => assert!(false),
                }
            );
        );

        test!("M4,2", MoveTo, Absolute, vec![4.0, 2.0]);
        test!("m4,\n2", MoveTo, Relative, vec![4.0, 2.0]);

        test!("Z", ClosePath);
        test!("z", ClosePath);

        test!("L7, 8  9", LineTo, Absolute, vec![7.0, 8.0, 9.0]);
        test!("l 7,8 \n9", LineTo, Relative, vec![7.0, 8.0, 9.0]);

        test!("H\t6,9", HorizontalLineTo, Absolute, vec![6.0, 9.0]);
        test!("h6,  \t9", HorizontalLineTo, Relative, vec![6.0, 9.0]);

        test!("V2.1,-3", VerticalLineTo, Absolute, vec![2.1, -3.0]);
        test!("v\n2.1 -3", VerticalLineTo, Relative, vec![2.1, -3.0]);

        test!("C0,1 0,2", CurveTo, Absolute, vec![0.0, 1.0, 0.0, 2.0]);
        test!("c0 ,1 0,  2", CurveTo, Relative, vec![0.0, 1.0, 0.0, 2.0]);

        test!("S42,0", SmoothCurveTo, Absolute, vec![42.0, 0.0]);
        test!("s \t 42,0", SmoothCurveTo, Relative, vec![42.0, 0.0]);

        test!("Q90.5 0", QuadraticBezierCurveTo, Absolute, vec![90.5, 0.0]);
        test!("q90.5\n, 0", QuadraticBezierCurveTo, Relative, vec![90.5, 0.0]);

        test!("T-1", SmoothQuadraticBezierCurveTo, Absolute, vec![-1.0]);
        test!("t -1", SmoothQuadraticBezierCurveTo, Relative, vec![-1.0]);

        test!("A2.6,0 -7", EllipticalArc, Absolute, vec![2.6, 0.0, -7.0]);
        test!("a 2.6 ,0 -7", EllipticalArc, Relative, vec![2.6, 0.0, -7.0]);
    }

    #[test]
    fn parser_read_parameters() {
        let mut parser = Parser::new("1,2 3,4 5 6.7");
        let params = parser.read_parameters().ok().unwrap();
        assert_eq!(params, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.7]);
    }

    #[test]
    fn parser_read_number() {
        let texts = vec!["-1", "3", "3.14"];
        let numbers = vec![-1.0, 3.0, 3.14];

        for (text, &number) in texts.iter().zip(numbers.iter()) {
            let mut parser = Parser::new(text);
            assert_eq!(parser.read_number().ok().unwrap().unwrap(), number);
        }
    }
}
<commit_msg>Eliminate a redundant macro<commit_after>use {Error, Result};
use reader::Reader;

/// The data attribute of a path.
///
/// http://www.w3.org/TR/SVG/paths.html#PathData
pub struct Data {
    commands: Vec<Command>,
}

/// A command used to draw a path.
#[derive(Debug)]
pub enum Command {
    /// [Establish][1] a new current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
    MoveTo(Positioning, Vec<f64>),

    /// [End][1] the current subpath.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
    ClosePath,

    /// [Draw][1] straight lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    LineTo(Positioning, Vec<f64>),

    /// [Draw][1] horizontal lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    HorizontalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] vertical lines.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataLinetoCommands
    VerticalLineTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    CurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a cubic Bézier curve assuming the first control point to be
    /// the reflection of the second control point on the previous command
    /// relative to the current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
    SmoothCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    QuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] a quadratic Bézier curve assuming the control point to be the
    /// reflection of the control point on the previous command relative to the
    /// current point.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
    SmoothQuadraticBezierCurveTo(Positioning, Vec<f64>),

    /// [Draw][1] an elliptical arc.
    ///
    /// [1]: http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
    EllipticalArc(Positioning, Vec<f64>),
}

/// An attribute of a command indicating whether the coordinates of the command
/// are absolute or relative to the current position.
#[derive(Debug)]
pub enum Positioning {
    Absolute,
    Relative,
}

impl Data {
    /// Parse the data attribute of a path.
    #[inline]
    pub fn parse(text: &str) -> Result<Data> {
        Parser::new(text).process()
    }

    #[inline]
    pub fn iter(&self) -> ::std::slice::Iter<Command> {
        self.commands.iter()
    }
}

struct Parser<'s> {
    reader: Reader<'s>,
}

macro_rules! raise(
    ($parser:expr, $($arg:tt)*) => ({
        let (line, column) = $parser.reader.position();
        return Err(Error {
            line: line,
            column: column,
            message: format!($($arg)*),
        })
    });
);

impl<'s> Parser<'s> {
    #[inline]
    fn new(text: &'s str) -> Parser<'s> {
        Parser {
            reader: Reader::new(text),
        }
    }

    fn process(&mut self) -> Result<Data> {
        let mut commands = Vec::new();

        loop {
            self.reader.consume_whitespace();

            match try!(self.read_command()) {
                Some(command) => commands.push(command),
                _ => break,
            }
        }

        Ok(Data {
            commands: commands,
        })
    }

    fn read_command(&mut self) -> Result<Option<Command>> {
        use self::Command::*;
        use self::Positioning::*;

        let name = match self.reader.next() {
            Some(name) => match name {
                'A'...'Z' | 'a'...'z' => name,
                _ => raise!(self, "expected a path command"),
            },
            _ => return Ok(None),
        };

        self.reader.consume_whitespace();

        let params = try!(self.read_parameters());

        Ok(Some(match name {
            'M' => MoveTo(Absolute, params),
            'm' => MoveTo(Relative, params),

            'Z' | 'z' => ClosePath,

            'L' => LineTo(Absolute, params),
            'l' => LineTo(Relative, params),

            'H' => HorizontalLineTo(Absolute, params),
            'h' => HorizontalLineTo(Relative, params),

            'V' => VerticalLineTo(Absolute, params),
            'v' => VerticalLineTo(Relative, params),

            'C' => CurveTo(Absolute, params),
            'c' => CurveTo(Relative, params),

            'S' => SmoothCurveTo(Absolute, params),
            's' => SmoothCurveTo(Relative, params),

            'Q' => QuadraticBezierCurveTo(Absolute, params),
            'q' => QuadraticBezierCurveTo(Relative, params),

            'T' => SmoothQuadraticBezierCurveTo(Absolute, params),
            't' => SmoothQuadraticBezierCurveTo(Relative, params),

            'A' => EllipticalArc(Absolute, params),
            'a' => EllipticalArc(Relative, params),

            _ => raise!(self, "found an unknown path command '{}'", name),
        }))
    }

    fn read_parameters(&mut self) -> Result<Vec<f64>> {
        let mut params = Vec::new();

        loop {
            match try!(self.read_number()) {
                Some(number) => params.push(number),
                _ => break,
            }

            self.reader.consume_whitespace();
            self.reader.consume_chars(",");
        }

        Ok(params)
    }

    pub fn read_number(&mut self) -> Result<Option<f64>> {
        self.reader.consume_whitespace();

        let number = {
            let number = self.reader.capture(|reader| {
                reader.consume_chars("-");
                reader.consume_digits();
                reader.consume_chars(".");
                reader.consume_digits();
            });

            if number.is_empty() {
                return Ok(None)
            }

            number.parse()
        };

        match number {
            Ok(number) => Ok(Some(number)),
            Err(_) => raise!(self, "failed to parse a number"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Data, Parser};
    use super::Command::*;
    use super::Positioning::*;

    #[test]
    fn parser() {
        let data = Data::parse("M1,2 l3,4").ok().unwrap();

        assert_eq!(data.commands.len(), 2);

        match data.commands[0] {
            MoveTo(Absolute, ref params) => assert_eq!(*params, vec![1.0, 2.0]),
            _ => assert!(false),
        }

        match data.commands[1] {
            LineTo(Relative, ref params) => assert_eq!(*params, vec![3.0, 4.0]),
            _ => assert!(false),
        }
    }

    #[test]
    fn parser_read_command() {
        macro_rules! run(
            ($text:expr) => ({
                let mut parser = Parser::new($text);
                parser.read_command().ok().unwrap().unwrap()
            });
        );

        macro_rules! test(
            ($text:expr, $command:ident, $positioning:ident, $params:expr) => (
                match run!($text) {
                    $command($positioning, params) => assert_eq!(params, $params),
                    _ => assert!(false),
                }
            );
            ($text:expr, $command:ident) => (
                match run!($text) {
                    $command => {},
                    _ => assert!(false),
                }
            );
        );

        test!("M4,2", MoveTo, Absolute, vec![4.0, 2.0]);
        test!("m4,\n2", MoveTo, Relative, vec![4.0, 2.0]);

        test!("Z", ClosePath);
        test!("z", ClosePath);

        test!("L7, 8  9", LineTo, Absolute, vec![7.0, 8.0, 9.0]);
        test!("l 7,8 \n9", LineTo, Relative, vec![7.0, 8.0, 9.0]);

        test!("H\t6,9", HorizontalLineTo, Absolute, vec![6.0, 9.0]);
        test!("h6,  \t9", HorizontalLineTo, Relative, vec![6.0, 9.0]);

        test!("V2.1,-3", VerticalLineTo, Absolute, vec![2.1, -3.0]);
        test!("v\n2.1 -3", VerticalLineTo, Relative, vec![2.1, -3.0]);

        test!("C0,1 0,2", CurveTo, Absolute, vec![0.0, 1.0, 0.0, 2.0]);
        test!("c0 ,1 0,  2", CurveTo, Relative, vec![0.0, 1.0, 0.0, 2.0]);

        test!("S42,0", SmoothCurveTo, Absolute, vec![42.0, 0.0]);
        test!("s \t 42,0", SmoothCurveTo, Relative, vec![42.0, 0.0]);

        test!("Q90.5 0", QuadraticBezierCurveTo, Absolute, vec![90.5, 0.0]);
        test!("q90.5\n, 0", QuadraticBezierCurveTo, Relative, vec![90.5, 0.0]);

        test!("T-1", SmoothQuadraticBezierCurveTo, Absolute, vec![-1.0]);
        test!("t -1", SmoothQuadraticBezierCurveTo, Relative, vec![-1.0]);

        test!("A2.6,0 -7", EllipticalArc, Absolute, vec![2.6, 0.0, -7.0]);
        test!("a 2.6 ,0 -7", EllipticalArc, Relative, vec![2.6, 0.0, -7.0]);
    }

    #[test]
    fn parser_read_parameters() {
        let mut parser = Parser::new("1,2 3,4 5 6.7");
        let params = parser.read_parameters().ok().unwrap();
        assert_eq!(params, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.7]);
    }

    #[test]
    fn parser_read_number() {
        let texts = vec!["-1", "3", "3.14"];
        let numbers = vec![-1.0, 3.0, 3.14];

        for (text, &number) in texts.iter().zip(numbers.iter()) {
            let mut parser = Parser::new(text);
            assert_eq!(parser.read_number().ok().unwrap().unwrap(), number);
        }
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::io;

use byteorder::{BigEndian, WriteBytesExt};

use mio;

use EventLoop;
use Message;
use protocol::Protocol as Protocol;
use transport::Connection as Connection;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PipeStateIdx {
	Handshake,
	Ready
}

pub struct Pipe {
	state: PipeStateIdx,
	handshake_state: HandshakePipeState,
	ready_state: ReadyPipeState
}

impl Pipe {

	pub fn new(id: usize, protocol: &Protocol, connection: Box<Connection>) -> Pipe {
		let conn_ref = Rc::new(RefCell::new(connection));

		Pipe {
			state: PipeStateIdx::Handshake,
			handshake_state: HandshakePipeState::new(id, protocol, conn_ref.clone()),
			ready_state: ReadyPipeState::new(id, conn_ref.clone())
		}
	}

	pub fn is_ready(&self) -> bool {
		self.state == PipeStateIdx::Ready
	}

	pub fn send(&mut self, msg: Message) {
		self.get_state().send(msg);
	}

	fn get_state<'a>(&'a mut self) -> &'a mut PipeState {
		match self.state {
			PipeStateIdx::Handshake => &mut self.handshake_state,
			PipeStateIdx::Ready => &mut self.ready_state
		}
	}

	fn set_state(&mut self, state: PipeStateIdx, event_loop: &mut EventLoop) -> io::Result<()> {
		self.state = state;
		self.get_state().enter(event_loop)
	}

	pub fn init(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		self.set_state(PipeStateIdx::Handshake, event_loop)
	}

	pub fn ready(&mut self, event_loop: &mut EventLoop, events: mio::EventSet)-> io::Result<()> {
		if events.is_readable() {
			try!(self.readable(event_loop));
		}
		if events.is_writable() {
			try!(self.writable(event_loop));
		}
		Ok(())
	}

	pub fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().readable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

	pub fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().writable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

}

trait PipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()>;
	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;
	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;

	fn send(&mut self, msg: Message) -> io::Result<()>;
}

struct HandshakePipeState {
	id: usize,
	protocol_id: u16,
	protocol_peer_id: u16,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool,
	received: bool
}

impl HandshakePipeState {

	fn new(id: usize, protocol: &Protocol, connection: Rc<RefCell<Box<Connection>>>) -> HandshakePipeState {
		HandshakePipeState {
			id: id,
			protocol_id: protocol.id(),
			protocol_peer_id: protocol.peer_id(),
			connection: connection,
			sent: false,
			received: false }
	}

	fn check_received_handshake(&self, handshake: &[u8; 8]) -> io::Result<()> {
		let mut expected_handshake = vec!(0, 83, 80, 0);
		try!(expected_handshake.write_u16::<BigEndian>(self.protocol_peer_id));
		try!(expected_handshake.write_u16::<BigEndian>(0));
		let mut both = handshake.iter().zip(expected_handshake.iter());

		if both.all(|(l,r)| l == r) {
			Ok(())
		} else {
			Err(io::Error::new(io::ErrorKind::InvalidData, "received wrong handshake"))
		}
	}

	fn check_sent_handshake(&self, written: Option<usize>) -> io::Result<()> {
		match written {
			Some(8) => Ok(()),
			_ => Err(io::Error::new(io::ErrorKind::InvalidData, "failed to send handshake"))
		}
	}

	fn read_handshake(&mut self) -> io::Result<()> {
		let mut handshake = [0u8; 8];
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_read(&mut handshake).
			and_then(|_| self.check_received_handshake(&handshake)));
		debug!("handshake received !");
		self.received = true;
		Ok(())
	}

	fn write_handshake(&mut self) -> io::Result<()> {
		// handshake is Zero, 'S', 'P', Version, Proto, Rsvd
		let mut handshake = vec!(0, 83, 80, 0);
		try!(handshake.write_u16::<BigEndian>(self.protocol_id));
		try!(handshake.write_u16::<BigEndian>(0));
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_write(&handshake).
			and_then(|w| self.check_sent_handshake(w)));
		debug!("handshake sent !");
		self.sent = true;
		Ok(())
	}

	fn register(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.register_opt(fd, token, interest, poll_opt)
	}

	fn reregister(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.reregister(fd, token, interest, poll_opt)
	}

}

impl PipeState for HandshakePipeState {

	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter handshake state");
		self.sent = false;
		self.received = false;

		let interest = mio::EventSet::readable() | mio::EventSet::writable() | mio::EventSet::hup();
		let poll_opt = mio::PollOpt::oneshot();

		try!(self.register(event_loop, interest, poll_opt));

		Ok(())
	}

	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.received {
			return Ok(None)
		}

		try!(self.read_handshake());

		if self.sent {
			Ok(Some(PipeStateIdx::Ready))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::writable();
			let poll_opt = mio::PollOpt::oneshot();

			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			return Ok(None)
		}

		try!(self.write_handshake());

		if self.received {
			Ok(Some(PipeStateIdx::Ready))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::readable();
			let poll_opt = mio::PollOpt::oneshot();
			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn send(&mut self, _: Message) -> io::Result<()> {
		Ok(())
	}

}

struct ReadyPipeState {
	id: usize,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool
}

impl ReadyPipeState {
	fn new(id: usize, connection: Rc<RefCell<Box<Connection>>>) -> ReadyPipeState {
		ReadyPipeState {
			id: id,
			connection: connection,
			sent: false }
	}
}

impl PipeState for ReadyPipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter ready state");
		let token = mio::Token(self.id);
		let connection = self.connection.borrow_mut();
		let fd = connection.as_evented();
		let interest = mio::EventSet::hup() | mio::EventSet::writable();
		let poll_opt = mio::PollOpt::edge();
		try!(event_loop.reregister(fd, token, interest, poll_opt));
		Ok(())
	}

	fn readable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		Ok(None)
	}

	fn writable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			Ok(None)
		} else {
			let mut connection = self.connection.borrow_mut();
			let header = [0, 0, 0, 0, 0, 0, 0, 3];
			let body = [65, 86, 67];

			try!(connection.try_write(&header));
			try!(connection.try_write(&body));
			self.sent = true;
			debug!("dummy msg sent !");

			Ok(None)
		}
	}

	fn send(&mut self, msg: Message) -> io::Result<()> {
		let mut connection = self.connection.borrow_mut();
		let mut prefix = Vec::with_capacity(8);
		try!(prefix.write_u64::<BigEndian>(msg.len() as u64));

		try!(connection.try_write(&prefix));

		if msg.get_header().len() > 0 {
			try!(connection.try_write(msg.get_header()));
		}

		if msg.get_body().len() > 0 {
			try!(connection.try_write(msg.get_body()));
		}

		debug!("msg sent !");
		Ok(())
	}
}<commit_msg>Renames ready pipe state enum variant to connected<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::io;

use byteorder::{BigEndian, WriteBytesExt};

use mio;

use EventLoop;
use Message;
use protocol::Protocol as Protocol;
use transport::Connection as Connection;

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PipeStateIdx {
	Handshake,
	Connected
}

pub struct Pipe {
	state: PipeStateIdx,
	handshake_state: HandshakePipeState,
	connected_state: ReadyPipeState
}

impl Pipe {

	pub fn new(id: usize, protocol: &Protocol, connection: Box<Connection>) -> Pipe {
		let conn_ref = Rc::new(RefCell::new(connection));

		Pipe {
			state: PipeStateIdx::Handshake,
			handshake_state: HandshakePipeState::new(id, protocol, conn_ref.clone()),
			connected_state: ReadyPipeState::new(id, conn_ref.clone())
		}
	}

	pub fn is_ready(&self) -> bool {
		self.state == PipeStateIdx::Connected
	}

	pub fn send(&mut self, msg: Message) {
		self.get_state().send(msg);
	}

	fn get_state<'a>(&'a mut self) -> &'a mut PipeState {
		match self.state {
			PipeStateIdx::Handshake => &mut self.handshake_state,
			PipeStateIdx::Connected => &mut self.connected_state
		}
	}

	fn set_state(&mut self, state: PipeStateIdx, event_loop: &mut EventLoop) -> io::Result<()> {
		self.state = state;
		self.get_state().enter(event_loop)
	}

	pub fn init(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		self.set_state(PipeStateIdx::Handshake, event_loop)
	}

	pub fn ready(&mut self, event_loop: &mut EventLoop, events: mio::EventSet)-> io::Result<()> {
		if events.is_readable() {
			try!(self.readable(event_loop));
		}
		if events.is_writable() {
			try!(self.writable(event_loop));
		}
		Ok(())
	}

	pub fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().readable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

	pub fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		if let Some(new_state) = try!(self.get_state().writable(event_loop)) {
			try!(self.set_state(new_state, event_loop));
		}

		Ok(())
	}

}

trait PipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()>;
	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;
	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>>;

	fn send(&mut self, msg: Message) -> io::Result<()>;
}

struct HandshakePipeState {
	id: usize,
	protocol_id: u16,
	protocol_peer_id: u16,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool,
	received: bool
}

impl HandshakePipeState {

	fn new(id: usize, protocol: &Protocol, connection: Rc<RefCell<Box<Connection>>>) -> HandshakePipeState {
		HandshakePipeState {
			id: id,
			protocol_id: protocol.id(),
			protocol_peer_id: protocol.peer_id(),
			connection: connection,
			sent: false,
			received: false }
	}

	fn check_received_handshake(&self, handshake: &[u8; 8]) -> io::Result<()> {
		let mut expected_handshake = vec!(0, 83, 80, 0);
		try!(expected_handshake.write_u16::<BigEndian>(self.protocol_peer_id));
		try!(expected_handshake.write_u16::<BigEndian>(0));
		let mut both = handshake.iter().zip(expected_handshake.iter());

		if both.all(|(l,r)| l == r) {
			Ok(())
		} else {
			Err(io::Error::new(io::ErrorKind::InvalidData, "received wrong handshake"))
		}
	}

	fn check_sent_handshake(&self, written: Option<usize>) -> io::Result<()> {
		match written {
			Some(8) => Ok(()),
			_ => Err(io::Error::new(io::ErrorKind::InvalidData, "failed to send handshake"))
		}
	}

	fn read_handshake(&mut self) -> io::Result<()> {
		let mut handshake = [0u8; 8];
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_read(&mut handshake).
			and_then(|_| self.check_received_handshake(&handshake)));
		debug!("handshake received !");
		self.received = true;
		Ok(())
	}

	fn write_handshake(&mut self) -> io::Result<()> {
		// handshake is Zero, 'S', 'P', Version, Proto, Rsvd
		let mut handshake = vec!(0, 83, 80, 0);
		try!(handshake.write_u16::<BigEndian>(self.protocol_id));
		try!(handshake.write_u16::<BigEndian>(0));
		let mut connection = self.connection.borrow_mut();
		try!(
			connection.try_write(&handshake).
			and_then(|w| self.check_sent_handshake(w)));
		debug!("handshake sent !");
		self.sent = true;
		Ok(())
	}

	fn register(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.register_opt(fd, token, interest, poll_opt)
	}

	fn reregister(&self, event_loop: &mut EventLoop, interest: mio::EventSet, poll_opt: mio::PollOpt) -> io::Result<()> {
		let token = mio::Token(self.id);
		let connection = self.connection.borrow();
		let fd = connection.as_evented();

		event_loop.reregister(fd, token, interest, poll_opt)
	}

}

impl PipeState for HandshakePipeState {

	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter handshake state");
		self.sent = false;
		self.received = false;

		let interest = mio::EventSet::readable() | mio::EventSet::writable() | mio::EventSet::hup();
		let poll_opt = mio::PollOpt::oneshot();

		try!(self.register(event_loop, interest, poll_opt));

		Ok(())
	}

	fn readable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.received {
			return Ok(None)
		}

		try!(self.read_handshake());

		if self.sent {
			Ok(Some(PipeStateIdx::Connected))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::writable();
			let poll_opt = mio::PollOpt::oneshot();

			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn writable(&mut self, event_loop: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			return Ok(None)
		}

		try!(self.write_handshake());

		if self.received {
			Ok(Some(PipeStateIdx::Connected))
		} else {
			let interest = mio::EventSet::hup() | mio::EventSet::readable();
			let poll_opt = mio::PollOpt::oneshot();
			try!(self.reregister(event_loop, interest, poll_opt));
			Ok(None)
		}
	}

	fn send(&mut self, _: Message) -> io::Result<()> {
		Ok(())
	}

}

struct ReadyPipeState {
	id: usize,
	connection: Rc<RefCell<Box<Connection>>>,
	sent: bool
}

impl ReadyPipeState {
	fn new(id: usize, connection: Rc<RefCell<Box<Connection>>>) -> ReadyPipeState {
		ReadyPipeState {
			id: id,
			connection: connection,
			sent: false }
	}
}

impl PipeState for ReadyPipeState {
	fn enter(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
		debug!("Enter ready state");
		let token = mio::Token(self.id);
		let connection = self.connection.borrow_mut();
		let fd = connection.as_evented();
		let interest = mio::EventSet::hup() | mio::EventSet::writable();
		let poll_opt = mio::PollOpt::edge();
		try!(event_loop.reregister(fd, token, interest, poll_opt));
		Ok(())
	}

	fn readable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		Ok(None)
	}

	fn writable(&mut self, _: &mut EventLoop) -> io::Result<Option<PipeStateIdx>> {
		if self.sent {
			Ok(None)
		} else {
			let mut connection = self.connection.borrow_mut();
			let header = [0, 0, 0, 0, 0, 0, 0, 3];
			let body = [65, 86, 67];

			try!(connection.try_write(&header));
			try!(connection.try_write(&body));
			self.sent = true;
			debug!("dummy msg sent !");

			Ok(None)
		}
	}

	fn send(&mut self, msg: Message) -> io::Result<()> {
		let mut connection = self.connection.borrow_mut();
		let mut prefix = Vec::with_capacity(8);
		try!(prefix.write_u64::<BigEndian>(msg.len() as u64));

		try!(connection.try_write(&prefix));

		if msg.get_header().len() > 0 {
			try!(connection.try_write(msg.get_header()));
		}

		if msg.get_body().len() > 0 {
			try!(connection.try_write(msg.get_body()));
		}

		debug!("msg sent !");
		Ok(())
	}
}<|endoftext|>