<commit_before>//! Updates crates.io index and builds new packages

use super::{DocBuilder, RustwideBuilder};
use crate::db::connect_db;
use crate::error::Result;
use crate::utils::{add_crate_to_queue, get_crate_priority};
use crates_index_diff::{ChangeKind, Index};
use log::{debug, error};

impl DocBuilder {
    /// Updates crates.io-index repository and adds new crates into build queue.
    /// Returns size of queue
    pub fn get_new_crates(&mut self) -> Result<usize> {
        let conn = connect_db()?;
        let index = Index::from_path_or_cloned(&self.options.crates_io_index_path)?;
        let (mut changes, oid) = index.peek_changes()?;

        // I believe this will fix ordering of queue if we get more than one crate from changes
        changes.reverse();

        for krate in &changes {
            match krate.kind {
                ChangeKind::Yanked => {
                    let res = conn.execute(
                        "
                        UPDATE releases
                            SET yanked = TRUE
                        FROM crates
                        WHERE crates.id = releases.crate_id
                            AND name = $1
                            AND version = $2
                        ",
                        &[&krate.name, &krate.version],
                    );
                    match res {
                        Ok(_) => debug!("{}-{} yanked", krate.name, krate.version),
                        Err(err) => error!(
                            "error while setting {}-{} to yanked: {}",
                            krate.name, krate.version, err
                        ),
                    }
                }

                ChangeKind::Added => {
                    let priority = get_crate_priority(&conn, &krate.name)?;

                    match add_crate_to_queue(&conn, &krate.name, &krate.version, priority) {
                        Ok(()) => debug!("{}-{} added into build queue", krate.name, krate.version),
                        Err(err) => error!(
                            "failed adding {}-{} into build queue: {}",
                            krate.name, krate.version, err
                        ),
                    }
                }
            }
        }

        index.set_last_seen_reference(oid)?;

        // Get the size of the queue
        let query = conn.query("SELECT COUNT(*) as size FROM queue", &[])?;
        let queue_size = query.iter().next().unwrap().get::<_, i64>("size") as usize;

        Ok(queue_size)
    }

    pub fn get_queue_count(&self) -> Result<i64> {
        let conn = connect_db()?;

        Ok(conn
            .query("SELECT COUNT(*) FROM queue WHERE attempt < 5", &[])?
            .get(0)
            .get(0))
    }

    /// Builds the top package from the queue. Returns whether the queue was empty.
    pub(crate) fn build_next_queue_package(
        &mut self,
        builder: &mut RustwideBuilder,
    ) -> Result<bool> {
        let conn = connect_db()?;

        let query = conn.query(
            "SELECT id, name, version
                                     FROM queue
                                     WHERE attempt < 5
                                     ORDER BY priority ASC, attempt ASC, id ASC
                                     LIMIT 1",
            &[],
        )?;

        if query.is_empty() {
            // nothing in the queue; bail
            return Ok(false);
        }

        let id: i32 = query.get(0).get(0);
        let name: String = query.get(0).get(1);
        let version: String = query.get(0).get(2);

        match builder.build_package(self, &name, &version, None) {
            Ok(_) => {
                let _ = conn.execute("DELETE FROM queue WHERE id = $1", &[&id]);
                crate::web::metrics::TOTAL_BUILDS.inc();
            }
            Err(e) => {
                // Increase attempt count
                let rows = conn.query(
                    "UPDATE queue SET attempt = attempt + 1 WHERE id = $1 RETURNING attempt",
                    &[&id],
                )?;
                let attempt: i32 = rows.get(0).get(0);
                if attempt >= 5 {
                    crate::web::metrics::FAILED_BUILDS.inc();
                    crate::web::metrics::TOTAL_BUILDS.inc();
                }
                error!(
                    "Failed to build package {}-{} from queue: {}",
                    name, version, e
                )
            }
        }

        Ok(true)
    }
}

#[cfg(test)]
mod test {
    use crate::{DocBuilder, DocBuilderOptions};
    use log::error;
    use std::path::PathBuf;

    #[test]
    #[ignore]
    fn test_get_new_crates() {
        let _ = env_logger::try_init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let mut docbuilder = DocBuilder::new(options);
        let res = docbuilder.get_new_crates();
        if res.is_err() {
            error!("{:?}", res);
        }
        assert!(res.is_ok());
    }
}
<commit_msg>Rustfmt<commit_after>//! Updates crates.io index and builds new packages

use super::{DocBuilder, RustwideBuilder};
use crate::db::connect_db;
use crate::error::Result;
use crate::utils::{add_crate_to_queue, get_crate_priority};
use crates_index_diff::{ChangeKind, Index};
use log::{debug, error};

impl DocBuilder {
    /// Updates crates.io-index repository and adds new crates into build queue.
    /// Returns size of queue
    pub fn get_new_crates(&mut self) -> Result<usize> {
        let conn = connect_db()?;
        let index = Index::from_path_or_cloned(&self.options.crates_io_index_path)?;
        let (mut changes, oid) = index.peek_changes()?;

        // I believe this will fix ordering of queue if we get more than one crate from changes
        changes.reverse();

        for krate in &changes {
            match krate.kind {
                ChangeKind::Yanked => {
                    let res = conn.execute(
                        "
                        UPDATE releases
                            SET yanked = TRUE
                        FROM crates
                        WHERE crates.id = releases.crate_id
                            AND name = $1
                            AND version = $2
                        ",
                        &[&krate.name, &krate.version],
                    );
                    match res {
                        Ok(_) => debug!("{}-{} yanked", krate.name, krate.version),
                        Err(err) => error!(
                            "error while setting {}-{} to yanked: {}",
                            krate.name, krate.version, err
                        ),
                    }
                }

                ChangeKind::Added => {
                    let priority = get_crate_priority(&conn, &krate.name)?;

                    match add_crate_to_queue(&conn, &krate.name, &krate.version, priority) {
                        Ok(()) => debug!("{}-{} added into build queue", krate.name, krate.version),
                        Err(err) => error!(
                            "failed adding {}-{} into build queue: {}",
                            krate.name, krate.version, err
                        ),
                    }
                }
            }
        }

        index.set_last_seen_reference(oid)?;

        // Get the size of the queue
        let query = conn.query("SELECT COUNT(*) as size FROM queue", &[])?;
        let queue_size = query.iter().next().unwrap().get::<_, i64>("size") as usize;

        Ok(queue_size)
    }

    pub fn get_queue_count(&self) -> Result<i64> {
        let conn = connect_db()?;

        Ok(conn
            .query("SELECT COUNT(*) FROM queue WHERE attempt < 5", &[])?
            .get(0)
            .get(0))
    }

    /// Builds the top package from the queue. Returns whether the queue was empty.
    pub(crate) fn build_next_queue_package(
        &mut self,
        builder: &mut RustwideBuilder,
    ) -> Result<bool> {
        let conn = connect_db()?;

        let query = conn.query(
            "SELECT id, name, version
                                     FROM queue
                                     WHERE attempt < 5
                                     ORDER BY priority ASC, attempt ASC, id ASC
                                     LIMIT 1",
            &[],
        )?;

        if query.is_empty() {
            // nothing in the queue; bail
            return Ok(false);
        }

        let id: i32 = query.get(0).get(0);
        let name: String = query.get(0).get(1);
        let version: String = query.get(0).get(2);

        match builder.build_package(self, &name, &version, None) {
            Ok(_) => {
                let _ = conn.execute("DELETE FROM queue WHERE id = $1", &[&id]);
                crate::web::metrics::TOTAL_BUILDS.inc();
            }
            Err(e) => {
                // Increase attempt count
                let rows = conn.query(
                    "UPDATE queue SET attempt = attempt + 1 WHERE id = $1 RETURNING attempt",
                    &[&id],
                )?;
                let attempt: i32 = rows.get(0).get(0);
                if attempt >= 5 {
                    crate::web::metrics::FAILED_BUILDS.inc();
                    crate::web::metrics::TOTAL_BUILDS.inc();
                }
                error!(
                    "Failed to build package {}-{} from queue: {}",
                    name, version, e
                )
            }
        }

        Ok(true)
    }
}

#[cfg(test)]
mod test {
    use crate::{DocBuilder, DocBuilderOptions};
    use log::error;
    use std::path::PathBuf;

    #[test]
    #[ignore]
    fn test_get_new_crates() {
        let _ = env_logger::try_init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let mut docbuilder = DocBuilder::new(options);
        let res = docbuilder.get_new_crates();
        if res.is_err() {
            error!("{:?}", res);
        }
        assert!(res.is_ok());
    }
}
<|endoftext|>


<commit_before>//extern crate sfml;

use engine::math::Vector2;
use sfml::graphics::{RenderWindow, Color, Drawable};
use sfml::window::{ContextSettings, VideoMode, Event, WindowStyle};

use std::collections::VecDeque;

pub struct RenderContext {
    viewport_dim: Vector2,
}

pub struct Window {
    window: RenderWindow,
}

impl Window {
    pub fn new(width: usize, height: usize, title: String, fullscreen: bool) -> Window {

        let videoMode = VideoMode::new_init(width, height, 32);
        let style: WindowStyle = if fullscreen {
            WindowStyle::FULLSCREEN
        } else {
            WindowStyle::CLOSE
        };
        let setting = ContextSettings::default();

        Window { window: RenderWindow::new(videoMode, title, style, &setting).unwrap() }
    }

    pub fn is_open(&self) -> bool {
        self.window.is_open()
    }

    pub fn poll(&mut self) -> Event {
        self.window.poll_event()
    }

    pub fn close(&mut self) {
        self.window.close()
    }

    pub fn clear(&mut self, clear_color: Color) {
        self.window.clear(&clear_color);
    }

    pub fn display(&mut self) {
        self.window.display();
    }

    pub fn width(&self) -> usize {
        self.window.get_size().x as usize
    }

    pub fn height(&self) -> usize {
        self.window.get_size().y as usize
    }

    pub fn get_context(&self) -> RenderContext {
        let size = self.window.get_size();
        RenderContext { viewport_dim: Vector2::new(size.x as f32, size.y as f32) }
    }

    //only use if you're sure
    pub fn get_render_window<'a>(&'a mut self) -> &'a mut RenderWindow {
        &mut self.window
    }
}

pub struct RenderQueue<'a> {
    // There was a colon at the end of Drawable before, it had
    // (probably) something to do with opting out of builtin traits
    // It was also in a couple of other locations too
    renderers: VecDeque<&'a Drawable>,
    clear_color: Color,
}

impl<'a> RenderQueue<'a> {
    pub fn new() -> RenderQueue<'a> {
        RenderQueue {
            renderers: VecDeque::new(),
            clear_color: Color::new_RGB(0, 0, 20),
        }
    }

    pub fn push(&mut self, renderer: &'a Drawable) {
        self.renderers.push_back(renderer);
    }

    pub fn set_clear_color(&mut self, clear_color: Color) {
        self.clear_color = clear_color;
    }

    pub fn render(&mut self, window: &mut Window) {
        window.clear(self.clear_color);

        let render_window = window.get_render_window();

        for renderer in self.renderers.iter() {
            renderer.draw_in_render_window(render_window);
        }

        self.renderers.clear();
    }
}
<commit_msg>Rename WindowStyle to Style<commit_after>//extern crate sfml;

use engine::math::Vector2;
use sfml::graphics::{RenderWindow, Color, Drawable};
use sfml::window::{ContextSettings, VideoMode, Event, Style};

use std::collections::VecDeque;

pub struct RenderContext {
    viewport_dim: Vector2,
}

pub struct Window {
    window: RenderWindow,
}

impl Window {
    pub fn new(width: usize, height: usize, title: String, fullscreen: bool) -> Window {

        let videoMode = VideoMode::new_init(width, height, 32);
        let style: Style = if fullscreen {
            Style::FULLSCREEN
        } else {
            Style::CLOSE
        };
        let setting = ContextSettings::default();

        Window { window: RenderWindow::new(videoMode, title, style, &setting).unwrap() }
    }

    pub fn is_open(&self) -> bool {
        self.window.is_open()
    }

    pub fn poll(&mut self) -> Event {
        self.window.poll_event()
    }

    pub fn close(&mut self) {
        self.window.close()
    }

    pub fn clear(&mut self, clear_color: Color) {
        self.window.clear(&clear_color);
    }

    pub fn display(&mut self) {
        self.window.display();
    }

    pub fn width(&self) -> usize {
        self.window.get_size().x as usize
    }

    pub fn height(&self) -> usize {
        self.window.get_size().y as usize
    }

    pub fn get_context(&self) -> RenderContext {
        let size = self.window.get_size();
        RenderContext { viewport_dim: Vector2::new(size.x as f32, size.y as f32) }
    }

    //only use if you're sure
    pub fn get_render_window<'a>(&'a mut self) -> &'a mut RenderWindow {
        &mut self.window
    }
}

pub struct RenderQueue<'a> {
    // There was a colon at the end of Drawable before, it had
    // (probably) something to do with opting out of builtin traits
    // It was also in a couple of other locations too
    renderers: VecDeque<&'a Drawable>,
    clear_color: Color,
}

impl<'a> RenderQueue<'a> {
    pub fn new() -> RenderQueue<'a> {
        RenderQueue {
            renderers: VecDeque::new(),
            clear_color: Color::new_RGB(0, 0, 20),
        }
    }

    pub fn push(&mut self, renderer: &'a Drawable) {
        self.renderers.push_back(renderer);
    }

    pub fn set_clear_color(&mut self, clear_color: Color) {
        self.clear_color = clear_color;
    }

    pub fn render(&mut self, window: &mut Window) {
        window.clear(self.clear_color);

        let render_window = window.get_render_window();

        for renderer in self.renderers.iter() {
            renderer.draw_in_render_window(render_window);
        }

        self.renderers.clear();
    }
}
<|endoftext|>


<commit_before>//! Utilities for game state management.

use super::timing::Duration;

/// Types of state transitions.
pub enum Trans {
    /// Continue as normal.
    None,
    /// Remove the active state and resume the next state on the stack (if any).
    Pop,
    /// Pause the active state and push a new state onto the stack.
    Push(Box<State>),
    /// Remove the current state on the stack and insert a different one.
    Switch(Box<State>),
    /// Stop and remove all states and shut down the engine.
    Quit,
}

/// A trait which defines game states that can be used by the state machine.
pub trait State {
    /// Executed when the game state begins.
    fn on_start(&mut self) {}

    /// Executed when the game state exits.
    fn on_stop(&mut self) {}

    /// Executed when a different game state is pushed onto the stack.
    fn on_pause(&mut self) {}

    /// Executed when the application returns to this game state once again.
    fn on_resume(&mut self) {}

    /// Executed on every frame before updating, for use in reacting to events.
    // TODO: Replace i32 with an actual Event type of some kind.
    fn handle_events(&mut self, _events: &[i32]) {}

    /// Executed repeatedly at stable, predictable intervals (1/60th of a second
    /// by default).
    fn fixed_update(&mut self, _delta: Duration) -> Trans { Trans::None }

    /// Executed on every frame immediately, as fast as the engine will allow.
    fn update(&mut self, _delta: Duration) -> Trans { Trans::Pop }
}

/// A simple stack-based state machine (pushdown automaton).
pub struct StateMachine {
    running: bool,
    state_stack: Vec<Box<State>>,
}

impl StateMachine {
    pub fn new<T: 'static>(initial_state: T) -> StateMachine
        where T: State
    {
        StateMachine {
            running: false,
            state_stack: vec![Box::new(initial_state)],
        }
    }

    /// Checks whether the state machine is running.
    pub fn is_running(&self) -> bool {
        self.running
    }

    /// Initializes the state machine.
    pub fn start(&mut self) {
        if !self.running {
            self.state_stack.last_mut().unwrap().on_start();
            self.running = true;
        }
    }

    /// Passes a vector of events to the active state to handle.
    // TODO: Replace i32 with an actual Event type of some kind.
    pub fn handle_events(&mut self, events: &[i32]) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.handle_events(events);
            }
        }
    }

    /// Updates the currently active state at a steady, fixed interval.
    pub fn fixed_update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.fixed_update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Updates the currently active state immediately.
    pub fn update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Performs a state transition, if requested by either update() or
    /// fixed_update().
    fn transition(&mut self, request: Trans) {
        if self.running {
            match request {
                Trans::None => (),
                Trans::Pop => self.pop(),
                Trans::Push(state) => self.push(state),
                Trans::Switch(state) => self.switch(state),
                Trans::Quit => self.stop(),
            }
        }
    }

    /// Removes the current state on the stack and inserts a different one.
    fn switch(&mut self, state: Box<State>) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Pauses the active state and pushes a new state onto the state stack.
    fn push(&mut self, state: Box<State>) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.on_pause();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Stops and removes the active state and un-pauses the next state on the
    /// stack (if any).
    fn pop(&mut self) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            if let Some(state) = self.state_stack.last_mut() {
                state.on_resume();
            } else {
                self.running = false;
            }
        }
    }

    /// Shuts the state machine down.
    fn stop(&mut self) {
        if self.running {
            while let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.running = false;
        }
    }
}
<commit_msg>Added a basic test for StateMachine.<commit_after>//! Utilities for game state management.

use super::timing::Duration;

/// Types of state transitions.
pub enum Trans {
    /// Continue as normal.
    None,
    /// Remove the active state and resume the next state on the stack or stop if there are none.
    Pop,
    /// Pause the active state and push a new state onto the stack.
    Push(Box<State>),
    /// Remove the current state on the stack and insert a different one.
    Switch(Box<State>),
    /// Stop and remove all states and shut down the engine.
    Quit,
}

/// A trait which defines game states that can be used by the state machine.
pub trait State {
    /// Executed when the game state begins.
    fn on_start(&mut self) {}

    /// Executed when the game state exits.
    fn on_stop(&mut self) {}

    /// Executed when a different game state is pushed onto the stack.
    fn on_pause(&mut self) {}

    /// Executed when the application returns to this game state once again.
    fn on_resume(&mut self) {}

    /// Executed on every frame before updating, for use in reacting to events.
    // TODO: Replace i32 with an actual Event type of some kind.
    fn handle_events(&mut self, _events: &[i32]) {}

    /// Executed repeatedly at stable, predictable intervals (1/60th of a second
    /// by default).
    fn fixed_update(&mut self, _delta: Duration) -> Trans { Trans::None }

    /// Executed on every frame immediately, as fast as the engine will allow.
    fn update(&mut self, _delta: Duration) -> Trans { Trans::Pop }
}

/// A simple stack-based state machine (pushdown automaton).
pub struct StateMachine {
    running: bool,
    state_stack: Vec<Box<State>>,
}

impl StateMachine {
    pub fn new<T: 'static>(initial_state: T) -> StateMachine
        where T: State
    {
        StateMachine {
            running: false,
            state_stack: vec![Box::new(initial_state)],
        }
    }

    /// Checks whether the state machine is running.
    pub fn is_running(&self) -> bool {
        self.running
    }

    /// Initializes the state machine.
    /// # Panics
    ///	Panics if no states are present in the stack.
    pub fn start(&mut self) {
        if !self.running {
            self.state_stack.last_mut().unwrap().on_start();
            self.running = true;
        }
    }

    /// Passes a vector of events to the active state to handle.
    // TODO: Replace i32 with an actual Event type of some kind.
    pub fn handle_events(&mut self, events: &[i32]) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.handle_events(events);
            }
        }
    }

    /// Updates the currently active state at a steady, fixed interval.
    pub fn fixed_update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.fixed_update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Updates the currently active state immediately.
    pub fn update(&mut self, delta_time: Duration) {
        if self.running {
            let mut trans = Trans::None;
            if let Some(state) = self.state_stack.last_mut() {
                trans = state.update(delta_time);
            }
            self.transition(trans);
        }
    }

    /// Performs a state transition, if requested by either update() or
    /// fixed_update().
    fn transition(&mut self, request: Trans) {
        if self.running {
            match request {
                Trans::None => (),
                Trans::Pop => self.pop(),
                Trans::Push(state) => self.push(state),
                Trans::Switch(state) => self.switch(state),
                Trans::Quit => self.stop(),
            }
        }
    }

    /// Removes the current state on the stack and inserts a different one.
    fn switch(&mut self, state: Box<State>) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Pauses the active state and pushes a new state onto the state stack.
    fn push(&mut self, state: Box<State>) {
        if self.running {
            if let Some(state) = self.state_stack.last_mut() {
                state.on_pause();
            }

            self.state_stack.push(state);
            self.state_stack.last_mut().unwrap().on_start();
        }
    }

    /// Stops and removes the active state and un-pauses the next state on the
    /// stack (if any).
    fn pop(&mut self) {
        if self.running {
            if let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            if let Some(state) = self.state_stack.last_mut() {
                state.on_resume();
            } else {
                self.running = false;
            }
        }
    }

    /// Shuts the state machine down.
    fn stop(&mut self) {
        if self.running {
            while let Some(mut state) = self.state_stack.pop() {
                state.on_stop();
            }

            self.running = false;
        }
    }
}

// Unit tests
#[cfg(test)]
mod test {
    use super::*;
    use timing::Duration;

    struct State1(u8);
    struct State2;

    impl State for State1 {
        fn update(&mut self, _delta: Duration) -> Trans {
            if self.0 > 0 {
                self.0 -= 1;
                Trans::None
            } else {
                Trans::Switch(Box::new(State2))
            }
        }
    }

    impl State for State2 {
        fn update(&mut self, _delta: Duration) -> Trans {
            Trans::Pop
        }
    }

    #[test]
    fn switch_pop() {
        let mut sm = StateMachine::new(State1(7));
        sm.start();
        for _ in 0..8 {
            sm.update(Duration::seconds(0));
            assert!(sm.is_running());
        }
        sm.update(Duration::seconds(0));
        assert!(!sm.is_running());
    }
}
<|endoftext|>


<commit_before>// Auto-generated. Do not modify this file! Instead modify examples/minimal.rs
//! Minimal binary size
//!
//! ``` rust,no_run
//! #![feature(lang_items)]
//! #![no_main]
//! #![no_std]
//!
//! extern crate f3;
//!
//! #[no_mangle]
//! pub fn main() -> ! {
//!     loop {}
//! }
//!
//! #[lang = "panic_fmt"]
//! extern "C" fn panic_fmt() -> ! {
//!     loop {}
//! }
//!
//! #[export_name = "_default_exception_handler"]
//! pub extern "C" fn handler() {
//!     loop {}
//! }
//!
//! #[export_name = "_init"]
//! pub fn init() {}
//! ```
<commit_msg>re-generate the examples module<commit_after>// Auto-generated. Do not modify this file! Instead modify examples/minimal.rs
//! Minimal binary size
//!
//! ``` rust,no_run
//! #![feature(lang_items)]
//! #![no_main]
//! #![no_std]
//!
//! extern crate f3;
//!
//! #[no_mangle]
//! pub fn main() -> ! {
//!     loop {}
//! }
//!
//! #[lang = "panic_fmt"]
//! #[no_mangle]
//! extern "C" fn panic_fmt() -> ! {
//!     loop {}
//! }
//!
//! #[export_name = "_default_exception_handler"]
//! pub extern "C" fn handler() {
//!     loop {}
//! }
//!
//! #[export_name = "_init"]
//! pub fn init() {}
//! ```
<|endoftext|>


<commit_before>//! Test command for testing the binary machine code emission.
//!
//! The `binemit` test command generates binary machine code for every instruction in the input
//! functions and compares the results to the expected output.

use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
use cretonne::binemit;
use cretonne::dbg::DisplayList;
use cretonne::ir;
use cretonne::ir::entities::AnyEntity;
use cretonne::binemit::RegDiversions;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use utils::{match_directive, pretty_error};

struct TestBinEmit;

pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
    assert_eq!(parsed.command, "binemit");
    if !parsed.options.is_empty() {
        Err(format!("No options allowed on {}", parsed))
    } else {
        Ok(Box::new(TestBinEmit))
    }
}

// Code sink that generates text.
struct TextSink {
    offset: binemit::CodeOffset,
    text: String,
}

impl TextSink {
    /// Create a new empty TextSink.
    pub fn new() -> Self {
        Self {
            offset: 0,
            text: String::new(),
        }
    }
}

impl binemit::CodeSink for TextSink {
    fn offset(&self) -> binemit::CodeOffset {
        self.offset
    }

    fn put1(&mut self, x: u8) {
        write!(self.text, "{:02x} ", x).unwrap();
        self.offset += 1;
    }

    fn put2(&mut self, x: u16) {
        write!(self.text, "{:04x} ", x).unwrap();
        self.offset += 2;
    }

    fn put4(&mut self, x: u32) {
        write!(self.text, "{:08x} ", x).unwrap();
        self.offset += 4;
    }

    fn put8(&mut self, x: u64) {
        write!(self.text, "{:016x} ", x).unwrap();
        self.offset += 8;
    }

    fn reloc_ebb(&mut self, reloc: binemit::Reloc, ebb_offset: binemit::CodeOffset) {
        write!(self.text, "{}({}) ", reloc, ebb_offset).unwrap();
    }

    fn reloc_external(
        &mut self,
        reloc: binemit::Reloc,
        name: &ir::ExternalName,
        addend: binemit::Addend,
    ) {
        write!(
            self.text,
            "{}({}",
            reloc,
            name,
        ).unwrap();
        if addend != 0 {
            write!(
                self.text,
                "{:+}",
                addend,
            ).unwrap();
        }
        write!(
            self.text,
            ") ",
        ).unwrap();
    }

    fn reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable) {
        write!(self.text, "{}({}) ", reloc, jt).unwrap();
    }
}

impl SubTest for TestBinEmit {
    fn name(&self) -> Cow<str> {
        Cow::from("binemit")
    }

    fn is_mutating(&self) -> bool {
        true
    }

    fn needs_isa(&self) -> bool {
        true
    }

    fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
        let isa = context.isa.expect("binemit needs an ISA");
        let encinfo = isa.encoding_info();
        // TODO: Run a verifier pass over the code first to detect any bad encodings or missing/bad
        // value locations. The current error reporting is just crashing...
        let mut func = func.into_owned();

        // Fix the stack frame layout so we can test spill/fill encodings.
        let min_offset = func.stack_slots
            .keys()
            .map(|ss| func.stack_slots[ss].offset.unwrap())
            .min();
        func.stack_slots.frame_size = min_offset.map(|off| (-off) as u32);

        let is_compressed = isa.flags().is_compressed();

        // Give an encoding to any instruction that doesn't already have one.
        let mut divert = RegDiversions::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            for inst in func.layout.ebb_insts(ebb) {
                if !func.encodings[inst].is_legal() {
                    // Find an encoding that satisfies both immediate field and register
                    // constraints.
                    if let Some(enc) = {
                        let mut legal_encodings = isa.legal_encodings(
                            &func.dfg,
                            &func.dfg[inst],
                            func.dfg.ctrl_typevar(inst),
                        ).filter(|e| {
                                let recipe_constraints = &encinfo.constraints[e.recipe()];
                                recipe_constraints.satisfied(inst, &divert, &func)
                            });

                        if is_compressed {
                            // Get the smallest legal encoding
                            legal_encodings.min_by_key(|&e| encinfo.bytes(e))
                        } else {
                            // If not using compressed, just use the first encoding.
                            legal_encodings.next()
                        }
                    }
                    {
                        func.encodings[inst] = enc;
                    }
                }
                divert.apply(&func.dfg[inst]);
            }
        }

        // Relax branches and compute EBB offsets based on the encodings.
        let code_size = binemit::relax_branches(&mut func, isa).map_err(|e| {
            pretty_error(&func, context.isa, e)
        })?;

        // Collect all of the 'bin:' directives on instructions.
        let mut bins = HashMap::new();
        for comment in &context.details.comments {
            if let Some(want) = match_directive(comment.text, "bin:") {
                match comment.entity {
                    AnyEntity::Inst(inst) => {
                        if let Some(prev) = bins.insert(inst, want) {
                            return Err(format!(
                                "multiple 'bin:' directives on {}: '{}' and '{}'",
                                func.dfg.display_inst(inst, isa),
                                prev,
                                want
                            ));
                        }
                    }
                    _ => {
                        return Err(format!(
                            "'bin:' directive on non-inst {}: {}",
                            comment.entity,
                            comment.text
                        ))
                    }
                }
            }
        }
        if bins.is_empty() {
            return Err("No 'bin:' directives found".to_string());
        }

        // Now emit all instructions.
        let mut sink = TextSink::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            // Correct header offsets should have been computed by `relax_branches()`.
            assert_eq!(
                sink.offset,
                func.offsets[ebb],
                "Inconsistent {} header offset",
                ebb
            );
            for (offset, inst, enc_bytes) in func.inst_offsets(ebb, &encinfo) {
                assert_eq!(sink.offset, offset);
                sink.text.clear();
                let enc = func.encodings[inst];

                // Send legal encodings into the emitter.
                if enc.is_legal() {
                    // Generate a better error message if output locations are not specified.
                    if let Some(&v) = func.dfg.inst_results(inst).iter().find(|&&v| {
                        !func.locations[v].is_assigned()
                    })
                    {
                        return Err(format!(
                            "Missing register/stack slot for {} in {}",
                            v,
                            func.dfg.display_inst(inst, isa)
                        ));
                    }
                    let before = sink.offset;
                    isa.emit_inst(&func, inst, &mut divert, &mut sink);
                    let emitted = sink.offset - before;
                    // Verify the encoding recipe sizes against the ISAs emit_inst implementation.
                    assert_eq!(
                        emitted,
                        enc_bytes,
                        "Inconsistent size for [{}] {}",
                        encinfo.display(enc),
                        func.dfg.display_inst(inst, isa)
                    );
                }

                // Check against bin: directives.
                if let Some(want) = bins.remove(&inst) {
                    if !enc.is_legal() {
                        // A possible cause of an unencoded instruction is a missing location for
                        // one of the input operands.
                        if let Some(&v) = func.dfg.inst_args(inst).iter().find(|&&v| {
                            !func.locations[v].is_assigned()
                        })
                        {
                            return Err(format!(
                                "Missing register/stack slot for {} in {}",
                                v,
                                func.dfg.display_inst(inst, isa)
                            ));
                        }

                        // Do any encodings exist?
                        let encodings = isa.legal_encodings(
                            &func.dfg,
                            &func.dfg[inst],
                            func.dfg.ctrl_typevar(inst),
                        ).map(|e| encinfo.display(e))
                            .collect::<Vec<_>>();

                        if encodings.is_empty() {
                            return Err(format!(
                                "No encodings found for: {}",
                                func.dfg.display_inst(inst, isa)
                            ));
                        } else {
                            return Err(format!(
                                "No matching encodings for {} in {}",
                                func.dfg.display_inst(inst, isa),
                                DisplayList(&encodings),
                            ));
                        }
                    }
                    let have = sink.text.trim();
                    if have != want {
                        return Err(format!(
                            "Bad machine code for {}: {}\nWant: {}\nGot:  {}",
                            inst,
                            func.dfg.display_inst(inst, isa),
                            want,
                            have
                        ));
                    }
                }
            }
        }

        if sink.offset != code_size {
            return Err(format!(
                "Expected code size {}, got {}",
                code_size,
                sink.offset
            ));
        }

        Ok(())
    }
}
<commit_msg>Style: Don't use `else` after a `return`.<commit_after>//! Test command for testing the binary machine code emission.
//!
//! The `binemit` test command generates binary machine code for every instruction in the input
//! functions and compares the results to the expected output.

use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Write;
use cretonne::binemit;
use cretonne::dbg::DisplayList;
use cretonne::ir;
use cretonne::ir::entities::AnyEntity;
use cretonne::binemit::RegDiversions;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use utils::{match_directive, pretty_error};

struct TestBinEmit;

pub fn subtest(parsed: &TestCommand) -> Result<Box<SubTest>> {
    assert_eq!(parsed.command, "binemit");
    if !parsed.options.is_empty() {
        Err(format!("No options allowed on {}", parsed))
    } else {
        Ok(Box::new(TestBinEmit))
    }
}

// Code sink that generates text.
struct TextSink {
    offset: binemit::CodeOffset,
    text: String,
}

impl TextSink {
    /// Create a new empty TextSink.
    pub fn new() -> Self {
        Self {
            offset: 0,
            text: String::new(),
        }
    }
}

impl binemit::CodeSink for TextSink {
    fn offset(&self) -> binemit::CodeOffset {
        self.offset
    }

    fn put1(&mut self, x: u8) {
        write!(self.text, "{:02x} ", x).unwrap();
        self.offset += 1;
    }

    fn put2(&mut self, x: u16) {
        write!(self.text, "{:04x} ", x).unwrap();
        self.offset += 2;
    }

    fn put4(&mut self, x: u32) {
        write!(self.text, "{:08x} ", x).unwrap();
        self.offset += 4;
    }

    fn put8(&mut self, x: u64) {
        write!(self.text, "{:016x} ", x).unwrap();
        self.offset += 8;
    }

    fn reloc_ebb(&mut self, reloc: binemit::Reloc, ebb_offset: binemit::CodeOffset) {
        write!(self.text, "{}({}) ", reloc, ebb_offset).unwrap();
    }

    fn reloc_external(
        &mut self,
        reloc: binemit::Reloc,
        name: &ir::ExternalName,
        addend: binemit::Addend,
    ) {
        write!(
            self.text,
            "{}({}",
            reloc,
            name,
        ).unwrap();
        if addend != 0 {
            write!(
                self.text,
                "{:+}",
                addend,
            ).unwrap();
        }
        write!(
            self.text,
            ") ",
        ).unwrap();
    }

    fn reloc_jt(&mut self, reloc: binemit::Reloc, jt: ir::JumpTable) {
        write!(self.text, "{}({}) ", reloc, jt).unwrap();
    }
}

impl SubTest for TestBinEmit {
    fn name(&self) -> Cow<str> {
        Cow::from("binemit")
    }

    fn is_mutating(&self) -> bool {
        true
    }

    fn needs_isa(&self) -> bool {
        true
    }

    fn run(&self, func: Cow<ir::Function>, context: &Context) -> Result<()> {
        let isa = context.isa.expect("binemit needs an ISA");
        let encinfo = isa.encoding_info();
        // TODO: Run a verifier pass over the code first to detect any bad encodings or missing/bad
        // value locations. The current error reporting is just crashing...
        let mut func = func.into_owned();

        // Fix the stack frame layout so we can test spill/fill encodings.
        let min_offset = func.stack_slots
            .keys()
            .map(|ss| func.stack_slots[ss].offset.unwrap())
            .min();
        func.stack_slots.frame_size = min_offset.map(|off| (-off) as u32);

        let is_compressed = isa.flags().is_compressed();

        // Give an encoding to any instruction that doesn't already have one.
        let mut divert = RegDiversions::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            for inst in func.layout.ebb_insts(ebb) {
                if !func.encodings[inst].is_legal() {
                    // Find an encoding that satisfies both immediate field and register
                    // constraints.
                    if let Some(enc) = {
                        let mut legal_encodings = isa.legal_encodings(
                            &func.dfg,
                            &func.dfg[inst],
                            func.dfg.ctrl_typevar(inst),
                        ).filter(|e| {
                                let recipe_constraints = &encinfo.constraints[e.recipe()];
                                recipe_constraints.satisfied(inst, &divert, &func)
                            });

                        if is_compressed {
                            // Get the smallest legal encoding
                            legal_encodings.min_by_key(|&e| encinfo.bytes(e))
                        } else {
                            // If not using compressed, just use the first encoding.
                            legal_encodings.next()
                        }
                    }
                    {
                        func.encodings[inst] = enc;
                    }
                }
                divert.apply(&func.dfg[inst]);
            }
        }

        // Relax branches and compute EBB offsets based on the encodings.
        let code_size = binemit::relax_branches(&mut func, isa).map_err(|e| {
            pretty_error(&func, context.isa, e)
        })?;

        // Collect all of the 'bin:' directives on instructions.
        let mut bins = HashMap::new();
        for comment in &context.details.comments {
            if let Some(want) = match_directive(comment.text, "bin:") {
                match comment.entity {
                    AnyEntity::Inst(inst) => {
                        if let Some(prev) = bins.insert(inst, want) {
                            return Err(format!(
                                "multiple 'bin:' directives on {}: '{}' and '{}'",
                                func.dfg.display_inst(inst, isa),
                                prev,
                                want
                            ));
                        }
                    }
                    _ => {
                        return Err(format!(
                            "'bin:' directive on non-inst {}: {}",
                            comment.entity,
                            comment.text
                        ))
                    }
                }
            }
        }
        if bins.is_empty() {
            return Err("No 'bin:' directives found".to_string());
        }

        // Now emit all instructions.
        let mut sink = TextSink::new();
        for ebb in func.layout.ebbs() {
            divert.clear();
            // Correct header offsets should have been computed by `relax_branches()`.
            assert_eq!(
                sink.offset,
                func.offsets[ebb],
                "Inconsistent {} header offset",
                ebb
            );
            for (offset, inst, enc_bytes) in func.inst_offsets(ebb, &encinfo) {
                assert_eq!(sink.offset, offset);
                sink.text.clear();
                let enc = func.encodings[inst];

                // Send legal encodings into the emitter.
                if enc.is_legal() {
                    // Generate a better error message if output locations are not specified.
                    if let Some(&v) = func.dfg.inst_results(inst).iter().find(|&&v| {
                        !func.locations[v].is_assigned()
                    })
                    {
                        return Err(format!(
                            "Missing register/stack slot for {} in {}",
                            v,
                            func.dfg.display_inst(inst, isa)
                        ));
                    }
                    let before = sink.offset;
                    isa.emit_inst(&func, inst, &mut divert, &mut sink);
                    let emitted = sink.offset - before;
                    // Verify the encoding recipe sizes against the ISAs emit_inst implementation.
                    assert_eq!(
                        emitted,
                        enc_bytes,
                        "Inconsistent size for [{}] {}",
                        encinfo.display(enc),
                        func.dfg.display_inst(inst, isa)
                    );
                }

                // Check against bin: directives.
                if let Some(want) = bins.remove(&inst) {
                    if !enc.is_legal() {
                        // A possible cause of an unencoded instruction is a missing location for
                        // one of the input operands.
                        if let Some(&v) = func.dfg.inst_args(inst).iter().find(|&&v| {
                            !func.locations[v].is_assigned()
                        })
                        {
                            return Err(format!(
                                "Missing register/stack slot for {} in {}",
                                v,
                                func.dfg.display_inst(inst, isa)
                            ));
                        }

                        // Do any encodings exist?
                        let encodings = isa.legal_encodings(
                            &func.dfg,
                            &func.dfg[inst],
                            func.dfg.ctrl_typevar(inst),
                        ).map(|e| encinfo.display(e))
                            .collect::<Vec<_>>();

                        if encodings.is_empty() {
                            return Err(format!(
                                "No encodings found for: {}",
                                func.dfg.display_inst(inst, isa)
                            ));
                        }
                        return Err(format!(
                                "No matching encodings for {} in {}",
                                func.dfg.display_inst(inst, isa),
                                DisplayList(&encodings),
                            ));
                    }
                    let have = sink.text.trim();
                    if have != want {
                        return Err(format!(
                            "Bad machine code for {}: {}\nWant: {}\nGot:  {}",
                            inst,
                            func.dfg.display_inst(inst, isa),
                            want,
                            have
                        ));
                    }
                }
            }
        }

        if sink.offset != code_size {
            return Err(format!(
                "Expected code size {}, got {}",
                code_size,
                sink.offset
            ));
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use dsl::*;
use dsl::Constraint::*;
use dsl::LpExpression::*;

use std::fs::File;
use std::io::prelude::*;

pub trait LpFileFormat {
    fn to_lp_file_format(&self) -> String;
    fn write_lp(&self, file_model: &str) -> std::io::Result<()> {
        let mut buffer = File::create(file_model)?;
        buffer.write(self.to_lp_file_format().as_bytes())?;
        Ok(())
    }
}

fn objective_lp_file_block(prob: &LpProblem) -> String {
    // Write objectives
    let obj_type = match prob.objective_type {
        LpObjective::Maximize => "Maximize\n  ",
        LpObjective::Minimize => "Minimize\n  "
    };
    match prob.obj_expr {
        Some(ref expr) => format!("{}obj: {}", obj_type, expr.to_lp_file_format()),
        _ => String::new()
    }
}
fn constraints_lp_file_block(prob: &LpProblem) -> String {
    let mut res = String::new();
    let mut constraints = prob.constraints.iter();
    let mut index = 1;
    while let Some(ref constraint) = constraints.next() {
        res.push_str(&format!("  c{}: {}\n", index.to_string(), &constraint.to_lp_file_format()));
        index += 1;
    }
    res
}

fn bounds_lp_file_block(prob: &LpProblem) -> String {
    let mut res = String::new();
    for (_, v) in prob.variables() {
        match v {
            &ConsInt(LpInteger {
                         ref name,
                         lower_bound,
                         upper_bound,
                     })
            | &ConsCont(LpContinuous {
                            ref name,
                            lower_bound,
                            upper_bound,
                        }) => {
                if let Some(l) = lower_bound {
                    res.push_str(&format!("  {} <= {}", &l.to_string(), &name));
                    if let Some(u) = upper_bound {
                        res.push_str(&format!(" <= {}", &u.to_string()));
                    }
                    res.push_str("\n");
                } else if let Some(u) = upper_bound {
                    res.push_str(&format!("  {} <= {}\n", &name, &u.to_string()));
                } else {
                    match v {
                        &ConsCont(LpContinuous { .. }) => {
                            res.push_str(&format!("  {} free\n", &name));
                        } // TODO: IntegerVar => -INF to INF
                        _ => (),
                    }
                }
            }
            _ => (),
        }
    }
    res
}

fn integers_lp_file_block(prob: &LpProblem) -> String {
    let mut res = String::new();
    for (_, v) in prob.variables() {
        match v {
            &ConsInt(LpInteger { ref name, .. }) => {
                res.push_str(format!("{} ", name).as_str());
            }
            _ => (),
        }
    }
    res
}

fn binaries_lp_file_block(prob: &LpProblem) -> String  {
    let mut res = String::new();
    for (_, v) in prob.variables() {
        match v {
            &ConsBin(LpBinary { ref name }) => {
                res.push_str(format!("{} ", name).as_str());
            }
            _ => (),
        }
    }
    res
}

impl LpFileFormat for LpProblem {

    fn to_lp_file_format(&self) -> String {

        let mut buffer = String::new();

        buffer.push_str(format!("\\ {}\n\n", &self.name).as_str());

        buffer.push_str( &objective_lp_file_block(self) );

        let constraints_block = constraints_lp_file_block(self);
        if constraints_block.len() > 0 {
            buffer.push_str(format!("\n\nSubject To\n{}", &constraints_block).as_str());
        }

        let bounds_block = bounds_lp_file_block(self);
        if bounds_block.len() > 0 {
            buffer.push_str(format!("\nBounds\n{}", &bounds_block).as_str());
        }

        let integers_block = integers_lp_file_block(self);
        if integers_block.len() > 0 {
            buffer.push_str(format!("\nGenerals\n  {}\n", &integers_block).as_str());
        }

        let binaries_block = binaries_lp_file_block(self);
        if binaries_block.len() > 0 {
            buffer.push_str(format!("\nBinary\n  {}\n", &binaries_block).as_str());
        }

        buffer.push_str("\nEnd");

        buffer
    }
}

impl LpFileFormat for LpExpression {
    fn to_lp_file_format(&self) -> String {
        fn formalize_signs(s: String) -> String {
            let mut s = s.clone();
            let mut t = "".to_string();
            while s != t {
                t = s.clone();
                s = s.replace("+ +", "+ ");
                s = s.replace("- +", "- ");
                s = s.replace("+ -", "- ");
                s = s.replace("- -", "+ ");
                s = s.replace("  ", " ");
            }
            s
        }

        formalize_signs(show(&simplify(self), false))
    }
}

fn show(e: &LpExpression, with_parenthesis: bool) -> String {
    let str_left_mult = if with_parenthesis { "(" } else { "" };
    let str_right_mult = if with_parenthesis { ")" } else { "" };
    let str_op_mult = if with_parenthesis { " * " } else { " " };
    match e {
        &LitVal(n) => n.to_string(),
        &AddExpr(ref e1, ref e2) => {
            str_left_mult.to_string()
                + &show(e1, with_parenthesis)
                + " + "
                + &show(e2, with_parenthesis)
                + str_right_mult
        }
        &SubExpr(ref e1, ref e2) => {
            str_left_mult.to_string()
                + &show(e1, with_parenthesis)
                + " - "
                + &show(e2, with_parenthesis)
                + str_right_mult
        }
        &MulExpr(ref e1, ref e2) => {
            let ref deref_e1 = **e1;

            match deref_e1 {
                &LitVal(v) if v == 1.0 => {
                    //e2.to_lp_file_format()
                    str_left_mult.to_string()
                        + &" ".to_string()
                        + &show(e2, with_parenthesis)
                        + str_right_mult
                }
                &LitVal(v) if v == -1.0 => {
                    //"-".to_string() + &e2.to_lp_file_format()
                    str_left_mult.to_string()
                        + &"-".to_string()
                        + &show(e2, with_parenthesis)
                        + str_right_mult
                }
                _ => {
                    str_left_mult.to_string()
                        + &show(e1, with_parenthesis)
                        + str_op_mult
                        + &show(e2, with_parenthesis)
                        + str_right_mult
                }
            }
        }
        &ConsBin(LpBinary { name: ref n, .. }) => n.to_string(),
        &ConsInt(LpInteger { name: ref n, .. }) => n.to_string(),
        &ConsCont(LpContinuous { name: ref n, .. }) => n.to_string(),
        _ => "EmptyExpr!!".to_string(),
    }
}

impl LpFileFormat for LpConstraint {
    fn to_lp_file_format(&self) -> String {
        let mut res = String::new();
        res.push_str(&self.0.to_lp_file_format());
        match self.1 {
            GreaterOrEqual => res.push_str(" >= "),
            LessOrEqual => res.push_str(" <= "),
            Equal => res.push_str(" = "),
        }
        res.push_str(&self.2.to_lp_file_format());
        res
    }
}
<commit_msg>Import std<commit_after>use dsl::*;
use dsl::Constraint::*;
use dsl::LpExpression::*;

use std::fs::File;
use std::io::prelude::*;
use std::io::Result;

pub trait LpFileFormat {
    fn to_lp_file_format(&self) -> String;
    fn write_lp(&self, file_model: &str) -> Result<()> {
        let mut buffer = File::create(file_model)?;
        buffer.write(self.to_lp_file_format().as_bytes())?;
        Ok(())
    }
}

impl LpFileFormat for LpProblem {

    fn to_lp_file_format(&self) -> String {

        let mut buffer = String::new();

        buffer.push_str(format!("\\ {}\n\n", &self.name).as_str());

        buffer.push_str( &objective_lp_file_block(self) );

        let constraints_block = constraints_lp_file_block(self);
        if constraints_block.len() > 0 {
            buffer.push_str(format!("\n\nSubject To\n{}", &constraints_block).as_str());
        }

        let bounds_block = bounds_lp_file_block(self);
        if bounds_block.len() > 0 {
            buffer.push_str(format!("\nBounds\n{}", &bounds_block).as_str());
        }

        let integers_block = integers_lp_file_block(self);
        if integers_block.len() > 0 {
            buffer.push_str(format!("\nGenerals\n  {}\n", &integers_block).as_str());
        }

        let binaries_block = binaries_lp_file_block(self);
        if binaries_block.len() > 0 {
            buffer.push_str(format!("\nBinary\n  {}\n", &binaries_block).as_str());
        }

        buffer.push_str("\nEnd");

        buffer
    }
}

fn objective_lp_file_block(prob: &LpProblem) -> String {
    // Write objectives
    let obj_type = match prob.objective_type {
        LpObjective::Maximize => "Maximize\n  ",
        LpObjective::Minimize => "Minimize\n  "
    };
    match prob.obj_expr {
        Some(ref expr) => format!("{}obj: {}", obj_type, expr.to_lp_file_format()),
        _ => String::new()
    }
}
fn constraints_lp_file_block(prob: &LpProblem) -> String {
    let mut res = String::new();
    let mut constraints = prob.constraints.iter();
    let mut index = 1;
    while let Some(ref constraint) = constraints.next() {
        res.push_str(&format!("  c{}: {}\n", index.to_string(), &constraint.to_lp_file_format()));
        index += 1;
    }
    res
}

fn bounds_lp_file_block(prob: &LpProblem) -> String {
    let mut res = String::new();
    for (_, v) in prob.variables() {
        match v {
            &ConsInt(LpInteger {
                         ref name,
                         lower_bound,
                         upper_bound,
                     })
            | &ConsCont(LpContinuous {
                            ref name,
                            lower_bound,
                            upper_bound,
                        }) => {
                if let Some(l) = lower_bound {
                    res.push_str(&format!("  {} <= {}", &l.to_string(), &name));
                    if let Some(u) = upper_bound {
                        res.push_str(&format!(" <= {}", &u.to_string()));
                    }
                    res.push_str("\n");
                } else if let Some(u) = upper_bound {
                    res.push_str(&format!("  {} <= {}\n", &name, &u.to_string()));
                } else {
                    match v {
                        &ConsCont(LpContinuous { .. }) => {
                            res.push_str(&format!("  {} free\n", &name));
                        } // TODO: IntegerVar => -INF to INF
                        _ => (),
                    }
                }
            }
            _ => (),
        }
    }
    res
}

fn integers_lp_file_block(prob: &LpProblem) -> String {
    let mut res = String::new();
    for (_, v) in prob.variables() {
        match v {
            &ConsInt(LpInteger { ref name, .. }) => {
                res.push_str(format!("{} ", name).as_str());
            }
            _ => (),
        }
    }
    res
}

fn binaries_lp_file_block(prob: &LpProblem) -> String  {
    let mut res = String::new();
    for (_, v) in prob.variables() {
        match v {
            &ConsBin(LpBinary { ref name }) => {
                res.push_str(format!("{} ", name).as_str());
            }
            _ => (),
        }
    }
    res
}

impl LpFileFormat for LpExpression {
    fn to_lp_file_format(&self) -> String {
        fn formalize_signs(s: String) -> String {
            let mut s = s.clone();
            let mut t = "".to_string();
            while s != t {
                t = s.clone();
                s = s.replace("+ +", "+ ");
                s = s.replace("- +", "- ");
                s = s.replace("+ -", "- ");
                s = s.replace("- -", "+ ");
                s = s.replace("  ", " ");
            }
            s
        }

        formalize_signs(show(&simplify(self), false))
    }
}

fn show(e: &LpExpression, with_parenthesis: bool) -> String {
    let str_left_mult = if with_parenthesis { "(" } else { "" };
    let str_right_mult = if with_parenthesis { ")" } else { "" };
    let str_op_mult = if with_parenthesis { " * " } else { " " };
    match e {
        &LitVal(n) => n.to_string(),
        &AddExpr(ref e1, ref e2) => {
            str_left_mult.to_string()
                + &show(e1, with_parenthesis)
                + " + "
                + &show(e2, with_parenthesis)
                + str_right_mult
        }
        &SubExpr(ref e1, ref e2) => {
            str_left_mult.to_string()
                + &show(e1, with_parenthesis)
                + " - "
                + &show(e2, with_parenthesis)
                + str_right_mult
        }
        &MulExpr(ref e1, ref e2) => {
            let ref deref_e1 = **e1;

            match deref_e1 {
                &LitVal(v) if v == 1.0 => {
                    //e2.to_lp_file_format()
                    str_left_mult.to_string()
                        + &" ".to_string()
                        + &show(e2, with_parenthesis)
                        + str_right_mult
                }
                &LitVal(v) if v == -1.0 => {
                    //"-".to_string() + &e2.to_lp_file_format()
                    str_left_mult.to_string()
                        + &"-".to_string()
                        + &show(e2, with_parenthesis)
                        + str_right_mult
                }
                _ => {
                    str_left_mult.to_string()
                        + &show(e1, with_parenthesis)
                        + str_op_mult
                        + &show(e2, with_parenthesis)
                        + str_right_mult
                }
            }
        }
        &ConsBin(LpBinary { name: ref n, .. }) => n.to_string(),
        &ConsInt(LpInteger { name: ref n, .. }) => n.to_string(),
        &ConsCont(LpContinuous { name: ref n, .. }) => n.to_string(),
        _ => "EmptyExpr!!".to_string(),
    }
}

impl LpFileFormat for LpConstraint {
    fn to_lp_file_format(&self) -> String {
        let mut res = String::new();
        res.push_str(&self.0.to_lp_file_format());
        match self.1 {
            GreaterOrEqual => res.push_str(" >= "),
            LessOrEqual => res.push_str(" <= "),
            Equal => res.push_str(" = "),
        }
        res.push_str(&self.2.to_lp_file_format());
        res
    }
}
<|endoftext|>


<commit_before>//! Pattern-sequence data structures and implementations.

use std::ops::Index;
use std::iter::FromIterator;

use util::set::{self, Contains};
use super::{Atom, Class, Element, Repetition, Union};
use super::{Reduce, flatten_and_reduce};

// To hide the implementation details, we wrap the type alias in
// a private submodule.
mod seq_impl {
    use ::pattern::Element;
    pub type Inner<T> = Vec<Element<T>>;
}

/// Sequence of patterns.
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Sequence<T: Atom>(seq_impl::Inner<T>);

impl<T: Atom> Sequence<T> {
    /// Create a new sequence from the given vector.
    ///
    /// This method will **panic** if the given vector is not at least two
    /// elements long.
    pub fn new(v: Vec<Element<T>>) -> Self {
        if v.len() < 2 {
            panic!("Sequences must have at least two elements");
        }
        Sequence(v)
    }

    /// Get the number of elements in the sequence.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Fetch an iterator over the elements in the sequence
    pub fn iter<'a>(&'a self) -> impl Iterator<Item=&'a Element<T>> {
        self.0.iter()
    }

    fn into_inner(self) -> seq_impl::Inner<T> {
        self.0
    }

    /// Transforms each contained atom using the supplied function or closure
    /// to produce a new sequence.
    pub fn map_atoms<U, F>(self, f: F) -> Sequence<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        self.0.into_iter().map(|elt| elt.map_atoms(&f)).collect()
    }
}

impl<T: Atom> Reduce for Sequence<T> {
    type Output = Option<Element<T>>;

    /// Flatten the sequence and apply implementation-defined optimizations.
    fn reduce(mut self) -> Self::Output {
        // Flatten and reduce the vector *first* so we can reduce `self` based
        // on the size of the *reduced* vector.
       flatten_and_reduce(&mut self.0, Element::Wildcard,
                           |elt| match elt { &Element::Sequence(ref s) => Some(s.len()), _ => None },
                           |elt| match elt { Element::Sequence(s) => s.into_inner(), _ => unreachable!() });
        match self.0.len() {
            0 => None,
            1 => Some(self.0.swap_remove(0)),
            _ => Some(Element::Sequence(self))
        }
    }
}

impl<T: Atom> Index<usize> for Sequence<T> {
    type Output = Element<T>;
    fn index(&self, index: usize) -> &Self::Output {
        &self.0[index]
    }

}

impl<T: Atom> FromIterator<Element<T>> for Sequence<T> {
    fn from_iter<I>(iter: I) -> Self
        where I: IntoIterator<Item=Element<T>>
    {
        Sequence(iter.into_iter().collect())
    }
}

impl<T: Atom> set::Contains<T> for Sequence<T> {
    /// A sequence always contains at least two elements, and thus will never
    /// match a single atom.
    #[inline(always)]
    fn contains(&self, _: T) -> bool {
        false
    }
}

impl<T: Atom> set::IsSubsetOf<T> for Sequence<T> {
    /// A sequence is a subset of a particular atom whenever the sequence has
    /// length one, and the first element is a subset of that atom.
    #[inline(always)]
    fn is_subset_of(&self, atom: &T) -> bool {
        self.len() == 1 && self[0].is_subset_of(atom)
    }
}

impl<T: Atom> set::IsSubsetOf<Sequence<T>> for Sequence<T> {
    /// A sequence `A` can be a subset of another sequence `B` *if* both have the
    /// same length and each element in `A` is a subset of the corresponding element
    /// in `B`:
    ///
    /// A ⊂ B ⇔ |A|=|B|=n ∧ Aᵢ ⊂ Bᵢ ∀ i∈[0,n]
    fn is_subset_of(&self, other: &Sequence<T>) -> bool {
        other.len() == self.len() &&
            self.iter().zip(other.iter()).all(|(u, v)| u.is_subset_of(v))
    }
}

impl<T: Atom> set::IsSubsetOf<Class<T>> for Sequence<T> {
    /// A sequence is never a subset of an atom class because sequences are
    /// guaranteed to contain at least two elements.
    #[inline(always)]
    fn is_subset_of(&self, _: &Class<T>) -> bool {
        false
    }
}

impl<T: Atom> set::IsSubsetOf<Union<T>> for Sequence<T> {
    /// A sequence is a subset of a union whenever it is a subset of one of the
    /// union's members.
    fn is_subset_of(&self, union: &Union<T>) -> bool {
        union.iter().any(|m| self.is_subset_of(m))
    }
}

impl<T: Atom> set::IsSubsetOf<Repetition<T>> for Sequence<T> {
    /// A sequence is a subset of a repetition if the sequence's length is
    /// contained by the repetition's count and all members of the sequence are
    /// subsets of the repeated element.
    fn is_subset_of(&self, rep: &Repetition<T>) -> bool {
        rep.count().contains(self.len()) && self.iter().all(|elt| elt.is_subset_of(rep.element()))
    }
}

impl<T: Atom> set::IsSubsetOf<Element<T>> for Sequence<T> {
    fn is_subset_of(&self, elt: &Element<T>) -> bool {
        match elt {
            &Element::Tagged{ref element, ..} => self.is_subset_of(&**element),
            &Element::Sequence(ref s) => self.is_subset_of(s),
            &Element::Union(ref u) => self.is_subset_of(u),
            &Element::Repeat(ref r) => self.is_subset_of(r),

            &Element::Anchor(_) => false,
            &Element::Atom(_) => false,
            &Element::Class(_) => false,
            &Element::Wildcard => false,
        }
    }
}
<commit_msg>pattern/sequence: add more IsSubsetOf specializations, and delegate<commit_after>//! Pattern-sequence data structures and implementations.

use std::ops::Index;
use std::iter::FromIterator;

use util::set::{self, Contains};
use super::{Anchor, Atom, Class, Element, Repetition, Union};
use super::{Reduce, flatten_and_reduce};

// To hide the implementation details, we wrap the type alias in
// a private submodule.
mod seq_impl {
    use ::pattern::Element;
    pub type Inner<T> = Vec<Element<T>>;
}

/// Sequence of patterns.
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
pub struct Sequence<T: Atom>(seq_impl::Inner<T>);

impl<T: Atom> Sequence<T> {
    /// Create a new sequence from the given vector.
    ///
    /// This method will **panic** if the given vector is not at least two
    /// elements long.
    pub fn new(v: Vec<Element<T>>) -> Self {
        if v.len() < 2 {
            panic!("Sequences must have at least two elements");
        }
        Sequence(v)
    }

    /// Get the number of elements in the sequence.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Fetch an iterator over the elements in the sequence
    pub fn iter<'a>(&'a self) -> impl Iterator<Item=&'a Element<T>> {
        self.0.iter()
    }

    fn into_inner(self) -> seq_impl::Inner<T> {
        self.0
    }

    /// Transforms each contained atom using the supplied function or closure
    /// to produce a new sequence.
    pub fn map_atoms<U, F>(self, f: F) -> Sequence<U>
        where F: Fn(T) -> U,
              U: Atom
    {
        self.0.into_iter().map(|elt| elt.map_atoms(&f)).collect()
    }
}

impl<T: Atom> Reduce for Sequence<T> {
    type Output = Option<Element<T>>;

    /// Flatten the sequence and apply implementation-defined optimizations.
    fn reduce(mut self) -> Self::Output {
        // Flatten and reduce the vector *first* so we can reduce `self` based
        // on the size of the *reduced* vector.
       flatten_and_reduce(&mut self.0, Element::Wildcard,
                           |elt| match elt { &Element::Sequence(ref s) => Some(s.len()), _ => None },
                           |elt| match elt { Element::Sequence(s) => s.into_inner(), _ => unreachable!() });
        match self.0.len() {
            0 => None,
            1 => Some(self.0.swap_remove(0)),
            _ => Some(Element::Sequence(self))
        }
    }
}

impl<T: Atom> Index<usize> for Sequence<T> {
    type Output = Element<T>;
    fn index(&self, index: usize) -> &Self::Output {
        &self.0[index]
    }

}

impl<T: Atom> FromIterator<Element<T>> for Sequence<T> {
    fn from_iter<I>(iter: I) -> Self
        where I: IntoIterator<Item=Element<T>>
    {
        Sequence(iter.into_iter().collect())
    }
}

impl<T: Atom> set::Contains<T> for Sequence<T> {
    /// A sequence always contains at least two elements, and thus will never
    /// match a single atom.
    #[inline(always)]
    fn contains(&self, _: T) -> bool {
        false
    }
}

// ----------------------------------------------------------------
// set::IsSubsetOf

macro_rules! is_subset_if_length_1_and_first_element_is_subset {
    ($T: ident,$Other: ty, $name: ident; $doc: expr) => {
        impl<$T: Atom> set::IsSubsetOf<$Other> for Sequence<$T> {
            #[doc = $doc]
            #[inline]
            fn is_subset_of(&self, $name: &$Other) -> bool {
                self.len() == 1 && self[0].is_subset_of($name)
            }
        }}}

is_subset_if_length_1_and_first_element_is_subset! {
    T, T, atom;
    "A sequence is a subset of a particular atom whenever the sequence has length one, and the first element is a subset of that atom."}

is_subset_if_length_1_and_first_element_is_subset! {
    T, Anchor<T>, anchor;
    "A sequence is a subset of an anchor whenever the sequence has length one, and the first element is a subset of that atom."}

is_subset_if_length_1_and_first_element_is_subset! {
    T, Class<T>, class;
    "A sequence is a subset of an atom class whenever the sequence has length one, and the first element is a subset of that class."}

impl<T: Atom> set::IsSubsetOf<Sequence<T>> for Sequence<T> {
    /// A sequence `A` can be a subset of another sequence `B` *if* both have the
    /// same length and each element in `A` is a subset of the corresponding element
    /// in `B`:
    ///
    /// A ⊂ B ⇔ |A|=|B|=n ∧ Aᵢ ⊂ Bᵢ ∀ i∈[0,n]
    fn is_subset_of(&self, other: &Sequence<T>) -> bool {
        other.len() == self.len() &&
            self.iter().zip(other.iter()).all(|(u, v)| u.is_subset_of(v))
    }
}

impl<T: Atom> set::IsSubsetOf<Union<T>> for Sequence<T> {
    /// A sequence is a subset of a union whenever it is a subset of one of the
    /// union's members.
    fn is_subset_of(&self, union: &Union<T>) -> bool {
        union.iter().any(|m| self.is_subset_of(m))
    }
}

impl<T: Atom> set::IsSubsetOf<Repetition<T>> for Sequence<T> {
    /// A sequence is a subset of a repetition if the sequence's length is
    /// contained by the repetition's count and all members of the sequence are
    /// subsets of the repeated element.
    fn is_subset_of(&self, rep: &Repetition<T>) -> bool {
        rep.count().contains(self.len()) && self.iter().all(|elt| elt.is_subset_of(rep.element()))
    }
}

impl<T: Atom> set::IsSubsetOf<Element<T>> for Sequence<T> {
    fn is_subset_of(&self, elt: &Element<T>) -> bool {
        match elt {
            &Element::Tagged{ref element, ..} => self.is_subset_of(&**element),
            &Element::Sequence(ref s) => self.is_subset_of(s),
            &Element::Union(ref u) => self.is_subset_of(u),
            &Element::Repeat(ref r) => self.is_subset_of(r),

            &Element::Anchor(ref a) => self.is_subset_of(a),
            &Element::Atom(ref a) => self.is_subset_of(a),
            &Element::Class(ref c) => self.is_subset_of(c),
            &Element::Wildcard => self.len() == 1 && self[0].is_subset_of(elt),
        }
    }
}
        }
    }
}
<|endoftext|>


<commit_before>use cgmath;
use mint;

use cgmath::Rotation3;
use input::{Input, Key, KeyAxis};
use object::Object;

#[derive(Clone)]
struct Axes {
    pub forward: Option<KeyAxis>,
    pub strafing: Option<KeyAxis>,
    pub vertical: Option<KeyAxis>,
}

impl Default for Axes {
    fn default() -> Self {
        Axes {
            forward: Some(KeyAxis {
                pos: Key::W,
                neg: Key::S,
            }),
            strafing: Some(KeyAxis {
                pos: Key::D,
                neg: Key::A,
            }),
            vertical: None,
        }
    }
}

/// Controls for first person camera.
pub struct FirstPerson {
    object: Object,
    position: mint::Point3<f32>,
    yaw: f32,
    pitch: f32,
    move_speed: f32,
    look_speed: f32,
    axes: Axes,
    vertical_move: bool,
    vertical_look: bool,
}

/// Constructs custom [`FirstPerson`](struct.FirstPerson.html) controls.
pub struct Builder {
    object: Object,
    position: mint::Point3<f32>,
    yaw: f32,
    pitch: f32,
    move_speed: f32,
    look_speed: f32,
    axes: Axes,
    vertical_move: bool,
    vertical_look: bool,
}

impl Builder {
    /// Create new `Builder` with default parameters.
    pub fn new(object: &Object) -> Self {
        Builder {
            object: object.clone(),
            position: [0.0, 0.0, 0.0].into(),
            yaw: 0.0,
            pitch: 0.0,
            move_speed: 1.0,
            look_speed: 0.5,
            axes: Axes::default(),
            vertical_move: true,
            vertical_look: true,
        }
    }

    /// Set the initial yaw angle in radians. Default is 0.0.
    pub fn yaw(
        &mut self,
        yaw: f32,
    ) -> &mut Self {
        self.yaw = yaw;
        self
    }

    /// Set the initial pitch angle in radians.
    ///
    /// Defaults to 0.0.
    pub fn pitch(
        &mut self,
        pitch: f32,
    ) -> &mut Self {
        self.pitch = pitch;
        self
    }

    /// Set the initial position.
    ///
    /// Defaults to the world origin.
    pub fn position<P>(
        &mut self,
        position: P,
    ) -> &mut Self
    where
        P: Into<mint::Point3<f32>>,
    {
        self.position = position.into();
        self
    }

    /// Setup the movement speed in world units per second.
    ///
    /// Defaults to 1.0 world units per second.
    pub fn move_speed(
        &mut self,
        speed: f32,
    ) -> &mut Self {
        self.move_speed = speed;
        self
    }

    /// Setup mouse sensitivity.
    ///
    /// Defaults to 0.5
    pub fn look_speed(
        &mut self,
        speed: f32,
    ) -> &mut Self {
        self.look_speed = speed;
        self
    }

    /// Setup whether controlled object should move along `y` axis when looking down or up.
    ///
    /// Defaults to true.
    pub fn vertical_movement(
        &mut self,
        value: bool,
    ) -> &mut Self {
        self.vertical_move = value;
        self
    }

    /// Setup whether controlled object can adjust pitch using mouse.
    ///
    /// Defaults to true.
    pub fn vertical_look(
        &mut self,
        value: bool,
    ) -> &mut Self {
        self.vertical_look = value;
        self
    }

    /// Setup key axis for moving forward/backward.
    ///
    /// Defaults to `W` and `S` keys.
    pub fn axis_forward(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.forward = axis;
        self
    }

    /// Setup button for "strafing" left/right.
    ///
    /// Defaults to `A` and `D` keys.
    pub fn axis_strafing(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.strafing = axis;
        self
    }

    /// Setup button for moving up/down.
    ///
    /// Defaults to `None`.
    pub fn axis_vertical(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.vertical = axis;
        self
    }

    /// Finalize builder and create new `FirstPerson` controls.
    pub fn build(&mut self) -> FirstPerson {
        FirstPerson {
            object: self.object.clone(),
            position: self.position,
            yaw: self.yaw,
            pitch: self.pitch,
            move_speed: self.move_speed,
            look_speed: self.look_speed,
            axes: self.axes.clone(),
            vertical_move: self.vertical_move,
            vertical_look: self.vertical_look,
        }
    }
}

impl FirstPerson {
    /// Create a `Builder`.
    pub fn builder(object: &Object) -> Builder {
        Builder::new(object)
    }

    /// Create `FirstPerson` controls with default parameters.
    pub fn default(object: &Object) -> Self {
        Self::builder(object).build()
    }

    /// Updates the position, yaw, and pitch of the controlled object according to
    /// the last frame input.
    pub fn update(
        &mut self,
        input: &Input,
    ) {
        let dtime = input.delta_time();
        let dlook = dtime * self.look_speed;

        let mouse = input.mouse_delta_raw();

        self.yaw += dlook * mouse.x;
        if self.vertical_look {
            self.pitch += dlook * mouse.y;
        }

        self.axes
            .vertical
            .map(|a| if let Some(time) = a.timed(input) {
                self.position.y += self.move_speed * time;
            });

        self.axes
            .forward
            .map(|a| if let Some(time) = a.timed(input) {
                self.position.x += self.move_speed * time * self.yaw.sin();
                self.position.z -= self.move_speed * time * self.yaw.cos();
                if self.vertical_move {
                    self.position.y -= self.move_speed * time * self.pitch.sin();
                }
            });
        self.axes
            .strafing
            .map(|a| if let Some(time) = a.timed(input) {
                self.position.x += self.move_speed * time * self.yaw.cos();
                self.position.z += self.move_speed * time * self.yaw.sin();
            });

        let yrot = cgmath::Quaternion::from_angle_y(cgmath::Rad(-self.yaw));
        let xrot = cgmath::Quaternion::from_angle_x(cgmath::Rad(-self.pitch));
        self.object.set_transform(self.position, yrot * xrot, 1.0);
    }
}
<commit_msg>Add setters to FirstPerson controls<commit_after>use cgmath;
use mint;

use cgmath::Rotation3;
use input::{Input, Key, KeyAxis};
use object::Object;

#[derive(Clone, Debug)]
struct Axes {
    pub forward: Option<KeyAxis>,
    pub strafing: Option<KeyAxis>,
    pub vertical: Option<KeyAxis>,
}

impl Default for Axes {
    fn default() -> Self {
        Axes {
            forward: Some(KeyAxis {
                pos: Key::W,
                neg: Key::S,
            }),
            strafing: Some(KeyAxis {
                pos: Key::D,
                neg: Key::A,
            }),
            vertical: None,
        }
    }
}

/// Controls for first person camera.
pub struct FirstPerson {
    object: Object,
    position: mint::Point3<f32>,
    yaw: f32,
    pitch: f32,
    move_speed: f32,
    look_speed: f32,
    axes: Axes,
    vertical_move: bool,
    vertical_look: bool,
}

/// Constructs custom [`FirstPerson`](struct.FirstPerson.html) controls.
pub struct Builder {
    object: Object,
    position: mint::Point3<f32>,
    yaw: f32,
    pitch: f32,
    move_speed: f32,
    look_speed: f32,
    axes: Axes,
    vertical_move: bool,
    vertical_look: bool,
}

impl Builder {
    /// Create new `Builder` with default parameters.
    pub fn new(object: &Object) -> Self {
        Builder {
            object: object.clone(),
            position: [0.0, 0.0, 0.0].into(),
            yaw: 0.0,
            pitch: 0.0,
            move_speed: 1.0,
            look_speed: 0.5,
            axes: Axes::default(),
            vertical_move: true,
            vertical_look: true,
        }
    }

    /// Set the initial yaw angle in radians.
    ///
    /// Default is 0.0.
    pub fn yaw(
        &mut self,
        yaw: f32,
    ) -> &mut Self {
        self.yaw = yaw;
        self
    }

    /// Set the initial pitch angle in radians.
    ///
    /// Defaults to 0.0.
    pub fn pitch(
        &mut self,
        pitch: f32,
    ) -> &mut Self {
        self.pitch = pitch;
        self
    }

    /// Set the initial position.
    ///
    /// Defaults to the world origin.
    pub fn position<P>(
        &mut self,
        position: P,
    ) -> &mut Self
    where
        P: Into<mint::Point3<f32>>,
    {
        self.position = position.into();
        self
    }

    /// Setup the movement speed in world units per second.
    ///
    /// Defaults to 1.0 world units per second.
    pub fn move_speed(
        &mut self,
        speed: f32,
    ) -> &mut Self {
        self.move_speed = speed;
        self
    }

    /// Setup mouse sensitivity.
    ///
    /// Defaults to 0.5
    pub fn look_speed(
        &mut self,
        speed: f32,
    ) -> &mut Self {
        self.look_speed = speed;
        self
    }

    /// Setup whether controlled object should move along `y` axis when looking
    /// down or up.
    ///
    /// Defaults to true.
    pub fn vertical_movement(
        &mut self,
        value: bool,
    ) -> &mut Self {
        self.vertical_move = value;
        self
    }

    /// Setup whether controlled object can adjust pitch using mouse.
    ///
    /// Defaults to true.
    pub fn vertical_look(
        &mut self,
        value: bool,
    ) -> &mut Self {
        self.vertical_look = value;
        self
    }

    /// Setup key axis for moving forward/backward.
    ///
    /// Defaults to `W` and `S` keys.
    pub fn axis_forward(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.forward = axis;
        self
    }

    /// Setup button for "strafing" left/right.
    ///
    /// Defaults to `A` and `D` keys.
    pub fn axis_strafing(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.strafing = axis;
        self
    }

    /// Setup button for moving up/down.
    ///
    /// Defaults to `None`.
    pub fn axis_vertical(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.vertical = axis;
        self
    }

    /// Finalize builder and create new `FirstPerson` controls.
    pub fn build(&mut self) -> FirstPerson {
        FirstPerson {
            object: self.object.clone(),
            position: self.position,
            yaw: self.yaw,
            pitch: self.pitch,
            move_speed: self.move_speed,
            look_speed: self.look_speed,
            axes: self.axes.clone(),
            vertical_move: self.vertical_move,
            vertical_look: self.vertical_look,
        }
    }
}

impl FirstPerson {
    /// Create a `Builder`.
    pub fn builder(object: &Object) -> Builder {
        Builder::new(object)
    }

    /// Create `FirstPerson` controls with default parameters.
    pub fn default(object: &Object) -> Self {
        Self::builder(object).build()
    }

    /// Sets the yaw angle in radians.
    pub fn set_yaw(
        &mut self,
        yaw: f32,
    ) -> &mut Self {
        self.yaw = yaw;
        self
    }

    /// Sets the pitch angle in radians.
    pub fn set_pitch(
        &mut self,
        pitch: f32,
    ) -> &mut Self {
        self.pitch = pitch;
        self
    }

    /// Sets the object position.
    pub fn set_position<P>(
        &mut self,
        position: P,
    ) -> &mut Self
    where
        P: Into<mint::Point3<f32>>,
    {
        self.position = position.into();
        self
    }

    /// Sets the movement speed in world units per second.
    pub fn set_move_speed(
        &mut self,
        speed: f32,
    ) -> &mut Self {
        self.move_speed = speed;
        self
    }

    /// Sets the mouse sensitivity.
    pub fn set_look_speed(
        &mut self,
        speed: f32,
    ) -> &mut Self {
        self.look_speed = speed;
        self
    }

    /// Specifies whether controlled object should move along `y` axis when looking
    /// down or up.
    pub fn set_vertical_movement(
        &mut self,
        value: bool,
    ) -> &mut Self {
        self.vertical_move = value;
        self
    }

    /// Specifies whether controlled object can adjust pitch using mouse.
    pub fn set_vertical_look(
        &mut self,
        value: bool,
    ) -> &mut Self {
        self.vertical_look = value;
        self
    }

    /// Sets the key axis for moving forward/backward.
    pub fn set_axis_forward(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.forward = axis;
        self
    }

    /// Sets the button for "strafing" left/right.
    pub fn set_axis_strafing(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.strafing = axis;
        self
    }

    /// Setup button for moving up/down.
    ///
    /// Defaults to `None`.
    pub fn set_axis_vertical(
        &mut self,
        axis: Option<KeyAxis>,
    ) -> &mut Self {
        self.axes.vertical = axis;
        self
    }

    /// Updates the position, yaw, and pitch of the controlled object according to
    /// the last frame input.
    pub fn update(
        &mut self,
        input: &Input,
    ) {
        let dtime = input.delta_time();
        let dlook = dtime * self.look_speed;

        let mouse = input.mouse_delta_raw();

        self.yaw += dlook * mouse.x;
        if self.vertical_look {
            self.pitch += dlook * mouse.y;
        }

        self.axes
            .vertical
            .map(|a| if let Some(time) = a.timed(input) {
                self.position.y += self.move_speed * time;
            });

        self.axes
            .forward
            .map(|a| if let Some(time) = a.timed(input) {
                self.position.x += self.move_speed * time * self.yaw.sin();
                self.position.z -= self.move_speed * time * self.yaw.cos();
                if self.vertical_move {
                    self.position.y -= self.move_speed * time * self.pitch.sin();
                }
            });
        self.axes
            .strafing
            .map(|a| if let Some(time) = a.timed(input) {
                self.position.x += self.move_speed * time * self.yaw.cos();
                self.position.z += self.move_speed * time * self.yaw.sin();
            });

        let yrot = cgmath::Quaternion::from_angle_y(cgmath::Rad(-self.yaw));
        let xrot = cgmath::Quaternion::from_angle_x(cgmath::Rad(-self.pitch));
        self.object.set_transform(self.position, yrot * xrot, 1.0);
    }
}
<|endoftext|>