<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # The bus publisher
//!
//! This publisher works by queueing up changes and then sending them as batches. It is
//! useful for cases where there may be feedback loops or where a single change can have
//! a large list of consequences.
//!
//! Bus publishers can also support multiple input sources: changes are published in the
//! order that they arrive. This can be used to aggregate the output of several components
//! into a single tree.
//!

use std::rc::*;
use std::cell::*;

use super::super::tree::*;
use super::component::*;
use super::immediate_publisher::*;
use super::subscriptionmanager::*;

///
/// A tree change bus queues up published changes until they are ready to send
///
pub struct TreeChangeBus {
    /// Changes that are waiting to be published
    waiting: Rc<RefCell<WaitingChanges>>,

    /// Consumers of this publisher
    subscriptions: Rc<SubscriptionManager<ConsumerRegistration>>
}

///
/// Changes waiting to be sent
///
struct WaitingChanges {
    waiting: Vec<Box<TreeChange>>
}

///
/// Stores a registration of a consumer
///
#[derive(Clone)]
struct ConsumerRegistration {
    address: TreeAddress,
    extent: TreeExtent
}

///
/// A consumer that receives changes from a TreeChangeBus
///
struct BusConsumer {
    subscriptions: Rc<SubscriptionManager<ConsumerRegistration>>
}

///
/// A publisher that sends changes to a TreeChangeBus
///
struct BusPublisher {
    /// Changes that are waiting to be published
    waiting: Rc<RefCell<WaitingChanges>>
}

impl TreeChangeBus {
    ///
    /// Creates a new bus publisher
    ///
    pub fn new() -> TreeChangeBus {
        TreeChangeBus {
            waiting:        Rc::new(RefCell::new(WaitingChanges { waiting: vec![] })),
            subscriptions:  Rc::new(SubscriptionManager::new())
        }
    }

    ///
    /// Creates a publisher that will send notifications to this object
    ///
    pub fn create_publisher(&self) -> PublisherRef {
        Box::new(BusPublisher { waiting: self.waiting.to_owned() })
    }

    ///
    /// Creates a consumer that will receive notifications from this publisher
    ///
    pub fn create_consumer(&self) -> ConsumerRef {
        Box::new(BusConsumer { subscriptions: self.subscriptions.clone() })
    }
}

impl Publisher for BusPublisher {
    ///
    /// Publishes a change to the consumers of this component
    ///
    #[inline]
    fn publish(&mut self, change: TreeChange) {
        self.waiting.borrow_mut().waiting.push(Box::new(change))
    }
}

impl Consumer for BusConsumer {
    ///
    /// Calls a function whenever a particular section of the tree has changed
    ///
    fn subscribe(&mut self, address: TreeAddress, extent: TreeExtent, callback: ConsumerCallback) {
        // Need to persuade rust that it can call the FnMut (assign parameter to a mutable variable)
        let mut also_callback = callback;

        self.subscriptions.add_subscription(ConsumerRegistration { address: address.clone(), extent: extent }, Box::new(move |change| {
            // The change we get from the subscription will have an address relative to the root of the tree
            // Make the subscription change relative to the address that was subscribed to
            let maybe_relative_change = change.relative_to(&address);
            if let Some(relative_change) = maybe_relative_change {
                also_callback(&relative_change);
            }
        }));
    }
}
<commit_msg>Add the ability to pump/flush the contents of a bus publisher<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # The bus publisher
//!
//! This publisher works by queueing up changes and then sending them as batches. It is
//! useful for cases where there may be feedback loops or where a single change can have
//! a large list of consequences.
//!
//! Bus publishers can also support multiple input sources: changes are published in the
//! order that they arrive. This can be used to aggregate the output of several components
//! into a single tree.
//!

use std::rc::*;
use std::cell::*;
use std::mem;

use super::super::tree::*;
use super::component::*;
use super::subscriptionmanager::*;

///
/// A tree change bus queues up published changes until they are ready to send
///
pub struct TreeChangeBus {
    /// Changes that are waiting to be published
    /// (Rc so we can share between publishers, RefCell so we can update, Box so we can swap)
    waiting: Rc<RefCell<Box<WaitingChanges>>>,

    /// Consumers of this publisher
    subscriptions: Rc<SubscriptionManager<ConsumerRegistration>>
}

///
/// Changes waiting to be sent
///
struct WaitingChanges {
    waiting: Vec<Box<TreeChange>>
}

///
/// Stores a registration of a consumer
///
#[derive(Clone)]
struct ConsumerRegistration {
    address: TreeAddress,
    extent: TreeExtent
}

///
/// A consumer that receives changes from a TreeChangeBus
///
struct BusConsumer {
    subscriptions: Rc<SubscriptionManager<ConsumerRegistration>>
}

///
/// A publisher that sends changes to a TreeChangeBus
///
struct BusPublisher {
    /// Changes that are waiting to be published
    waiting: Rc<RefCell<Box<WaitingChanges>>>
}

impl TreeChangeBus {
    ///
    /// Creates a new bus publisher
    ///
    pub fn new() -> TreeChangeBus {
        TreeChangeBus {
            waiting:        Rc::new(RefCell::new(Box::new(WaitingChanges { waiting: vec![] }))),
            subscriptions:  Rc::new(SubscriptionManager::new())
        }
    }

    ///
    /// Creates a publisher that will send notifications to this object
    ///
    pub fn create_publisher(&self) -> PublisherRef {
        Box::new(BusPublisher { waiting: self.waiting.to_owned() })
    }

    ///
    /// Creates a consumer that will receive notifications from this publisher
    ///
    pub fn create_consumer(&self) -> ConsumerRef {
        Box::new(BusConsumer { subscriptions: self.subscriptions.clone() })
    }

    ///
    /// Pumps any published messages to the consumer
    ///
    pub fn pump(&mut self) {
        // Create a new list of waiting items and swap it for the active list
        let to_send = {
            let mut borrowed_waiting    = self.waiting.borrow_mut();
            let mut current_value       = Box::new(WaitingChanges { waiting: vec![] });

            mem::swap(&mut *borrowed_waiting, &mut current_value);

            current_value
        };

        // Publish the items in to_send
        for change in to_send.waiting {
            self.subscriptions.call_subscriptions(&|registration| {
                change.applies_to(&registration.address, &registration.extent).unwrap_or(false)
            }, &change);
        }
    }

    ///
    /// Pumps published messages to the consumer repeatedly until there are none left to process
    ///
    pub fn flush(&mut self) {
        // Pump published messages until no more are generated
        loop {
            if self.waiting.borrow().waiting.len() <= 0 {
                return;
            }

            self.pump();
        }
    }
}

impl Publisher for BusPublisher {
    ///
    /// Publishes a change to the consumers of this component
    ///
    #[inline]
    fn publish(&mut self, change: TreeChange) {
        self.waiting.borrow_mut().waiting.push(Box::new(change))
    }
}

impl Consumer for BusConsumer {
    ///
    /// Calls a function whenever a particular section of the tree has changed
    ///
    fn subscribe(&mut self, address: TreeAddress, extent: TreeExtent, callback: ConsumerCallback) {
        // Need to persuade rust that it can call the FnMut (assign parameter to a mutable variable)
        let mut also_callback = callback;

        self.subscriptions.add_subscription(ConsumerRegistration { address: address.clone(), extent: extent }, Box::new(move |change| {
            // The change we get from the subscription will have an address relative to the root of the tree
            // Make the subscription change relative to the address that was subscribed to
            let maybe_relative_change = change.relative_to(&address);
            if let Some(relative_change) = maybe_relative_change {
                also_callback(&relative_change);
            }
        }));
    }
}

#[cfg(test)]
mod bus_publisher_tests {
    use super::super::super::component::*;
    use super::super::output_tree_publisher::*;
    use super::*;

    #[test]
    pub fn can_pump_bus() {
        let mut input_bus           = TreeChangeBus::new();
        let mut input_publisher     = input_bus.create_publisher();
        let output_publisher        = OutputTreePublisher::new();
        let input_consumer          = input_bus.create_consumer();
        let output_reader           = output_publisher.get_tree_reader();
        let add_one                 = component_fn(|x: &i32| { x+1 });

        let _add_component          = add_one.into_component(input_consumer, output_publisher);

        input_publisher.publish(TreeChange::new(&(), TreeChangeType::Child, Some(&1.to_tree_node())));
        input_bus.pump();
        let output = output_reader();
        assert!(output.get_value().to_int(0) == 2);
    }

    #[test]
    pub fn can_have_feedback() {
        let mut input_bus           = TreeChangeBus::new();
        let mut input_publisher     = input_bus.create_publisher();
        let mut feedback_publisher  = input_bus.create_publisher();
        let output_publisher        = OutputTreePublisher::new();
        let input_consumer          = input_bus.create_consumer();
        let output_reader           = output_publisher.get_tree_reader();

        // Feedback component that sends a message back to itself to reduce the value if it's greater than 0
        let tend_to_zero            = component_fn_mut(move |x: &i32| {
            if *x > 0 {
                feedback_publisher.publish(TreeChange::new(&(), TreeChangeType::Child, Some(&(x-1).to_tree_node())));
            }
            *x
        });

        let _becomes_zero_component = tend_to_zero.into_component(input_consumer, output_publisher);

        input_publisher.publish(TreeChange::new(&(), TreeChangeType::Child, Some(&10.to_tree_node())));
        input_bus.pump();
        assert!(output_reader().get_value().to_int(0) == 10);

        input_bus.pump();
        assert!(output_reader().get_value().to_int(0) == 9);

        input_bus.pump();
        assert!(output_reader().get_value().to_int(0) == 8);

        input_bus.flush();
        assert!(output_reader().get_value().to_int(0) == 0);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::local_data;
use cssparser::ast::{SyntaxError, SourceLocation};

pub struct ErrorLoggerIterator<I>(I);

impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLoggerIterator<I> {
    fn next(&mut self) -> Option<T> {
        let ErrorLoggerIterator(ref mut this) = *self;
        loop {
            match this.next() {
                Some(Ok(v)) => return Some(v),
                Some(Err(error)) => log_css_error(error.location, format!("{:?}", error.reason)),
                None => return None,
            }
        }
   }
}

local_data_key!(silence_errors: ())

pub fn log_css_error(location: SourceLocation, message: &str) {
    // TODO eventually this will got into a "web console" or something.
    if local_data::get(silence_errors, |silenced| silenced.is_none()) {
        error!("{:u}:{:u} {:s}", location.line, location.column, message)
    }
}

pub fn with_errors_silenced<T>(f: || -> T) -> T {
    local_data::set(silence_errors, ());
    let result = f();
    local_data::pop(silence_errors);
    result
}
<commit_msg>auto merge of #2042 : SimonSapin/servo/silence-ua-stylesheet, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::local_data;
use cssparser::ast::{SyntaxError, SourceLocation};

pub struct ErrorLoggerIterator<I>(I);

impl<T, I: Iterator<Result<T, SyntaxError>>> Iterator<T> for ErrorLoggerIterator<I> {
    fn next(&mut self) -> Option<T> {
        let ErrorLoggerIterator(ref mut this) = *self;
        loop {
            match this.next() {
                Some(Ok(v)) => return Some(v),
                Some(Err(error)) => log_css_error(error.location, format!("{:?}", error.reason)),
                None => return None,
            }
        }
   }
}

// FIXME: go back to `()` instead of `bool` after upgrading Rust
// past 898669c4e203ae91e2048fb6c0f8591c867bccc6
// Using bool is a work-around for https://github.com/mozilla/rust/issues/13322
local_data_key!(silence_errors: bool)

pub fn log_css_error(location: SourceLocation, message: &str) {
    // TODO eventually this will got into a "web console" or something.
    if local_data::get(silence_errors, |silenced| silenced.is_none()) {
        error!("{:u}:{:u} {:s}", location.line, location.column, message)
    }
}

pub fn with_errors_silenced<T>(f: || -> T) -> T {
    local_data::set(silence_errors, true);
    let result = f();
    local_data::pop(silence_errors);
    result
}
<|endoftext|>


<commit_before>use parser::Identifier;
use operations::{Operation};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_boolean(scope: &mut ScopeStack) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let bool_type = scope.declare_type(
        Identifier::from("bool"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("bool", bool_type);

    scope.declare_constant(
        Identifier::from("true"),
        bool_type,
        vec![1u8]
    );

    scope.declare_constant(
        Identifier::from("false"),
        bool_type,
        vec![0u8]
    );

    let unit_type = scope.primitives().unit();

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: unit_type,
        },
        move |scope, args, _| {
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => Ok(vec![
                    Operation::Write {
                        target: memory,
                    }
                ]),
                ScopeItem::Constant {type_id, ref bytes} => {
                    let bool_type = scope.primitives().bool();
                    debug_assert_eq!(type_id, bool_type);

                    let u8_type = scope.primitives().u8();
                    let mem = scope.allocate(u8_type);

                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);
                    let value = b'0' + bytes[0];
                    Ok(vec![Operation::TempAllocate {
                        temp: mem,
                        body: vec![
                            Operation::Increment {
                                target: mem.position(),
                                amount: value,
                            },
                            Operation::Write {
                                target: mem,
                            },
                            Operation::Decrement {
                                target: mem.position(),
                                amount: value,
                            },
                        ],
                        should_zero: false,
                    }])
                },
                _ => unreachable!(),
            }
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::ops::Not"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let bool_type = scope.primitives().bool();
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => {
                    let temp0 = scope.allocate(bool_type);
                    Ok(vec![Operation::TempAllocate {
                        temp: temp0,
                        body: vec![
                            // Need to copy to the destination because the algorithm consumes
                            // the operand
                            // After this copy the operand becomes `target` (x in the algorithm)
                            Operation::Copy {
                                source: memory.position(),
                                target: target.position(),
                                size: target.size(),
                            },

                            // Algorithm from: https://esolangs.org/wiki/Brainfuck_algorithms#x_.3D_not_x_.28boolean.2C_logical.29
                            //
                            // temp0[-]+
                            // x[-temp0-]temp0[x+temp0-]
                            //
                            // Results in x = !x and temp0 = 0
                            Operation::Increment {
                                target: temp0.position(),
                                amount: 1,
                            },
                            Operation::Loop {
                                cond: target.position(),
                                body: vec![
                                    Operation::Decrement {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                            Operation::Loop {
                                cond: temp0.position(),
                                body: vec![
                                    Operation::Increment {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                        ],
                        should_zero: false,
                    }])
                },
                ScopeItem::Constant {type_id, ref bytes} => {
                    debug_assert_eq!(type_id, bool_type);
                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);

                    Ok(match bytes[0] {
                        //TODO: Verify if this makes sense or if we should explicitly zero
                        1 => Vec::new(),
                        0 => vec![
                            Operation::Increment {
                                target: target.position(),
                                amount: 1,
                            },
                        ],
                        _ => unreachable!(),
                    })
                },
                _ => unreachable!(),
            }
        }
    );

    // boolean and operator (operator&&) and boolean or operator (operator||)
    // These operations have special names because they are not regular functions
    // that can be defined or overloaded
    // The reason these are not definable is because they have to support short
    // circuiting. This behaviour cannot be modelled by a trait, so these special
    // operators are not definable by the user.
    //TODO

    bool_type
}

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

    use parser::Identifier;
    use operations::scope::{ScopeType, ScopeItem};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().bool(), bool_type_id);
    }

    #[test]
    fn constants() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };

        let true_bytes = match **scope.lookup(&Identifier::from("true")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![1]);
                bytes
            },
            _ => unreachable!(),
        };

        let false_bytes = match **scope.lookup(&Identifier::from("false")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![0]);
                bytes
            },
            _ => unreachable!(),
        };

        // No matter what the value of both, this property must always hold
        assert!(true_bytes != false_bytes);
    }
}
<commit_msg>Fixing a bug in boolean printing where only literals were being printed properly<commit_after>use parser::Identifier;
use operations::{Operation};
use operations::item_type::{ItemType, FuncArgType};
use operations::scope::{ScopeStack, ScopeItem, TypeId};

pub fn define_boolean(scope: &mut ScopeStack) -> TypeId {
    // Taking advantage of the scope system to simulate modules
    // This will be replaced with something better in:
    // https://github.com/brain-lang/brain/issues/37
    scope.push_scope();

    let bool_type = scope.declare_type(
        Identifier::from("bool"),
        ItemType::Primitive(1)
    );
    scope.register_primitive("bool", bool_type);

    scope.declare_constant(
        Identifier::from("true"),
        bool_type,
        vec![1u8]
    );

    scope.declare_constant(
        Identifier::from("false"),
        bool_type,
        vec![0u8]
    );

    let unit_type = scope.primitives().unit();

    scope.declare_builtin_function(
        // Special method for displaying this primitive (used from print/println)
        // This name is such that it could never be called directly
        // from the language itself
        Identifier::from("std::fmt::Display::print"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: unit_type,
        },
        move |scope, args, _| {
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => Ok(vec![
                    Operation::Increment {
                        target: memory.position(),
                        amount: b'0',
                    },
                    Operation::Write {
                        target: memory,
                    },
                    // Need to make sure we reverse the operation after so this function has no
                    // side effects
                    Operation::Decrement {
                        target: memory.position(),
                        amount: b'0',
                    },
                ]),
                ScopeItem::Constant {type_id, ref bytes} => {
                    let bool_type = scope.primitives().bool();
                    debug_assert_eq!(type_id, bool_type);

                    let u8_type = scope.primitives().u8();
                    let mem = scope.allocate(u8_type);

                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);
                    let value = b'0' + bytes[0];
                    Ok(vec![Operation::TempAllocate {
                        temp: mem,
                        body: vec![
                            Operation::Increment {
                                target: mem.position(),
                                amount: value,
                            },
                            Operation::Write {
                                target: mem,
                            },
                            Operation::Decrement {
                                target: mem.position(),
                                amount: value,
                            },
                        ],
                        should_zero: false,
                    }])
                },
                _ => unreachable!(),
            }
        }
    );

    scope.declare_builtin_function(
        Identifier::from("std::ops::Not"),
        ItemType::Function {
            args: vec![FuncArgType::Arg(bool_type)],
            return_type: bool_type,
        },
        move |scope, args, target| {
            let bool_type = scope.primitives().bool();
            match args[0] {
                ScopeItem::TypedBlock {memory, ..} => {
                    let temp0 = scope.allocate(bool_type);
                    Ok(vec![Operation::TempAllocate {
                        temp: temp0,
                        body: vec![
                            // Need to copy to the destination because the algorithm consumes
                            // the operand
                            // After this copy the operand becomes `target` (x in the algorithm)
                            Operation::Copy {
                                source: memory.position(),
                                target: target.position(),
                                size: target.size(),
                            },

                            // Algorithm from: https://esolangs.org/wiki/Brainfuck_algorithms#x_.3D_not_x_.28boolean.2C_logical.29
                            //
                            // temp0[-]+
                            // x[-temp0-]temp0[x+temp0-]
                            //
                            // Results in x = !x and temp0 = 0
                            Operation::Increment {
                                target: temp0.position(),
                                amount: 1,
                            },
                            Operation::Loop {
                                cond: target.position(),
                                body: vec![
                                    Operation::Decrement {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                            Operation::Loop {
                                cond: temp0.position(),
                                body: vec![
                                    Operation::Increment {
                                        target: target.position(),
                                        amount: 1,
                                    },
                                    Operation::Decrement {
                                        target: temp0.position(),
                                        amount: 1,
                                    },
                                ],
                            },
                        ],
                        should_zero: false,
                    }])
                },
                ScopeItem::Constant {type_id, ref bytes} => {
                    debug_assert_eq!(type_id, bool_type);
                    // This code assumes that this is 1
                    debug_assert_eq!(bytes.len(), 1);

                    Ok(match bytes[0] {
                        //TODO: Verify if this makes sense or if we should explicitly zero
                        1 => Vec::new(),
                        0 => vec![
                            Operation::Increment {
                                target: target.position(),
                                amount: 1,
                            },
                        ],
                        _ => unreachable!(),
                    })
                },
                _ => unreachable!(),
            }
        }
    );

    // boolean and operator (operator&&) and boolean or operator (operator||)
    // These operations have special names because they are not regular functions
    // that can be defined or overloaded
    // The reason these are not definable is because they have to support short
    // circuiting. This behaviour cannot be modelled by a trait, so these special
    // operators are not definable by the user.
    //TODO

    bool_type
}

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

    use parser::Identifier;
    use operations::scope::{ScopeType, ScopeItem};

    #[test]
    fn defines_primitive() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };
        assert_eq!(scope.primitives().bool(), bool_type_id);
    }

    #[test]
    fn constants() {
        let mut scope = ScopeStack::new();
        define_boolean(&mut scope);

        let bool_type_id = match **scope.lookup_type(&Identifier::from("bool")).first().unwrap() {
            ScopeType::Type(id) => id,
        };

        let true_bytes = match **scope.lookup(&Identifier::from("true")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![1]);
                bytes
            },
            _ => unreachable!(),
        };

        let false_bytes = match **scope.lookup(&Identifier::from("false")).first().unwrap() {
            ScopeItem::Constant {type_id, ref bytes} => {
                assert_eq!(type_id, bool_type_id);
                assert_eq!(*bytes, vec![0]);
                bytes
            },
            _ => unreachable!(),
        };

        // No matter what the value of both, this property must always hold
        assert!(true_bytes != false_bytes);
    }
}
<|endoftext|>


<commit_before>use std::hash::{Hash, Hasher};
use siphasher::sip::SipHasher;
use std::fmt;
use std::io::{self, Write};
use rand::{Rng, ChaChaRng};
use serde::{Serialize};
use bincode;

/// A 128-bit random ID used for rows and ExtendedTimes.
#[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DeterministicRandomId {
  data: [u64; 2],
}
pub struct SiphashIdGenerator {
  data: [SipHasher; 2],
}
impl Write for SiphashIdGenerator {
  fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
    self.data[0].write(bytes);
    self.data[1].write(bytes);
    Ok(bytes.len())
  }
  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}
impl SiphashIdGenerator {
  pub fn generate(&self) -> DeterministicRandomId {
    DeterministicRandomId { data: [self.data[0].finish(), self.data[1].finish()] }
  }
  pub fn new() -> SiphashIdGenerator {
    SiphashIdGenerator {
      data: [SipHasher::new_with_keys(0xb82a9426fd1a574f, 0x9d9d5b703dcb1bcc),
             SipHasher::new_with_keys(0x03e0d6037ff980a4, 0x65b790a0825b83bd)],
    }
  }
}
impl DeterministicRandomId {
  /// Generates a new DeterministicRandomId from any Serialize type, using a cryptographic hash function.
  ///
  /// Generally, calling this with unique inputs is guaranteed to give unique outputs.
  /// However, take note: the serialization doesn't generally include any representation of the
  /// *type* of data serialized, so (for instance) two different structs with the
  /// same fields will produce the same result. If you want to make sure and ID is unique,
  /// a good technique is to use a tuple containing a unique numeric literal
  /// (randomly-generated to be unique to that part of your code):
  ///
  /// ```rust
  /// # type YourDataType = Vec<u64>;
  /// # use time_steward::DeterministicRandomId;
  /// let id = DeterministicRandomId::new(&(0xdefacab1e_bad_1du64, YourDataType::new()));
  /// ```
  ///
  /// Why do we use Serialize rather than Hash?
  /// We need to make sure that DeterministicRandomIds do not vary with CPU endianness.
  /// The trait [Hasher](https://doc.rust-lang.org/std/hash/trait.Hasher.html) "represents the ability to hash an arbitrary stream of bytes",
  /// and typical implementations of Hash submit the bytes in the order they appear
  /// on the current system, not in a standardized order.
  /// Therefore, we generate IDs from Serialize implementors,
  /// because Serialize IS meant to be compatible between platforms.
  pub fn new<T: Serialize>(data: &T) -> DeterministicRandomId {
    let mut writer = SiphashIdGenerator::new();
    bincode::serialize_into(&mut writer, data, bincode::Infinite).unwrap();
    writer.generate()
  }
  /// Rather than implement Rand for this type, we make sure that it can
  /// ONLY be generated from specific RNGs known to be cryptographically secure.
  pub fn from_rng(rng: &mut ChaChaRng) -> DeterministicRandomId {
    DeterministicRandomId { data: [rng.gen::<u64>(), rng.gen::<u64>()] }
  }
  /// TimeSteward implementors use this internally to make sure fiat events have unique ids.
  ///
  /// We combine fiat event ids with unique random data so that TimeSteward impls
  /// can trust them not to collide with *other* TimeIds.
  /// We use + instead of XOR so that this won't fail if the user accidentally
  /// or maliciously calls this BEFORE passing the ids in, too.
  #[doc (hidden)]
  pub fn for_fiat_event_internal(&self) -> DeterministicRandomId {
    DeterministicRandomId {
      data: [self.data[0].wrapping_add(0xc1d40daaee67461d),
             self.data[1].wrapping_add(0xb23ce1f459edefff)],
    }
  }
  /// Returns the internal data of the DeterministicRandomId.
  pub fn data(&self) -> &[u64; 2] {
    &self.data
  }
}
impl fmt::Display for DeterministicRandomId {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "id:{:016x}{:016x}", self.data[0], self.data[1])
  }
}

impl Hash for DeterministicRandomId {
  /// Since this id is already random, we don't need to hash more than 64 bits of it.
  /// We use the lower bits to avoid the small bias created by using the ordering.
  fn hash <H: Hasher> (&self, state: &mut H) {
    self.data [1].hash (state);
  }
}

#[cfg (test)]
mod tests {
  use super::*;
  use serde::Serialize;
  use std::fmt::Debug;

  #[test]
  fn index_1_is_lower_bits() {
    assert! (DeterministicRandomId {data: [1, 0]} > DeterministicRandomId {data: [0, 1]});
  }

  fn test_id_endianness_impl<T: Serialize + Debug>(thing: T, confirm: DeterministicRandomId) {
    println!("DeterministicRandomId::new({:?}) = {:?}", thing, DeterministicRandomId::new(& thing));
    assert_eq! (DeterministicRandomId::new(& thing), confirm);
  }

  #[test]
  fn test_id_endianness() {
    test_id_endianness_impl((),
                            DeterministicRandomId {
                              data: [18033283813966546569, 10131395250899649866],
                            });
    test_id_endianness_impl(1337,
                            DeterministicRandomId {
                              data: [3453333590764588377, 1257515737963236726],
                            });
    let a: (Option<Option<i32>>,) = (Some(None),);
    test_id_endianness_impl(a,
                            DeterministicRandomId {
                              data: [16808472249412258235, 2826611911447572457],
                            });
    test_id_endianness_impl(DeterministicRandomId::new(&0x70f7b85b08ba4fd5u64),
                            DeterministicRandomId {
                              data: [12393903562314107346, 11644372085838480024],
                            });
  }
}
<commit_msg>Corrected test for the new default of little-endian byte order<commit_after>use std::hash::{Hash, Hasher};
use siphasher::sip::SipHasher;
use std::fmt;
use std::io::{self, Write};
use rand::{Rng, ChaChaRng};
use serde::{Serialize};
use bincode;

/// A 128-bit random ID used for rows and ExtendedTimes.
#[derive (Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct DeterministicRandomId {
  data: [u64; 2],
}
pub struct SiphashIdGenerator {
  data: [SipHasher; 2],
}
impl Write for SiphashIdGenerator {
  fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
    self.data[0].write(bytes);
    self.data[1].write(bytes);
    Ok(bytes.len())
  }
  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}
impl SiphashIdGenerator {
  pub fn generate(&self) -> DeterministicRandomId {
    DeterministicRandomId { data: [self.data[0].finish(), self.data[1].finish()] }
  }
  pub fn new() -> SiphashIdGenerator {
    SiphashIdGenerator {
      data: [SipHasher::new_with_keys(0xb82a9426fd1a574f, 0x9d9d5b703dcb1bcc),
             SipHasher::new_with_keys(0x03e0d6037ff980a4, 0x65b790a0825b83bd)],
    }
  }
}
impl DeterministicRandomId {
  /// Generates a new DeterministicRandomId from any Serialize type, using a cryptographic hash function.
  ///
  /// Generally, calling this with unique inputs is guaranteed to give unique outputs.
  /// However, take note: the serialization doesn't generally include any representation of the
  /// *type* of data serialized, so (for instance) two different structs with the
  /// same fields will produce the same result. If you want to make sure and ID is unique,
  /// a good technique is to use a tuple containing a unique numeric literal
  /// (randomly-generated to be unique to that part of your code):
  ///
  /// ```rust
  /// # type YourDataType = Vec<u64>;
  /// # use time_steward::DeterministicRandomId;
  /// let id = DeterministicRandomId::new(&(0xdefacab1e_bad_1du64, YourDataType::new()));
  /// ```
  ///
  /// Why do we use Serialize rather than Hash?
  /// We need to make sure that DeterministicRandomIds do not vary with CPU endianness.
  /// The trait [Hasher](https://doc.rust-lang.org/std/hash/trait.Hasher.html) "represents the ability to hash an arbitrary stream of bytes",
  /// and typical implementations of Hash submit the bytes in the order they appear
  /// on the current system, not in a standardized order.
  /// Therefore, we generate IDs from Serialize implementors,
  /// because Serialize IS meant to be compatible between platforms.
  pub fn new<T: Serialize>(data: &T) -> DeterministicRandomId {
    let mut writer = SiphashIdGenerator::new();
    bincode::serialize_into(&mut writer, data, bincode::Infinite).unwrap();
    writer.generate()
  }
  /// Rather than implement Rand for this type, we make sure that it can
  /// ONLY be generated from specific RNGs known to be cryptographically secure.
  pub fn from_rng(rng: &mut ChaChaRng) -> DeterministicRandomId {
    DeterministicRandomId { data: [rng.gen::<u64>(), rng.gen::<u64>()] }
  }
  /// TimeSteward implementors use this internally to make sure fiat events have unique ids.
  ///
  /// We combine fiat event ids with unique random data so that TimeSteward impls
  /// can trust them not to collide with *other* TimeIds.
  /// We use + instead of XOR so that this won't fail if the user accidentally
  /// or maliciously calls this BEFORE passing the ids in, too.
  #[doc (hidden)]
  pub fn for_fiat_event_internal(&self) -> DeterministicRandomId {
    DeterministicRandomId {
      data: [self.data[0].wrapping_add(0xc1d40daaee67461d),
             self.data[1].wrapping_add(0xb23ce1f459edefff)],
    }
  }
  /// Returns the internal data of the DeterministicRandomId.
  pub fn data(&self) -> &[u64; 2] {
    &self.data
  }
}
impl fmt::Display for DeterministicRandomId {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "id:{:016x}{:016x}", self.data[0], self.data[1])
  }
}

impl Hash for DeterministicRandomId {
  /// Since this id is already random, we don't need to hash more than 64 bits of it.
  /// We use the lower bits to avoid the small bias created by using the ordering.
  fn hash <H: Hasher> (&self, state: &mut H) {
    self.data [1].hash (state);
  }
}

#[cfg (test)]
mod tests {
  use super::*;
  use serde::Serialize;
  use std::fmt::Debug;

  #[test]
  fn index_1_is_lower_bits() {
    assert! (DeterministicRandomId {data: [1, 0]} > DeterministicRandomId {data: [0, 1]});
  }

  fn test_id_endianness_impl<T: Serialize + Debug>(thing: T, confirm: DeterministicRandomId) {
    println!("DeterministicRandomId::new({:?}) = {:?}", thing, DeterministicRandomId::new(& thing));
    assert_eq! (DeterministicRandomId::new(& thing), confirm);
  }

  #[test]
  fn test_id_endianness() {
    // note: these values should be correct if bincode serializes in little-endian order,
    // which is the current default.
    test_id_endianness_impl((),
                            DeterministicRandomId {
                              data: [18033283813966546569, 10131395250899649866],
                            });
    test_id_endianness_impl(1337,
                            DeterministicRandomId {
                              data: [13768737740279017279, 4442460339052638143],
                            });
    let a: (Option<Option<i32>>,) = (Some(None),);
    test_id_endianness_impl(a,
                            DeterministicRandomId {
                              data: [16808472249412258235, 2826611911447572457],
                            });
    test_id_endianness_impl(DeterministicRandomId::new(&0x70f7b85b08ba4fd5u64),
                            DeterministicRandomId {
                              data: [15806623539012513099, 2804789490945853517],
                            });
  }
}
<|endoftext|>


<commit_before>//! Interpreting user leaderboard page results.

use super::find_rank;
use EndpointResult;
use data;
use error::{Result, ApiError};
use std::collections::HashMap;
use std::marker::PhantomData;

/// Raw list results.
#[derive(Deserialize, Clone, Debug)]
#[doc(hidden)]
pub struct Response {
    ok: i32,
    count: u64,
    list: Vec<ResponseRankedUser>,
    users: HashMap<String, ExtendedUserInfo>,
}

#[derive(Deserialize, Clone, Hash, Debug)]
struct ResponseRankedUser {
    //_id: String, // exists, but I don't know what it's for.
    rank: u32,
    score: u64,
    season: String,
    user: String,
}

#[derive(Deserialize, Clone, Hash, Debug)]
struct ExtendedUserInfo {
    _id: String,
    username: String,
    gcl: u64,
    badge: data::Badge,
}

/// Single leaderboard page of users.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LeaderboardPage {
    /// The total number of users present in this leaderboard.
    pub total_count: u64,
    /// List of users with user IDs, rank, score and season. I'm... not sure why season is in here and not a top-level
    /// thing, but that seems to be how it is.
    pub ranks: Vec<RankedUser>,
    /// Details about all users listed. This is a map from user_id to info struct containing username, gcl, and badge.
    pub user_details: HashMap<String, UserDetails>,
    /// Phantom data in order to allow adding any additional fields in the future.
    _phantom: PhantomData<()>,
}

/// Alias since the format is the same for the inner user ranks and found user ranks.
pub type RankedUser = find_rank::FoundUserRank;

/// Details on any user in a given leaderboard page result.
#[derive(Serialize, Deserialize, Clone, Hash, Debug)]
pub struct UserDetails {
    /// The user's id.
    pub user_id: String,
    /// The user's badge.
    pub badge: data::Badge,
    /// The user's GCL points (calculate to get GCL)
    pub gcl_points: u64,
    /// The user's username.
    pub username: String,
    /// Phantom data in order to allow adding any additional fields in the future.
    _phantom: PhantomData<()>,
}

impl EndpointResult for LeaderboardPage {
    type RequestResult = Response;
    type ErrorResult = data::ApiError;

    fn from_raw(raw: Response) -> Result<LeaderboardPage> {
        let Response { ok, count: total_count, list, users: user_details } = raw;

        if ok != 1 {
            return Err(ApiError::NotOk(ok).into());
        }

        Ok(LeaderboardPage {
            total_count: total_count,
            ranks: list.into_iter()
                .map(|info| {
                    RankedUser {
                        season_id: info.season,
                        user_id: info.user,
                        rank: info.rank,
                        raw_score: info.score,
                        _phantom: PhantomData,
                    }
                })
                .collect(),
            user_details: user_details.into_iter()
                .map(|(user_id, data)| {
                    (user_id,
                     UserDetails {
                         user_id: data._id,
                         badge: data.badge,
                         gcl_points: data.gcl,
                         username: data.username,
                         _phantom: PhantomData,
                     })
                })
                .collect(),
            _phantom: PhantomData,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::LeaderboardPage;
    use EndpointResult;
    use serde_json;

    fn test_parse(json: serde_json::Value) {
        let response = serde_json::from_value(json).unwrap();

        let _ = LeaderboardPage::from_raw(response).unwrap();
    }

    #[test]
    fn parse_sample_gcl() {
        test_parse(json! ({
            "count": 2322,
            "list": [
                {
                    "_id": "589125339c76d517f2424784",
                    "rank": 2,
                    "score": 408713655i64,
                    "season": "2017-02",
                    "user": "5769c4e64673b865097b926e"
                },
                {
                    "_id": "589125339c76d517f242475e",
                    "rank": 3,
                    "score": 362128212i64,
                    "season": "2017-02",
                    "user": "56dce30812162c1c122ae1b5"
                }
            ],
            "ok": 1,
            "users": {
                "56dce30812162c1c122ae1b5": {
                    "_id": "56dce30812162c1c122ae1b5",
                    "badge": {
                        "color1": "#511e00",
                        "color2": "#003058",
                        "color3": "#00450e",
                        "flip": true,
                        "param": -47,
                        "type": 1
                    },
                    "gcl": 2097651263i64,
                    "username": "taiga"
                },
                "5769c4e64673b865097b926e": {
                    "_id": "5769c4e64673b865097b926e",
                    "badge": {
                        "color1": "#000000",
                        "color2": "#000000",
                        "color3": "#430000",
                        "flip": false,
                        "param": 49,
                        "type": 8
                    },
                    "gcl": 1845096413i64,
                    "username": "Bovius"
                }
            }
        }));
    }
    #[test]
    fn parse_sample_power() {
        test_parse(json! ({
            "count": 60,
            "list": [
                {
                    "_id": "589125359c76d517f2424cea",
                    "rank": 2,
                    "score": 2653410i64,
                    "season": "2017-02",
                    "user": "567d9401f60a26fc4c41bd38"
                },
                {
                    "_id": "5891259a9c76d517f2427e9d",
                    "rank": 3,
                    "score": 2320123i64,
                    "season": "2017-02",
                    "user": "576aa0ce36db3b70321317fe"
                }
            ],
            "ok": 1,
            "users": {
                "567d9401f60a26fc4c41bd38": {
                    "_id": "567d9401f60a26fc4c41bd38",
                    "badge": {
                        "color1": 73,
                        "color2": 31,
                        "color3": 8,
                        "flip": false,
                        "param": 68,
                        "type": 10
                    },
                    "gcl": 2993541124i64,
                    "username": "bonzaiferroni"
                },
                "576aa0ce36db3b70321317fe": {
                    "_id": "576aa0ce36db3b70321317fe",
                    "badge": {
                        "color1": "#f9a603",
                        "color2": "#f7efe2",
                        "color3": "#f25c00",
                        "flip": false,
                        "param": 0,
                        "type": 9
                    },
                    "gcl": 1982028763i64,
                    "username": "Atavus"
                }
            }
        }));
    }
}
<commit_msg>Expose user details as a Vec to facilitate sending the user results to a larger HashMap for storage.<commit_after>//! Interpreting user leaderboard page results.

use super::find_rank;
use EndpointResult;
use data;
use error::{Result, ApiError};
use std::collections::HashMap;
use std::marker::PhantomData;

/// Raw list results.
#[derive(Deserialize, Clone, Debug)]
#[doc(hidden)]
pub struct Response {
    ok: i32,
    count: u64,
    list: Vec<ResponseRankedUser>,
    users: HashMap<String, ExtendedUserInfo>,
}

#[derive(Deserialize, Clone, Hash, Debug)]
struct ResponseRankedUser {
    //_id: String, // exists, but I don't know what it's for.
    rank: u32,
    score: u64,
    season: String,
    user: String,
}

#[derive(Deserialize, Clone, Hash, Debug)]
struct ExtendedUserInfo {
    _id: String,
    username: String,
    gcl: u64,
    badge: data::Badge,
}

/// Single leaderboard page of users.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LeaderboardPage {
    /// The total number of users present in this leaderboard.
    pub total_count: u64,
    /// List of users with user IDs, rank, score and season. I'm... not sure why season is in here and not a top-level
    /// thing, but that seems to be how it is.
    pub ranks: Vec<RankedUser>,
    /// Details about all users listed. This is a map from user_id to info struct containing username, gcl, and badge.
    #[serde(with = "::tuple_vec_map")]
    pub user_details: Vec<(String, UserDetails)>,
    /// Phantom data in order to allow adding any additional fields in the future.
    _phantom: PhantomData<()>,
}

/// Alias since the format is the same for the inner user ranks and found user ranks.
pub type RankedUser = find_rank::FoundUserRank;

/// Details on any user in a given leaderboard page result.
#[derive(Serialize, Deserialize, Clone, Hash, Debug)]
pub struct UserDetails {
    /// The user's id.
    pub user_id: String,
    /// The user's badge.
    pub badge: data::Badge,
    /// The user's GCL points (calculate to get GCL)
    pub gcl_points: u64,
    /// The user's username.
    pub username: String,
    /// Phantom data in order to allow adding any additional fields in the future.
    _phantom: PhantomData<()>,
}

impl EndpointResult for LeaderboardPage {
    type RequestResult = Response;
    type ErrorResult = data::ApiError;

    fn from_raw(raw: Response) -> Result<LeaderboardPage> {
        let Response { ok, count: total_count, list, users: user_details } = raw;

        if ok != 1 {
            return Err(ApiError::NotOk(ok).into());
        }

        Ok(LeaderboardPage {
            total_count: total_count,
            ranks: list.into_iter()
                .map(|info| {
                    RankedUser {
                        season_id: info.season,
                        user_id: info.user,
                        rank: info.rank,
                        raw_score: info.score,
                        _phantom: PhantomData,
                    }
                })
                .collect(),
            user_details: user_details.into_iter()
                .map(|(user_id, data)| {
                    (user_id,
                     UserDetails {
                         user_id: data._id,
                         badge: data.badge,
                         gcl_points: data.gcl,
                         username: data.username,
                         _phantom: PhantomData,
                     })
                })
                .collect(),
            _phantom: PhantomData,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::LeaderboardPage;
    use EndpointResult;
    use serde_json;

    fn test_parse(json: serde_json::Value) {
        let response = serde_json::from_value(json).unwrap();

        let _ = LeaderboardPage::from_raw(response).unwrap();
    }

    #[test]
    fn parse_sample_gcl() {
        test_parse(json! ({
            "count": 2322,
            "list": [
                {
                    "_id": "589125339c76d517f2424784",
                    "rank": 2,
                    "score": 408713655i64,
                    "season": "2017-02",
                    "user": "5769c4e64673b865097b926e"
                },
                {
                    "_id": "589125339c76d517f242475e",
                    "rank": 3,
                    "score": 362128212i64,
                    "season": "2017-02",
                    "user": "56dce30812162c1c122ae1b5"
                }
            ],
            "ok": 1,
            "users": {
                "56dce30812162c1c122ae1b5": {
                    "_id": "56dce30812162c1c122ae1b5",
                    "badge": {
                        "color1": "#511e00",
                        "color2": "#003058",
                        "color3": "#00450e",
                        "flip": true,
                        "param": -47,
                        "type": 1
                    },
                    "gcl": 2097651263i64,
                    "username": "taiga"
                },
                "5769c4e64673b865097b926e": {
                    "_id": "5769c4e64673b865097b926e",
                    "badge": {
                        "color1": "#000000",
                        "color2": "#000000",
                        "color3": "#430000",
                        "flip": false,
                        "param": 49,
                        "type": 8
                    },
                    "gcl": 1845096413i64,
                    "username": "Bovius"
                }
            }
        }));
    }
    #[test]
    fn parse_sample_power() {
        test_parse(json! ({
            "count": 60,
            "list": [
                {
                    "_id": "589125359c76d517f2424cea",
                    "rank": 2,
                    "score": 2653410i64,
                    "season": "2017-02",
                    "user": "567d9401f60a26fc4c41bd38"
                },
                {
                    "_id": "5891259a9c76d517f2427e9d",
                    "rank": 3,
                    "score": 2320123i64,
                    "season": "2017-02",
                    "user": "576aa0ce36db3b70321317fe"
                }
            ],
            "ok": 1,
            "users": {
                "567d9401f60a26fc4c41bd38": {
                    "_id": "567d9401f60a26fc4c41bd38",
                    "badge": {
                        "color1": 73,
                        "color2": 31,
                        "color3": 8,
                        "flip": false,
                        "param": 68,
                        "type": 10
                    },
                    "gcl": 2993541124i64,
                    "username": "bonzaiferroni"
                },
                "576aa0ce36db3b70321317fe": {
                    "_id": "576aa0ce36db3b70321317fe",
                    "badge": {
                        "color1": "#f9a603",
                        "color2": "#f7efe2",
                        "color3": "#f25c00",
                        "flip": false,
                        "param": 0,
                        "type": 9
                    },
                    "gcl": 1982028763i64,
                    "username": "Atavus"
                }
            }
        }));
    }
}
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;
use std::cmp;
use std::default::Default;
use std::fmt;
use std::str;

/// Represents a quality used in quality values.
///
/// Can be created with the `q` function.
///
/// # Implementation notes
///
/// The quality value is defined as a number between 0 and 1 with three decimal places. This means
/// there are 1000 possible values. Since floating point numbers are not exact and the smallest
/// floating point data type (`f32`) consumes four bytes, hyper uses an `u16` value to store the
/// quality internally. For performance reasons you may set quality directly to a value between
/// 0 and 1000 e.g. `Quality(532)` matches the quality `q=0.532`.
///
/// [RFC7231 Section 5.3.1](https://tools.ietf.org/html/rfc7231#section-5.3.1)
/// gives more information on quality values in HTTP header fields.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Quality(pub u16);

impl fmt::Display for Quality {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            1000 => Ok(()),
            0 => f.write_str("; q=0"),
            x => write!(f, "; q=0.{}", format!("{:03}", x).trim_right_matches('0'))
        }
    }
}

impl Default for Quality {
    fn default() -> Quality {
        Quality(1000)
    }
}

/// Represents an item with a quality value as defined in
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.1).
#[derive(Clone, PartialEq, Debug)]
pub struct QualityItem<T> {
    /// The actual contents of the field.
    pub item: T,
    /// The quality (client or server preference) for the value.
    pub quality: Quality,
}

impl<T> QualityItem<T> {
    /// Creates a new `QualityItem` from an item and a quality.
    /// The item can be of any type.
    /// The quality should be a value in the range [0, 1].
    pub fn new(item: T, quality: Quality) -> QualityItem<T> {
        QualityItem {
            item: item,
            quality: quality
        }
    }
}

impl<T: PartialEq> cmp::PartialOrd for QualityItem<T> {
    fn partial_cmp(&self, other: &QualityItem<T>) -> Option<cmp::Ordering> {
        self.quality.partial_cmp(&other.quality)
    }
}

impl<T: fmt::Display> fmt::Display for QualityItem<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.item, format!("{}", self.quality))
    }
}

impl<T: str::FromStr> str::FromStr for QualityItem<T> {
    type Err = ::Error;
    fn from_str(s: &str) -> ::Result<QualityItem<T>> {
        if !s.is_ascii() {
            return Err(::Error::Header);
        }
        // Set defaults used if parsing fails.
        let mut raw_item = s;
        let mut quality = 1f32;

        let parts: Vec<&str> = s.rsplitn(2, ';').map(|x| x.trim()).collect();
        if parts.len() == 2 {
            if parts[0].len() < 2 {
                return Err(::Error::Header);
            }
            let start = &parts[0][0..2];
            if start == "q=" || start == "Q=" {
                let q_part = &parts[0][2..parts[0].len()];
                if q_part.len() > 5 {
                    return Err(::Error::Header);
                }
                match q_part.parse::<f32>() {
                    Ok(q_value) => {
                        if 0f32 <= q_value && q_value <= 1f32 {
                            quality = q_value;
                            raw_item = parts[1];
                            } else {
                                return Err(::Error::Header);
                            }
                        },
                    Err(_) => return Err(::Error::Header),
                }
            }
        }
        match raw_item.parse::<T>() {
            // we already checked above that the quality is within range
            Ok(item) => Ok(QualityItem::new(item, from_f32(quality))),
            Err(_) => Err(::Error::Header),
        }
    }
}

fn from_f32(f: f32) -> Quality {
    // this function is only used internally. A check that `f` is within range
    // should be done before calling this method. Just in case, this
    // debug_assert should catch if we were forgetful
    debug_assert!(f >= 0f32 && f <= 1f32, "q value must be between 0.0 and 1.0");
    Quality((f * 1000f32) as u16)
}

/// Convenience function to wrap a value in a `QualityItem`
/// Sets `q` to the default 1.0
pub fn qitem<T>(item: T) -> QualityItem<T> {
    QualityItem::new(item, Default::default())
}

/// Convenience function to create a `Quality` from a float.
pub fn q(f: f32) -> Quality {
    assert!(f >= 0f32 && f <= 1f32, "q value must be between 0.0 and 1.0");
    from_f32(f)
}

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

    #[test]
    fn test_quality_item_show1() {
        let x = qitem(Chunked);
        assert_eq!(format!("{}", x), "chunked");
    }
    #[test]
    fn test_quality_item_show2() {
        let x = QualityItem::new(Chunked, Quality(1));
        assert_eq!(format!("{}", x), "chunked; q=0.001");
    }
    #[test]
    fn test_quality_item_show3() {
        // Custom value
        let x = QualityItem{
            item: EncodingExt("identity".to_owned()),
            quality: Quality(500),
        };
        assert_eq!(format!("{}", x), "identity; q=0.5");
    }

    #[test]
    fn test_quality_item_from_str1() {
        let x: ::Result<QualityItem<Encoding>> = "chunked".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Chunked, quality: Quality(1000), });
    }
    #[test]
    fn test_quality_item_from_str2() {
        let x: ::Result<QualityItem<Encoding>> = "chunked; q=1".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Chunked, quality: Quality(1000), });
    }
    #[test]
    fn test_quality_item_from_str3() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=0.5".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Gzip, quality: Quality(500), });
    }
    #[test]
    fn test_quality_item_from_str4() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=0.273".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Gzip, quality: Quality(273), });
    }
    #[test]
    fn test_quality_item_from_str5() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=0.2739999".parse();
        assert!(x.is_err());
    }
    #[test]
    fn test_quality_item_from_str6() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=2".parse();
        assert!(x.is_err());
    }
    #[test]
    fn test_quality_item_ordering() {
        let x: QualityItem<Encoding> = "gzip; q=0.5".parse().ok().unwrap();
        let y: QualityItem<Encoding> = "gzip; q=0.273".parse().ok().unwrap();
        let comparision_result: bool = x.gt(&y);
        assert!(comparision_result)
    }

    #[test]
    fn test_quality() {
        assert_eq!(q(0.5), Quality(500));
    }

    #[test]
    fn test_quality2() {
        assert_eq!(format!("{}", q(0.0)), "; q=0");
    }

    #[test]
    #[should_panic] // FIXME - 32-bit msvc unwinding broken
    #[cfg_attr(all(target_arch="x86", target_env="msvc"), ignore)]
    fn test_quality_invalid() {
        q(-1.0);
    }

    #[test]
    #[should_panic] // FIXME - 32-bit msvc unwinding broken
    #[cfg_attr(all(target_arch="x86", target_env="msvc"), ignore)]
    fn test_quality_invalid2() {
        q(2.0);
    }

    #[test]
    fn test_fuzzing_bugs() {
        assert!("99999;".parse::<QualityItem<String>>().is_err());
        assert!("\x0d;;;=\u{d6aa}==".parse::<QualityItem<String>>().is_err())
    }
}
<commit_msg>docs(quality_item): fix typo<commit_after>use std::ascii::AsciiExt;
use std::cmp;
use std::default::Default;
use std::fmt;
use std::str;

/// Represents a quality used in quality values.
///
/// Can be created with the `q` function.
///
/// # Implementation notes
///
/// The quality value is defined as a number between 0 and 1 with three decimal places. This means
/// there are 1001 possible values. Since floating point numbers are not exact and the smallest
/// floating point data type (`f32`) consumes four bytes, hyper uses an `u16` value to store the
/// quality internally. For performance reasons you may set quality directly to a value between
/// 0 and 1000 e.g. `Quality(532)` matches the quality `q=0.532`.
///
/// [RFC7231 Section 5.3.1](https://tools.ietf.org/html/rfc7231#section-5.3.1)
/// gives more information on quality values in HTTP header fields.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Quality(pub u16);

impl fmt::Display for Quality {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            1000 => Ok(()),
            0 => f.write_str("; q=0"),
            x => write!(f, "; q=0.{}", format!("{:03}", x).trim_right_matches('0'))
        }
    }
}

impl Default for Quality {
    fn default() -> Quality {
        Quality(1000)
    }
}

/// Represents an item with a quality value as defined in
/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.1).
#[derive(Clone, PartialEq, Debug)]
pub struct QualityItem<T> {
    /// The actual contents of the field.
    pub item: T,
    /// The quality (client or server preference) for the value.
    pub quality: Quality,
}

impl<T> QualityItem<T> {
    /// Creates a new `QualityItem` from an item and a quality.
    /// The item can be of any type.
    /// The quality should be a value in the range [0, 1].
    pub fn new(item: T, quality: Quality) -> QualityItem<T> {
        QualityItem {
            item: item,
            quality: quality
        }
    }
}

impl<T: PartialEq> cmp::PartialOrd for QualityItem<T> {
    fn partial_cmp(&self, other: &QualityItem<T>) -> Option<cmp::Ordering> {
        self.quality.partial_cmp(&other.quality)
    }
}

impl<T: fmt::Display> fmt::Display for QualityItem<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.item, format!("{}", self.quality))
    }
}

impl<T: str::FromStr> str::FromStr for QualityItem<T> {
    type Err = ::Error;
    fn from_str(s: &str) -> ::Result<QualityItem<T>> {
        if !s.is_ascii() {
            return Err(::Error::Header);
        }
        // Set defaults used if parsing fails.
        let mut raw_item = s;
        let mut quality = 1f32;

        let parts: Vec<&str> = s.rsplitn(2, ';').map(|x| x.trim()).collect();
        if parts.len() == 2 {
            if parts[0].len() < 2 {
                return Err(::Error::Header);
            }
            let start = &parts[0][0..2];
            if start == "q=" || start == "Q=" {
                let q_part = &parts[0][2..parts[0].len()];
                if q_part.len() > 5 {
                    return Err(::Error::Header);
                }
                match q_part.parse::<f32>() {
                    Ok(q_value) => {
                        if 0f32 <= q_value && q_value <= 1f32 {
                            quality = q_value;
                            raw_item = parts[1];
                            } else {
                                return Err(::Error::Header);
                            }
                        },
                    Err(_) => return Err(::Error::Header),
                }
            }
        }
        match raw_item.parse::<T>() {
            // we already checked above that the quality is within range
            Ok(item) => Ok(QualityItem::new(item, from_f32(quality))),
            Err(_) => Err(::Error::Header),
        }
    }
}

fn from_f32(f: f32) -> Quality {
    // this function is only used internally. A check that `f` is within range
    // should be done before calling this method. Just in case, this
    // debug_assert should catch if we were forgetful
    debug_assert!(f >= 0f32 && f <= 1f32, "q value must be between 0.0 and 1.0");
    Quality((f * 1000f32) as u16)
}

/// Convenience function to wrap a value in a `QualityItem`
/// Sets `q` to the default 1.0
pub fn qitem<T>(item: T) -> QualityItem<T> {
    QualityItem::new(item, Default::default())
}

/// Convenience function to create a `Quality` from a float.
pub fn q(f: f32) -> Quality {
    assert!(f >= 0f32 && f <= 1f32, "q value must be between 0.0 and 1.0");
    from_f32(f)
}

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

    #[test]
    fn test_quality_item_show1() {
        let x = qitem(Chunked);
        assert_eq!(format!("{}", x), "chunked");
    }
    #[test]
    fn test_quality_item_show2() {
        let x = QualityItem::new(Chunked, Quality(1));
        assert_eq!(format!("{}", x), "chunked; q=0.001");
    }
    #[test]
    fn test_quality_item_show3() {
        // Custom value
        let x = QualityItem{
            item: EncodingExt("identity".to_owned()),
            quality: Quality(500),
        };
        assert_eq!(format!("{}", x), "identity; q=0.5");
    }

    #[test]
    fn test_quality_item_from_str1() {
        let x: ::Result<QualityItem<Encoding>> = "chunked".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Chunked, quality: Quality(1000), });
    }
    #[test]
    fn test_quality_item_from_str2() {
        let x: ::Result<QualityItem<Encoding>> = "chunked; q=1".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Chunked, quality: Quality(1000), });
    }
    #[test]
    fn test_quality_item_from_str3() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=0.5".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Gzip, quality: Quality(500), });
    }
    #[test]
    fn test_quality_item_from_str4() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=0.273".parse();
        assert_eq!(x.unwrap(), QualityItem{ item: Gzip, quality: Quality(273), });
    }
    #[test]
    fn test_quality_item_from_str5() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=0.2739999".parse();
        assert!(x.is_err());
    }
    #[test]
    fn test_quality_item_from_str6() {
        let x: ::Result<QualityItem<Encoding>> = "gzip; q=2".parse();
        assert!(x.is_err());
    }
    #[test]
    fn test_quality_item_ordering() {
        let x: QualityItem<Encoding> = "gzip; q=0.5".parse().ok().unwrap();
        let y: QualityItem<Encoding> = "gzip; q=0.273".parse().ok().unwrap();
        let comparision_result: bool = x.gt(&y);
        assert!(comparision_result)
    }

    #[test]
    fn test_quality() {
        assert_eq!(q(0.5), Quality(500));
    }

    #[test]
    fn test_quality2() {
        assert_eq!(format!("{}", q(0.0)), "; q=0");
    }

    #[test]
    #[should_panic] // FIXME - 32-bit msvc unwinding broken
    #[cfg_attr(all(target_arch="x86", target_env="msvc"), ignore)]
    fn test_quality_invalid() {
        q(-1.0);
    }

    #[test]
    #[should_panic] // FIXME - 32-bit msvc unwinding broken
    #[cfg_attr(all(target_arch="x86", target_env="msvc"), ignore)]
    fn test_quality_invalid2() {
        q(2.0);
    }

    #[test]
    fn test_fuzzing_bugs() {
        assert!("99999;".parse::<QualityItem<String>>().is_err());
        assert!("\x0d;;;=\u{d6aa}==".parse::<QualityItem<String>>().is_err())
    }
}
<|endoftext|>


<commit_before>use crate::homectl_core::{
    device::{Device, DeviceColor, DeviceState, Light},
    events::{Message, TxEventChannel},
    integration::{Integration, IntegrationId},
    scene::{color_config_as_device_color, ColorConfig},
};
use anyhow::{Context, Result};
use async_trait::async_trait;
use palette::Gradient;
use serde::Deserialize;
use std::time::Duration;
use tokio::time::{interval_at, Instant};

#[derive(Clone, Debug, Deserialize)]
pub struct CircadianConfig {
    device_name: String,
    day_color: ColorConfig,
    day_fade_start: String,
    day_fade_duration_hours: i64,
    night_color: ColorConfig,
    night_fade_start: String,
    night_fade_duration_hours: i64,
}

#[derive(Clone)]
pub struct Circadian {
    id: String,
    config: CircadianConfig,
    sender: TxEventChannel,
    converted_day_color: DeviceColor,
    converted_night_color: DeviceColor,
}

#[async_trait]
impl Integration for Circadian {
    fn new(id: &IntegrationId, config: &config::Value, sender: TxEventChannel) -> Result<Self> {
        let config: CircadianConfig = config
            .clone()
            .try_into()
            .context("Failed to deserialize config of Circadian integration")?;

        Ok(Circadian {
            id: id.clone(),
            config: config.clone(),
            sender,
            converted_day_color: color_config_as_device_color(config.day_color),
            converted_night_color: color_config_as_device_color(config.night_color),
        })
    }

    async fn register(&mut self) -> Result<()> {
        let device = mk_circadian_device(self);

        self.sender
            .send(Message::IntegrationDeviceRefresh { device })
            .await;

        println!("registered circadian integration {}", self.id);

        Ok(())
    }

    async fn start(&mut self) -> Result<()> {
        println!("started circadian integration {}", self.id);

        let circadian = self.clone();

        // FIXME: can we restructure the integrations / devices systems such
        // that polling is not needed here?
        tokio::spawn(async { poll_sensor(circadian).await });

        Ok(())
    }

    async fn set_integration_device_state(&mut self, _device: Device) {
        // do nothing
    }
}

fn get_night_fade(circadian: &Circadian) -> f32 {
    let local = chrono::Local::now().naive_local().time();

    let day_fade_start =
        chrono::NaiveTime::parse_from_str(&circadian.config.day_fade_start, "%H:%M").unwrap();
    let day_fade_duration = chrono::Duration::hours(circadian.config.day_fade_duration_hours);
    let day_fade_end = day_fade_start + day_fade_duration;

    let night_fade_start =
        chrono::NaiveTime::parse_from_str(&circadian.config.night_fade_start, "%H:%M").unwrap();
    let night_fade_duration = chrono::Duration::hours(circadian.config.night_fade_duration_hours);
    let night_fade_end = night_fade_start + night_fade_duration;

    if local <= day_fade_start || local >= night_fade_end {
        return 1.0;
    }
    if local >= day_fade_end && local <= night_fade_start {
        return 0.0;
    }

    if local < day_fade_end {
        // fading from night to day
        let d = local - day_fade_start;
        let p = d.num_milliseconds() as f32 / day_fade_duration.num_milliseconds() as f32;

        1.0 - p
    } else {
        // fading from day to night
        let d = local - night_fade_start;
        let p = d.num_milliseconds() as f32 / night_fade_duration.num_milliseconds() as f32;

        p
    }
}

fn get_circadian_color(circadian: &Circadian) -> DeviceColor {
    let gradient = Gradient::new(vec![
        circadian.converted_day_color,
        circadian.converted_night_color,
    ]);

    let i = get_night_fade(circadian);

    gradient.get(i)
}

async fn poll_sensor(circadian: Circadian) {
    let poll_rate = Duration::from_millis(60 * 1000);
    let start = Instant::now() + poll_rate;
    let mut interval = interval_at(start, poll_rate);

    loop {
        interval.tick().await;

        let sender = circadian.sender.clone();

        let device = mk_circadian_device(&circadian);
        sender.send(Message::SetDeviceState { device }).await;
    }
}

fn mk_circadian_device(circadian: &Circadian) -> Device {
    let state = DeviceState::Light(Light {
        power: true,
        brightness: Some(1.0),
        color: Some(get_circadian_color(circadian)),
    });

    let device = Device {
        id: "color".into(),
        name: circadian.config.device_name.clone(),
        integration_id: circadian.id.clone(),
        scene: None,
        state,
    };

    device
}
<commit_msg>Parse circadian config at bootup, not in computations<commit_after>use crate::homectl_core::{
    device::{Device, DeviceColor, DeviceState, Light},
    events::{Message, TxEventChannel},
    integration::{Integration, IntegrationId},
    scene::{color_config_as_device_color, ColorConfig},
};
use anyhow::{Context, Result};
use async_trait::async_trait;
use palette::Gradient;
use serde::{de, Deserialize};
use std::time::Duration;
use tokio::time::{interval_at, Instant};

fn from_hh_mm<'de, D>(d: D) -> Result<chrono::NaiveTime, D::Error>
where
    D: de::Deserializer<'de>,
{
    let str = String::deserialize(d)?;
    chrono::NaiveTime::parse_from_str(&str, "%H:%M").map_err(serde::de::Error::custom)
}

#[derive(Clone, Debug, Deserialize)]
pub struct CircadianConfig {
    device_name: String,

    #[serde(deserialize_with = "from_hh_mm")]
    day_fade_start: chrono::NaiveTime,
    day_fade_duration_hours: i64,
    day_color: ColorConfig,

    #[serde(deserialize_with = "from_hh_mm")]
    night_fade_start: chrono::NaiveTime,
    night_fade_duration_hours: i64,
    night_color: ColorConfig,
}

#[derive(Clone)]
pub struct Circadian {
    id: String,
    config: CircadianConfig,
    sender: TxEventChannel,
    converted_day_color: DeviceColor,
    converted_night_color: DeviceColor,
}

#[async_trait]
impl Integration for Circadian {
    fn new(id: &IntegrationId, config: &config::Value, sender: TxEventChannel) -> Result<Self> {
        let config: CircadianConfig = config
            .clone()
            .try_into()
            .context("Failed to deserialize config of Circadian integration")?;

        Ok(Circadian {
            id: id.clone(),
            config: config.clone(),
            sender,
            converted_day_color: color_config_as_device_color(config.day_color),
            converted_night_color: color_config_as_device_color(config.night_color),
        })
    }

    async fn register(&mut self) -> Result<()> {
        let device = mk_circadian_device(self);

        self.sender
            .send(Message::IntegrationDeviceRefresh { device })
            .await;

        println!("registered circadian integration {}", self.id);

        Ok(())
    }

    async fn start(&mut self) -> Result<()> {
        println!("started circadian integration {}", self.id);

        let circadian = self.clone();

        // FIXME: can we restructure the integrations / devices systems such
        // that polling is not needed here?
        tokio::spawn(async { poll_sensor(circadian).await });

        Ok(())
    }

    async fn set_integration_device_state(&mut self, _device: Device) {
        // do nothing
    }
}

fn get_night_fade(circadian: &Circadian) -> f32 {
    let local = chrono::Local::now().naive_local().time();

    let day_fade_start = circadian.config.day_fade_start;
    let day_fade_duration = chrono::Duration::hours(circadian.config.day_fade_duration_hours);
    let day_fade_end = day_fade_start + day_fade_duration;

    let night_fade_start = circadian.config.night_fade_start;
    let night_fade_duration = chrono::Duration::hours(circadian.config.night_fade_duration_hours);
    let night_fade_end = night_fade_start + night_fade_duration;

    if local <= day_fade_start || local >= night_fade_end {
        return 1.0;
    }
    if local >= day_fade_end && local <= night_fade_start {
        return 0.0;
    }

    if local < day_fade_end {
        // fading from night to day
        let d = local - day_fade_start;
        let p = d.num_milliseconds() as f32 / day_fade_duration.num_milliseconds() as f32;

        1.0 - p
    } else {
        // fading from day to night
        let d = local - night_fade_start;
        let p = d.num_milliseconds() as f32 / night_fade_duration.num_milliseconds() as f32;

        p
    }
}

fn get_circadian_color(circadian: &Circadian) -> DeviceColor {
    let gradient = Gradient::new(vec![
        circadian.converted_day_color,
        circadian.converted_night_color,
    ]);

    let i = get_night_fade(circadian);

    gradient.get(i)
}

async fn poll_sensor(circadian: Circadian) {
    let poll_rate = Duration::from_millis(60 * 1000);
    let start = Instant::now() + poll_rate;
    let mut interval = interval_at(start, poll_rate);

    loop {
        interval.tick().await;

        let sender = circadian.sender.clone();

        let device = mk_circadian_device(&circadian);
        sender.send(Message::SetDeviceState { device }).await;
    }
}

fn mk_circadian_device(circadian: &Circadian) -> Device {
    let state = DeviceState::Light(Light {
        power: true,
        brightness: Some(1.0),
        color: Some(get_circadian_color(circadian)),
    });

    let device = Device {
        id: "color".into(),
        name: circadian.config.device_name.clone(),
        integration_id: circadian.id.clone(),
        scene: None,
        state,
    };

    device
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use geometry::bbox::{union_point, union_points, BBox};
use geometry::prim::Prim;
use material::Material;
use raytracer::{Ray, Intersection};
use vec3::Vec3;

pub struct Triangle {
    pub v0: TriangleVertex,
    pub v1: TriangleVertex,
    pub v2: TriangleVertex,
    pub material: Box<Material+Send+Share>
}

pub struct TriangleVertex {
    pub pos: Vec3,
    pub n: Vec3,
    pub u: f64,
    pub v: f64
}

impl Triangle {
    /// All three normals at vertices are perpendicular to the triangle plane
    pub fn auto_normal(v0: Vec3, v1: Vec3, v2: Vec3, uv0: (f64, f64), uv1: (f64, f64), uv2: (f64, f64), material: Box<Material+Send+Share>) -> Triangle {
        let n = (v1 - v0).cross(&(v2 - v0));
        let (ut0, vt0) = uv0;
        let (ut1, vt1) = uv1;
        let (ut2, vt2) = uv2;

        Triangle {
            v0: TriangleVertex{ pos: v0, n: n, u: ut0, v: vt0 },
            v1: TriangleVertex{ pos: v1, n: n, u: ut1, v: vt1 },
            v2: TriangleVertex{ pos: v2, n: n, u: ut2, v: vt2 },
            material: material
        }
    }
}

impl Prim for Triangle {
    /// Barycentric coordinates.
    fn intersects<'a>(&'a self, ray: &Ray, t_min: f64, t_max: f64) -> Option<Intersection<'a>> {
        let e1 = self.v1.pos - self.v0.pos;
        let e2 = self.v2.pos - self.v0.pos;
        let p = ray.direction.cross(&e2);
        let det = e1.dot(&p);

        // if determinant is near zero, ray lies in plane of triangle
        if det > -::std::f64::EPSILON && det < ::std::f64::EPSILON {
            return None
        }

        let inv_det = 1.0 / det;
        let s = ray.origin - self.v0.pos;
        let beta = inv_det * s.dot(&p);
        if beta < 0.0 || beta > 1.0 { return None }

        let q = s.cross(&e1);
        let gamma = inv_det * ray.direction.dot(&q);
        if gamma < 0.0 || beta + gamma > 1.0 { return None }

        let t = inv_det * e2.dot(&q);

        if t < t_min || t > t_max {
            None
        } else {
            let intersection_point = ray.origin + ray.direction.scale(t);

            let alpha = 1.0 - beta - gamma;

            // Interpolate normals at vertices to get normal
            let n = self.v0.n.scale(alpha) + self.v1.n.scale(beta) + self.v2.n.scale(gamma);

            // Interpolate UVs at vertices to get UV
            let u = self.v0.u * alpha + self.v1.u * beta + self.v2.u * gamma;
            let v = self.v0.v * alpha + self.v1.v * beta + self.v2.v * gamma;

            Some(Intersection {
                n: n,
                t: t,
                u: u,
                v: v,
                position: intersection_point,
                material: &self.material
            })
        }
    }

    fn bounding(&self) -> Option<BBox> {
        return Some(union_point(&union_points(&self.v0.pos, &self.v1.pos), &self.v2.pos));
    }
}
<commit_msg>Add tests to Triangle<commit_after>#![allow(dead_code)]

use geometry::bbox::{union_point, union_points, BBox};
use geometry::prim::Prim;
use material::Material;
use raytracer::{Ray, Intersection};
use vec3::Vec3;

#[cfg(test)]
use material::materials::FlatMaterial;

pub struct Triangle {
    pub v0: TriangleVertex,
    pub v1: TriangleVertex,
    pub v2: TriangleVertex,
    pub material: Box<Material+Send+Share>
}

pub struct TriangleVertex {
    pub pos: Vec3,
    pub n: Vec3,
    pub u: f64,
    pub v: f64
}

impl Triangle {
    /// All three normals at vertices are perpendicular to the triangle plane
    pub fn auto_normal(v0: Vec3, v1: Vec3, v2: Vec3, uv0: (f64, f64), uv1: (f64, f64), uv2: (f64, f64), material: Box<Material+Send+Share>) -> Triangle {
        let n = (v1 - v0).cross(&(v2 - v0));
        let (ut0, vt0) = uv0;
        let (ut1, vt1) = uv1;
        let (ut2, vt2) = uv2;

        Triangle {
            v0: TriangleVertex{ pos: v0, n: n, u: ut0, v: vt0 },
            v1: TriangleVertex{ pos: v1, n: n, u: ut1, v: vt1 },
            v2: TriangleVertex{ pos: v2, n: n, u: ut2, v: vt2 },
            material: material
        }
    }
}

impl Prim for Triangle {
    /// http://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
    /// Barycentric coordinates.
    fn intersects<'a>(&'a self, ray: &Ray, t_min: f64, t_max: f64) -> Option<Intersection<'a>> {
        let e1 = self.v1.pos - self.v0.pos;
        let e2 = self.v2.pos - self.v0.pos;
        let p = ray.direction.cross(&e2);
        let det = e1.dot(&p);

        // if determinant is near zero, ray lies in plane of triangle
        if det > -::std::f64::EPSILON && det < ::std::f64::EPSILON {
            return None
        }

        let inv_det = 1.0 / det;
        let s = ray.origin - self.v0.pos;
        let beta = inv_det * s.dot(&p);
        if beta < 0.0 || beta > 1.0 { return None }

        let q = s.cross(&e1);
        let gamma = inv_det * ray.direction.dot(&q);
        if gamma < 0.0 || beta + gamma > 1.0 { return None }

        let t = inv_det * e2.dot(&q);

        if t < t_min || t > t_max {
            None
        } else {
            let intersection_point = ray.origin + ray.direction.scale(t);

            let alpha = 1.0 - beta - gamma;

            // Interpolate normals at vertices to get normal
            let n = self.v0.n.scale(alpha) + self.v1.n.scale(beta) + self.v2.n.scale(gamma);

            // Interpolate UVs at vertices to get UV
            let u = self.v0.u * alpha + self.v1.u * beta + self.v2.u * gamma;
            let v = self.v0.v * alpha + self.v1.v * beta + self.v2.v * gamma;

            Some(Intersection {
                n: n,
                t: t,
                u: u,
                v: v,
                position: intersection_point,
                material: &self.material
            })
        }
    }

    fn bounding(&self) -> Option<BBox> {
        return Some(union_point(&union_points(&self.v0.pos, &self.v1.pos), &self.v2.pos));
    }
}

#[test]
fn it_intersects_and_interpolates() {
    let triangle = Triangle {
        v0: TriangleVertex {pos: Vec3 {x: -1.0, y: 0.0, z: 0.0}, n: Vec3 {x: -1.0, y: 0.0, z: 0.0}, u: 0.0, v: 0.0},
        v1: TriangleVertex {pos: Vec3 {x:  1.0, y: 0.0, z: 0.0}, n: Vec3 {x:  1.0, y: 0.0, z: 0.0}, u: 1.0, v: 0.0},
        v2: TriangleVertex {pos: Vec3 {x:  0.0, y: 1.0, z: 0.0}, n: Vec3 {x:  0.0, y: 1.0, z: 0.0}, u: 0.0, v: 1.0},
        material: box FlatMaterial {color: Vec3::one()}
    };

    // Tests actual intersection
    let intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.5, z: -1.0}, Vec3 {x: 0.0, y: 0.0, z: 1.0});
    let intersection = triangle.intersects(&intersecting_ray, 0.0, 10.0).unwrap();
    assert_eq!(intersection.position.x, 0.0);
    assert_eq!(intersection.position.y, 0.5);
    assert_eq!(intersection.position.z, 0.0);
    assert_eq!(intersection.u, 0.25);
    assert_eq!(intersection.v, 0.5);
    assert_eq!(intersection.n.x, 0.0);
    assert_eq!(intersection.n.y, 0.5);
    assert_eq!(intersection.n.z, 0.0);

    // Ray off to the sides
    let mut non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.5, z: -1.0}, Vec3 {x: 100.0, y: 100.0, z: 1.0});
    let mut non_intersection = triangle.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());

    non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.5, z: -1.0}, Vec3 {x: -100.0, y: -100.0, z: 1.0});
    non_intersection = triangle.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());

    // Ray in opposite direction
    non_intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.5, z: -1.0}, Vec3 {x: 0.0, y: 0.0, z: -1.0});
    non_intersection = triangle.intersects(&non_intersecting_ray, 0.0, 10.0);
    assert!(non_intersection.is_none());
}

#[test]
fn it_intersects_only_in_tmin_tmax() {
    let triangle = Triangle {
        v0: TriangleVertex {pos: Vec3 {x: -1.0, y: 0.0, z: 0.0}, n: Vec3::zero(), u: 0.0, v: 0.0},
        v1: TriangleVertex {pos: Vec3 {x: 1.0, y: 0.0, z: 0.0},  n: Vec3::zero(), u: 1.0, v: 0.0},
        v2: TriangleVertex {pos: Vec3 {x: 0.0, y: 1.0, z: 0.0},  n: Vec3::one(),  u: 0.0, v: 1.0},
        material: box FlatMaterial {color: Vec3::one()}
    };

    // Tests tmin
    let mut intersecting_ray = Ray::new(Vec3 {x: 0.0, y: 0.5, z: -1.0}, Vec3 {x: 0.0, y: 0.0, z: 1.0});
    let mut non_intersection = triangle.intersects(&intersecting_ray, 1000.0, 10000.0);
    assert!(non_intersection.is_none());

    // Tests tmax
    non_intersection = triangle.intersects(&intersecting_ray, 0.0, 0.0001);
    assert!(non_intersection.is_none());
}
<|endoftext|>