<commit_before>//! The `event` module contains traits and structs to actually run your game mainloop
//! and handle top-level state, as well as handle input events such as keyboard
//! and mouse.
//!
//! If you don't want to use `ggez`'s built in event loop, you can
//! write your own mainloop and check for events on your own.  This is
//! not particularly hard, there's nothing special about the
//! `EventHandler` trait.  It just tries to simplify the process a
//! little.  For examples of how to write your own main loop, see the
//! source code for this module, or the [`eventloop`
//! example](https://github.com/ggez/ggez/blob/master/examples/eventloop.rs).

use winit::{self, dpi};

/// A mouse button.
pub use winit::event::MouseButton;

/// An analog axis of some device (gamepad thumbstick, joystick...).
pub use gilrs::Axis;
/// A button of some device (gamepad, joystick...).
pub use gilrs::Button;

/// `winit` events; nested in a module for re-export neatness.
pub mod winit_event {
    pub use super::winit::event::{
        DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, MouseScrollDelta,
        TouchPhase, WindowEvent,
    };
}
pub use crate::input::gamepad::GamepadId;
pub use crate::input::keyboard::{KeyCode, KeyMods};

use self::winit_event::*;
/// `winit` event loop.
pub use winit::event_loop::{ControlFlow, EventLoop};

use crate::context::Context;
use crate::error::GameResult;

/// A trait defining event callbacks.  This is your primary interface with
/// `ggez`'s event loop.  Implement this trait for a type and
/// override at least the [`update()`](#tymethod.update) and
/// [`draw()`](#tymethod.draw) methods, then pass it to
/// [`event::run()`](fn.run.html) to run the game's mainloop.
///
/// The default event handlers do nothing, apart from
/// [`key_down_event()`](#tymethod.key_down_event), which will by
/// default exit the game if the escape key is pressed.  Just
/// override the methods you want to use.
pub trait EventHandler {
    /// Called upon each logic update to the game.
    /// This should be where the game's logic takes place.
    fn update(&mut self, _ctx: &mut Context) -> GameResult;

    /// Called to do the drawing of your game.
    /// You probably want to start this with
    /// [`graphics::clear()`](../graphics/fn.clear.html) and end it
    /// with [`graphics::present()`](../graphics/fn.present.html) and
    /// maybe [`timer::yield_now()`](../timer/fn.yield_now.html).
    fn draw(&mut self, _ctx: &mut Context) -> GameResult;

    /// A mouse button was pressed
    fn mouse_button_down_event(
        &mut self,
        _ctx: &mut Context,
        _button: MouseButton,
        _x: f32,
        _y: f32,
    ) {
    }

    /// A mouse button was released
    fn mouse_button_up_event(
        &mut self,
        _ctx: &mut Context,
        _button: MouseButton,
        _x: f32,
        _y: f32,
    ) {
    }

    /// The mouse was moved; it provides both absolute x and y coordinates in the window,
    /// and relative x and y coordinates compared to its last position.
    fn mouse_motion_event(&mut self, _ctx: &mut Context, _x: f32, _y: f32, _dx: f32, _dy: f32) {}

    /// mouse entered or left window area
    fn mouse_enter_or_leave(&mut self, _ctx: &mut Context, _entered: bool) {}

    /// The mousewheel was scrolled, vertically (y, positive away from and negative toward the user)
    /// or horizontally (x, positive to the right and negative to the left).
    fn mouse_wheel_event(&mut self, _ctx: &mut Context, _x: f32, _y: f32) {}

    /// A keyboard button was pressed.
    ///
    /// The default implementation of this will call `ggez::event::quit()`
    /// when the escape key is pressed.  If you override this with
    /// your own event handler you have to re-implment that
    /// functionality yourself.
    fn key_down_event(
        &mut self,
        ctx: &mut Context,
        keycode: KeyCode,
        _keymods: KeyMods,
        _repeat: bool,
    ) {
        if keycode == KeyCode::Escape {
            quit(ctx);
        }
    }

    /// A keyboard button was released.
    fn key_up_event(&mut self, _ctx: &mut Context, _keycode: KeyCode, _keymods: KeyMods) {}

    /// A unicode character was received, usually from keyboard input.
    /// This is the intended way of facilitating text input.
    fn text_input_event(&mut self, _ctx: &mut Context, _character: char) {}

    /// A gamepad button was pressed; `id` identifies which gamepad.
    /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
    /// the gamepad.
    fn gamepad_button_down_event(&mut self, _ctx: &mut Context, _btn: Button, _id: GamepadId) {}

    /// A gamepad button was released; `id` identifies which gamepad.
    /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
    /// the gamepad.
    fn gamepad_button_up_event(&mut self, _ctx: &mut Context, _btn: Button, _id: GamepadId) {}

    /// A gamepad axis moved; `id` identifies which gamepad.
    /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
    /// the gamepad.
    fn gamepad_axis_event(&mut self, _ctx: &mut Context, _axis: Axis, _value: f32, _id: GamepadId) {
    }

    /// Called when the window is shown or hidden.
    fn focus_event(&mut self, _ctx: &mut Context, _gained: bool) {}

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit (the quit event is cancelled).
    fn quit_event(&mut self, _ctx: &mut Context) -> bool {
        debug!("quit_event() callback called, quitting...");
        false
    }

    /// Called when the user resizes the window, or when it is resized
    /// via [`graphics::set_mode()`](../graphics/fn.set_mode.html).
    fn resize_event(&mut self, _ctx: &mut Context, _width: f32, _height: f32) {}
}

/// Terminates the [`ggez::event::run()`](fn.run.html) loop by setting
/// [`Context.continuing`](struct.Context.html#structfield.continuing)
/// to `false`.
pub fn quit(ctx: &mut Context) {
    ctx.continuing = false;
}

/// Runs the game's main loop, calling event callbacks on the given state
/// object as events occur.
///
/// It does not try to do any type of framerate limiting.  See the
/// documentation for the [`timer`](../timer/index.html) module for more info.
pub fn run<S: 'static>(mut ctx: Context, event_loop: EventLoop<()>, mut state: S) -> !
where
    S: EventHandler,
{
    use crate::input::{keyboard, mouse};

    event_loop.run(move |event, _, control_flow| {
        if !ctx.continuing {
            *control_flow = ControlFlow::Exit;
            return;
        }

        *control_flow = ControlFlow::Poll;

        let ctx = &mut ctx;
        let state = &mut state;

        process_event(ctx, &event);
        match event {
            Event::WindowEvent { event, .. } => match event {
                WindowEvent::Resized(logical_size) => {
                    // let actual_size = logical_size;
                    state.resize_event(ctx, logical_size.width as f32, logical_size.height as f32);
                }
                WindowEvent::CloseRequested => {
                    if !state.quit_event(ctx) {
                        quit(ctx);
                    }
                }
                WindowEvent::Focused(gained) => {
                    state.focus_event(ctx, gained);
                }
                WindowEvent::ReceivedCharacter(ch) => {
                    state.text_input_event(ctx, ch);
                }
                WindowEvent::ModifiersChanged(mods) => {
                    ctx.keyboard_context.set_modifiers(KeyMods::from(mods))
                }
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            state: ElementState::Pressed,
                            virtual_keycode: Some(keycode),
                            ..
                        },
                    ..
                } => {
                    let repeat = keyboard::is_key_repeated(ctx);
                    state.key_down_event(ctx, keycode, ctx.keyboard_context.active_mods(), repeat);
                }
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            state: ElementState::Released,
                            virtual_keycode: Some(keycode),
                            ..
                        },
                    ..
                } => {
                    state.key_up_event(ctx, keycode, ctx.keyboard_context.active_mods());
                }
                WindowEvent::MouseWheel { delta, .. } => {
                    let (x, y) = match delta {
                        MouseScrollDelta::LineDelta(x, y) => (x, y),
                        MouseScrollDelta::PixelDelta(pos) => {
                            let scale_factor = ctx.gfx_context.window.window().scale_factor();
                            let dpi::LogicalPosition { x, y } = pos.to_logical::<f32>(scale_factor);
                            (x, y)
                        }
                    };
                    state.mouse_wheel_event(ctx, x, y);
                }
                WindowEvent::MouseInput {
                    state: element_state,
                    button,
                    ..
                } => {
                    let position = mouse::position(ctx);
                    match element_state {
                        ElementState::Pressed => {
                            state.mouse_button_down_event(ctx, button, position.x, position.y)
                        }
                        ElementState::Released => {
                            state.mouse_button_up_event(ctx, button, position.x, position.y)
                        }
                    }
                }
                WindowEvent::CursorMoved { .. } => {
                    let position = mouse::position(ctx);
                    let delta = mouse::delta(ctx);
                    state.mouse_motion_event(ctx, position.x, position.y, delta.x, delta.y);
                }
                _x => {
                    // trace!("ignoring window event {:?}", x);
                }
            },
            Event::DeviceEvent { .. } => (),
            Event::Resumed => (),
            Event::Suspended => (),
            Event::NewEvents(_) => (),
            Event::UserEvent(_) => (),
            Event::MainEventsCleared => {
                // If you are writing your own event loop, make sure
                // you include `timer_context.tick()` and
                // `ctx.process_event()` calls.  These update ggez's
                // internal state however necessary.
                ctx.timer_context.tick();

                // Handle gamepad events if necessary.
                if ctx.conf.modules.gamepad {
                    while let Some(gilrs::Event { id, event, .. }) =
                        ctx.gamepad_context.next_event()
                    {
                        match event {
                            gilrs::EventType::ButtonPressed(button, _) => {
                                state.gamepad_button_down_event(ctx, button, GamepadId(id));
                            }
                            gilrs::EventType::ButtonReleased(button, _) => {
                                state.gamepad_button_up_event(ctx, button, GamepadId(id));
                            }
                            gilrs::EventType::AxisChanged(axis, value, _) => {
                                state.gamepad_axis_event(ctx, axis, value, GamepadId(id));
                            }
                            _ => {}
                        }
                    }
                }

                if let Err(e) = state.update(ctx) {
                    error!("Error on EventHandler::update(): {:?}", e);
                    *control_flow = ControlFlow::Exit;
                    return;
                }

                if let Err(e) = state.draw(ctx) {
                    error!("Error on EventHandler::draw(): {:?}", e);
                    *control_flow = ControlFlow::Exit;
                    return;
                }
            }
            Event::RedrawRequested(_) => (),
            Event::RedrawEventsCleared => (),
            Event::LoopDestroyed => (),
        }
    })
}

/// Feeds an `Event` into the `Context` so it can update any internal
/// state it needs to, such as detecting window resizes.  If you are
/// rolling your own event loop, you should call this on the events
/// you receive before processing them yourself.
pub fn process_event(ctx: &mut Context, event: &winit::event::Event<()>) {
    match event {
        winit_event::Event::WindowEvent { event, .. } => match event {
            winit_event::WindowEvent::Resized(physical_size) => {
                ctx.gfx_context.window.resize(*physical_size);
                ctx.gfx_context.resize_viewport();
            }
            winit_event::WindowEvent::CursorMoved {
                position: logical_position,
                ..
            } => {
                ctx.mouse_context
                    .set_last_position(crate::graphics::Point2::new(
                        logical_position.x as f32,
                        logical_position.y as f32,
                    ));
            }
            winit_event::WindowEvent::MouseInput { button, state, .. } => {
                let pressed = match state {
                    winit_event::ElementState::Pressed => true,
                    winit_event::ElementState::Released => false,
                };
                ctx.mouse_context.set_button(*button, pressed);
            }
            winit_event::WindowEvent::ModifiersChanged(mods) => ctx
                .keyboard_context
                .set_modifiers(crate::input::keyboard::KeyMods::from(*mods)),
            winit_event::WindowEvent::KeyboardInput {
                input:
                    winit::event::KeyboardInput {
                        state,
                        virtual_keycode: Some(keycode),
                        ..
                    },
                ..
            } => {
                let pressed = match state {
                    winit_event::ElementState::Pressed => true,
                    winit_event::ElementState::Released => false,
                };
                ctx.keyboard_context.set_key(*keycode, pressed);
            }
            winit_event::WindowEvent::ScaleFactorChanged { .. } => {
                // Nope.
            }
            _ => (),
        },
        winit_event::Event::DeviceEvent {
            event: winit_event::DeviceEvent::MouseMotion { delta: (x, y) },
            ..
        } => ctx
            .mouse_context
            .set_last_delta(crate::graphics::Point2::new(*x as f32, *y as f32)),

        _ => (),
    };
}
<commit_msg>Prints error when update or draw returns one<commit_after>//! The `event` module contains traits and structs to actually run your game mainloop
//! and handle top-level state, as well as handle input events such as keyboard
//! and mouse.
//!
//! If you don't want to use `ggez`'s built in event loop, you can
//! write your own mainloop and check for events on your own.  This is
//! not particularly hard, there's nothing special about the
//! `EventHandler` trait.  It just tries to simplify the process a
//! little.  For examples of how to write your own main loop, see the
//! source code for this module, or the [`eventloop`
//! example](https://github.com/ggez/ggez/blob/master/examples/eventloop.rs).

use winit::{self, dpi};

/// A mouse button.
pub use winit::event::MouseButton;

/// An analog axis of some device (gamepad thumbstick, joystick...).
pub use gilrs::Axis;
/// A button of some device (gamepad, joystick...).
pub use gilrs::Button;

/// `winit` events; nested in a module for re-export neatness.
pub mod winit_event {
    pub use super::winit::event::{
        DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, MouseScrollDelta,
        TouchPhase, WindowEvent,
    };
}
pub use crate::input::gamepad::GamepadId;
pub use crate::input::keyboard::{KeyCode, KeyMods};

use self::winit_event::*;
/// `winit` event loop.
pub use winit::event_loop::{ControlFlow, EventLoop};

use crate::context::Context;
use crate::error::GameResult;

/// A trait defining event callbacks.  This is your primary interface with
/// `ggez`'s event loop.  Implement this trait for a type and
/// override at least the [`update()`](#tymethod.update) and
/// [`draw()`](#tymethod.draw) methods, then pass it to
/// [`event::run()`](fn.run.html) to run the game's mainloop.
///
/// The default event handlers do nothing, apart from
/// [`key_down_event()`](#tymethod.key_down_event), which will by
/// default exit the game if the escape key is pressed.  Just
/// override the methods you want to use.
pub trait EventHandler {
    /// Called upon each logic update to the game.
    /// This should be where the game's logic takes place.
    fn update(&mut self, _ctx: &mut Context) -> GameResult;

    /// Called to do the drawing of your game.
    /// You probably want to start this with
    /// [`graphics::clear()`](../graphics/fn.clear.html) and end it
    /// with [`graphics::present()`](../graphics/fn.present.html) and
    /// maybe [`timer::yield_now()`](../timer/fn.yield_now.html).
    fn draw(&mut self, _ctx: &mut Context) -> GameResult;

    /// A mouse button was pressed
    fn mouse_button_down_event(
        &mut self,
        _ctx: &mut Context,
        _button: MouseButton,
        _x: f32,
        _y: f32,
    ) {
    }

    /// A mouse button was released
    fn mouse_button_up_event(
        &mut self,
        _ctx: &mut Context,
        _button: MouseButton,
        _x: f32,
        _y: f32,
    ) {
    }

    /// The mouse was moved; it provides both absolute x and y coordinates in the window,
    /// and relative x and y coordinates compared to its last position.
    fn mouse_motion_event(&mut self, _ctx: &mut Context, _x: f32, _y: f32, _dx: f32, _dy: f32) {}

    /// mouse entered or left window area
    fn mouse_enter_or_leave(&mut self, _ctx: &mut Context, _entered: bool) {}

    /// The mousewheel was scrolled, vertically (y, positive away from and negative toward the user)
    /// or horizontally (x, positive to the right and negative to the left).
    fn mouse_wheel_event(&mut self, _ctx: &mut Context, _x: f32, _y: f32) {}

    /// A keyboard button was pressed.
    ///
    /// The default implementation of this will call `ggez::event::quit()`
    /// when the escape key is pressed.  If you override this with
    /// your own event handler you have to re-implment that
    /// functionality yourself.
    fn key_down_event(
        &mut self,
        ctx: &mut Context,
        keycode: KeyCode,
        _keymods: KeyMods,
        _repeat: bool,
    ) {
        if keycode == KeyCode::Escape {
            quit(ctx);
        }
    }

    /// A keyboard button was released.
    fn key_up_event(&mut self, _ctx: &mut Context, _keycode: KeyCode, _keymods: KeyMods) {}

    /// A unicode character was received, usually from keyboard input.
    /// This is the intended way of facilitating text input.
    fn text_input_event(&mut self, _ctx: &mut Context, _character: char) {}

    /// A gamepad button was pressed; `id` identifies which gamepad.
    /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
    /// the gamepad.
    fn gamepad_button_down_event(&mut self, _ctx: &mut Context, _btn: Button, _id: GamepadId) {}

    /// A gamepad button was released; `id` identifies which gamepad.
    /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
    /// the gamepad.
    fn gamepad_button_up_event(&mut self, _ctx: &mut Context, _btn: Button, _id: GamepadId) {}

    /// A gamepad axis moved; `id` identifies which gamepad.
    /// Use [`input::gamepad()`](../input/fn.gamepad.html) to get more info about
    /// the gamepad.
    fn gamepad_axis_event(&mut self, _ctx: &mut Context, _axis: Axis, _value: f32, _id: GamepadId) {
    }

    /// Called when the window is shown or hidden.
    fn focus_event(&mut self, _ctx: &mut Context, _gained: bool) {}

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit (the quit event is cancelled).
    fn quit_event(&mut self, _ctx: &mut Context) -> bool {
        debug!("quit_event() callback called, quitting...");
        false
    }

    /// Called when the user resizes the window, or when it is resized
    /// via [`graphics::set_mode()`](../graphics/fn.set_mode.html).
    fn resize_event(&mut self, _ctx: &mut Context, _width: f32, _height: f32) {}
}

/// Terminates the [`ggez::event::run()`](fn.run.html) loop by setting
/// [`Context.continuing`](struct.Context.html#structfield.continuing)
/// to `false`.
pub fn quit(ctx: &mut Context) {
    ctx.continuing = false;
}

/// Runs the game's main loop, calling event callbacks on the given state
/// object as events occur.
///
/// It does not try to do any type of framerate limiting.  See the
/// documentation for the [`timer`](../timer/index.html) module for more info.
pub fn run<S: 'static>(mut ctx: Context, event_loop: EventLoop<()>, mut state: S) -> !
where
    S: EventHandler,
{
    use crate::input::{keyboard, mouse};

    event_loop.run(move |event, _, control_flow| {
        if !ctx.continuing {
            *control_flow = ControlFlow::Exit;
            return;
        }

        *control_flow = ControlFlow::Poll;

        let ctx = &mut ctx;
        let state = &mut state;

        process_event(ctx, &event);
        match event {
            Event::WindowEvent { event, .. } => match event {
                WindowEvent::Resized(logical_size) => {
                    // let actual_size = logical_size;
                    state.resize_event(ctx, logical_size.width as f32, logical_size.height as f32);
                }
                WindowEvent::CloseRequested => {
                    if !state.quit_event(ctx) {
                        quit(ctx);
                    }
                }
                WindowEvent::Focused(gained) => {
                    state.focus_event(ctx, gained);
                }
                WindowEvent::ReceivedCharacter(ch) => {
                    state.text_input_event(ctx, ch);
                }
                WindowEvent::ModifiersChanged(mods) => {
                    ctx.keyboard_context.set_modifiers(KeyMods::from(mods))
                }
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            state: ElementState::Pressed,
                            virtual_keycode: Some(keycode),
                            ..
                        },
                    ..
                } => {
                    let repeat = keyboard::is_key_repeated(ctx);
                    state.key_down_event(ctx, keycode, ctx.keyboard_context.active_mods(), repeat);
                }
                WindowEvent::KeyboardInput {
                    input:
                        KeyboardInput {
                            state: ElementState::Released,
                            virtual_keycode: Some(keycode),
                            ..
                        },
                    ..
                } => {
                    state.key_up_event(ctx, keycode, ctx.keyboard_context.active_mods());
                }
                WindowEvent::MouseWheel { delta, .. } => {
                    let (x, y) = match delta {
                        MouseScrollDelta::LineDelta(x, y) => (x, y),
                        MouseScrollDelta::PixelDelta(pos) => {
                            let scale_factor = ctx.gfx_context.window.window().scale_factor();
                            let dpi::LogicalPosition { x, y } = pos.to_logical::<f32>(scale_factor);
                            (x, y)
                        }
                    };
                    state.mouse_wheel_event(ctx, x, y);
                }
                WindowEvent::MouseInput {
                    state: element_state,
                    button,
                    ..
                } => {
                    let position = mouse::position(ctx);
                    match element_state {
                        ElementState::Pressed => {
                            state.mouse_button_down_event(ctx, button, position.x, position.y)
                        }
                        ElementState::Released => {
                            state.mouse_button_up_event(ctx, button, position.x, position.y)
                        }
                    }
                }
                WindowEvent::CursorMoved { .. } => {
                    let position = mouse::position(ctx);
                    let delta = mouse::delta(ctx);
                    state.mouse_motion_event(ctx, position.x, position.y, delta.x, delta.y);
                }
                _x => {
                    // trace!("ignoring window event {:?}", x);
                }
            },
            Event::DeviceEvent { .. } => (),
            Event::Resumed => (),
            Event::Suspended => (),
            Event::NewEvents(_) => (),
            Event::UserEvent(_) => (),
            Event::MainEventsCleared => {
                // If you are writing your own event loop, make sure
                // you include `timer_context.tick()` and
                // `ctx.process_event()` calls.  These update ggez's
                // internal state however necessary.
                ctx.timer_context.tick();

                // Handle gamepad events if necessary.
                if ctx.conf.modules.gamepad {
                    while let Some(gilrs::Event { id, event, .. }) =
                        ctx.gamepad_context.next_event()
                    {
                        match event {
                            gilrs::EventType::ButtonPressed(button, _) => {
                                state.gamepad_button_down_event(ctx, button, GamepadId(id));
                            }
                            gilrs::EventType::ButtonReleased(button, _) => {
                                state.gamepad_button_up_event(ctx, button, GamepadId(id));
                            }
                            gilrs::EventType::AxisChanged(axis, value, _) => {
                                state.gamepad_axis_event(ctx, axis, value, GamepadId(id));
                            }
                            _ => {}
                        }
                    }
                }

                if let Err(e) = state.update(ctx) {
                    error!("Error on EventHandler::update(): {:?}", e);
                    eprintln!("Error on EventHandler::update(): {:?}", e);
                    *control_flow = ControlFlow::Exit;
                    return;
                }

                if let Err(e) = state.draw(ctx) {
                    error!("Error on EventHandler::draw(): {:?}", e);
                    eprintln!("Error on EventHandler::draw(): {:?}", e);
                    *control_flow = ControlFlow::Exit;
                    return;
                }
            }
            Event::RedrawRequested(_) => (),
            Event::RedrawEventsCleared => (),
            Event::LoopDestroyed => (),
        }
    })
}

/// Feeds an `Event` into the `Context` so it can update any internal
/// state it needs to, such as detecting window resizes.  If you are
/// rolling your own event loop, you should call this on the events
/// you receive before processing them yourself.
pub fn process_event(ctx: &mut Context, event: &winit::event::Event<()>) {
    match event {
        winit_event::Event::WindowEvent { event, .. } => match event {
            winit_event::WindowEvent::Resized(physical_size) => {
                ctx.gfx_context.window.resize(*physical_size);
                ctx.gfx_context.resize_viewport();
            }
            winit_event::WindowEvent::CursorMoved {
                position: logical_position,
                ..
            } => {
                ctx.mouse_context
                    .set_last_position(crate::graphics::Point2::new(
                        logical_position.x as f32,
                        logical_position.y as f32,
                    ));
            }
            winit_event::WindowEvent::MouseInput { button, state, .. } => {
                let pressed = match state {
                    winit_event::ElementState::Pressed => true,
                    winit_event::ElementState::Released => false,
                };
                ctx.mouse_context.set_button(*button, pressed);
            }
            winit_event::WindowEvent::ModifiersChanged(mods) => ctx
                .keyboard_context
                .set_modifiers(crate::input::keyboard::KeyMods::from(*mods)),
            winit_event::WindowEvent::KeyboardInput {
                input:
                    winit::event::KeyboardInput {
                        state,
                        virtual_keycode: Some(keycode),
                        ..
                    },
                ..
            } => {
                let pressed = match state {
                    winit_event::ElementState::Pressed => true,
                    winit_event::ElementState::Released => false,
                };
                ctx.keyboard_context.set_key(*keycode, pressed);
            }
            winit_event::WindowEvent::ScaleFactorChanged { .. } => {
                // Nope.
            }
            _ => (),
        },
        winit_event::Event::DeviceEvent {
            event: winit_event::DeviceEvent::MouseMotion { delta: (x, y) },
            ..
        } => ctx
            .mouse_context
            .set_last_delta(crate::graphics::Point2::new(*x as f32, *y as f32)),

        _ => (),
    };
}
<|endoftext|>


<commit_before>//! This module contains functions for reading FASTA data from either within
//! memory or from files.
//!
//! # Design Note
//!
//! These functions are designed to take callbacks to process the FASTX records
//! they read. It would be nice to present a FASTX Iterator that downstream users
//! can use at some point, but this is only possible for in-memory data (using
//! MemProducer from nom) because otherwise the lifetime of each record is linked
//! to what part of the file we're reading and Rust doesn't support "streaming
//! iterators" otherwise. Maybe some day.
//!
//! See: https://github.com/emk/rust-streaming

use memchr::{memchr, memchr2};
use std::str;
use nom::{ErrorKind, IResult, Input, Move, Needed};
use nom::{Consumer, ConsumerState, FileProducer, MemProducer, Producer};

/// A generic FASTX record containing:
///   seq.0 - an id
///   seq.1 - the sequence itself
///   seq.2 - an optional set of quality scores for the sequence
pub type SeqRecord<'a> = (&'a str, Vec<u8>, Option<&'a [u8]>);

fn strip_whitespace(seq: &[u8]) -> Vec<u8> {
    seq.iter()
       .filter(|&n| *n != b'\r' && *n != b'\n')
       .map(|n| n.clone())
       .collect::<Vec<u8>>()
}

fn fasta_record<'a>(input: &'a [u8], last: bool) -> IResult<&[u8], SeqRecord<'a>> {
    let mut pos = 0;
    let id;
    match memchr(b'\n', &input) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => match str::from_utf8(&input[pos + 1..pos_end]) {
            Ok(v) => {
                id = v;
                pos += pos_end + 1;
            },
            // "Invalid UTF-8 in FASTA id: {}"
            Err(_) => return IResult::Error(ErrorKind::IsNotStr),
        },
    };

    match memchr2(b'\n', b'>', &input[pos..]) {
        None => match last {
            false => IResult::Incomplete(Needed::Unknown),
            true => IResult::Done(&[], (id, strip_whitespace(&input[pos..input.len()]), None)),
        },
        Some(pos_end) => {
            IResult::Done(&input[pos + pos_end + 1..],
                          (id, strip_whitespace(&input[pos..pos + pos_end]), None))
        },
    }
}

fn fastq_record<'a>(input: &'a [u8]) -> IResult<&[u8], SeqRecord<'a>> {
    if input[0] != b'@' {
        // TODO: return IResult::Error();
        panic!("Invalid FASTQ record");  // before line ...?
    }
    let mut pos = 0;
    let id;
    match memchr(b'\n', &input) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => match str::from_utf8(&input[pos + 1..pos_end]) {
            Ok(v) => {
                id = v;
                pos += pos_end + 1;
            },
            // "Invalid UTF-8 in FASTA id: {}"
            Err(_) => return IResult::Error(ErrorKind::IsNotStr),
        },
    };

    let seq;
    match memchr2(b'\n', b'+', &input[pos..]) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => {
            seq = input[pos..pos + pos_end].to_vec();
            pos += pos_end + 1;
        },
    };

    match memchr(b'\n', &input[pos..]) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => pos += pos_end + 1,
    };

    let end_len = pos + seq.len();
    if end_len > input.len() {
        IResult::Incomplete(Needed::Size(end_len - input.len()))
    } else {
        IResult::Done(&input[end_len + 1..], (id, seq, Some(&input[pos..end_len])))
    }
}

#[test]
fn test_parsers() {
    let parsed = fasta_record(b">\n", true);
    let res = ("", b"".to_vec(), None);
    assert_eq!(parsed, IResult::Done(&b""[..], res));

    let no_more = &b">"[..];

    let parsed = fasta_record(b">\n\n>", false);
    let res = ("", b"".to_vec(), None);
    assert_eq!(parsed, IResult::Done(no_more, res));

    let parsed = fasta_record(b">test\nagct\n>", false);
    let res = ("test", b"agct".to_vec(), None);
    assert_eq!(parsed, IResult::Done(no_more, res));

    let parsed = fasta_record(b">test2\nagct", true);
    let res = ("test2", b"agct".to_vec(), None);
    assert_eq!(parsed, IResult::Done(&b""[..], res));

    let parsed = fastq_record(b"@test\nagct\n+test\nAAAA\n");
    let res = ("test", b"agct".to_vec(), Some(&b"AAAA"[..]));
    assert_eq!(parsed, IResult::Done(&b""[..], res));
}

#[derive(PartialEq, Eq, Debug)]
enum FASTXState {
    Start,
    FASTA,
    FASTQ,
    Done,
}

struct FASTXConsumer<'x> {
    consumer_state: ConsumerState<(), (), Move>,
    state: FASTXState,
    callback: &'x mut for<'a> FnMut(SeqRecord<'a>) -> (),
}

impl<'x> FASTXConsumer<'x> {
    pub fn new<F>(callback: &'x mut F) -> FASTXConsumer<'x>
        where F: for<'a> FnMut(SeqRecord<'a>) -> (),
    {
        FASTXConsumer {
            consumer_state: ConsumerState::Continue(Move::Consume(0)),
            state: FASTXState::Start,
            callback: callback,
        }
    }
}

impl<'a, 'x> Consumer<&'a [u8], (), (), Move> for FASTXConsumer<'x> {
    fn state(&self) -> &ConsumerState<(), (), Move> {
        &self.consumer_state
    }

    fn handle(&mut self, input: Input<&[u8]>) -> &ConsumerState<(), (), Move> {
        match (&self.state, input) {
            (&FASTXState::Done, _) => {
                // TODO: read after close error
                self.consumer_state = ConsumerState::Error(());
            },
            (_, Input::Empty) |
            (_, Input::Eof(None)) => {
                // TODO: empty file error
                self.consumer_state = ConsumerState::Error(());
                self.state = FASTXState::Done;
            },
            (_, Input::Error) => {
                panic!("Error reading records; buffer may be too small");
            },
            (&FASTXState::Start, Input::Element(slice)) |
            (&FASTXState::Start, Input::Eof(Some(slice))) => {
                match slice[0] as char {
                    '>' => {
                        self.consumer_state = ConsumerState::Continue(Move::Consume(0usize));
                        self.state = FASTXState::FASTA;
                    },
                    '@' => {
                        self.consumer_state = ConsumerState::Continue(Move::Consume(0usize));
                        self.state = FASTXState::FASTQ;
                    },
                    _ => {
                        // TODO: not a valid FASTX file
                        self.consumer_state = ConsumerState::Error(());
                        self.state = FASTXState::Done;
                    },
                }
            },
            (&FASTXState::FASTA, inp) => {
                let (slice, last_record) = match inp {
                    Input::Element(slice) => (slice, false),
                    Input::Eof(Some(slice)) => (slice, true),
                    _ => panic!("Should never happen"),
                };
                match fasta_record(slice, last_record) {
                    IResult::Error(_) => {
                        // TODO: error report
                        self.consumer_state = ConsumerState::Error(());
                        self.state = FASTXState::Done;
                    },
                    IResult::Incomplete(n) => {
                        self.consumer_state = ConsumerState::Continue(Move::Await(n));
                    },
                    IResult::Done(remaining_slice, seq) => {
                        (self.callback)(seq);
                        if remaining_slice.len() == 0 {
                            self.state = FASTXState::Done;
                        }
                        self.consumer_state =
                            ConsumerState::Continue(Move::Consume(slice.len() -
                                                                  remaining_slice.len()));
                    },
                }
            },
            (&FASTXState::FASTQ, Input::Element(slice)) |
            (&FASTXState::FASTQ, Input::Eof(Some(slice))) => {
                match fastq_record(slice) {
                    IResult::Error(_) => {
                        // TODO: error report
                        self.consumer_state = ConsumerState::Error(());
                        self.state = FASTXState::Done;
                    },
                    IResult::Incomplete(n) => {
                        self.consumer_state = ConsumerState::Continue(Move::Await(n));
                    },
                    IResult::Done(remaining_slice, seq) => {
                        (self.callback)(seq);
                        if remaining_slice.len() <= 1 {
                            self.state = FASTXState::Done;
                        }
                        self.consumer_state =
                            ConsumerState::Continue(Move::Consume(slice.len() -
                                                                  remaining_slice.len()));
                    },
                }
            },
        }
        &self.consumer_state
    }
}

pub fn fastx_bytes<'b, F>(bytes: &'b [u8], ref mut callback: F)
    where F: for<'a> FnMut(SeqRecord<'a>) -> (),
{
    //! Parse a collection of bytes into FASTX records and calls `callback` on each.
    //!
    let mut producer = MemProducer::new(bytes, 10000000);
    let mut consumer = FASTXConsumer::new(callback);

    while consumer.state != FASTXState::Done {
        producer.apply(&mut consumer);
    }
}

pub fn fastx_file<F>(filename: &str, ref mut callback: F)
    where F: for<'a> FnMut(SeqRecord<'a>) -> (),
{
    //! Parse a file (given its name) into FASTX records and calls `callback` on each.
    //!
    //! Note there's currently a bug where the chunk size of 10Mb here prevents
    //! reading any individual FASTA records longer than that (and will cause the
    //! parser to prematurely end when it hits records longer than that).
    //! We should file a bug upstream on nom (or fix FileProducer's `fill` method to
    //! allocate longer than that).
    let mut producer = FileProducer::new(filename, 10000000).unwrap();
    let mut consumer = FASTXConsumer::new(callback);

    while consumer.state != FASTXState::Done {
        producer.apply(&mut consumer);
    }
}

#[test]
fn test_callback() {
    let mut i = 0;
    fastx_bytes(&b">test\nAGCT\n>test2\nGATC"[..], |seq| {
        match i {
            0 => {
                assert_eq!(seq.0, "test");
                assert_eq!(&seq.1[..], &b"AGCT"[..]);
                assert_eq!(seq.2, None);
            },
            1 => {
                assert_eq!(seq.0, "test2");
                assert_eq!(&seq.1[..], &b"GATC"[..]);
                assert_eq!(seq.2, None);
            },
            _ => {
                assert!(false);
            },
        }
        i += 1;
    });

    i = 0;
    fastx_file("./tests/data/test.fa", |seq| {
        match i {
            0 => {
                assert_eq!(seq.0, "test");
                assert_eq!(&seq.1[..], b"AGCTGATCGA");
                assert_eq!(seq.2, None);
            },
            1 => {
                assert_eq!(seq.0, "test2");
                assert_eq!(&seq.1[..], b"TAGC");
                assert_eq!(seq.2, None);
            },
            _ => {
                assert!(false);
            },
        }
        i += 1;
    });
}

fn quality_mask<'a>(seq_rec: SeqRecord<'a>, ref score: u8) -> Vec<u8> {
    match seq_rec.2 {
        None => seq_rec.1,
        Some(quality) => {
            // could maybe speed this up by doing a copy of base and then
            // iterating though qual and masking?
            seq_rec.1
                   .iter()
                   .zip(quality.iter())
                   .map(|(base, qual)| {
                       if qual < score {
                           b'N'
                       } else {
                           base.clone()
                       }
                   })
                   .collect()

        },
    }
}

#[test]
fn test_quality_mask() {
    let seq_rec = ("", b"AGCT".to_vec(), Some(&b"AAA0"[..]));
    let filtered = quality_mask(seq_rec, '5' as u8);
    assert_eq!(&filtered[..], &b"AGCN"[..]);
}
<commit_msg>Fix improper memchr2 usage<commit_after>//! This module contains functions for reading FASTA data from either within
//! memory or from files.
//!
//! # Design Note
//!
//! These functions are designed to take callbacks to process the FASTX records
//! they read. It would be nice to present a FASTX Iterator that downstream users
//! can use at some point, but this is only possible for in-memory data (using
//! MemProducer from nom) because otherwise the lifetime of each record is linked
//! to what part of the file we're reading and Rust doesn't support "streaming
//! iterators" otherwise. Maybe some day.
//!
//! See: https://github.com/emk/rust-streaming

use memchr::memchr;
use std::str;
use nom::{ErrorKind, IResult, Input, Move, Needed};
use nom::{Consumer, ConsumerState, FileProducer, MemProducer, Producer};

/// A generic FASTX record containing:
///   seq.0 - an id
///   seq.1 - the sequence itself
///   seq.2 - an optional set of quality scores for the sequence
pub type SeqRecord<'a> = (&'a str, Vec<u8>, Option<&'a [u8]>);

fn strip_whitespace(seq: &[u8]) -> Vec<u8> {
    seq.iter()
       .filter(|&n| *n != b'\r' && *n != b'\n')
       .map(|n| n.clone())
       .collect::<Vec<u8>>()
}

fn memchr2(b1: u8, b2: u8, seq: &[u8]) -> Option<usize> {
    // unlike memchr::memchr2, this looks for the bytes in sequence (not either/or)
    let mut pos = 0;
    while true {
        match memchr(b1, &seq[pos..]) {
            None => return None,
            Some(match_pos) => {
                if pos + match_pos + 1 == seq.len() {
                    return None;
                } else if seq[pos + match_pos + 1] == b2 {
                    return Some(pos + match_pos);
                } else {
                    pos += match_pos + 1;
                }
            },
        }
    }
    None
}

fn fasta_record<'a>(input: &'a [u8], last: bool) -> IResult<&[u8], SeqRecord<'a>> {
    let mut pos = 0;
    let id;
    match memchr(b'\n', &input) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => match str::from_utf8(&input[pos + 1..pos_end]) {
            Ok(v) => {
                id = v;
                pos += pos_end + 1;
            },
            // "Invalid UTF-8 in FASTA id: {}"
            Err(_) => return IResult::Error(ErrorKind::IsNotStr),
        },
    };

    match memchr2(b'\n', b'>', &input[pos..]) {
        None => match last {
            false => IResult::Incomplete(Needed::Unknown),
            true => IResult::Done(&[], (id, strip_whitespace(&input[pos..input.len()]), None)),
        },
        Some(pos_end) => {
            IResult::Done(&input[pos + pos_end + 1..],
                          (id, strip_whitespace(&input[pos..pos + pos_end]), None))
        },
    }
}

fn fastq_record<'a>(input: &'a [u8]) -> IResult<&[u8], SeqRecord<'a>> {
    if input[0] != b'@' {
        // TODO: return IResult::Error();
        panic!("Invalid FASTQ record");  // before line ...?
    }
    let mut pos = 0;
    let id;
    match memchr(b'\n', &input) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => match str::from_utf8(&input[pos + 1..pos_end]) {
            Ok(v) => {
                id = v;
                pos += pos_end + 1;
            },
            // "Invalid UTF-8 in FASTA id: {}"
            Err(_) => return IResult::Error(ErrorKind::IsNotStr),
        },
    };

    let seq;
    match memchr2(b'\n', b'+', &input[pos..]) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => {
            seq = input[pos..pos + pos_end].to_vec();
            pos += pos_end + 1;
        },
    };

    match memchr(b'\n', &input[pos..]) {
        None => return IResult::Incomplete(Needed::Unknown),
        Some(pos_end) => pos += pos_end + 1,
    };

    let end_len = pos + seq.len();
    if end_len > input.len() {
        IResult::Incomplete(Needed::Size(end_len - input.len()))
    } else {
        IResult::Done(&input[end_len + 1..], (id, seq, Some(&input[pos..end_len])))
    }
}

#[test]
fn test_parsers() {
    let parsed = fasta_record(b">\n", true);
    let res = ("", b"".to_vec(), None);
    assert_eq!(parsed, IResult::Done(&b""[..], res));

    let no_more = &b">"[..];

    let parsed = fasta_record(b">\n\n>", false);
    let res = ("", b"".to_vec(), None);
    assert_eq!(parsed, IResult::Done(no_more, res));

    let parsed = fasta_record(b">test\nagct\n>", false);
    let res = ("test", b"agct".to_vec(), None);
    assert_eq!(parsed, IResult::Done(no_more, res));

    let parsed = fasta_record(b">test2\nagct", true);
    let res = ("test2", b"agct".to_vec(), None);
    assert_eq!(parsed, IResult::Done(&b""[..], res));

    let parsed = fastq_record(b"@test\nagct\n+test\nAAAA\n");
    let res = ("test", b"agct".to_vec(), Some(&b"AAAA"[..]));
    assert_eq!(parsed, IResult::Done(&b""[..], res));
}

#[derive(PartialEq, Eq, Debug)]
enum FASTXState {
    Start,
    FASTA,
    FASTQ,
    Done,
}

struct FASTXConsumer<'x> {
    consumer_state: ConsumerState<(), (), Move>,
    state: FASTXState,
    callback: &'x mut for<'a> FnMut(SeqRecord<'a>) -> (),
}

impl<'x> FASTXConsumer<'x> {
    pub fn new<F>(callback: &'x mut F) -> FASTXConsumer<'x>
        where F: for<'a> FnMut(SeqRecord<'a>) -> (),
    {
        FASTXConsumer {
            consumer_state: ConsumerState::Continue(Move::Consume(0)),
            state: FASTXState::Start,
            callback: callback,
        }
    }
}

impl<'a, 'x> Consumer<&'a [u8], (), (), Move> for FASTXConsumer<'x> {
    fn state(&self) -> &ConsumerState<(), (), Move> {
        &self.consumer_state
    }

    fn handle(&mut self, input: Input<&[u8]>) -> &ConsumerState<(), (), Move> {
        match (&self.state, input) {
            (&FASTXState::Done, _) => {
                // TODO: read after close error
                self.consumer_state = ConsumerState::Error(());
            },
            (_, Input::Empty) |
            (_, Input::Eof(None)) => {
                // TODO: empty file error
                self.consumer_state = ConsumerState::Error(());
                self.state = FASTXState::Done;
            },
            (_, Input::Error) => {
                panic!("Error reading records; buffer may be too small");
            },
            (&FASTXState::Start, Input::Element(slice)) |
            (&FASTXState::Start, Input::Eof(Some(slice))) => {
                match slice[0] as char {
                    '>' => {
                        self.consumer_state = ConsumerState::Continue(Move::Consume(0usize));
                        self.state = FASTXState::FASTA;
                    },
                    '@' => {
                        self.consumer_state = ConsumerState::Continue(Move::Consume(0usize));
                        self.state = FASTXState::FASTQ;
                    },
                    _ => {
                        // TODO: not a valid FASTX file
                        self.consumer_state = ConsumerState::Error(());
                        self.state = FASTXState::Done;
                    },
                }
            },
            (&FASTXState::FASTA, inp) => {
                let (slice, last_record) = match inp {
                    Input::Element(slice) => (slice, false),
                    Input::Eof(Some(slice)) => (slice, true),
                    _ => panic!("Should never happen"),
                };
                match fasta_record(slice, last_record) {
                    IResult::Error(_) => {
                        // TODO: error report
                        self.consumer_state = ConsumerState::Error(());
                        self.state = FASTXState::Done;
                    },
                    IResult::Incomplete(n) => {
                        self.consumer_state = ConsumerState::Continue(Move::Await(n));
                    },
                    IResult::Done(remaining_slice, seq) => {
                        (self.callback)(seq);
                        if remaining_slice.len() == 0 {
                            self.state = FASTXState::Done;
                        }
                        self.consumer_state =
                            ConsumerState::Continue(Move::Consume(slice.len() -
                                                                  remaining_slice.len()));
                    },
                }
            },
            (&FASTXState::FASTQ, Input::Element(slice)) |
            (&FASTXState::FASTQ, Input::Eof(Some(slice))) => {
                match fastq_record(slice) {
                    IResult::Error(_) => {
                        // TODO: error report
                        self.consumer_state = ConsumerState::Error(());
                        self.state = FASTXState::Done;
                    },
                    IResult::Incomplete(n) => {
                        self.consumer_state = ConsumerState::Continue(Move::Await(n));
                    },
                    IResult::Done(remaining_slice, seq) => {
                        (self.callback)(seq);
                        if remaining_slice.len() <= 1 {
                            self.state = FASTXState::Done;
                        }
                        self.consumer_state =
                            ConsumerState::Continue(Move::Consume(slice.len() -
                                                                  remaining_slice.len()));
                    },
                }
            },
        }
        &self.consumer_state
    }
}

pub fn fastx_bytes<'b, F>(bytes: &'b [u8], ref mut callback: F)
    where F: for<'a> FnMut(SeqRecord<'a>) -> (),
{
    //! Parse a collection of bytes into FASTX records and calls `callback` on each.
    //!
    let mut producer = MemProducer::new(bytes, 10000000);
    let mut consumer = FASTXConsumer::new(callback);

    while consumer.state != FASTXState::Done {
        producer.apply(&mut consumer);
    }
}

pub fn fastx_file<F>(filename: &str, ref mut callback: F)
    where F: for<'a> FnMut(SeqRecord<'a>) -> (),
{
    //! Parse a file (given its name) into FASTX records and calls `callback` on each.
    //!
    //! Note there's currently a bug where the chunk size of 10Mb here prevents
    //! reading any individual FASTA records longer than that (and will cause the
    //! parser to prematurely end when it hits records longer than that).
    //! We should file a bug upstream on nom (or fix FileProducer's `fill` method to
    //! allocate longer than that).
    let mut producer = FileProducer::new(filename, 10000000).unwrap();
    let mut consumer = FASTXConsumer::new(callback);

    while consumer.state != FASTXState::Done {
        producer.apply(&mut consumer);
    }
}

#[test]
fn test_callback() {
    let mut i = 0;
    fastx_bytes(&b">test\nAGCT\n>test2\nGATC"[..], |seq| {
        match i {
            0 => {
                assert_eq!(seq.0, "test");
                assert_eq!(&seq.1[..], &b"AGCT"[..]);
                assert_eq!(seq.2, None);
            },
            1 => {
                assert_eq!(seq.0, "test2");
                assert_eq!(&seq.1[..], &b"GATC"[..]);
                assert_eq!(seq.2, None);
            },
            _ => {
                assert!(false);
            },
        }
        i += 1;
    });

    i = 0;
    fastx_file("./tests/data/test.fa", |seq| {
        match i {
            0 => {
                assert_eq!(seq.0, "test");
                assert_eq!(&seq.1[..], b"AGCTGATCGA");
                assert_eq!(seq.2, None);
            },
            1 => {
                assert_eq!(seq.0, "test2");
                assert_eq!(&seq.1[..], b"TAGC");
                assert_eq!(seq.2, None);
            },
            _ => {
                assert!(false);
            },
        }
        i += 1;
    });
}

fn quality_mask<'a>(seq_rec: SeqRecord<'a>, ref score: u8) -> Vec<u8> {
    match seq_rec.2 {
        None => seq_rec.1,
        Some(quality) => {
            // could maybe speed this up by doing a copy of base and then
            // iterating though qual and masking?
            seq_rec.1
                   .iter()
                   .zip(quality.iter())
                   .map(|(base, qual)| {
                       if qual < score {
                           b'N'
                       } else {
                           base.clone()
                       }
                   })
                   .collect()

        },
    }
}

#[test]
fn test_quality_mask() {
    let seq_rec = ("", b"AGCT".to_vec(), Some(&b"AAA0"[..]));
    let filtered = quality_mask(seq_rec, '5' as u8);
    assert_eq!(&filtered[..], &b"AGCN"[..]);
}

#[test]
fn test_wrapped_fasta() {
    let mut i = 0;
    fastx_bytes(&b">test\nAGCT\nTCG\n>test2\nG"[..], |seq| {
        match i {
            0 => {
                assert_eq!(seq.0, "test");
                assert_eq!(&seq.1[..], &b"AGCTTCG"[..]);
                assert_eq!(seq.2, None);
            },
            1 => {
                assert_eq!(seq.0, "test2");
                assert_eq!(&seq.1[..], &b"G"[..]);
                assert_eq!(seq.2, None);
            },
            _ => {
                assert!(false);
            },
        }
        i += 1;
    });
}
<|endoftext|>


<commit_before>use ::nom::{IResult, double, is_alphabetic};

use std::str;
use std::f64::consts::*;

// I don't know how to copy a nom::IError for long-term storage
// (i.e. when it can no longer reference the &[u8]).  So, we explode a
// nom::IError into a simple error struct that can be passed around.
#[derive(Debug, Clone, PartialEq)]
pub struct ParseError {
    pub display: String
}

impl ParseError {
    pub fn new<T: AsRef<str>> (msg: T) -> ParseError {
        ParseError { display: msg.as_ref ().to_string () }
    }
}

/*
impl<'a> From<IError<&'a [u8]>> for NomError {
    fn from (e: IError<&[u8]>) -> NomError {
        match e {
            IError::Error (err) => NomError { display: format! ("{}", err) },

            IError::Incomplete (_) => NomError { display: "incomplete data".to_string () }
        }
    }
}
*/

fn is_whitespace (c: u8) -> bool {
    match c as char {
        ' ' | '\t' | '\r' | '\n' => true,
        _ => false
    }
}

// comma-wsp:
//     (wsp+ comma? wsp*) | (comma wsp*)

named! (comma, complete! (tag! (",")));
named! (wsp, recognize! (take_while1! (is_whitespace)));
named! (wsp_opt, recognize! (take_while! (is_whitespace)));

named! (comma_wsp,
    alt! (recognize! (tuple! (comma,
                              wsp_opt))
          | recognize! (tuple! (wsp,
                                opt! (comma),
                                wsp_opt))));

// angle:
// https://www.w3.org/TR/SVG/types.html#DataTypeAngle
//
// angle ::= number ("deg" | "grad" | "rad")?
//
// Returns an f64 angle in degrees

fn is_alphabetic_or_dash (c: u8) -> bool {
     is_alphabetic (c) || c == '-' as u8 || c == '%' as u8
}

named! (pub number_and_units<(f64, &[u8])>,
        tuple! (double,
                take_while! (is_alphabetic_or_dash)));

pub fn angle_degrees (s: &str) -> Result <f64, ParseError> {
    let r = number_and_units (s.as_bytes ()).to_full_result ();

    match r {
        Ok ((value, unit)) => {
            match unit {
                b"deg"  => Ok (value),
                b"grad" => Ok (value * 360.0 / 400.0),
                b"rad"  => Ok (value * 180.0 / PI),
                b""     => Ok (value),
                _       => Err (ParseError::new ("expected (\"deg\", \"rad\", \"grad\")? after number"))
            }
        },

        _ => Err (ParseError::new ("expected a number"))
    }
}

// Parse a viewBox attribute
// https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
//
// viewBox: double [,] double [,] double [,] double [,]
//
// x, y, w, h
//
// Where w and h must be nonnegative.

named! (pub view_box<(f64, f64, f64, f64)>,
        ws! (do_parse! (x: double    >>
                        opt! (comma) >>
                        y: double    >>
                        opt! (comma) >>
                        w: double    >>
                        opt! (comma) >>
                        h: double    >>
                        eof! ()      >>
                        (x, y, w, h))));

// Coordinate pairs, separated by optional (whitespace-and/or comma)
//
// All of these yield (1, -2): "1 -2", "1, -2", "1-2"

named! (pub coordinate_pair<(f64, f64)>,
        do_parse! (x: double        >>
                   opt! (comma_wsp) >>
                   y: double        >>
                   (x, y)));

// Parse a list-of-points as for polyline and polygon elements
// https://www.w3.org/TR/SVG/shapes.html#PointsBNF

named! (list_of_points_impl<Vec<(f64, f64)>>,
        terminated! (separated_list! (comma_wsp, coordinate_pair),
                     eof! ()));

pub fn list_of_points (string: &[u8]) -> Result <Vec<(f64, f64)>, ParseError> {
    list_of_points_impl (string)
        .to_full_result ()
        .map_err (|_| ParseError::new ("invalid syntax for list of points"))
    /*
        .map_err (|e| match e { IError::Error (err) => ParseError::new (format! ("{}", err)),
                                _ => ParseError::new ("incomplete list of points")
        })
     */
}

named! (pub separated_numbers<Vec<f64>>,
        separated_list! (comma_wsp, double));

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_view_box () {
        assert_eq! (view_box (b"1 2 3 4"), IResult::Done (&b""[..], (1.0, 2.0, 3.0, 4.0)));
        assert_eq! (view_box (b"1,2,3 4"), IResult::Done (&b""[..], (1.0, 2.0, 3.0, 4.0)));
        assert_eq! (view_box (b" 1,2,3 4 "), IResult::Done (&b""[..], (1.0, 2.0, 3.0, 4.0)));

        let result = view_box (b"1 2 3 4 5");

        match result {
            IResult::Error (_) => { },
            _ => { panic! ("{:?} should be an invalid viewBox", result); }
        }
    }

    #[test]
    fn parses_coordinate_pairs () {
        assert_eq! (coordinate_pair (b"1 2"),    IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1-2"),    IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1,2"),    IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1, 2"),   IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1 ,2"),   IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1 , 2"),  IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1 -2"),   IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1,-2"),   IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1, -2"),  IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1 , -2"), IResult::Done (&b""[..], (1.0, -2.0)));
    }

    #[test]
    fn detects_incomplete_coordinate_pair () {
        let result = coordinate_pair (b"1");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1,");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1, ");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1-");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1,-");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"-1 -");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }
    }

    #[test]
    fn parses_comma_wsp () {
        assert_eq! (comma_wsp (b" , "), IResult::Done (&b""[..], &b" , "[..]));
        assert_eq! (comma_wsp (b","),   IResult::Done (&b""[..], &b","[..]));
        assert_eq! (comma_wsp (b" "),   IResult::Done (&b""[..], &b" "[..]));
        assert_eq! (comma_wsp (b", "),  IResult::Done (&b""[..], &b", "[..]));
        assert_eq! (comma_wsp (b" ,"),  IResult::Done (&b""[..], &b" ,"[..]));
    }

    #[test]
    fn parses_separated_numbers () {
        assert_eq! (separated_numbers (b"1 2 3 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1,2,3,4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1 ,2 ,3 ,4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1  ,2  ,3  ,4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1, 2, 3, 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1,  2,  3,  4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1 , 2 , 3 , 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1  , 2  , 3  , 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1  ,  2  ,  3  ,  4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
    }

    #[test]
    fn parses_list_of_points () {
        // FIXME: we are missing optional whitespace at the beginning and end of the list
        assert_eq! (list_of_points (b"1 2"),      Ok (vec! [(1.0, 2.0)]));
        assert_eq! (list_of_points (b"1 2 3 4"),  Ok (vec! [(1.0, 2.0), (3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2,3,4"),  Ok (vec! [(1.0, 2.0), (3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2 3,4"),  Ok (vec! [(1.0, 2.0), (3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2 -3,4"), Ok (vec! [(1.0, 2.0), (-3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2,-3,4"), Ok (vec! [(1.0, 2.0), (-3.0, 4.0)]));
    }

    #[test]
    fn errors_on_invalid_list_of_points () {
        assert! (list_of_points (b"-1-2-3-4").is_err ());
        assert! (list_of_points (b"1 2-3,-4").is_err ());
    }

    #[test]
    fn parses_number_and_units () {
        assert_eq! (number_and_units (b"-1"), IResult::Done (&b""[..], (-1.0, &b""[..])));
        assert_eq! (number_and_units (b"0x"), IResult::Done (&b""[..], (0.0, &b"x"[..])));
        assert_eq! (number_and_units (b"-55.5x-large"), IResult::Done (&b""[..], (-55.5, &b"x-large"[..])));
    }

    #[test]
    fn parses_angle () {
        assert_eq! (angle_degrees ("0"),        Ok (0.0));
        assert_eq! (angle_degrees ("15"),       Ok (15.0));
        assert_eq! (angle_degrees ("180.5deg"), Ok (180.5));
        assert_eq! (angle_degrees ("1rad"),     Ok (180.0 / PI));
        assert_eq! (angle_degrees ("-400grad"), Ok (-360.0));

        assert! (angle_degrees ("").is_err ());
        assert! (angle_degrees ("foo").is_err ());
        assert! (angle_degrees ("300foo").is_err ());
    }
}
<commit_msg>parsers::number_optional_number(): New parser<commit_after>use ::cssparser::{Parser, Token, BasicParseError};
use ::nom::{IResult, double, is_alphabetic};

use std::str;
use std::f64::consts::*;

// I don't know how to copy a nom::IError for long-term storage
// (i.e. when it can no longer reference the &[u8]).  So, we explode a
// nom::IError into a simple error struct that can be passed around.
#[derive(Debug, Clone, PartialEq)]
pub struct ParseError {
    pub display: String
}

impl ParseError {
    pub fn new<T: AsRef<str>> (msg: T) -> ParseError {
        ParseError { display: msg.as_ref ().to_string () }
    }
}

impl<'a> From<BasicParseError<'a>> for ParseError {
    fn from (_: BasicParseError) -> ParseError {
        ParseError::new ("parse error")
    }
}

/*
impl<'a> From<IError<&'a [u8]>> for NomError {
    fn from (e: IError<&[u8]>) -> NomError {
        match e {
            IError::Error (err) => NomError { display: format! ("{}", err) },

            IError::Incomplete (_) => NomError { display: "incomplete data".to_string () }
        }
    }
}
*/

fn is_whitespace (c: u8) -> bool {
    match c as char {
        ' ' | '\t' | '\r' | '\n' => true,
        _ => false
    }
}

// comma-wsp:
//     (wsp+ comma? wsp*) | (comma wsp*)

named! (comma, complete! (tag! (",")));
named! (wsp, recognize! (take_while1! (is_whitespace)));
named! (wsp_opt, recognize! (take_while! (is_whitespace)));

named! (comma_wsp,
    alt! (recognize! (tuple! (comma,
                              wsp_opt))
          | recognize! (tuple! (wsp,
                                opt! (comma),
                                wsp_opt))));

// angle:
// https://www.w3.org/TR/SVG/types.html#DataTypeAngle
//
// angle ::= number ("deg" | "grad" | "rad")?
//
// Returns an f64 angle in degrees

fn is_alphabetic_or_dash (c: u8) -> bool {
     is_alphabetic (c) || c == '-' as u8 || c == '%' as u8
}

named! (pub number_and_units<(f64, &[u8])>,
        tuple! (double,
                take_while! (is_alphabetic_or_dash)));

pub fn angle_degrees (s: &str) -> Result <f64, ParseError> {
    let r = number_and_units (s.as_bytes ()).to_full_result ();

    match r {
        Ok ((value, unit)) => {
            match unit {
                b"deg"  => Ok (value),
                b"grad" => Ok (value * 360.0 / 400.0),
                b"rad"  => Ok (value * 180.0 / PI),
                b""     => Ok (value),
                _       => Err (ParseError::new ("expected (\"deg\", \"rad\", \"grad\")? after number"))
            }
        },

        _ => Err (ParseError::new ("expected a number"))
    }
}

// Parse a viewBox attribute
// https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
//
// viewBox: double [,] double [,] double [,] double [,]
//
// x, y, w, h
//
// Where w and h must be nonnegative.

named! (pub view_box<(f64, f64, f64, f64)>,
        ws! (do_parse! (x: double    >>
                        opt! (comma) >>
                        y: double    >>
                        opt! (comma) >>
                        w: double    >>
                        opt! (comma) >>
                        h: double    >>
                        eof! ()      >>
                        (x, y, w, h))));

// Coordinate pairs, separated by optional (whitespace-and/or comma)
//
// All of these yield (1, -2): "1 -2", "1, -2", "1-2"

named! (pub coordinate_pair<(f64, f64)>,
        do_parse! (x: double        >>
                   opt! (comma_wsp) >>
                   y: double        >>
                   (x, y)));

// number-optional-number
//
// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber

pub fn number_optional_number (s: &str) -> Result <(f64, f64), ParseError> {
    let mut parser = Parser::new (s);

    let x = parser.expect_number ()? as f64;

    if !parser.is_exhausted () {
        let position = parser.position ();

        match parser.next ()? {
            Token::Comma => {},
            _ => parser.reset (position)
        };

        let y = parser.expect_number ()? as f64;

        parser.expect_exhausted ()?;

        Ok ((x, y))
    } else {
        Ok ((x, x))
    }
}

// Parse a list-of-points as for polyline and polygon elements
// https://www.w3.org/TR/SVG/shapes.html#PointsBNF

named! (list_of_points_impl<Vec<(f64, f64)>>,
        terminated! (separated_list! (comma_wsp, coordinate_pair),
                     eof! ()));

pub fn list_of_points (string: &[u8]) -> Result <Vec<(f64, f64)>, ParseError> {
    list_of_points_impl (string)
        .to_full_result ()
        .map_err (|_| ParseError::new ("invalid syntax for list of points"))
    /*
        .map_err (|e| match e { IError::Error (err) => ParseError::new (format! ("{}", err)),
                                _ => ParseError::new ("incomplete list of points")
        })
     */
}

named! (pub separated_numbers<Vec<f64>>,
        separated_list! (comma_wsp, double));

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parses_view_box () {
        assert_eq! (view_box (b"1 2 3 4"), IResult::Done (&b""[..], (1.0, 2.0, 3.0, 4.0)));
        assert_eq! (view_box (b"1,2,3 4"), IResult::Done (&b""[..], (1.0, 2.0, 3.0, 4.0)));
        assert_eq! (view_box (b" 1,2,3 4 "), IResult::Done (&b""[..], (1.0, 2.0, 3.0, 4.0)));

        let result = view_box (b"1 2 3 4 5");

        match result {
            IResult::Error (_) => { },
            _ => { panic! ("{:?} should be an invalid viewBox", result); }
        }
    }

    #[test]
    fn parses_coordinate_pairs () {
        assert_eq! (coordinate_pair (b"1 2"),    IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1-2"),    IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1,2"),    IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1, 2"),   IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1 ,2"),   IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1 , 2"),  IResult::Done (&b""[..], (1.0, 2.0)));
        assert_eq! (coordinate_pair (b"1 -2"),   IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1,-2"),   IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1, -2"),  IResult::Done (&b""[..], (1.0, -2.0)));
        assert_eq! (coordinate_pair (b"1 , -2"), IResult::Done (&b""[..], (1.0, -2.0)));
    }

    #[test]
    fn detects_incomplete_coordinate_pair () {
        let result = coordinate_pair (b"1");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1,");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1, ");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1-");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"1,-");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }

        let result = coordinate_pair (b"-1 -");
        match result {
            IResult::Incomplete (_) => { },
            _ => { panic! ("{:?} should be an incomplete coordinate-pair", result); }
        }
    }

    #[test]
    fn parses_number_optional_number () {
        assert_eq! (number_optional_number ("1, 2"), Ok ((1.0, 2.0)));
        assert_eq! (number_optional_number ("1 2"),  Ok ((1.0, 2.0)));
        assert_eq! (number_optional_number ("1"),    Ok ((1.0, 1.0)));

        assert_eq! (number_optional_number ("-1, -2"), Ok ((-1.0, -2.0)));
        assert_eq! (number_optional_number ("-1 -2"),  Ok ((-1.0, -2.0)));
        assert_eq! (number_optional_number ("-1"),     Ok ((-1.0, -1.0)));
    }

    #[test]
    fn invalid_number_optional_number () {
        assert! (number_optional_number ("").is_err ());
        assert! (number_optional_number ("1x").is_err ());
        assert! (number_optional_number ("x1").is_err ());
        assert! (number_optional_number ("1 x").is_err ());
        assert! (number_optional_number ("1 , x").is_err ());
        assert! (number_optional_number ("1 , 2x").is_err ());
        assert! (number_optional_number ("1 2 x").is_err ());
    }

    #[test]
    fn parses_comma_wsp () {
        assert_eq! (comma_wsp (b" , "), IResult::Done (&b""[..], &b" , "[..]));
        assert_eq! (comma_wsp (b","),   IResult::Done (&b""[..], &b","[..]));
        assert_eq! (comma_wsp (b" "),   IResult::Done (&b""[..], &b" "[..]));
        assert_eq! (comma_wsp (b", "),  IResult::Done (&b""[..], &b", "[..]));
        assert_eq! (comma_wsp (b" ,"),  IResult::Done (&b""[..], &b" ,"[..]));
    }

    #[test]
    fn parses_separated_numbers () {
        assert_eq! (separated_numbers (b"1 2 3 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1,2,3,4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1 ,2 ,3 ,4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1  ,2  ,3  ,4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1, 2, 3, 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1,  2,  3,  4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1 , 2 , 3 , 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1  , 2  , 3  , 4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
        assert_eq! (separated_numbers (b"1  ,  2  ,  3  ,  4"), IResult::Done (&b""[..], vec! [1.0, 2.0, 3.0, 4.0]));
    }

    #[test]
    fn parses_list_of_points () {
        // FIXME: we are missing optional whitespace at the beginning and end of the list
        assert_eq! (list_of_points (b"1 2"),      Ok (vec! [(1.0, 2.0)]));
        assert_eq! (list_of_points (b"1 2 3 4"),  Ok (vec! [(1.0, 2.0), (3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2,3,4"),  Ok (vec! [(1.0, 2.0), (3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2 3,4"),  Ok (vec! [(1.0, 2.0), (3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2 -3,4"), Ok (vec! [(1.0, 2.0), (-3.0, 4.0)]));
        assert_eq! (list_of_points (b"1,2,-3,4"), Ok (vec! [(1.0, 2.0), (-3.0, 4.0)]));
    }

    #[test]
    fn errors_on_invalid_list_of_points () {
        assert! (list_of_points (b"-1-2-3-4").is_err ());
        assert! (list_of_points (b"1 2-3,-4").is_err ());
    }

    #[test]
    fn parses_number_and_units () {
        assert_eq! (number_and_units (b"-1"), IResult::Done (&b""[..], (-1.0, &b""[..])));
        assert_eq! (number_and_units (b"0x"), IResult::Done (&b""[..], (0.0, &b"x"[..])));
        assert_eq! (number_and_units (b"-55.5x-large"), IResult::Done (&b""[..], (-55.5, &b"x-large"[..])));
    }

    #[test]
    fn parses_angle () {
        assert_eq! (angle_degrees ("0"),        Ok (0.0));
        assert_eq! (angle_degrees ("15"),       Ok (15.0));
        assert_eq! (angle_degrees ("180.5deg"), Ok (180.5));
        assert_eq! (angle_degrees ("1rad"),     Ok (180.0 / PI));
        assert_eq! (angle_degrees ("-400grad"), Ok (-360.0));

        assert! (angle_degrees ("").is_err ());
        assert! (angle_degrees ("foo").is_err ());
        assert! (angle_degrees ("300foo").is_err ());
    }
}
<|endoftext|>