<commit_before>use std::{cell::RefCell, collections::BTreeMap};

use dces::prelude::{Entity, EntityComponentManager};

use super::{MessageBox, WidgetContainer};

use crate::{
    application::{Global, Tree},
    event::{Event, EventQueue, EventStrategy},
    properties::Selector,
    theme::Theme,
};

/// The `Context` is provides access for the states to objects they could work with.
pub struct Context<'a> {
    ecm: &'a mut EntityComponentManager,
    tree: &'a Tree,
    event_queue: &'a RefCell<EventQueue>,
    messages: Option<&'a RefCell<BTreeMap<Entity, Vec<MessageBox>>>>,
    pub entity: Entity,
    pub theme: &'a Theme,
}

impl<'a> Context<'a> {
    /// Creates a new container.
    pub fn new(
        entity: Entity,
        ecm: &'a mut EntityComponentManager,
        tree: &'a Tree,
        event_queue: &'a RefCell<EventQueue>,
        theme: &'a Theme,
        messages: Option<&'a RefCell<BTreeMap<Entity, Vec<MessageBox>>>>,
    ) -> Self {
        Context {
            entity,
            ecm,
            tree,
            event_queue,
            messages,
            theme,
        }
    }

    /// Returns the widget of the current state context.
    pub fn widget(&mut self) -> WidgetContainer<'_> {
        WidgetContainer::new(self.entity, &mut self.ecm)
    }

    /// Returns a child of the widget of the current state referenced by css `id`.
    /// If the no id is defined None will returned.
    pub fn child_by_id<S: Into<String>>(&mut self, id: S) -> Option<WidgetContainer<'_>> {
        let id = id.into();
        for child in self.tree.start_node(self.entity).into_iter() {
            if let Ok(selector) = self.ecm.borrow_component::<Selector>(child) {
                if let Some(child_id) = &selector.0.id {
                    if child_id.eq(&id) {
                        return Some(WidgetContainer::new(child, &mut self.ecm));
                    }
                }
            }
        }

        None
    }

    /// Returns the child of the current widget.
    /// If the index is out of the children index bounds or the widget has no children None will be returned.
    pub fn widget_from_child_index(&mut self, index: usize) -> Option<WidgetContainer<'_>> {
        if index >= self.tree.children[&self.entity].len() {
            return None;
        }

        Some(WidgetContainer::new(
            self.tree.children[&self.entity][index],
            &mut self.ecm,
        ))
    }

    /// Returns the parent of the current widget.
    /// If the current widget is the root None will be returned.
    pub fn parent_widget(&mut self) -> Option<WidgetContainer<'_>> {
        if self.tree.parent[&self.entity] == None {
            return None;
        }

        Some(WidgetContainer::new(
            self.tree.parent[&self.entity].unwrap(),
            &mut self.ecm,
        ))
    }

    /// Sends a message to the widget with the given id over the message channel.
    pub fn send_message(&mut self, target_widget: &str, message: impl Into<MessageBox>) {
        if let Some(messages) = &self.messages {
            let mut entity = None;
            if let Ok(global) = self.ecm.borrow_component::<Global>(0) {
                if let Some(en) = global.id_map.get(target_widget) {
                    entity = Some(*en);
                }
            }

            if let Some(entity) = entity {
                if !messages.borrow().contains_key(&entity) {
                    messages.borrow_mut().insert(entity, vec![]);
                }
                messages
                    .borrow_mut()
                    .get_mut(&entity)
                    .unwrap()
                    .push(message.into());
            } else {
                println!(
                    "Context send_message: widget id {} not found.",
                    target_widget
                );
            }
        }
    }

    /// Pushes an event to the event queue with the given `strategy`.
    pub fn push_event_strategy<E: Event>(&mut self, event: E, strategy: EventStrategy) {
        self.event_queue
            .borrow_mut()
            .register_event_with_strategy(event, strategy, self.entity);
    }

    /// Pushes an event to the event queue.
    pub fn push_event<E: Event>(&self, event: E) {
        self.event_queue
            .borrow_mut()
            .register_event(event, self.entity);
    }
}
<commit_msg>Add access to window widget.<commit_after>use std::{cell::RefCell, collections::BTreeMap};

use dces::prelude::{Entity, EntityComponentManager};

use super::{MessageBox, WidgetContainer};

use crate::{
    application::{Global, Tree},
    event::{Event, EventQueue, EventStrategy},
    properties::Selector,
    theme::Theme,
};

/// The `Context` is provides access for the states to objects they could work with.
pub struct Context<'a> {
    ecm: &'a mut EntityComponentManager,
    tree: &'a Tree,
    event_queue: &'a RefCell<EventQueue>,
    messages: Option<&'a RefCell<BTreeMap<Entity, Vec<MessageBox>>>>,
    pub entity: Entity,
    pub theme: &'a Theme,
}

impl<'a> Context<'a> {
    /// Creates a new container.
    pub fn new(
        entity: Entity,
        ecm: &'a mut EntityComponentManager,
        tree: &'a Tree,
        event_queue: &'a RefCell<EventQueue>,
        theme: &'a Theme,
        messages: Option<&'a RefCell<BTreeMap<Entity, Vec<MessageBox>>>>,
    ) -> Self {
        Context {
            entity,
            ecm,
            tree,
            event_queue,
            messages,
            theme,
        }
    }

    /// Returns the widget of the current state context.
    pub fn widget(&mut self) -> WidgetContainer<'_> {
        WidgetContainer::new(self.entity, &mut self.ecm)
    }

    /// Returns the window widget.
    pub fn window(&mut self) -> WidgetContainer<'_> {
        WidgetContainer::new(0, &mut self.ecm)
    }

    /// Returns a child of the widget of the current state referenced by css `id`.
    /// If the no id is defined None will returned.
    pub fn child_by_id<S: Into<String>>(&mut self, id: S) -> Option<WidgetContainer<'_>> {
        let id = id.into();
        for child in self.tree.start_node(self.entity).into_iter() {
            if let Ok(selector) = self.ecm.borrow_component::<Selector>(child) {
                if let Some(child_id) = &selector.0.id {
                    if child_id.eq(&id) {
                        return Some(WidgetContainer::new(child, &mut self.ecm));
                    }
                }
            }
        }

        None
    }

    /// Returns the child of the current widget.
    /// If the index is out of the children index bounds or the widget has no children None will be returned.
    pub fn widget_from_child_index(&mut self, index: usize) -> Option<WidgetContainer<'_>> {
        if index >= self.tree.children[&self.entity].len() {
            return None;
        }

        Some(WidgetContainer::new(
            self.tree.children[&self.entity][index],
            &mut self.ecm,
        ))
    }

    /// Returns the parent of the current widget.
    /// If the current widget is the root None will be returned.
    pub fn parent_widget(&mut self) -> Option<WidgetContainer<'_>> {
        if self.tree.parent[&self.entity] == None {
            return None;
        }

        Some(WidgetContainer::new(
            self.tree.parent[&self.entity].unwrap(),
            &mut self.ecm,
        ))
    }

    /// Sends a message to the widget with the given id over the message channel.
    pub fn send_message(&mut self, target_widget: &str, message: impl Into<MessageBox>) {
        if let Some(messages) = &self.messages {
            let mut entity = None;
            if let Ok(global) = self.ecm.borrow_component::<Global>(0) {
                if let Some(en) = global.id_map.get(target_widget) {
                    entity = Some(*en);
                }
            }

            if let Some(entity) = entity {
                if !messages.borrow().contains_key(&entity) {
                    messages.borrow_mut().insert(entity, vec![]);
                }
                messages
                    .borrow_mut()
                    .get_mut(&entity)
                    .unwrap()
                    .push(message.into());
            } else {
                println!(
                    "Context send_message: widget id {} not found.",
                    target_widget
                );
            }
        }
    }

    /// Pushes an event to the event queue with the given `strategy`.
    pub fn push_event_strategy<E: Event>(&mut self, event: E, strategy: EventStrategy) {
        self.event_queue
            .borrow_mut()
            .register_event_with_strategy(event, strategy, self.entity);
    }

    /// Pushes an event to the event queue.
    pub fn push_event<E: Event>(&self, event: E) {
        self.event_queue
            .borrow_mut()
            .register_event(event, self.entity);
    }
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]
#![feature(plugin)]

#![unstable]

#[plugin]
#[no_link]
extern crate compile_msg;

extern crate libc;

use std::ffi::{CString};
use std::sync::mpsc::{Sender};
use std::sync::Mutex;
use std::thread::Thread;

#[doc(hidden)]
pub mod ffi;

/// This static variable  will store the android_app* on creation, and set it back to 0 at
///  destruction.
/// Apart from this, the static is never written, so there is no risk of race condition.
static mut ANDROID_APP: *mut ffi::android_app = 0 as *mut ffi::android_app;

/// This is the structure that serves as user data in the android_app*
#[doc(hidden)]
struct Context {
    senders: Mutex<Vec<Sender<Event>>>,
}

/// An event triggered by the Android environment.
pub enum Event {
    EventUp,
    EventDown,
    EventMove(i32, i32),
}

impl Copy for Event {}

#[cfg(not(target_os = "android"))]
compile_note!("You are not compiling for Android");

#[macro_export]
macro_rules! android_start(
    ($main: ident) => (
        pub mod __android_start {
            extern crate android_glue;

            // this function is here because we are sure that it will be included by the linker
            // so we call app_dummy in it, in order to be sure that the native glue will be included
            #[start]
            pub fn start(_: int, _: *const *const u8) -> int {
                unsafe { android_glue::ffi::app_dummy() };
                1
            }

            #[no_mangle]
            #[inline(never)]
            #[allow(non_snake_case)]
            pub extern "C" fn android_main(app: *mut ()) {
                android_glue::android_main2(app, move|| super::$main());
            }
        }
    )
);

/// This is the function that must be called by `android_main`
#[doc(hidden)]
pub fn android_main2<F>(app: *mut (), main_function: F)
    where F: FnOnce(), F: Send
{
    use std::{mem, ptr};

    write_log("Entering android_main");

    unsafe { ANDROID_APP = std::mem::transmute(app) };
    let app: &mut ffi::android_app = unsafe { std::mem::transmute(app) };

    // creating the context that will be passed to the callback
    let context = Context { senders: Mutex::new(Vec::new()) };
    app.onAppCmd = commands_callback;
    app.onInputEvent = inputs_callback;
    app.userData = unsafe { std::mem::transmute(&context) };

    // executing the main function in parallel
    let g = Thread::spawn(move|| {
        std::old_io::stdio::set_stdout(box std::old_io::LineBufferedWriter::new(ToLogWriter));
        std::old_io::stdio::set_stderr(box std::old_io::LineBufferedWriter::new(ToLogWriter));
        main_function()
    });

    // polling for events forever
    // note that this must be done in the same thread as android_main because ALooper are
    //  thread-local
    unsafe {
        loop {
            let mut events = mem::uninitialized();
            let mut source = mem::uninitialized();

            // passing -1 means that we are blocking
            let ident = ffi::ALooper_pollAll(-1, ptr::null_mut(), &mut events,
                &mut source);

            // processing the event
            if !source.is_null() {
                let source: *mut ffi::android_poll_source = mem::transmute(source);
                ((*source).process)(ANDROID_APP, source);
            }
        }
    }

    // terminating the application
    unsafe { ANDROID_APP = 0 as *mut ffi::android_app };
}

/// Writer that will redirect what is written to it to the logs.
struct ToLogWriter;

impl Writer for ToLogWriter {
    fn write_all(&mut self, buf: &[u8]) -> std::old_io::IoResult<()> {
        let message = CString::from_slice(buf).as_slice_with_nul().as_ptr();
        let tag = b"RustAndroidGlueStdouterr";
        let tag = CString::from_slice(tag).as_slice_with_nul().as_ptr();
        unsafe { ffi::__android_log_write(3, tag, message) };
        Ok(())
    }
}

/// The callback for inputs.
pub extern fn inputs_callback(_: *mut ffi::android_app, event: *const ffi::AInputEvent)
    -> libc::int32_t
{
    fn send_event(event: Event) {
        let senders = get_context().senders.lock().ok().unwrap();
        for sender in senders.iter() {
            sender.send(event);
        }
    }
    fn get_xy(event: *const ffi::AInputEvent) -> (i32, i32) {
        let x = unsafe { ffi::AMotionEvent_getX(event, 0) };
        let y = unsafe { ffi::AMotionEvent_getY(event, 0) };
        (x as i32, y as i32)
    }
    let action = unsafe { ffi::AMotionEvent_getAction(event) };
    let action_code = action & ffi::AMOTION_EVENT_ACTION_MASK;
    match action_code {
        ffi::AMOTION_EVENT_ACTION_UP
            | ffi::AMOTION_EVENT_ACTION_OUTSIDE
            | ffi::AMOTION_EVENT_ACTION_CANCEL
            | ffi::AMOTION_EVENT_ACTION_POINTER_UP =>
        {
            send_event(Event::EventUp);
        },
        ffi::AMOTION_EVENT_ACTION_DOWN
            | ffi::AMOTION_EVENT_ACTION_POINTER_DOWN =>
        {
            let (x, y) = get_xy(event);
            send_event(Event::EventMove(x, y));
            send_event(Event::EventDown);
        },
        _ => {
            let (x, y) = get_xy(event);
            send_event(Event::EventMove(x, y));
        },
    }
    0
}

/// The callback for commands.
#[doc(hidden)]
pub extern fn commands_callback(_: *mut ffi::android_app, command: libc::int32_t) {
    let context = get_context();

    match command {
        ffi::APP_CMD_INIT_WINDOW => {

        },

        ffi::APP_CMD_SAVE_STATE => {

        },

        ffi::APP_CMD_TERM_WINDOW => {

        },

        ffi::APP_CMD_GAINED_FOCUS => {

        },

        ffi::APP_CMD_LOST_FOCUS => {

        },

        _ => ()
    }
}

/// Returns the current Context.
fn get_context() -> &'static Context {
    let context = unsafe { (*ANDROID_APP).userData };
    unsafe { std::mem::transmute(context) }
}

/// Adds a sender where events will be sent to.
pub fn add_sender(sender: Sender<Event>) {
    get_context().senders.lock().ok().unwrap().push(sender);
}

/// Returns a handle to the native window.
pub unsafe fn get_native_window() -> ffi::NativeWindowType {
    if ANDROID_APP.is_null() {
        panic!("The application was not initialized from android_main");
    }

    loop {
        let value = (*ANDROID_APP).window;
        if !value.is_null() {
            return value;
        }

        // spin-locking
        std::old_io::timer::sleep(std::time::Duration::milliseconds(10));
    }
}

///
pub fn write_log(message: &str) {
    let message = message.as_bytes();
    let message = CString::from_slice(message).as_slice_with_nul().as_ptr();
    let tag = b"RustAndroidGlueStdouterr";
    let tag = CString::from_slice(tag).as_slice_with_nul().as_ptr();
    unsafe { ffi::__android_log_write(3, tag, message) };
}

pub enum AssetError {
    AssetMissing,
    EmptyBuffer,
}

pub fn load_asset(filename: &str) -> Result<Vec<u8>, AssetError> {
    struct AssetCloser {
        asset: *const ffi::Asset,
    }

    impl Drop for AssetCloser {
        fn drop(&mut self) {
            unsafe {
                ffi::AAsset_close(self.asset)
            };
        }
    }

    unsafe fn get_asset_manager() -> *const ffi::AAssetManager {
        let app = &*ANDROID_APP;
        let activity = &*app.activity;
        activity.assetManager
    }

    let filename_c_str = CString::from_slice(filename.as_bytes())
        .as_slice_with_nul().as_ptr();
    let asset = unsafe {
        ffi::AAssetManager_open(
            get_asset_manager(), filename_c_str, ffi::MODE_STREAMING)
    };
    if asset.is_null() {
        return Err(AssetError::AssetMissing);
    }
    let _asset_closer = AssetCloser{asset: asset};
    let len = unsafe {
        ffi::AAsset_getLength(asset)
    };
    let buff = unsafe {
        ffi::AAsset_getBuffer(asset)
    };
    if buff.is_null() {
        return Err(AssetError::EmptyBuffer);
    }
    let vec = unsafe {
        Vec::from_raw_buf(buff as *const u8, len as usize)
    };
    Ok(vec)
}
<commit_msg>Fixed CString usage<commit_after>#![feature(box_syntax)]
#![feature(plugin)]

#![unstable]

#[plugin]
#[no_link]
extern crate compile_msg;

extern crate libc;

use std::ffi::{CString};
use std::sync::mpsc::{Sender};
use std::sync::Mutex;
use std::thread::Thread;

#[doc(hidden)]
pub mod ffi;

/// This static variable  will store the android_app* on creation, and set it back to 0 at
///  destruction.
/// Apart from this, the static is never written, so there is no risk of race condition.
static mut ANDROID_APP: *mut ffi::android_app = 0 as *mut ffi::android_app;

/// This is the structure that serves as user data in the android_app*
#[doc(hidden)]
struct Context {
    senders: Mutex<Vec<Sender<Event>>>,
}

/// An event triggered by the Android environment.
pub enum Event {
    EventUp,
    EventDown,
    EventMove(i32, i32),
}

impl Copy for Event {}

#[cfg(not(target_os = "android"))]
compile_note!("You are not compiling for Android");

#[macro_export]
macro_rules! android_start(
    ($main: ident) => (
        pub mod __android_start {
            extern crate android_glue;

            // this function is here because we are sure that it will be included by the linker
            // so we call app_dummy in it, in order to be sure that the native glue will be included
            #[start]
            pub fn start(_: int, _: *const *const u8) -> int {
                unsafe { android_glue::ffi::app_dummy() };
                1
            }

            #[no_mangle]
            #[inline(never)]
            #[allow(non_snake_case)]
            pub extern "C" fn android_main(app: *mut ()) {
                android_glue::android_main2(app, move|| super::$main());
            }
        }
    )
);

/// This is the function that must be called by `android_main`
#[doc(hidden)]
pub fn android_main2<F>(app: *mut (), main_function: F)
    where F: FnOnce(), F: Send
{
    use std::{mem, ptr};

    write_log("Entering android_main");

    unsafe { ANDROID_APP = std::mem::transmute(app) };
    let app: &mut ffi::android_app = unsafe { std::mem::transmute(app) };

    // creating the context that will be passed to the callback
    let context = Context { senders: Mutex::new(Vec::new()) };
    app.onAppCmd = commands_callback;
    app.onInputEvent = inputs_callback;
    app.userData = unsafe { std::mem::transmute(&context) };

    // executing the main function in parallel
    let g = Thread::spawn(move|| {
        std::old_io::stdio::set_stdout(box std::old_io::LineBufferedWriter::new(ToLogWriter));
        std::old_io::stdio::set_stderr(box std::old_io::LineBufferedWriter::new(ToLogWriter));
        main_function()
    });

    // polling for events forever
    // note that this must be done in the same thread as android_main because ALooper are
    //  thread-local
    unsafe {
        loop {
            let mut events = mem::uninitialized();
            let mut source = mem::uninitialized();

            // passing -1 means that we are blocking
            let ident = ffi::ALooper_pollAll(-1, ptr::null_mut(), &mut events,
                &mut source);

            // processing the event
            if !source.is_null() {
                let source: *mut ffi::android_poll_source = mem::transmute(source);
                ((*source).process)(ANDROID_APP, source);
            }
        }
    }

    // terminating the application
    unsafe { ANDROID_APP = 0 as *mut ffi::android_app };
}

/// Writer that will redirect what is written to it to the logs.
struct ToLogWriter;

impl Writer for ToLogWriter {
    fn write_all(&mut self, buf: &[u8]) -> std::old_io::IoResult<()> {
        let message = CString::from_slice(buf);
        let message = message.as_slice_with_nul().as_ptr();
        let tag = b"RustAndroidGlueStdouterr";
        let tag = CString::from_slice(tag);
        let tag = tag.as_slice_with_nul().as_ptr();
        unsafe { ffi::__android_log_write(3, tag, message) };
        Ok(())
    }
}

/// The callback for inputs.
pub extern fn inputs_callback(_: *mut ffi::android_app, event: *const ffi::AInputEvent)
    -> libc::int32_t
{
    fn send_event(event: Event) {
        let senders = get_context().senders.lock().ok().unwrap();
        for sender in senders.iter() {
            sender.send(event);
        }
    }
    fn get_xy(event: *const ffi::AInputEvent) -> (i32, i32) {
        let x = unsafe { ffi::AMotionEvent_getX(event, 0) };
        let y = unsafe { ffi::AMotionEvent_getY(event, 0) };
        (x as i32, y as i32)
    }
    let action = unsafe { ffi::AMotionEvent_getAction(event) };
    let action_code = action & ffi::AMOTION_EVENT_ACTION_MASK;
    match action_code {
        ffi::AMOTION_EVENT_ACTION_UP
            | ffi::AMOTION_EVENT_ACTION_OUTSIDE
            | ffi::AMOTION_EVENT_ACTION_CANCEL
            | ffi::AMOTION_EVENT_ACTION_POINTER_UP =>
        {
            send_event(Event::EventUp);
        },
        ffi::AMOTION_EVENT_ACTION_DOWN
            | ffi::AMOTION_EVENT_ACTION_POINTER_DOWN =>
        {
            let (x, y) = get_xy(event);
            send_event(Event::EventMove(x, y));
            send_event(Event::EventDown);
        },
        _ => {
            let (x, y) = get_xy(event);
            send_event(Event::EventMove(x, y));
        },
    }
    0
}

/// The callback for commands.
#[doc(hidden)]
pub extern fn commands_callback(_: *mut ffi::android_app, command: libc::int32_t) {
    let context = get_context();

    match command {
        ffi::APP_CMD_INIT_WINDOW => {

        },

        ffi::APP_CMD_SAVE_STATE => {

        },

        ffi::APP_CMD_TERM_WINDOW => {

        },

        ffi::APP_CMD_GAINED_FOCUS => {

        },

        ffi::APP_CMD_LOST_FOCUS => {

        },

        _ => ()
    }
}

/// Returns the current Context.
fn get_context() -> &'static Context {
    let context = unsafe { (*ANDROID_APP).userData };
    unsafe { std::mem::transmute(context) }
}

/// Adds a sender where events will be sent to.
pub fn add_sender(sender: Sender<Event>) {
    get_context().senders.lock().ok().unwrap().push(sender);
}

/// Returns a handle to the native window.
pub unsafe fn get_native_window() -> ffi::NativeWindowType {
    if ANDROID_APP.is_null() {
        panic!("The application was not initialized from android_main");
    }

    loop {
        let value = (*ANDROID_APP).window;
        if !value.is_null() {
            return value;
        }

        // spin-locking
        std::old_io::timer::sleep(std::time::Duration::milliseconds(10));
    }
}

///
pub fn write_log(message: &str) {
    let message = CString::from_slice(message.as_bytes());
    let message = message.as_slice_with_nul().as_ptr();
    let tag = b"RustAndroidGlueStdouterr";
    let tag = CString::from_slice(tag);
    let tag = tag.as_slice_with_nul().as_ptr();
    unsafe { ffi::__android_log_write(3, tag, message) };
}

pub enum AssetError {
    AssetMissing,
    EmptyBuffer,
}

pub fn load_asset(filename: &str) -> Result<Vec<u8>, AssetError> {
    struct AssetCloser {
        asset: *const ffi::Asset,
    }

    impl Drop for AssetCloser {
        fn drop(&mut self) {
            unsafe {
                ffi::AAsset_close(self.asset)
            };
        }
    }

    unsafe fn get_asset_manager() -> *const ffi::AAssetManager {
        let app = &*ANDROID_APP;
        let activity = &*app.activity;
        activity.assetManager
    }

    let filename_c_str = CString::from_slice(filename.as_bytes());
    let filename_c_str = filename_c_str.as_slice_with_nul().as_ptr();
    let asset = unsafe {
        ffi::AAssetManager_open(
            get_asset_manager(), filename_c_str, ffi::MODE_STREAMING)
    };
    if asset.is_null() {
        return Err(AssetError::AssetMissing);
    }
    let _asset_closer = AssetCloser{asset: asset};
    let len = unsafe {
        ffi::AAsset_getLength(asset)
    };
    let buff = unsafe {
        ffi::AAsset_getBuffer(asset)
    };
    if buff.is_null() {
        return Err(AssetError::EmptyBuffer);
    }
    let vec = unsafe {
        Vec::from_raw_buf(buff as *const u8, len as usize)
    };
    Ok(vec)
}
<|endoftext|>


<commit_before><commit_msg>support "tun_device_fd_from_path" in configure (#856)<commit_after><|endoftext|>


<commit_before>//! Parallel testing.
//!
//! mcuboot simulator is strictly single threaded, as there is a lock around running the C startup
//! code, because it contains numerous global variables.
//!
//! To help speed up testing, the Workflow configuration defines all of the configurations that can
//! be run in parallel.  Fortunately, cargo works well this way, and these can be run by simply
//! using subprocess for each particular thread.
//!
//! For now, we assume all of the features are listed under
//! jobs->environment->strategy->matric->features

use chrono::Local;
use log::{debug, error, warn};
use std::{
    collections::HashSet,
    fs::{self, OpenOptions},
    io::{ErrorKind, stdout, Write},
    process::{Command, Output},
    result,
    sync::{
        Arc,
        Mutex,
    },
    thread,
    time::Duration,
};
use std_semaphore::Semaphore;
use yaml_rust::{
    Yaml,
    YamlLoader,
};

type Result<T> = result::Result<T, failure::Error>;

fn main() -> Result<()> {
    env_logger::init();

    let workflow_text = fs::read_to_string("../.github/workflows/sim.yaml")?;
    let workflow = YamlLoader::load_from_str(&workflow_text)?;

    let ncpus = num_cpus::get();
    let limiter = Arc::new(Semaphore::new(ncpus as isize));

    let matrix = Matrix::from_yaml(&workflow)?;

    let mut children = vec![];
    let state = State::new(matrix.envs.len());
    let st2 = state.clone();
    let _status = thread::spawn(move || {
        loop {
            thread::sleep(Duration::new(15, 0));
            st2.lock().unwrap().status();
        }
    });
    for env in matrix.envs {
        let state = state.clone();
        let limiter = limiter.clone();

        let child = thread::spawn(move || {
            let _run = limiter.access();
            state.lock().unwrap().start(&env);
            let out = env.run();
            state.lock().unwrap().done(&env, out);
        });
        children.push(child);
    }

    for child in children {
        child.join().unwrap();
    }

    println!("");

    Ok(())
}

/// State, for printing status.
struct State {
    running: HashSet<String>,
    done: HashSet<String>,
    total: usize,
}

impl State {
    fn new(total: usize) -> Arc<Mutex<State>> {
        Arc::new(Mutex::new(State {
            running: HashSet::new(),
            done: HashSet::new(),
            total: total,
        }))
    }

    fn start(&mut self, fs: &FeatureSet) {
        let key = fs.textual();
        if self.running.contains(&key) || self.done.contains(&key) {
            warn!("Duplicate: {:?}", key);
        }
        debug!("Starting: {} ({} running)", key, self.running.len() + 1);
        self.running.insert(key);
        self.status();
    }

    fn done(&mut self, fs: &FeatureSet, output: Result<Option<Output>>) {
        let key = fs.textual();
        self.running.remove(&key);
        self.done.insert(key.clone());
        match output {
            Ok(None) => {
                // println!("Success {} ({} running)", key, self.running.len());
            }
            Ok(Some(output)) => {
                // Write the output into a file.
                let mut count = 1;
                let (mut fd, logname) = loop {
                    let name = format!("./failure-{:04}.log", count);
                    count += 1;
                    match OpenOptions::new()
                        .create_new(true)
                        .write(true)
                        .open(&name)
                    {
                        Ok(file) => break (file, name),
                        Err(ref err) if err.kind() == ErrorKind::AlreadyExists => continue,
                        Err(err) => {
                            error!("Unable to write log file to current directory: {:?}", err);
                            return;
                        }
                    }
                };
                writeln!(&mut fd, "Test failure {}", key).unwrap();
                writeln!(&mut fd, "time: {}", Local::now().to_rfc3339()).unwrap();
                writeln!(&mut fd, "----------------------------------------").unwrap();
                writeln!(&mut fd, "stdout:").unwrap();
                fd.write_all(&output.stdout).unwrap();
                writeln!(&mut fd, "----------------------------------------").unwrap();
                writeln!(&mut fd, "\nstderr:").unwrap();
                fd.write_all(&output.stderr).unwrap();
                error!("Failure {} log:{:?} ({} running)", key, logname,
                    self.running.len());
            }
            Err(err) => {
                error!("Unable to run test {:?} ({:?})", key, err);
            }
        }
        self.status();
    }

    fn status(&self) {
        let running = self.running.len();
        let done = self.done.len();
        print!(" {} running ({}/{}/{} done)\r", running, done, running + done, self.total);
        stdout().flush().unwrap();
    }
}

/// The extracted configurations from the workflow config
#[derive(Debug)]
struct Matrix {
    envs: Vec<FeatureSet>,
}

#[derive(Debug, Eq, Hash, PartialEq)]
struct FeatureSet {
    // The environment variable to set.
    env: String,
    // The successive values to set it to.
    values: Vec<String>,
}

impl Matrix {
    fn from_yaml(yaml: &[Yaml]) -> Result<Matrix> {
        let mut envs = vec![];

        let mut all_tests = HashSet::new();

        for y in yaml {
            let m = match lookup_matrix(y) {
                Some (m) => m,
                None => continue,
            };
            for elt in m {
                let elt = match elt.as_str() {
                    None => {
                        warn!("Unexpected yaml: {:?}", elt);
                        continue;
                    }
                    Some(e) => e,
                };
                let fset = match FeatureSet::decode(elt) {
                    Ok(fset) => fset,
                    Err(err) => {
                        warn!("Skipping: {:?}", err);
                        continue;
                    }
                };

                if false {
                    // Respect the groupings in the `.workflow.yml` file.
                    envs.push(fset);
                } else {
                    // Break each test up so we can run more in
                    // parallel.
                    let env = fset.env.clone();
                    for val in fset.values {
                        if !all_tests.contains(&val) {
                            all_tests.insert(val.clone());
                            envs.push(FeatureSet {
                                env: env.clone(),
                                values: vec![val],
                            });
                        } else {
                            warn!("Duplicate: {:?}: {:?}", env, val);
                        }
                    }
                }
            }
        }

        Ok(Matrix {
            envs: envs,
        })
    }
}

impl FeatureSet {
    fn decode(text: &str) -> Result<FeatureSet> {
        // The github workflow is just a space separated set of values.
        let values: Vec<_> = text
            .split(',')
            .map(|s| s.to_string())
            .collect();
        Ok(FeatureSet {
            env: "MULTI_FEATURES".to_string(),
            values: values,
        })
    }

    /// Run a test for this given feature set.  Output is captured and will be returned if there is
    /// an error.  Each will be run successively, and the first failure will be returned.
    /// Otherwise, it returns None, which means everything worked.
    fn run(&self) -> Result<Option<Output>> {
        for v in &self.values {
            let output = Command::new("bash")
               .arg("./ci/sim_run.sh")
               .current_dir("..")
               .env(&self.env, v)
               .output()?;
            if !output.status.success() {
                return Ok(Some(output));
            }
        }
        return Ok(None);
    }

    /// Convert this feature set into a textual representation
    fn textual(&self) -> String {
        use std::fmt::Write;

        let mut buf = String::new();

        write!(&mut buf, "{}:", self.env).unwrap();
        for v in &self.values {
            write!(&mut buf, " {}", v).unwrap();
        }

        buf
    }
}

fn lookup_matrix(y: &Yaml) -> Option<&Vec<Yaml>> {
    let jobs = Yaml::String("jobs".to_string());
    let environment = Yaml::String("environment".to_string());
    let strategy = Yaml::String("strategy".to_string());
    let matrix = Yaml::String("matrix".to_string());
    let features = Yaml::String("features".to_string());
    y
        .as_hash()?.get(&jobs)?
        .as_hash()?.get(&environment)?
        .as_hash()?.get(&strategy)?
        .as_hash()?.get(&matrix)?
        .as_hash()?.get(&features)?
        .as_vec()
}
<commit_msg>ptest: Allow all test runs to be logged<commit_after>//! Parallel testing.
//!
//! mcuboot simulator is strictly single threaded, as there is a lock around running the C startup
//! code, because it contains numerous global variables.
//!
//! To help speed up testing, the Workflow configuration defines all of the configurations that can
//! be run in parallel.  Fortunately, cargo works well this way, and these can be run by simply
//! using subprocess for each particular thread.
//!
//! For now, we assume all of the features are listed under
//! jobs->environment->strategy->matric->features

use chrono::Local;
use log::{debug, error, warn};
use std::{
    collections::HashSet,
    env,
    fs::{self, OpenOptions},
    io::{ErrorKind, stdout, Write},
    process::Command,
    result,
    sync::{
        Arc,
        Mutex,
    },
    thread,
    time::Duration,
};
use std_semaphore::Semaphore;
use yaml_rust::{
    Yaml,
    YamlLoader,
};

type Result<T> = result::Result<T, failure::Error>;

fn main() -> Result<()> {
    env_logger::init();

    let workflow_text = fs::read_to_string("../.github/workflows/sim.yaml")?;
    let workflow = YamlLoader::load_from_str(&workflow_text)?;

    let ncpus = num_cpus::get();
    let limiter = Arc::new(Semaphore::new(ncpus as isize));

    let matrix = Matrix::from_yaml(&workflow)?;

    let mut children = vec![];
    let state = State::new(matrix.envs.len());
    let st2 = state.clone();
    let _status = thread::spawn(move || {
        loop {
            thread::sleep(Duration::new(15, 0));
            st2.lock().unwrap().status();
        }
    });
    for env in matrix.envs {
        let state = state.clone();
        let limiter = limiter.clone();

        let child = thread::spawn(move || {
            let _run = limiter.access();
            state.lock().unwrap().start(&env);
            let out = env.run();
            state.lock().unwrap().done(&env, out);
        });
        children.push(child);
    }

    for child in children {
        child.join().unwrap();
    }

    println!("");

    Ok(())
}

/// State, for printing status.
struct State {
    running: HashSet<String>,
    done: HashSet<String>,
    total: usize,
}

/// Result of a test run.
struct TestResult {
    /// Was this run successful.
    success: bool,

    /// The captured output.
    output: Vec<u8>,
}

impl State {
    fn new(total: usize) -> Arc<Mutex<State>> {
        Arc::new(Mutex::new(State {
            running: HashSet::new(),
            done: HashSet::new(),
            total: total,
        }))
    }

    fn start(&mut self, fs: &FeatureSet) {
        let key = fs.textual();
        if self.running.contains(&key) || self.done.contains(&key) {
            warn!("Duplicate: {:?}", key);
        }
        debug!("Starting: {} ({} running)", key, self.running.len() + 1);
        self.running.insert(key);
        self.status();
    }

    fn done(&mut self, fs: &FeatureSet, output: Result<TestResult>) {
        let key = fs.textual();
        self.running.remove(&key);
        self.done.insert(key.clone());
        match output {
            Ok(output) => {
                if !output.success || log_all() {
                    // Write the output into a file.
                    let mut count = 1;
                    let (mut fd, logname) = loop {
                        let base = if output.success { "success" } else { "failure" };
                        let name = format!("./{}-{:04}.log", base, count);
                        count += 1;
                        match OpenOptions::new()
                            .create_new(true)
                            .write(true)
                            .open(&name)
                        {
                            Ok(file) => break (file, name),
                            Err(ref err) if err.kind() == ErrorKind::AlreadyExists => continue,
                            Err(err) => {
                                error!("Unable to write log file to current directory: {:?}", err);
                                return;
                            }
                        }
                    };
                    fd.write_all(&output.output).unwrap();
                    if !output.success {
                        error!("Failure {} log:{:?} ({} running)", key, logname,
                        self.running.len());
                    }
                }
            }
            Err(err) => {
                error!("Unable to run test {:?} ({:?}", key, err);
            }
        }
        self.status();
    }

    fn status(&self) {
        let running = self.running.len();
        let done = self.done.len();
        print!(" {} running ({}/{}/{} done)\r", running, done, running + done, self.total);
        stdout().flush().unwrap();
    }
}

/// The extracted configurations from the workflow config
#[derive(Debug)]
struct Matrix {
    envs: Vec<FeatureSet>,
}

#[derive(Debug, Eq, Hash, PartialEq)]
struct FeatureSet {
    // The environment variable to set.
    env: String,
    // The successive values to set it to.
    values: Vec<String>,
}

impl Matrix {
    fn from_yaml(yaml: &[Yaml]) -> Result<Matrix> {
        let mut envs = vec![];

        let mut all_tests = HashSet::new();

        for y in yaml {
            let m = match lookup_matrix(y) {
                Some (m) => m,
                None => continue,
            };
            for elt in m {
                let elt = match elt.as_str() {
                    None => {
                        warn!("Unexpected yaml: {:?}", elt);
                        continue;
                    }
                    Some(e) => e,
                };
                let fset = match FeatureSet::decode(elt) {
                    Ok(fset) => fset,
                    Err(err) => {
                        warn!("Skipping: {:?}", err);
                        continue;
                    }
                };

                if false {
                    // Respect the groupings in the `.workflow.yml` file.
                    envs.push(fset);
                } else {
                    // Break each test up so we can run more in
                    // parallel.
                    let env = fset.env.clone();
                    for val in fset.values {
                        if !all_tests.contains(&val) {
                            all_tests.insert(val.clone());
                            envs.push(FeatureSet {
                                env: env.clone(),
                                values: vec![val],
                            });
                        } else {
                            warn!("Duplicate: {:?}: {:?}", env, val);
                        }
                    }
                }
            }
        }

        Ok(Matrix {
            envs: envs,
        })
    }
}

impl FeatureSet {
    fn decode(text: &str) -> Result<FeatureSet> {
        // The github workflow is just a space separated set of values.
        let values: Vec<_> = text
            .split(',')
            .map(|s| s.to_string())
            .collect();
        Ok(FeatureSet {
            env: "MULTI_FEATURES".to_string(),
            values: values,
        })
    }

    /// Run a test for this given feature set.  Output is captured and will be returned if there is
    /// an error.  Each will be run successively, and the first failure will be returned.
    /// Otherwise, it returns None, which means everything worked.
    fn run(&self) -> Result<TestResult> {
        let mut output = vec![];
        let mut success = true;
        for v in &self.values {
            let cmdout = Command::new("bash")
               .arg("./ci/sim_run.sh")
               .current_dir("..")
               .env(&self.env, v)
               .output()?;
            // Grab the output for logging, etc.
            writeln!(&mut output, "Test {} {}",
                if cmdout.status.success() { "success" } else { "FAILURE" },
                self.textual())?;
            writeln!(&mut output, "time: {}", Local::now().to_rfc3339())?;
            writeln!(&mut output, "----------------------------------------")?;
            writeln!(&mut output, "stdout:")?;
            output.extend(&cmdout.stdout);
            writeln!(&mut output, "----------------------------------------")?;
            writeln!(&mut output, "stderr:")?;
            output.extend(&cmdout.stderr);
            if !cmdout.status.success() {
                success = false;
            }
        }
        Ok(TestResult { success, output })
    }

    /// Convert this feature set into a textual representation
    fn textual(&self) -> String {
        use std::fmt::Write;

        let mut buf = String::new();

        write!(&mut buf, "{}:", self.env).unwrap();
        for v in &self.values {
            write!(&mut buf, " {}", v).unwrap();
        }

        buf
    }
}

fn lookup_matrix(y: &Yaml) -> Option<&Vec<Yaml>> {
    let jobs = Yaml::String("jobs".to_string());
    let environment = Yaml::String("environment".to_string());
    let strategy = Yaml::String("strategy".to_string());
    let matrix = Yaml::String("matrix".to_string());
    let features = Yaml::String("features".to_string());
    y
        .as_hash()?.get(&jobs)?
        .as_hash()?.get(&environment)?
        .as_hash()?.get(&strategy)?
        .as_hash()?.get(&matrix)?
        .as_hash()?.get(&features)?
        .as_vec()
}

/// Query if we should be logging all tests and not only failures.
fn log_all() -> bool {
    env::var("PTEST_LOG_ALL").is_ok()
}
<|endoftext|>


<commit_before>use codec::{
    eyre::{bail, Result},
    stencila_schema::{Node, Person},
    utils::vec_string,
    Codec, CodecTrait, DecodeOptions,
};
use human_name::Name;
use once_cell::sync::Lazy;
use regex::Regex;

// A codec for people's names, honorifics and contact details
pub struct PersonCodec {}

impl CodecTrait for PersonCodec {
    fn spec() -> Codec {
        Codec {
            formats: vec_string!["person"],
            root_types: vec_string!["Person"],
            from_string: true,
            from_path: true,
            ..Default::default()
        }
    }

    fn from_str(str: &str, _options: Option<DecodeOptions>) -> Result<Node> {
        if let Some(name) = Name::parse(str) {
            let given_names = if let Some(first_name) = name.given_name() {
                let mut given_names = vec![first_name.to_string()];
                if let Some(middle_names) = name.middle_names() {
                    let mut middle_names = middle_names
                        .iter()
                        .map(|str| str.to_string())
                        .collect::<Vec<String>>();
                    given_names.append(&mut middle_names)
                }
                Some(given_names)
            } else {
                None
            };

            let family_names = if name.surnames().is_empty() {
                None
            } else {
                Some(
                    name.surnames()
                        .iter()
                        .map(|str| str.to_string())
                        .collect::<Vec<String>>(),
                )
            };

            // Note: currently not using the "generational suffix" e.g "Jr" parsed
            // by `human_name`.

            // Note: there is no easy way to extract honorific prefix and suffix
            // See https://github.com/djudd/human-name/issues/6

            static EMAIL_REGEX: Lazy<Regex> =
                Lazy::new(|| Regex::new("<([^@]+@.+)>").expect("Unable to create regex"));
            let emails = EMAIL_REGEX
                .captures(str)
                .map(|email| vec![email[1].to_string()]);

            static URL_REGEX: Lazy<Regex> =
                Lazy::new(|| Regex::new("\\((https?://[^)]+)\\)").expect("Unable to create regex"));
            let url = URL_REGEX
                .captures(str)
                .map(|url| Box::new(url[1].to_string()));

            Ok(Node::Person(Person {
                emails,
                family_names,
                given_names,
                url,
                ..Default::default()
            }))
        } else {
            bail!("Unable to decode as a `Person`: {}", str)
        }
    }
}
<commit_msg>feat(Person codec): Implement `to_string`<commit_after>use codec::{
    eyre::{bail, Result},
    stencila_schema::{Node, Person},
    utils::vec_string,
    Codec, CodecTrait, DecodeOptions, EncodeOptions,
};
use human_name::Name;
use once_cell::sync::Lazy;
use regex::Regex;

// A codec for people's names, honorifics and contact details
pub struct PersonCodec {}

impl CodecTrait for PersonCodec {
    fn spec() -> Codec {
        Codec {
            status: "beta".to_string(),
            formats: vec_string!["person"],
            root_types: vec_string!["Person"],
            ..Default::default()
        }
    }

    fn from_str(str: &str, _options: Option<DecodeOptions>) -> Result<Node> {
        if let Some(name) = Name::parse(str) {
            let given_names = if let Some(first_name) = name.given_name() {
                let mut given_names = vec![first_name.to_string()];
                if let Some(middle_names) = name.middle_names() {
                    let mut middle_names = middle_names
                        .iter()
                        .map(|str| str.to_string())
                        .collect::<Vec<String>>();
                    given_names.append(&mut middle_names)
                }
                Some(given_names)
            } else {
                None
            };

            let family_names = if name.surnames().is_empty() {
                None
            } else {
                Some(
                    name.surnames()
                        .iter()
                        .map(|str| str.to_string())
                        .collect::<Vec<String>>(),
                )
            };

            // Note: currently not using the "generational suffix" e.g "Jr" parsed
            // by `human_name`.

            // Note: there is no easy way to extract honorific prefix and suffix
            // See https://github.com/djudd/human-name/issues/6

            static EMAIL_REGEX: Lazy<Regex> =
                Lazy::new(|| Regex::new("<([^@]+@.+)>").expect("Unable to create regex"));
            let emails = EMAIL_REGEX
                .captures(str)
                .map(|email| vec![email[1].to_string()]);

            static URL_REGEX: Lazy<Regex> =
                Lazy::new(|| Regex::new("\\((https?://[^)]+)\\)").expect("Unable to create regex"));
            let url = URL_REGEX
                .captures(str)
                .map(|url| Box::new(url[1].to_string()));

            Ok(Node::Person(Person {
                emails,
                family_names,
                given_names,
                url,
                ..Default::default()
            }))
        } else {
            bail!("Unable to decode string to a `Person`: {}", str)
        }
    }

    fn to_string(node: &Node, _options: Option<EncodeOptions>) -> Result<String> {
        let person = match node {
            Node::Person(person) => person,
            _ => bail!("Unable to encode node that is not a `Person`"),
        };

        let mut string = String::new();
        if let Some(honorific_prefix) = person.honorific_prefix.as_deref() {
            string.push_str(honorific_prefix);
        }
        if let Some(given_names) = &person.given_names {
            string.push(' ');
            string.push_str(&given_names.join(" "));
        }
        if let Some(family_names) = &person.family_names {
            string.push(' ');
            string.push_str(&family_names.join(" "));
        }
        if let Some(honorific_suffix) = person.honorific_suffix.as_deref() {
            string.push(' ');
            string.push_str(honorific_suffix);
        }
        if let Some(emails) = &person.emails {
            string.push_str(" <");
            string.push_str(&emails.join(" "));
            string.push('>');
        }
        if let Some(url) = person.url.as_deref() {
            string.push_str(" (");
            string.push_str(url);
            string.push(')');
        }

        Ok(string.trim().to_string())
    }
}

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

    /// Fairly trivial tests at present given that we're relying on `human_name` crate
    /// for parsing.
    #[test]
    fn from_str() -> Result<()> {
        assert_json_eq!(
            PersonCodec::from_str("Joe Bloggs", None)?,
            Node::Person(Person {
                given_names: Some(vec_string!["Joe"]),
                family_names: Some(vec_string!["Bloggs"]),
                ..Default::default()
            })
        );
        assert_json_eq!(
            PersonCodec::from_str(
                "Mary Mavis Maven <mary@example.com> (https://mary.example.com)",
                None
            )?,
            Node::Person(Person {
                given_names: Some(vec_string!["Mary", "Mavis"]),
                family_names: Some(vec_string!["Maven"]),
                emails: Some(vec_string!["mary@example.com"]),
                url: Some(Box::new("https://mary.example.com".to_string())),
                ..Default::default()
            })
        );
        Ok(())
    }

    #[test]
    fn to_string() -> Result<()> {
        assert_eq!(
            PersonCodec::to_string(
                &Node::Person(Person {
                    given_names: Some(vec_string!["Joe"]),
                    family_names: Some(vec_string!["Bloggs"]),
                    ..Default::default()
                }),
                None
            )?,
            "Joe Bloggs"
        );
        assert_eq!(
            PersonCodec::to_string(
                &Node::Person(Person {
                    given_names: Some(vec!["Mary".to_string(), "Mavis".to_string()]),
                    family_names: Some(vec_string!("Maven")),
                    emails: Some(vec_string!("mary@example.com")),
                    url: Some(Box::new("https://mary.example.com".to_string())),
                    ..Default::default()
                }),
                None
            )?,
            "Mary Mavis Maven <mary@example.com> (https://mary.example.com)"
        );
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate num;
extern crate rand;
extern crate time;
extern crate vec_map;
extern crate serde;

use num::Float;
use rand::Rng;
pub use rng::{EncodeRng, RandomPermutation, RngExt};
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
pub use text::{LineSplit, split_line};

mod parser;
mod rng;
mod text;
pub mod timing;

pub mod ease;

/// Clamp a value to range.
pub fn clamp<C: PartialOrd + Copy>(mn: C, mx: C, x: C) -> C {
    if x < mn {
        mn
    } else if x > mx {
        mx
    } else {
        x
    }
}

/// Deterministic noise.
pub fn noise(n: i32) -> f32 {
    use std::num::Wrapping;

    let n = Wrapping(n);
    let n = (n << 13) ^ n;
    let m = (n * (n * n * Wrapping(15731) + Wrapping(789221)) + Wrapping(1376312589)) &
        Wrapping(0x7fffffff);
    let Wrapping(m) = m;
    1.0 - m as f32 / 1073741824.0
}

/// A deciban unit log odds value.
///
/// Log odds correspond to the Bayesian probability for a hypothesis that
/// has decibans * 1/10 log_2(10) bits of evidence in favor of it. They're
/// a bit like rolling a d20 but better.
///
/// # Examples
///
/// ```
/// use calx_alg::Deciban;
/// assert_eq!(0.0, Deciban::new(0.5).0);
/// assert_eq!(10, Deciban::new(0.909091).0 as i32);
///
/// assert_eq!(0.5, Deciban(0.0).to_p());
/// assert_eq!(24, (Deciban(-5.0).to_p() * 100.0) as i32);
/// ```
#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug)]
pub struct Deciban(pub f32);

impl rand::Rand for Deciban {
    fn rand<R: Rng>(rng: &mut R) -> Self { Deciban::new(rng.next_f32()) }
}

impl Deciban {
    /// Build a deciban value from a probability in [0, 1).
    pub fn new(p: f32) -> Deciban {
        debug_assert!(p >= 0.0 && p < 1.0);
        Deciban(10.0 * (p / (1.0 - p)).log(10.0))
    }

    /// Convert a deciban value to the corresponding probability in [0, 1).
    pub fn to_p(self) -> f32 { 1.0 - 1.0 / (1.0 + 10.0.powf(self.0 / 10.0)) }
}

impl Add for Deciban {
    type Output = Deciban;
    fn add(self, rhs: Deciban) -> Deciban { Deciban(self.0 + rhs.0) }
}

impl AddAssign for Deciban {
    fn add_assign(&mut self, rhs: Deciban) { self.0 += rhs.0; }
}

impl Sub for Deciban {
    type Output = Deciban;
    fn sub(self, rhs: Deciban) -> Deciban { Deciban(self.0 - rhs.0) }
}

impl SubAssign for Deciban {
    fn sub_assign(&mut self, rhs: Deciban) { self.0 -= rhs.0; }
}

/// Interpolate linearly between two values.
pub fn lerp<T, U>(a: U, b: U, t: T) -> U
where
    U: Add<U, Output = U> + Sub<U, Output = U> + Mul<T, Output = U> + Copy,
{
    a + (b - a) * t
}

/// Return the two arguments sorted to order.
pub fn sorted_pair<T: PartialOrd>(a: T, b: T) -> (T, T) { if a < b { (a, b) } else { (b, a) } }

// TODO: Remove this thing once Rust has a proper way of counting macro
// arguments.

/// Macro hack for counting the number of arguments.
///
/// Use for determining the size of a fixed array type created by a macro that
/// takes variadic arguments. Due to its hackiness, will fail around 100
/// elements.
#[macro_export]
macro_rules! count_exprs {
    () => { 0 };
    ($e:expr) => { 1 };
    ($e:expr, $($es:expr),+) => { 1 + count_exprs!($($es),*) };
}

/// Single-pass weighted sample for iterations.
pub trait WeightedChoice {
    type Item;

    /// Choose an item from the iteration with probability weighted by item weight.
    fn weighted_choice<R: Rng, F>(self, rng: &mut R, weight_fn: F) -> Option<Self::Item>
    where
        F: Fn(&Self::Item) -> f32;
}

impl<T, I: Iterator<Item = T> + Sized> WeightedChoice for I {
    type Item = T;

    fn weighted_choice<R: Rng, F>(self, rng: &mut R, weight_fn: F) -> Option<Self::Item>
    where
        F: Fn(&Self::Item) -> f32,
    {
        let (_, ret) = self.fold((0.0, None), |(weight_sum, prev_item), item| {
            let item_weight = weight_fn(&item);
            debug_assert!(item_weight >= 0.0);
            let p = item_weight / (weight_sum + item_weight);
            let next_item = if rng.next_f32() < p {
                Some(item)
            } else {
                prev_item
            };
            (weight_sum + item_weight, next_item)
        });
        ret
    }
}

/// Insert a 0 bit between the low 16 bits of a number.
///
/// Useful for https://en.wikipedia.org/wiki/Z-order_curve
#[inline(always)]
pub fn spread_bits_by_2(mut bits: u32) -> u32 {
    // from https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
    bits &= /* --------------- */ 0b00000000_00000000_11111111_11111111;
    bits = (bits ^ (bits << 8)) & 0b00000000_11111111_00000000_11111111;
    bits = (bits ^ (bits << 4)) & 0b00001111_00001111_00001111_00001111;
    bits = (bits ^ (bits << 2)) & 0b00110011_00110011_00110011_00110011;
    bits = (bits ^ (bits << 1)) & 0b01010101_01010101_01010101_01010101;
    bits
}

/// Remove every odd bit and compact the even bits into the lower half of the number.
///
/// Useful for https://en.wikipedia.org/wiki/Z-order_curve
#[inline(always)]
pub fn compact_bits_by_2(mut bits: u32) -> u32 {
    // from https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
    bits &= /* --------------- */ 0b01010101_01010101_01010101_01010101;
    bits = (bits ^ (bits >> 1)) & 0b00110011_00110011_00110011_00110011;
    bits = (bits ^ (bits >> 2)) & 0b00001111_00001111_00001111_00001111;
    bits = (bits ^ (bits >> 4)) & 0b00000000_11111111_00000000_11111111;
    bits = (bits ^ (bits >> 8)) & 0b00000000_00000000_11111111_11111111;
    bits
}
<commit_msg>Parametrize on IntoIter for better genericity<commit_after>extern crate num;
extern crate rand;
extern crate time;
extern crate vec_map;
extern crate serde;

use num::Float;
use rand::Rng;
pub use rng::{EncodeRng, RandomPermutation, RngExt};
use std::ops::{Add, AddAssign, Mul, Sub, SubAssign};
pub use text::{LineSplit, split_line};

mod parser;
mod rng;
mod text;
pub mod timing;

pub mod ease;

/// Clamp a value to range.
pub fn clamp<C: PartialOrd + Copy>(mn: C, mx: C, x: C) -> C {
    if x < mn {
        mn
    } else if x > mx {
        mx
    } else {
        x
    }
}

/// Deterministic noise.
pub fn noise(n: i32) -> f32 {
    use std::num::Wrapping;

    let n = Wrapping(n);
    let n = (n << 13) ^ n;
    let m = (n * (n * n * Wrapping(15731) + Wrapping(789221)) + Wrapping(1376312589)) &
        Wrapping(0x7fffffff);
    let Wrapping(m) = m;
    1.0 - m as f32 / 1073741824.0
}

/// A deciban unit log odds value.
///
/// Log odds correspond to the Bayesian probability for a hypothesis that
/// has decibans * 1/10 log_2(10) bits of evidence in favor of it. They're
/// a bit like rolling a d20 but better.
///
/// # Examples
///
/// ```
/// use calx_alg::Deciban;
/// assert_eq!(0.0, Deciban::new(0.5).0);
/// assert_eq!(10, Deciban::new(0.909091).0 as i32);
///
/// assert_eq!(0.5, Deciban(0.0).to_p());
/// assert_eq!(24, (Deciban(-5.0).to_p() * 100.0) as i32);
/// ```
#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug)]
pub struct Deciban(pub f32);

impl rand::Rand for Deciban {
    fn rand<R: Rng>(rng: &mut R) -> Self { Deciban::new(rng.next_f32()) }
}

impl Deciban {
    /// Build a deciban value from a probability in [0, 1).
    pub fn new(p: f32) -> Deciban {
        debug_assert!(p >= 0.0 && p < 1.0);
        Deciban(10.0 * (p / (1.0 - p)).log(10.0))
    }

    /// Convert a deciban value to the corresponding probability in [0, 1).
    pub fn to_p(self) -> f32 { 1.0 - 1.0 / (1.0 + 10.0.powf(self.0 / 10.0)) }
}

impl Add for Deciban {
    type Output = Deciban;
    fn add(self, rhs: Deciban) -> Deciban { Deciban(self.0 + rhs.0) }
}

impl AddAssign for Deciban {
    fn add_assign(&mut self, rhs: Deciban) { self.0 += rhs.0; }
}

impl Sub for Deciban {
    type Output = Deciban;
    fn sub(self, rhs: Deciban) -> Deciban { Deciban(self.0 - rhs.0) }
}

impl SubAssign for Deciban {
    fn sub_assign(&mut self, rhs: Deciban) { self.0 -= rhs.0; }
}

/// Interpolate linearly between two values.
pub fn lerp<T, U>(a: U, b: U, t: T) -> U
where
    U: Add<U, Output = U> + Sub<U, Output = U> + Mul<T, Output = U> + Copy,
{
    a + (b - a) * t
}

/// Return the two arguments sorted to order.
pub fn sorted_pair<T: PartialOrd>(a: T, b: T) -> (T, T) { if a < b { (a, b) } else { (b, a) } }

// TODO: Remove this thing once Rust has a proper way of counting macro
// arguments.

/// Macro hack for counting the number of arguments.
///
/// Use for determining the size of a fixed array type created by a macro that
/// takes variadic arguments. Due to its hackiness, will fail around 100
/// elements.
#[macro_export]
macro_rules! count_exprs {
    () => { 0 };
    ($e:expr) => { 1 };
    ($e:expr, $($es:expr),+) => { 1 + count_exprs!($($es),*) };
}

/// Single-pass weighted sample for iterations.
pub trait WeightedChoice {
    type Item;

    /// Choose an item from the iteration with probability weighted by item weight.
    fn weighted_choice<R: Rng, F>(self, rng: &mut R, weight_fn: F) -> Option<Self::Item>
    where
        F: Fn(&Self::Item) -> f32;
}

impl<T, I: IntoIterator<Item = T> + Sized> WeightedChoice for I {
    type Item = T;

    fn weighted_choice<R: Rng, F>(self, rng: &mut R, weight_fn: F) -> Option<Self::Item>
    where
        F: Fn(&Self::Item) -> f32,
    {
        let (_, ret) = self.into_iter().fold((0.0, None), |(weight_sum, prev_item), item| {
            let item_weight = weight_fn(&item);
            debug_assert!(item_weight >= 0.0);
            let p = item_weight / (weight_sum + item_weight);
            let next_item = if rng.next_f32() < p {
                Some(item)
            } else {
                prev_item
            };
            (weight_sum + item_weight, next_item)
        });
        ret
    }
}

/// Insert a 0 bit between the low 16 bits of a number.
///
/// Useful for https://en.wikipedia.org/wiki/Z-order_curve
#[inline(always)]
pub fn spread_bits_by_2(mut bits: u32) -> u32 {
    // from https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
    bits &= /* --------------- */ 0b00000000_00000000_11111111_11111111;
    bits = (bits ^ (bits << 8)) & 0b00000000_11111111_00000000_11111111;
    bits = (bits ^ (bits << 4)) & 0b00001111_00001111_00001111_00001111;
    bits = (bits ^ (bits << 2)) & 0b00110011_00110011_00110011_00110011;
    bits = (bits ^ (bits << 1)) & 0b01010101_01010101_01010101_01010101;
    bits
}

/// Remove every odd bit and compact the even bits into the lower half of the number.
///
/// Useful for https://en.wikipedia.org/wiki/Z-order_curve
#[inline(always)]
pub fn compact_bits_by_2(mut bits: u32) -> u32 {
    // from https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
    bits &= /* --------------- */ 0b01010101_01010101_01010101_01010101;
    bits = (bits ^ (bits >> 1)) & 0b00110011_00110011_00110011_00110011;
    bits = (bits ^ (bits >> 2)) & 0b00001111_00001111_00001111_00001111;
    bits = (bits ^ (bits >> 4)) & 0b00000000_11111111_00000000_11111111;
    bits = (bits ^ (bits >> 8)) & 0b00000000_00000000_11111111_11111111;
    bits
}
<|endoftext|>


<commit_before>#![recursion_limit = "1024"]
#[macro_use]
extern crate quote;
extern crate proc_macro;

use proc_macro::TokenStream;
use std::{env, path::Path};
use syn::{export::TokenStream2, Data, DeriveInput, Fields, Lit, Meta};

fn embedded(ident: &syn::Ident, folder_path: String) -> TokenStream2 {
  extern crate rust_embed_utils;

  let mut match_values = Vec::<TokenStream2>::new();
  let mut list_values = Vec::<String>::new();

  for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(folder_path) {
    match_values.push(embed_file(&rel_path, &full_canonical_path));
    list_values.push(rel_path);
  }

  let array_len = list_values.len();

  // If debug-embed is on, unconditionally include the code below. Otherwise,
  // make it conditional on cfg(not(debug_assertions)).
  let not_debug_attr = if cfg!(feature = "debug-embed") {
    quote! {}
  } else {
    quote! { #[cfg(not(debug_assertions))]}
  };

  quote! {
      #not_debug_attr
      impl #ident {
          pub fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
            match file_path.replace("\\", "/").as_str() {
                #(#match_values)*
                _ => None,
            }
          }

          fn names() -> std::slice::Iter<'static, &'static str> {
              const items: [&str; #array_len] = [#(#list_values),*];
              items.iter()
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              Self::names().map(|x| std::borrow::Cow::from(*x))
          }
      }

      #not_debug_attr
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          rust_embed::Filenames::Embedded(#ident::names())
        }
      }
  }
}

fn dynamic(ident: &syn::Ident, folder_path: String) -> TokenStream2 {
  quote! {
      #[cfg(debug_assertions)]
      impl #ident {
          pub fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
              use std::fs;
              use std::path::Path;

              let file_path = Path::new(#folder_path).join(file_path.replace("\\", "/"));
              match fs::read(file_path) {
                  Ok(contents) => Some(std::borrow::Cow::from(contents)),
                  Err(_e) =>  {
                      return None
                  }
              }
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              use std::path::Path;
              rust_embed::utils::get_files(String::from(#folder_path)).map(|e| std::borrow::Cow::from(e.rel_path))
          }
      }

      #[cfg(debug_assertions)]
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          // the return type of iter() is unnamable, so we have to box it
          rust_embed::Filenames::Dynamic(Box::new(#ident::iter()))
        }
      }
  }
}

fn generate_assets(ident: &syn::Ident, folder_path: String) -> TokenStream2 {
  let embedded_impl = embedded(ident, folder_path.clone());
  if cfg!(feature = "rust-embed") {
    return embedded_impl;
  }

  let dynamic_impl = dynamic(ident, folder_path);

  quote! {
      #embedded_impl
      #dynamic_impl
  }
}

#[cfg(not(feature = "compression"))]
fn embed_file(rel_path: &str, full_canonical_path: &str) -> TokenStream2 {
  quote! {
    #rel_path => {
        let bytes = &include_bytes!(#full_canonical_path)[..];
        Some(std::borrow::Cow::from(bytes))
    },
  }
}

#[cfg(feature = "compression")]
fn embed_file(rel_path: &str, full_canonical_path: &str) -> TokenStream2 {
  quote! {
    #rel_path => {
        rust_embed::flate!(static FILE: [u8] from #full_canonical_path);

        let bytes = &FILE[..];
        Some(std::borrow::Cow::from(bytes))
    },
  }
}

fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
  match ast.data {
    Data::Struct(ref data) => match data.fields {
      Fields::Unit => {}
      _ => panic!("RustEmbed can only be derived for unit structs"),
    },
    _ => panic!("RustEmbed can only be derived for unit structs"),
  };

  let attribute = ast
    .attrs
    .iter()
    .find(|value| value.path.is_ident("folder"))
    .expect("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]");
  let meta = attribute
    .parse_meta()
    .expect("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]");
  let literal_value = match meta {
    Meta::NameValue(ref data) => &data.lit,
    _ => panic!("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]"),
  };
  let folder_path = match literal_value {
    Lit::Str(ref val) => val.clone().value(),
    _ => {
      panic!("#[derive(RustEmbed)] attribute value must be a string literal");
    }
  };

  #[cfg(feature = "interpolate-folder-path")]
  let folder_path = shellexpand::full(&folder_path).unwrap().to_string();

  // Base relative paths on the Cargo.toml location
  let folder_path = if Path::new(&folder_path).is_relative() {
    Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
      .join(folder_path)
      .to_str()
      .unwrap()
      .to_owned()
  } else {
    folder_path
  };

  if !Path::new(&folder_path).exists() {
    let mut message = format!(
      "#[derive(RustEmbed)] folder '{}' does not exist. cwd: '{}'",
      folder_path,
      std::env::current_dir().unwrap().to_str().unwrap()
    );

    // Add a message about the interpolate-folder-path feature if the path may
    // include a variable
    if folder_path.contains('$') && cfg!(not(feature = "interpolate-folder-path")) {
      message += "\nA variable has been detected. RustEmbed can expand variables \
                  when the `interpolate-folder-path` feature is enabled.";
    }

    panic!(message);
  };

  generate_assets(&ast.ident, folder_path)
}

#[proc_macro_derive(RustEmbed, attributes(folder))]
pub fn derive_input_object(input: TokenStream) -> TokenStream {
  let ast: DeriveInput = syn::parse(input).unwrap();
  let gen = impl_rust_embed(&ast);
  gen.into()
}
<commit_msg>Fix feature flag typo<commit_after>#![recursion_limit = "1024"]
#[macro_use]
extern crate quote;
extern crate proc_macro;

use proc_macro::TokenStream;
use std::{env, path::Path};
use syn::{export::TokenStream2, Data, DeriveInput, Fields, Lit, Meta};

fn embedded(ident: &syn::Ident, folder_path: String) -> TokenStream2 {
  extern crate rust_embed_utils;

  let mut match_values = Vec::<TokenStream2>::new();
  let mut list_values = Vec::<String>::new();

  for rust_embed_utils::FileEntry { rel_path, full_canonical_path } in rust_embed_utils::get_files(folder_path) {
    match_values.push(embed_file(&rel_path, &full_canonical_path));
    list_values.push(rel_path);
  }

  let array_len = list_values.len();

  // If debug-embed is on, unconditionally include the code below. Otherwise,
  // make it conditional on cfg(not(debug_assertions)).
  let not_debug_attr = if cfg!(feature = "debug-embed") {
    quote! {}
  } else {
    quote! { #[cfg(not(debug_assertions))]}
  };

  quote! {
      #not_debug_attr
      impl #ident {
          pub fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
            match file_path.replace("\\", "/").as_str() {
                #(#match_values)*
                _ => None,
            }
          }

          fn names() -> std::slice::Iter<'static, &'static str> {
              const items: [&str; #array_len] = [#(#list_values),*];
              items.iter()
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              Self::names().map(|x| std::borrow::Cow::from(*x))
          }
      }

      #not_debug_attr
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          rust_embed::Filenames::Embedded(#ident::names())
        }
      }
  }
}

fn dynamic(ident: &syn::Ident, folder_path: String) -> TokenStream2 {
  quote! {
      #[cfg(debug_assertions)]
      impl #ident {
          pub fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
              use std::fs;
              use std::path::Path;

              let file_path = Path::new(#folder_path).join(file_path.replace("\\", "/"));
              match fs::read(file_path) {
                  Ok(contents) => Some(std::borrow::Cow::from(contents)),
                  Err(_e) =>  {
                      return None
                  }
              }
          }

          pub fn iter() -> impl Iterator<Item = std::borrow::Cow<'static, str>> {
              use std::path::Path;
              rust_embed::utils::get_files(String::from(#folder_path)).map(|e| std::borrow::Cow::from(e.rel_path))
          }
      }

      #[cfg(debug_assertions)]
      impl rust_embed::RustEmbed for #ident {
        fn get(file_path: &str) -> Option<std::borrow::Cow<'static, [u8]>> {
          #ident::get(file_path)
        }
        fn iter() -> rust_embed::Filenames {
          // the return type of iter() is unnamable, so we have to box it
          rust_embed::Filenames::Dynamic(Box::new(#ident::iter()))
        }
      }
  }
}

fn generate_assets(ident: &syn::Ident, folder_path: String) -> TokenStream2 {
  let embedded_impl = embedded(ident, folder_path.clone());
  if cfg!(feature = "debug-embed") {
    return embedded_impl;
  }

  let dynamic_impl = dynamic(ident, folder_path);

  quote! {
      #embedded_impl
      #dynamic_impl
  }
}

#[cfg(not(feature = "compression"))]
fn embed_file(rel_path: &str, full_canonical_path: &str) -> TokenStream2 {
  quote! {
    #rel_path => {
        let bytes = &include_bytes!(#full_canonical_path)[..];
        Some(std::borrow::Cow::from(bytes))
    },
  }
}

#[cfg(feature = "compression")]
fn embed_file(rel_path: &str, full_canonical_path: &str) -> TokenStream2 {
  quote! {
    #rel_path => {
        rust_embed::flate!(static FILE: [u8] from #full_canonical_path);

        let bytes = &FILE[..];
        Some(std::borrow::Cow::from(bytes))
    },
  }
}

fn impl_rust_embed(ast: &syn::DeriveInput) -> TokenStream2 {
  match ast.data {
    Data::Struct(ref data) => match data.fields {
      Fields::Unit => {}
      _ => panic!("RustEmbed can only be derived for unit structs"),
    },
    _ => panic!("RustEmbed can only be derived for unit structs"),
  };

  let attribute = ast
    .attrs
    .iter()
    .find(|value| value.path.is_ident("folder"))
    .expect("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]");
  let meta = attribute
    .parse_meta()
    .expect("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]");
  let literal_value = match meta {
    Meta::NameValue(ref data) => &data.lit,
    _ => panic!("#[derive(RustEmbed)] should contain one attribute like this #[folder = \"examples/public/\"]"),
  };
  let folder_path = match literal_value {
    Lit::Str(ref val) => val.clone().value(),
    _ => {
      panic!("#[derive(RustEmbed)] attribute value must be a string literal");
    }
  };

  #[cfg(feature = "interpolate-folder-path")]
  let folder_path = shellexpand::full(&folder_path).unwrap().to_string();

  // Base relative paths on the Cargo.toml location
  let folder_path = if Path::new(&folder_path).is_relative() {
    Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
      .join(folder_path)
      .to_str()
      .unwrap()
      .to_owned()
  } else {
    folder_path
  };

  if !Path::new(&folder_path).exists() {
    let mut message = format!(
      "#[derive(RustEmbed)] folder '{}' does not exist. cwd: '{}'",
      folder_path,
      std::env::current_dir().unwrap().to_str().unwrap()
    );

    // Add a message about the interpolate-folder-path feature if the path may
    // include a variable
    if folder_path.contains('$') && cfg!(not(feature = "interpolate-folder-path")) {
      message += "\nA variable has been detected. RustEmbed can expand variables \
                  when the `interpolate-folder-path` feature is enabled.";
    }

    panic!(message);
  };

  generate_assets(&ast.ident, folder_path)
}

#[proc_macro_derive(RustEmbed, attributes(folder))]
pub fn derive_input_object(input: TokenStream) -> TokenStream {
  let ast: DeriveInput = syn::parse(input).unwrap();
  let gen = impl_rust_embed(&ast);
  gen.into()
}
<|endoftext|>


<commit_before>use diesel;
use diesel::prelude::*;
use rocket_contrib::{JSON, SerdeError};
use uuid::Uuid;

use data::types::*;
use data::modules::get_module_list;
use data::helper::{get_module_dependency_strings, dependencies_fulfilled};

use helpers::db::DB;
use responses::{APIResponse, bad_request, created, ok};

use models::user::User;
use models::module::{NewModule,Module};
use models::research::Research;
use models::resource::Resource;

use schema::modules;
use schema::queue_entries;

use schema::modules::dsl as module_dsl;
use schema::resources::dsl as resources_dsl;
use schema::researches::dsl as research_dsl;
use schema::queue_entries::dsl as queue_entries_dsl;

use models::queue::{QueueEntry, NewQueueEntry};
use validation::queue::NewModuleSerializer;

/// The user needs to be logged in to access this route!
///
/// This route returns the list of all modules and their current levels for the pod of the current user.
#[get("/pod")]
pub fn get_modules(current_user: User, db: DB) -> APIResponse {

    let pod = current_user.get_pod(&db);
    let module_result = module_dsl::modules
        .filter(module_dsl::pod_id.eq(pod.id))
        .get_results::<Module>(&*db);

    if module_result.is_ok() {
        let modules = module_result.unwrap();
        return ok().message("Module data.").data(json!(&modules));
    }
    ok().message("No module installed.")
}

/// Add a new module to pod
///
/// This endpoint:
/// - Checks if dependencies for the module are fulfilled
/// - Checks if there are enough resources
/// - Removes resources from db
/// - Creates a new queue entry for this research
#[post("/pod/new", data = "<request_data>", format = "application/json")]
pub fn add_module(request_data: Result<JSON<NewModuleSerializer>, SerdeError>,
                          current_user: User,
                          db: DB)
                          -> APIResponse {

    // Unwrap or return specific error if invalid JSON has been sent.
    if let Err(error) = request_data {
        return bad_request().message(format!("{}", error).as_str());
    };
    let module_data = request_data.unwrap();

    // Check if the given module name maps to a module type.
    let result = ModuleTypes::from_string(&module_data.module_name);
    if result.is_err() {
        return bad_request().message(format!("No such module type `{}`",
                                             module_data.module_name)
                                             .as_str());
    }
    let module_type = result.unwrap();
    let dependency_strings = get_module_dependency_strings(&module_type);
    let module_list = get_module_list();

    let (pod, queue) = current_user.get_pod_and_queue(&db);

    let existing_module;
    // Check if there already exists a module for this position
    // We distinguish between stationary and normal modules.

    // Stationary modules have unique names and only exists once
    // per pod.
    if module_data.stationary {
        existing_module = module_dsl::modules
            .count()
            .filter(module_dsl::pod_id.eq(pod.id))
            .filter(module_dsl::name.eq(&module_data.module_name))
            .execute(&*db)
            .unwrap_or(0);
    }
    // Normal modules can exist multiple times. Thereby we just
    // have to check if the requested position is free.
    else {
        existing_module = module_dsl::modules
            .count()
            .filter(module_dsl::pod_id.eq(pod.id))
            .filter(module_dsl::x_pos.eq(module_data.position_x))
            .filter(module_dsl::y_pos.eq(module_data.position_y))
            .execute(&*db)
            .unwrap_or(0);
    };

    // Early return if there already is a module.
    if existing_module != 0 {
        return bad_request().message("There already is a module at this position.");
    }

    // Get the researches the module is depending on
    let dependencies = research_dsl::researches
        .filter(research_dsl::name.eq_any(dependency_strings))
        .get_results::<Research>(&*db);

    // Check if the dependencies are fulfilled.
    // If they're not fulfilled, return a bad request.
    let fulfilled = dependencies_fulfilled(&module_type,
                                           dependencies,
                                           &module_list);
    if !fulfilled {
        return bad_request().message("Dependencies not fulfilled.");
    }

    // Query all pod resources
    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results(&*db)
        .expect("Failed to get user resources.");

    // Get cost for level 1
    let costs = &module_list
                          .get(&module_type)
                          .as_ref()
                          .expect("No module in yml for this type.")
                          .levels[0].resources;

    if costs.is_some() && !Resource::check_resources(costs, pod_resources, &db) {
        return bad_request().message("Insufficient resources.");
    }

    // Create the new module
    let new_module = NewModule {
        name: module_data.module_name.clone(),
        stationary: module_data.stationary,
        x_pos: module_data.position_x,
        y_pos: module_data.position_y,

        pod_id: Some(pod.id),
        base_id: None,
    };

    let module = diesel::insert(&new_module)
        .into(modules::table)
        .get_result::<Module>(&*db)
        .expect("Failed to create module.");

    // Create a new queue entry with the given module type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: None,
        module_name: Some(module.name),
        module_id: Some(module.id),
        level: 1,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to create queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

/// Remove module from pod
#[delete("/pod/<module_uuid>")]
pub fn remove_module(module_uuid: &str, current_user: User, db: DB) -> APIResponse {
    // Parse and check if we got a valid id
    let result = Uuid::parse_str(module_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();

    let pod = current_user.get_pod(&db);

    // Get the module
    let module_result = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .filter(module_dsl::pod_id.eq(pod.id))
        .first::<Module>(&*db);
    if module_result.is_err() {
        return bad_request().message("No module with this id.");
    }
    let module = module_result.unwrap();

    // Remove queue_entry from database
    diesel::delete(module_dsl::modules
                       .filter(module_dsl::id.eq(module.id)))
            .execute(&*db)
            .expect("Failed to remove module.");

    ok().message("Module removed.")
}

/// upgrade module from pod
#[post("/pod/upgrade/<module_uuid>")]
pub fn upgrade_module(module_uuid: &str, current_user: User, db: DB) -> APIResponse {
    // Parse and check if we got a valid id
    let result = Uuid::parse_str(module_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();
    let pod = current_user.get_pod(&db);

    // Get the module
    let module_result = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .filter(module_dsl::pod_id.eq(pod.id))
        .first::<Module>(&*db);
    if module_result.is_err() {
        return bad_request().message("No module with this id.");
    }
    let module = module_result.unwrap();
    let level = module.level + 1;

    // Get all needed info for resource manipulation
    let module_list = get_module_list();

    let (pod, queue) = current_user.get_pod_and_queue(&db);

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results::<Resource>(&*db)
        .expect("Failed to get user resources.");

    // Add resources from module to pod resources
    let all_levels = &module_list
                          .get(&ModuleTypes::from_string(&module.name).unwrap())
                          .unwrap()
                          .levels;

    // Check if there is a next level.
    if level > all_levels.len() as i32 {
        return bad_request().message("Already at max level");
    }

    let level_index: usize = (level-1) as usize;
    let costs = &all_levels[level_index].resources;

    if costs.is_some() && !Resource::check_resources(costs, pod_resources, &db) {
        return bad_request().message("Insufficient resources.");
    }

    // Create a new queue entry with the given research type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: None,
        module_name: Some(module.name),
        module_id: Some(queue.id.clone()),
        level: level,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to insert new queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

/// Remove module upgrade from pod queue
#[delete("/pod/upgrade/<module_uuid>")]
pub fn stop_module_upgrade(module_uuid: &str, current_user: User, db: DB) -> APIResponse {
    // Parse and check if we got a valid id
    let result = Uuid::parse_str(module_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();
    let (pod, queue) = current_user.get_pod_and_queue(&db);

    // Get the queue entry
    let queue_entry_result = queue_entries_dsl::queue_entries
        .filter(queue_entries_dsl::module_id.eq(module_id))
        .filter(queue_entries_dsl::queue_id.eq(queue.id))
        .order(queue_entries_dsl::level.desc())
        .first::<QueueEntry>(&*db);
    if queue_entry_result.is_err() {
        return bad_request().message("No queue entry with this id.");
    }
    let queue_entry = queue_entry_result.unwrap();

    let module = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .get_result::<Module>(&*db)
        .expect("Failed to get a module");

    // Get all needed info for resource manipulation
    let module_list = get_module_list();

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results::<Resource>(&*db)
        .expect("Failed to get user resources.");

    // Add resources from module to pod resources
    let all_levels = &module_list
                          .get(&ModuleTypes::from_string(&module.name).unwrap())
                          .unwrap()
                          .levels;
    let costs_result = &all_levels[module.level as usize].resources;

    if let Some(ref costs) = *costs_result {
        Resource::update_resources(costs, pod_resources, false, &db);
    }

    // Remove queue_entry from database
    diesel::delete(queue_entries_dsl::queue_entries
                       .filter(queue_entries_dsl::id.eq(queue_entry.id)))
            .execute(&*db)
            .expect("Failed to remove queue_entry.");

    // Remove module if it's just a upgrade dummy.
    if module.level == 0 {
        diesel::delete(module_dsl::modules
                       .filter(module_dsl::id.eq(module.id)))
            .execute(&*db)
            .expect("Failed to remove module.");
    }

    ok().message("Module upgrade stopped.")
}
<commit_msg>Code restructuring, use pod's get_resources function<commit_after>use diesel;
use diesel::prelude::*;
use rocket_contrib::{JSON, SerdeError};
use uuid::Uuid;

use data::types::*;
use data::modules::get_module_list;
use data::helper::{get_module_dependency_strings, dependencies_fulfilled};

use helpers::db::DB;
use responses::{APIResponse, bad_request, created, ok};

use models::user::User;
use models::module::{NewModule,Module};
use models::research::Research;
use models::resource::Resource;

use schema::modules;
use schema::queue_entries;

use schema::modules::dsl as module_dsl;
use schema::researches::dsl as research_dsl;
use schema::queue_entries::dsl as queue_entries_dsl;

use models::queue::{QueueEntry, NewQueueEntry};
use validation::queue::NewModuleSerializer;

/// The user needs to be logged in to access this route!
///
/// This route returns the list of all modules and their current levels for the pod of the current user.
#[get("/pod")]
pub fn get_modules(current_user: User, db: DB) -> APIResponse {

    let pod = current_user.get_pod(&db);
    let module_result = module_dsl::modules
        .filter(module_dsl::pod_id.eq(pod.id))
        .get_results::<Module>(&*db);

    if module_result.is_ok() {
        let modules = module_result.unwrap();
        return ok().message("Module data.").data(json!(&modules));
    }
    ok().message("No module installed.")
}

/// Add a new module to pod
///
/// This endpoint:
/// - Checks if dependencies for the module are fulfilled
/// - Checks if there are enough resources
/// - Removes resources from db
/// - Creates a new queue entry for this research
#[post("/pod/new", data = "<request_data>", format = "application/json")]
pub fn add_module(request_data: Result<JSON<NewModuleSerializer>, SerdeError>,
                          current_user: User,
                          db: DB)
                          -> APIResponse {

    // Unwrap or return specific error if invalid JSON has been sent.
    if let Err(error) = request_data {
        return bad_request().message(format!("{}", error).as_str());
    };
    let module_data = request_data.unwrap();

    // Check if the given module name maps to a module type.
    let result = ModuleTypes::from_string(&module_data.module_name);
    if result.is_err() {
        return bad_request().message(format!("No such module type `{}`",
                                             module_data.module_name)
                                             .as_str());
    }
    let module_type = result.unwrap();
    let dependency_strings = get_module_dependency_strings(&module_type);

    let (pod, queue) = current_user.get_pod_and_queue(&db);

    let existing_module;
    // Check if there already exists a module for this position
    // We distinguish between stationary and normal modules.

    // Stationary modules have unique names and only exists once
    // per pod.
    if module_data.stationary {
        existing_module = module_dsl::modules
            .count()
            .filter(module_dsl::pod_id.eq(pod.id))
            .filter(module_dsl::name.eq(&module_data.module_name))
            .execute(&*db)
            .unwrap_or(0);
    }
    // Normal modules can exist multiple times. Thereby we just
    // have to check if the requested position is free.
    else {
        existing_module = module_dsl::modules
            .count()
            .filter(module_dsl::pod_id.eq(pod.id))
            .filter(module_dsl::x_pos.eq(module_data.position_x))
            .filter(module_dsl::y_pos.eq(module_data.position_y))
            .execute(&*db)
            .unwrap_or(0);
    };

    // Early return if there already is a module.
    if existing_module != 0 {
        return bad_request().message("There already is a module at this position.");
    }

    // Get the researches the module is depending on
    let dependencies = research_dsl::researches
        .filter(research_dsl::name.eq_any(dependency_strings))
        .get_results::<Research>(&*db);

    // Check if the dependencies are fulfilled.
    // If they're not fulfilled, return a bad request.
    let module_list = get_module_list();
    let fulfilled = dependencies_fulfilled(&module_type,
                                           dependencies,
                                           &module_list);
    if !fulfilled {
        return bad_request().message("Dependencies not fulfilled.");
    }

    // Get cost for level 1
    let costs = &module_list
                          .get(&module_type)
                          .as_ref()
                          .expect("No module in yml for this type.")
                          .levels[0].resources;

    // Check if we have enough resources and subtract them.
    let pod_resources = pod.get_resources(&db);
    if costs.is_some() && !Resource::check_resources(costs, pod_resources, &db) {
        return bad_request().message("Insufficient resources.");
    }

    // Create the new module
    let new_module = NewModule {
        name: module_data.module_name.clone(),
        stationary: module_data.stationary,
        x_pos: module_data.position_x,
        y_pos: module_data.position_y,

        pod_id: Some(pod.id),
        base_id: None,
    };
    let module = diesel::insert(&new_module)
        .into(modules::table)
        .get_result::<Module>(&*db)
        .expect("Failed to create module.");

    // Create a new queue entry with the given module type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: None,
        module_name: Some(module.name),
        module_id: Some(module.id),
        level: 1,
    };
    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to create queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

/// Remove module from pod
#[delete("/pod/<module_uuid>")]
pub fn remove_module(module_uuid: &str, current_user: User, db: DB) -> APIResponse {
    // Parse and check if we got a valid id
    let result = Uuid::parse_str(module_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();

    let pod = current_user.get_pod(&db);

    // Get the module
    let module_result = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .filter(module_dsl::pod_id.eq(pod.id))
        .first::<Module>(&*db);
    if module_result.is_err() {
        return bad_request().message("No module with this id.");
    }
    let module = module_result.unwrap();

    // Remove queue_entry from database
    diesel::delete(module_dsl::modules
                       .filter(module_dsl::id.eq(module.id)))
            .execute(&*db)
            .expect("Failed to remove module.");

    ok().message("Module removed.")
}

/// upgrade module from pod
#[post("/pod/upgrade/<module_uuid>")]
pub fn upgrade_module(module_uuid: &str, current_user: User, db: DB) -> APIResponse {
    // Parse and check if we got a valid id
    let result = Uuid::parse_str(module_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();
    let pod = current_user.get_pod(&db);

    // Get the module
    let module_result = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .filter(module_dsl::pod_id.eq(pod.id))
        .first::<Module>(&*db);
    if module_result.is_err() {
        return bad_request().message("No module with this id.");
    }
    let module = module_result.unwrap();
    let level = module.level + 1;

    // Get all needed info for resource manipulation
    let module_list = get_module_list();

    let (pod, queue) = current_user.get_pod_and_queue(&db);
    let pod_resources = pod.get_resources(&db);

    // Add resources from module to pod resources
    let all_levels = &module_list
                          .get(&ModuleTypes::from_string(&module.name).unwrap())
                          .unwrap()
                          .levels;

    // Check if there is a next level.
    if level > all_levels.len() as i32 {
        return bad_request().message("Already at max level");
    }

    let level_index: usize = (level-1) as usize;
    let costs = &all_levels[level_index].resources;

    if costs.is_some() && !Resource::check_resources(costs, pod_resources, &db) {
        return bad_request().message("Insufficient resources.");
    }

    // Create a new queue entry with the given research type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: None,
        module_name: Some(module.name),
        module_id: Some(queue.id.clone()),
        level: level,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to insert new queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

/// Remove module upgrade from pod queue
#[delete("/pod/upgrade/<module_uuid>")]
pub fn stop_module_upgrade(module_uuid: &str, current_user: User, db: DB) -> APIResponse {
    // Parse and check if we got a valid id
    let result = Uuid::parse_str(module_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let module_id = result.unwrap();
    let (pod, queue) = current_user.get_pod_and_queue(&db);

    // Get the queue entry
    let queue_entry_result = queue_entries_dsl::queue_entries
        .filter(queue_entries_dsl::module_id.eq(module_id))
        .filter(queue_entries_dsl::queue_id.eq(queue.id))
        .order(queue_entries_dsl::level.desc())
        .first::<QueueEntry>(&*db);
    if queue_entry_result.is_err() {
        return bad_request().message("No queue entry with this id.");
    }
    let queue_entry = queue_entry_result.unwrap();

    // Get the module from the queue entry
    let module = module_dsl::modules
        .filter(module_dsl::id.eq(module_id))
        .get_result::<Module>(&*db)
        .expect("Failed to get a module");

    // Get all needed info for resource manipulation
    let module_list = get_module_list();
    let all_levels = &module_list
                          .get(&ModuleTypes::from_string(&module.name).unwrap())
                          .unwrap()
                          .levels;
    let costs_result = &all_levels[module.level as usize].resources;

    // Refund resources
    let pod_resources = pod.get_resources(&db);
    if let Some(ref costs) = *costs_result {
        Resource::update_resources(costs, pod_resources, false, &db);
    }

    // Remove queue_entry from database
    diesel::delete(queue_entries_dsl::queue_entries
                       .filter(queue_entries_dsl::id.eq(queue_entry.id)))
            .execute(&*db)
            .expect("Failed to remove queue_entry.");

    // Remove module if it's just a upgrade dummy.
    if module.level == 0 {
        diesel::delete(module_dsl::modules
                       .filter(module_dsl::id.eq(module.id)))
            .execute(&*db)
            .expect("Failed to remove module.");
    }

    ok().message("Module upgrade stopped.")
}
<|endoftext|>