<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 url::Url;

use selectors::bloom::BloomFilter;
use selectors::matching::{SelectorMap, Rule};
use selectors::matching::DeclarationBlock as GenericDeclarationBlock;
use selectors::parser::PseudoElement;
use selectors::tree::TNode;
use std::process;
use util::resource_files::read_resource_file;
use util::smallvec::VecLike;

use legacy::PresentationalHintSynthesis;
use media_queries::Device;
use node::TElementAttributes;
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use stylesheets::{Stylesheet, CSSRuleIteratorExt, Origin};
use viewport::{ViewportConstraints, ViewportRuleCascade};

pub type DeclarationBlock = GenericDeclarationBlock<Vec<PropertyDeclaration>>;

pub struct Stylist {
    // List of stylesheets (including all media rules)
    stylesheets: Vec<Stylesheet>,

    // Device that the stylist is currently evaluating against.
    pub device: Device,

    // If true, a stylesheet has been added or the device has
    // changed, and the stylist needs to be updated.
    is_dirty: bool,

    // The current selector maps, after evaluating media
    // rules against the current device.
    element_map: PerPseudoElementSelectorMap,
    before_map: PerPseudoElementSelectorMap,
    after_map: PerPseudoElementSelectorMap,
    rules_source_order: usize,
}

impl Stylist {
    #[inline]
    pub fn new(device: Device) -> Stylist {
        let mut stylist = Stylist {
            stylesheets: vec!(),
            device: device,
            is_dirty: true,

            element_map: PerPseudoElementSelectorMap::new(),
            before_map: PerPseudoElementSelectorMap::new(),
            after_map: PerPseudoElementSelectorMap::new(),
            rules_source_order: 0,
        };
        // FIXME: Add iso-8859-9.css when the document’s encoding is ISO-8859-8.
        // FIXME: presentational-hints.css should be at author origin with zero specificity.
        //        (Does it make a difference?)
        for &filename in ["user-agent.css", "servo.css", "presentational-hints.css"].iter() {
            match read_resource_file(&[filename]) {
                Ok(res) => {
                    let ua_stylesheet = Stylesheet::from_bytes(
                        &res,
                        Url::parse(&format!("chrome:///{:?}", filename)).unwrap(),
                        None,
                        None,
                        Origin::UserAgent);
                    stylist.add_stylesheet(ua_stylesheet);
                }
                Err(..) => {
                    error!("Stylist::new() failed at loading {}!", filename);
                    process::exit(1);
                }
            }
        }
        stylist
    }

    pub fn constrain_viewport(&self) -> Option<ViewportConstraints> {
        let cascaded_rule = self.stylesheets.iter()
            .flat_map(|s| s.effective_rules(&self.device).viewport())
            .cascade();

        ViewportConstraints::maybe_new(self.device.viewport_size, &cascaded_rule)
    }

    pub fn update(&mut self) -> bool {
        if self.is_dirty {
            self.element_map = PerPseudoElementSelectorMap::new();
            self.before_map = PerPseudoElementSelectorMap::new();
            self.after_map = PerPseudoElementSelectorMap::new();
            self.rules_source_order = 0;

            for stylesheet in self.stylesheets.iter() {
                let (mut element_map, mut before_map, mut after_map) = match stylesheet.origin {
                    Origin::UserAgent => (
                        &mut self.element_map.user_agent,
                        &mut self.before_map.user_agent,
                        &mut self.after_map.user_agent,
                    ),
                    Origin::Author => (
                        &mut self.element_map.author,
                        &mut self.before_map.author,
                        &mut self.after_map.author,
                    ),
                    Origin::User => (
                        &mut self.element_map.user,
                        &mut self.before_map.user,
                        &mut self.after_map.user,
                    ),
                };
                let mut rules_source_order = self.rules_source_order;

                // Take apart the StyleRule into individual Rules and insert
                // them into the SelectorMap of that priority.
                macro_rules! append(
                    ($style_rule: ident, $priority: ident) => {
                        if $style_rule.declarations.$priority.len() > 0 {
                            for selector in $style_rule.selectors.iter() {
                                let map = match selector.pseudo_element {
                                    None => &mut element_map,
                                    Some(PseudoElement::Before) => &mut before_map,
                                    Some(PseudoElement::After) => &mut after_map,
                                };
                                map.$priority.insert(Rule {
                                        selector: selector.compound_selectors.clone(),
                                        declarations: DeclarationBlock {
                                            specificity: selector.specificity,
                                            declarations: $style_rule.declarations.$priority.clone(),
                                            source_order: rules_source_order,
                                        },
                                });
                            }
                        }
                    };
                );

                for style_rule in stylesheet.effective_rules(&self.device).style() {
                    append!(style_rule, normal);
                    append!(style_rule, important);
                    rules_source_order += 1;
                }
                self.rules_source_order = rules_source_order;
            }

            self.is_dirty = false;
            return true;
        }

        false
    }

    pub fn set_device(&mut self, device: Device) {
        let is_dirty = self.is_dirty || self.stylesheets.iter()
            .flat_map(|stylesheet| stylesheet.rules().media())
            .any(|media_rule| media_rule.evaluate(&self.device) != media_rule.evaluate(&device));

        self.device = device;
        self.is_dirty |= is_dirty;
    }

    pub fn add_quirks_mode_stylesheet(&mut self) {
        self.add_stylesheet(Stylesheet::from_bytes(
            &read_resource_file(&["quirks-mode.css"]).unwrap(),
            Url::parse("chrome:///quirks-mode.css").unwrap(),
            None,
            None,
            Origin::UserAgent))
    }

    pub fn add_stylesheet(&mut self, stylesheet: Stylesheet) {
        self.stylesheets.push(stylesheet);
        self.is_dirty = true;
    }

    /// Returns the applicable CSS declarations for the given element. This corresponds to
    /// `ElementRuleCollector` in WebKit.
    ///
    /// The returned boolean indicates whether the style is *shareable*; that is, whether the
    /// matched selectors are simple enough to allow the matching logic to be reduced to the logic
    /// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
    pub fn push_applicable_declarations<'a,N,V>(
                                        &self,
                                        element: &N,
                                        parent_bf: &Option<Box<BloomFilter>>,
                                        style_attribute: Option<&PropertyDeclarationBlock>,
                                        pseudo_element: Option<PseudoElement>,
                                        applicable_declarations: &mut V)
                                        -> bool
                                        where N: TNode<'a>,
                                              N::Element: TElementAttributes<'a>,
                                              V: VecLike<DeclarationBlock> {
        assert!(!self.is_dirty);
        assert!(element.is_element());
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements");

        let map = match pseudo_element {
            None => &self.element_map,
            Some(PseudoElement::Before) => &self.before_map,
            Some(PseudoElement::After) => &self.after_map,
        };

        let mut shareable = true;

        // Step 1: Normal user-agent rules.
        map.user_agent.normal.get_all_matching_rules(element,
                                                     parent_bf,
                                                     applicable_declarations,
                                                     &mut shareable);

        // Step 2: Presentational hints.
        self.synthesize_presentational_hints_for_legacy_attributes(element,
                                                                   applicable_declarations,
                                                                   &mut shareable);

        // Step 3: User and author normal rules.
        map.user.normal.get_all_matching_rules(element,
                                               parent_bf,
                                               applicable_declarations,
                                               &mut shareable);
        map.author.normal.get_all_matching_rules(element,
                                                 parent_bf,
                                                 applicable_declarations,
                                                 &mut shareable);

        // Step 4: Normal style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.normal.clone()))
        });

        // Step 5: Author-supplied `!important` rules.
        map.author.important.get_all_matching_rules(element,
                                                    parent_bf,
                                                    applicable_declarations,
                                                    &mut shareable);

        // Step 6: `!important` style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.important.clone()))
        });

        // Step 7: User and UA `!important` rules.
        map.user.important.get_all_matching_rules(element,
                                                  parent_bf,
                                                  applicable_declarations,
                                                  &mut shareable);
        map.user_agent.important.get_all_matching_rules(element,
                                                        parent_bf,
                                                        applicable_declarations,
                                                        &mut shareable);

        shareable
    }

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

struct PerOriginSelectorMap {
    normal: SelectorMap<Vec<PropertyDeclaration>>,
    important: SelectorMap<Vec<PropertyDeclaration>>,
}

impl PerOriginSelectorMap {
    #[inline]
    fn new() -> PerOriginSelectorMap {
        PerOriginSelectorMap {
            normal: SelectorMap::new(),
            important: SelectorMap::new(),
        }
    }
}

struct PerPseudoElementSelectorMap {
    user_agent: PerOriginSelectorMap,
    author: PerOriginSelectorMap,
    user: PerOriginSelectorMap,
}

impl PerPseudoElementSelectorMap {
    #[inline]
    fn new() -> PerPseudoElementSelectorMap {
        PerPseudoElementSelectorMap {
            user_agent: PerOriginSelectorMap::new(),
            author: PerOriginSelectorMap::new(),
            user: PerOriginSelectorMap::new(),
        }
    }
}
<commit_msg>fix Stylist::add_quirks_mode_stylesheet() to not panic when resources cannot be loaded<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 url::Url;

use selectors::bloom::BloomFilter;
use selectors::matching::{SelectorMap, Rule};
use selectors::matching::DeclarationBlock as GenericDeclarationBlock;
use selectors::parser::PseudoElement;
use selectors::tree::TNode;
use std::process;
use util::resource_files::read_resource_file;
use util::smallvec::VecLike;

use legacy::PresentationalHintSynthesis;
use media_queries::Device;
use node::TElementAttributes;
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use stylesheets::{Stylesheet, CSSRuleIteratorExt, Origin};
use viewport::{ViewportConstraints, ViewportRuleCascade};

pub type DeclarationBlock = GenericDeclarationBlock<Vec<PropertyDeclaration>>;

pub struct Stylist {
    // List of stylesheets (including all media rules)
    stylesheets: Vec<Stylesheet>,

    // Device that the stylist is currently evaluating against.
    pub device: Device,

    // If true, a stylesheet has been added or the device has
    // changed, and the stylist needs to be updated.
    is_dirty: bool,

    // The current selector maps, after evaluating media
    // rules against the current device.
    element_map: PerPseudoElementSelectorMap,
    before_map: PerPseudoElementSelectorMap,
    after_map: PerPseudoElementSelectorMap,
    rules_source_order: usize,
}

impl Stylist {
    #[inline]
    pub fn new(device: Device) -> Stylist {
        let mut stylist = Stylist {
            stylesheets: vec!(),
            device: device,
            is_dirty: true,

            element_map: PerPseudoElementSelectorMap::new(),
            before_map: PerPseudoElementSelectorMap::new(),
            after_map: PerPseudoElementSelectorMap::new(),
            rules_source_order: 0,
        };
        // FIXME: Add iso-8859-9.css when the document’s encoding is ISO-8859-8.
        // FIXME: presentational-hints.css should be at author origin with zero specificity.
        //        (Does it make a difference?)
        for &filename in ["user-agent.css", "servo.css", "presentational-hints.css"].iter() {
            match read_resource_file(&[filename]) {
                Ok(res) => {
                    let ua_stylesheet = Stylesheet::from_bytes(
                        &res,
                        Url::parse(&format!("chrome:///{:?}", filename)).unwrap(),
                        None,
                        None,
                        Origin::UserAgent);
                    stylist.add_stylesheet(ua_stylesheet);
                }
                Err(..) => {
                    error!("Stylist::new() failed at loading {}!", filename);
                    process::exit(1);
                }
            }
        }
        stylist
    }

    pub fn constrain_viewport(&self) -> Option<ViewportConstraints> {
        let cascaded_rule = self.stylesheets.iter()
            .flat_map(|s| s.effective_rules(&self.device).viewport())
            .cascade();

        ViewportConstraints::maybe_new(self.device.viewport_size, &cascaded_rule)
    }

    pub fn update(&mut self) -> bool {
        if self.is_dirty {
            self.element_map = PerPseudoElementSelectorMap::new();
            self.before_map = PerPseudoElementSelectorMap::new();
            self.after_map = PerPseudoElementSelectorMap::new();
            self.rules_source_order = 0;

            for stylesheet in self.stylesheets.iter() {
                let (mut element_map, mut before_map, mut after_map) = match stylesheet.origin {
                    Origin::UserAgent => (
                        &mut self.element_map.user_agent,
                        &mut self.before_map.user_agent,
                        &mut self.after_map.user_agent,
                    ),
                    Origin::Author => (
                        &mut self.element_map.author,
                        &mut self.before_map.author,
                        &mut self.after_map.author,
                    ),
                    Origin::User => (
                        &mut self.element_map.user,
                        &mut self.before_map.user,
                        &mut self.after_map.user,
                    ),
                };
                let mut rules_source_order = self.rules_source_order;

                // Take apart the StyleRule into individual Rules and insert
                // them into the SelectorMap of that priority.
                macro_rules! append(
                    ($style_rule: ident, $priority: ident) => {
                        if $style_rule.declarations.$priority.len() > 0 {
                            for selector in $style_rule.selectors.iter() {
                                let map = match selector.pseudo_element {
                                    None => &mut element_map,
                                    Some(PseudoElement::Before) => &mut before_map,
                                    Some(PseudoElement::After) => &mut after_map,
                                };
                                map.$priority.insert(Rule {
                                        selector: selector.compound_selectors.clone(),
                                        declarations: DeclarationBlock {
                                            specificity: selector.specificity,
                                            declarations: $style_rule.declarations.$priority.clone(),
                                            source_order: rules_source_order,
                                        },
                                });
                            }
                        }
                    };
                );

                for style_rule in stylesheet.effective_rules(&self.device).style() {
                    append!(style_rule, normal);
                    append!(style_rule, important);
                    rules_source_order += 1;
                }
                self.rules_source_order = rules_source_order;
            }

            self.is_dirty = false;
            return true;
        }

        false
    }

    pub fn set_device(&mut self, device: Device) {
        let is_dirty = self.is_dirty || self.stylesheets.iter()
            .flat_map(|stylesheet| stylesheet.rules().media())
            .any(|media_rule| media_rule.evaluate(&self.device) != media_rule.evaluate(&device));

        self.device = device;
        self.is_dirty |= is_dirty;
    }

    pub fn add_quirks_mode_stylesheet(&mut self) {
        match read_resource_file(&["quirks-mode.css"]) {
            Ok(res) => {
            self.add_stylesheet(Stylesheet::from_bytes(
                &res,
                Url::parse("chrome:///quirks-mode.css").unwrap(),
                None,
                None,
                Origin::UserAgent));
            }
            Err(..) => {
                error!("Stylist::add_quirks_mode_stylesheet() failed at loading 'quirks-mode.css'!");
                process::exit(1);
            }
        }
    }

    pub fn add_stylesheet(&mut self, stylesheet: Stylesheet) {
        self.stylesheets.push(stylesheet);
        self.is_dirty = true;
    }

    /// Returns the applicable CSS declarations for the given element. This corresponds to
    /// `ElementRuleCollector` in WebKit.
    ///
    /// The returned boolean indicates whether the style is *shareable*; that is, whether the
    /// matched selectors are simple enough to allow the matching logic to be reduced to the logic
    /// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
    pub fn push_applicable_declarations<'a,N,V>(
                                        &self,
                                        element: &N,
                                        parent_bf: &Option<Box<BloomFilter>>,
                                        style_attribute: Option<&PropertyDeclarationBlock>,
                                        pseudo_element: Option<PseudoElement>,
                                        applicable_declarations: &mut V)
                                        -> bool
                                        where N: TNode<'a>,
                                              N::Element: TElementAttributes<'a>,
                                              V: VecLike<DeclarationBlock> {
        assert!(!self.is_dirty);
        assert!(element.is_element());
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements");

        let map = match pseudo_element {
            None => &self.element_map,
            Some(PseudoElement::Before) => &self.before_map,
            Some(PseudoElement::After) => &self.after_map,
        };

        let mut shareable = true;

        // Step 1: Normal user-agent rules.
        map.user_agent.normal.get_all_matching_rules(element,
                                                     parent_bf,
                                                     applicable_declarations,
                                                     &mut shareable);

        // Step 2: Presentational hints.
        self.synthesize_presentational_hints_for_legacy_attributes(element,
                                                                   applicable_declarations,
                                                                   &mut shareable);

        // Step 3: User and author normal rules.
        map.user.normal.get_all_matching_rules(element,
                                               parent_bf,
                                               applicable_declarations,
                                               &mut shareable);
        map.author.normal.get_all_matching_rules(element,
                                                 parent_bf,
                                                 applicable_declarations,
                                                 &mut shareable);

        // Step 4: Normal style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.normal.clone()))
        });

        // Step 5: Author-supplied `!important` rules.
        map.author.important.get_all_matching_rules(element,
                                                    parent_bf,
                                                    applicable_declarations,
                                                    &mut shareable);

        // Step 6: `!important` style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.important.clone()))
        });

        // Step 7: User and UA `!important` rules.
        map.user.important.get_all_matching_rules(element,
                                                  parent_bf,
                                                  applicable_declarations,
                                                  &mut shareable);
        map.user_agent.important.get_all_matching_rules(element,
                                                        parent_bf,
                                                        applicable_declarations,
                                                        &mut shareable);

        shareable
    }

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

struct PerOriginSelectorMap {
    normal: SelectorMap<Vec<PropertyDeclaration>>,
    important: SelectorMap<Vec<PropertyDeclaration>>,
}

impl PerOriginSelectorMap {
    #[inline]
    fn new() -> PerOriginSelectorMap {
        PerOriginSelectorMap {
            normal: SelectorMap::new(),
            important: SelectorMap::new(),
        }
    }
}

struct PerPseudoElementSelectorMap {
    user_agent: PerOriginSelectorMap,
    author: PerOriginSelectorMap,
    user: PerOriginSelectorMap,
}

impl PerPseudoElementSelectorMap {
    #[inline]
    fn new() -> PerPseudoElementSelectorMap {
        PerPseudoElementSelectorMap {
            user_agent: PerOriginSelectorMap::new(),
            author: PerOriginSelectorMap::new(),
            user: PerOriginSelectorMap::new(),
        }
    }
}
<|endoftext|>


<commit_before>//! Module for the dynamic state of the game.
//!
//! This module defines the `State` type and its entourage. The data needed to
//! run an RBattle game is split into three categories:
//!
//! - `State` holds all the significant varying state of a game. What changes as the game proceeds
//!   are the amount of goop in each node, and the outflows the user has chosen;
//!   these are the `State`.
//!
//! - The positions of nodes and their boundaries and the locations of goop
//!   sources are all fixed, and are part of the `Map`.
//!
//! - Interface elements enter hover and active states as the user mouses
//!   around, but those states are ephemeral; they are part of the `Mouse` type.
//!   When the user actually completes an interaction with an interface element,
//!   only then is the `State` affected.

use graph::Node;
use visible_graph::VisibleGraph;
use map::{Map, Player};
use std::rc::Rc;
use rand::{Rng, SeedableRng, XorShiftRng};

use std::iter::repeat;

/// The complete state of an RBattle game board.
#[derive(Clone)]
pub struct State<G: VisibleGraph> {
    /// The map being played on.
    pub map: Rc<Map<G>>,

    /// Which nodes are occupied, and which are vacant. Indexed by node id.
    pub nodes: Vec<Option<Occupied>>,

    /// The random number generator used to drive the goop flow algorithm.
    rng: XorShiftRng,
}

/// The state of a node that is occupied by some player.
#[derive(Debug, Clone)]
pub struct Occupied {
    /// The player who controls this node.
    pub player: Player,

    /// Which neighbors of this node it sends goop out to.
    pub outflows: Vec<Node>,

    /// How much goop this node holds. Ranges from 0 to MAX_GOOP.
    pub goop: usize,

}

pub const MAX_GOOP: usize = 15;

/// Return a pair of mutable references to the `i`'th and `j`'th elements of
/// `slice`, where `i != j`.
fn index_mut_pair<T>(slice: &mut [T], i: usize, j: usize) -> (&mut T, &mut T) {
    if i < j {
        let (left, right) = slice.split_at_mut(j);
        (&mut left[i], &mut right[0])
    } else if j < i {
        let (left, right) = slice.split_at_mut(i);
        (&mut right[0], &mut left[j])
    } else {
        panic!("can't borrow two mutable references to the same element");
    }
}

impl<G: VisibleGraph> State<G> {
    pub fn new(map: Rc<Map<G>>) -> State<G> {
        const SEED: [u32; 4] = [0xcd9d5eaa, 0xf04bc9a7, 0x4602cc70, 0x98d01ef9];
        let nodes = repeat(None).take(map.graph.nodes()).collect();
        State { map, nodes, rng: XorShiftRng::from_seed(SEED) }
    }

    /// Let one unit of goop flow through each outflow.
    ///
    /// There are algorithms for finding the flow through a graph precisely, but
    /// we need something simpler here. We just visit every outgoing edge in a
    /// random order, and propagate a unit of goop if the destination permits
    /// it.
    pub fn flow(&mut self) {
        // Build a vector of (from, to) pairs.
        let mut outflow_list = Vec::new();
        for node in 0..self.map.graph.nodes() {
            if let &Some(ref occupied) = &self.nodes[node] {
                for &outflow in &occupied.outflows {
                    outflow_list.push((node, outflow))
                }
            }
        }

        // Put the pairs in a random order.
        self.rng.shuffle(&mut outflow_list);

        while let Some((from_index, to_index)) = outflow_list.pop() {
            match index_mut_pair(&mut self.nodes, from_index, to_index) {
                // We shouldn't have generated a pair for an empty source, and
                // when we clear a node we're supposed to remove pairs from
                // `outflow` that originate there.
                (&mut None, _) => panic!("outflow from empty node"),

                // Source has no goop. No effect.
                (&mut Some(Occupied { goop: 0, .. }), _) => (),

                // Goop flowing into an unoccupied node. New player claims ownership.
                (&mut Some(Occupied { player, ref mut goop, .. }),
                 &mut ref mut to @ None) => {
                    *goop -= 1;
                    *to = Some(Occupied { player, outflows: vec![], goop: 1 });
                },

                // Goop flowing into a node occupied by the same player.
                (&mut Some(Occupied { player: from_player, goop: ref mut from_goop, .. }),
                 &mut Some(Occupied { player: to_player,   goop: ref mut to_goop, .. }))
                    if from_player == to_player =>
                {
                    if *from_goop > 0 && *to_goop < MAX_GOOP {
                        *from_goop -= 1;
                        *to_goop += 1;
                    }
                }

                // Goop flowing into a node occupied by another player, but
                // doesn't clear it. All outflow from destination stopped.
                (&mut Some(Occupied { goop: ref mut from_goop, .. }),
                 &mut Some(Occupied { outflows: ref mut to_outflows,
                                       goop:     ref mut to_goop, .. }))
                    if *to_goop > 1 =>
                {
                    *from_goop -= 1;
                    *to_goop -= 1;
                    to_outflows.clear();
                    // Since all outflow from `to` is cancelled, remove any
                    // pending pairs from `outflow_list`.
                    outflow_list.retain(|&(from, _)| from != to_index);
                },

                // Goop flowing into an occupied node, succeeds in clearing it.
                (&mut Some(Occupied { player, goop: ref mut from_goop, .. }),
                 &mut ref mut to) => {
                    *from_goop -= 1;
                    *to = Some(Occupied { player, outflows: vec![], goop: 0 });
                    // Since all outflow from `to` is cancelled, remove any
                    // pending pairs from `outflow_list`.
                    outflow_list.retain(|&(from, _)| from != to_index);
                }
            }
        }
    }

    /// Let sources generate new goop.
    pub fn generate_goop(&mut self) {
        for &source in &self.map.sources {
            match &mut self.nodes[source] {
                &mut None => panic!("source nodes should always be occupied by someone"),
                &mut Some(Occupied { ref mut goop, .. }) => {
                    if *goop < MAX_GOOP {
                        *goop += 1;
                    }
                }
            }
        }
    }

    /// Apply `action` to this state.
    pub fn take_action(&mut self, action: Action) {
        println!("take_action({:?})", action);
        match action {
            Action::ToggleOutflow((from, to)) => {
                // This is really just a click on the shared boundary of the two
                // nodes; it doesn't express any particular desired direction.
                // Try to infer the user's intent from the situation.
                let (from, to) = match (&self.nodes[from], &self.nodes[to]) {
                    (&Some(_), &None) => (from, to),
                    (&None, &Some(_)) => (to, from),
                    _ => return
                };

                let outflows = &mut self.nodes[from].as_mut().unwrap().outflows;
                if outflows.contains(&to) {
                    outflows.retain(|&dest| dest != to);
                } else {
                    outflows.push(to);
                }
            }
        }
    }
}

/// Actions that can be taken on a `State`.
#[derive(Debug)]
pub enum Action {
    /// Toggle the state of the given outflow.
    ToggleOutflow((Node, Node)),
}
<commit_msg>Give State a unified `advance` method; hide details.<commit_after>//! Module for the dynamic state of the game.
//!
//! This module defines the `State` type and its entourage. The data needed to
//! run an RBattle game is split into three categories:
//!
//! - `State` holds all the significant varying state of a game. What changes as the game proceeds
//!   are the amount of goop in each node, and the outflows the user has chosen;
//!   these are the `State`.
//!
//! - The positions of nodes and their boundaries and the locations of goop
//!   sources are all fixed, and are part of the `Map`.
//!
//! - Interface elements enter hover and active states as the user mouses
//!   around, but those states are ephemeral; they are part of the `Mouse` type.
//!   When the user actually completes an interaction with an interface element,
//!   only then is the `State` affected.

use graph::Node;
use visible_graph::VisibleGraph;
use map::{Map, Player};
use std::rc::Rc;
use rand::{Rng, SeedableRng, XorShiftRng};

use std::iter::repeat;

/// The complete state of an RBattle game board.
#[derive(Clone)]
pub struct State<G: VisibleGraph> {
    /// The map being played on.
    pub map: Rc<Map<G>>,

    /// Which nodes are occupied, and which are vacant. Indexed by node id.
    pub nodes: Vec<Option<Occupied>>,

    /// The random number generator used to drive the goop flow algorithm.
    rng: XorShiftRng,
}

/// The state of a node that is occupied by some player.
#[derive(Debug, Clone)]
pub struct Occupied {
    /// The player who controls this node.
    pub player: Player,

    /// Which neighbors of this node it sends goop out to.
    pub outflows: Vec<Node>,

    /// How much goop this node holds. Ranges from 0 to MAX_GOOP.
    pub goop: usize,

}

pub const MAX_GOOP: usize = 15;

/// Return a pair of mutable references to the `i`'th and `j`'th elements of
/// `slice`, where `i != j`.
fn index_mut_pair<T>(slice: &mut [T], i: usize, j: usize) -> (&mut T, &mut T) {
    if i < j {
        let (left, right) = slice.split_at_mut(j);
        (&mut left[i], &mut right[0])
    } else if j < i {
        let (left, right) = slice.split_at_mut(i);
        (&mut right[0], &mut left[j])
    } else {
        panic!("can't borrow two mutable references to the same element");
    }
}

impl<G: VisibleGraph> State<G> {
    pub fn new(map: Rc<Map<G>>) -> State<G> {
        const SEED: [u32; 4] = [0xcd9d5eaa, 0xf04bc9a7, 0x4602cc70, 0x98d01ef9];
        let nodes = repeat(None).take(map.graph.nodes()).collect();
        State { map, nodes, rng: XorShiftRng::from_seed(SEED) }
    }

    /// Let one unit of goop flow through each outflow.
    ///
    /// There are algorithms for finding the flow through a graph precisely, but
    /// we need something simpler here. We just visit every outgoing edge in a
    /// random order, and propagate a unit of goop if the destination permits
    /// it.
    fn flow(&mut self) {
        // Build a vector of (from, to) pairs.
        let mut outflow_list = Vec::new();
        for node in 0..self.map.graph.nodes() {
            if let &Some(ref occupied) = &self.nodes[node] {
                for &outflow in &occupied.outflows {
                    outflow_list.push((node, outflow))
                }
            }
        }

        // Put the pairs in a random order.
        self.rng.shuffle(&mut outflow_list);

        while let Some((from_index, to_index)) = outflow_list.pop() {
            match index_mut_pair(&mut self.nodes, from_index, to_index) {
                // We shouldn't have generated a pair for an empty source, and
                // when we clear a node we're supposed to remove pairs from
                // `outflow` that originate there.
                (&mut None, _) => panic!("outflow from empty node"),

                // Source has no goop. No effect.
                (&mut Some(Occupied { goop: 0, .. }), _) => (),

                // Goop flowing into an unoccupied node. New player claims ownership.
                (&mut Some(Occupied { player, ref mut goop, .. }),
                 &mut ref mut to @ None) => {
                    *goop -= 1;
                    *to = Some(Occupied { player, outflows: vec![], goop: 1 });
                },

                // Goop flowing into a node occupied by the same player.
                (&mut Some(Occupied { player: from_player, goop: ref mut from_goop, .. }),
                 &mut Some(Occupied { player: to_player,   goop: ref mut to_goop, .. }))
                    if from_player == to_player =>
                {
                    if *from_goop > 0 && *to_goop < MAX_GOOP {
                        *from_goop -= 1;
                        *to_goop += 1;
                    }
                }

                // Goop flowing into a node occupied by another player, but
                // doesn't clear it. All outflow from destination stopped.
                (&mut Some(Occupied { goop: ref mut from_goop, .. }),
                 &mut Some(Occupied { outflows: ref mut to_outflows,
                                       goop:     ref mut to_goop, .. }))
                    if *to_goop > 1 =>
                {
                    *from_goop -= 1;
                    *to_goop -= 1;
                    to_outflows.clear();
                    // Since all outflow from `to` is cancelled, remove any
                    // pending pairs from `outflow_list`.
                    outflow_list.retain(|&(from, _)| from != to_index);
                },

                // Goop flowing into an occupied node, succeeds in clearing it.
                (&mut Some(Occupied { player, goop: ref mut from_goop, .. }),
                 &mut ref mut to) => {
                    *from_goop -= 1;
                    *to = Some(Occupied { player, outflows: vec![], goop: 0 });
                    // Since all outflow from `to` is cancelled, remove any
                    // pending pairs from `outflow_list`.
                    outflow_list.retain(|&(from, _)| from != to_index);
                }
            }
        }
    }

    /// Let sources generate new goop.
    fn generate_goop(&mut self) {
        for &source in &self.map.sources {
            match &mut self.nodes[source] {
                &mut None => panic!("source nodes should always be occupied by someone"),
                &mut Some(Occupied { ref mut goop, .. }) => {
                    if *goop < MAX_GOOP {
                        *goop += 1;
                    }
                }
            }
        }
    }

    /// Advance `self` to the next state.
    pub fn advance(&mut self) {
        self.flow();
        self.generate_goop();
    }

    /// Apply `action` to this state.
    pub fn take_action(&mut self, action: Action) {
        println!("take_action({:?})", action);
        match action {
            Action::ToggleOutflow((from, to)) => {
                // This is really just a click on the shared boundary of the two
                // nodes; it doesn't express any particular desired direction.
                // Try to infer the user's intent from the situation.
                let (from, to) = match (&self.nodes[from], &self.nodes[to]) {
                    (&Some(_), &None) => (from, to),
                    (&None, &Some(_)) => (to, from),
                    _ => return
                };

                let outflows = &mut self.nodes[from].as_mut().unwrap().outflows;
                if outflows.contains(&to) {
                    outflows.retain(|&dest| dest != to);
                } else {
                    outflows.push(to);
                }
            }
        }
    }
}

/// Actions that can be taken on a `State`.
#[derive(Debug)]
pub enum Action {
    /// Toggle the state of the given outflow.
    ToggleOutflow((Node, Node)),
}
<|endoftext|>


<commit_before>#![cfg_attr(
    thiserror_nightly_testing,
    feature(error_generic_member_access, provide_any)
)]

use thiserror::Error;

#[derive(Error, Debug)]
#[error("...")]
pub struct Inner;

#[cfg(thiserror_nightly_testing)]
#[derive(Error, Debug)]
#[error("...")]
pub struct InnerBacktrace {
    backtrace: std::backtrace::Backtrace,
}

#[cfg(thiserror_nightly_testing)]
pub mod structs {
    use super::{Inner, InnerBacktrace};
    use std::any;
    use std::backtrace::Backtrace;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct PlainBacktrace {
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ExplicitBacktrace {
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktrace {
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktrace {
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct BacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct CombinedBacktraceFrom {
        #[from]
        #[backtrace]
        source: InnerBacktrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct AnyhowBacktrace {
        #[backtrace]
        source: anyhow::Error,
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ExplicitBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktrace {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktrace {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = CombinedBacktraceFrom::from(InnerBacktrace {
            backtrace: Backtrace::capture(),
        });
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = AnyhowBacktrace {
            source: anyhow::Error::msg("..."),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());
    }
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
    use super::{Inner, InnerBacktrace};
    use std::any;
    use std::backtrace::Backtrace;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    pub enum PlainBacktrace {
        #[error("...")]
        Test { backtrace: Backtrace },
    }

    #[derive(Error, Debug)]
    pub enum ExplicitBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum BacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum CombinedBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            #[backtrace]
            source: InnerBacktrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ExplicitBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktrace::Test {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktrace::Test {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = CombinedBacktraceFrom::from(InnerBacktrace {
            backtrace: Backtrace::capture(),
        });
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());
    }
}

#[test]
#[cfg_attr(not(thiserror_nightly_testing), ignore)]
fn test_backtrace() {}
<commit_msg>Add test involving backtrace from a Box<dyn Error><commit_after>#![cfg_attr(
    thiserror_nightly_testing,
    feature(error_generic_member_access, provide_any)
)]

use thiserror::Error;

#[derive(Error, Debug)]
#[error("...")]
pub struct Inner;

#[cfg(thiserror_nightly_testing)]
#[derive(Error, Debug)]
#[error("...")]
pub struct InnerBacktrace {
    backtrace: std::backtrace::Backtrace,
}

#[cfg(thiserror_nightly_testing)]
pub mod structs {
    use super::{Inner, InnerBacktrace};
    use std::any;
    use std::backtrace::Backtrace;
    use std::error::Error;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct PlainBacktrace {
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ExplicitBacktrace {
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktrace {
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktrace {
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct BacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Backtrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct CombinedBacktraceFrom {
        #[from]
        #[backtrace]
        source: InnerBacktrace,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct OptBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Option<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct ArcBacktraceFrom {
        #[from]
        source: Inner,
        #[backtrace]
        backtrace: Arc<Backtrace>,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct AnyhowBacktrace {
        #[backtrace]
        source: anyhow::Error,
    }

    #[derive(Error, Debug)]
    #[error("...")]
    pub struct BoxDynErrorBacktrace {
        #[backtrace]
        source: Box<dyn Error>,
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ExplicitBacktrace {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktrace {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktrace {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = CombinedBacktraceFrom::from(InnerBacktrace {
            backtrace: Backtrace::capture(),
        });
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = AnyhowBacktrace {
            source: anyhow::Error::msg("..."),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = BoxDynErrorBacktrace {
            source: Box::new(PlainBacktrace {
                backtrace: Backtrace::capture(),
            }),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());
    }
}

#[cfg(thiserror_nightly_testing)]
pub mod enums {
    use super::{Inner, InnerBacktrace};
    use std::any;
    use std::backtrace::Backtrace;
    use std::sync::Arc;
    use thiserror::Error;

    #[derive(Error, Debug)]
    pub enum PlainBacktrace {
        #[error("...")]
        Test { backtrace: Backtrace },
    }

    #[derive(Error, Debug)]
    pub enum ExplicitBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktrace {
        #[error("...")]
        Test {
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum BacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Backtrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum CombinedBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            #[backtrace]
            source: InnerBacktrace,
        },
    }

    #[derive(Error, Debug)]
    pub enum OptBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Option<Backtrace>,
        },
    }

    #[derive(Error, Debug)]
    pub enum ArcBacktraceFrom {
        #[error("...")]
        Test {
            #[from]
            source: Inner,
            #[backtrace]
            backtrace: Arc<Backtrace>,
        },
    }

    #[test]
    fn test_backtrace() {
        let error = PlainBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ExplicitBacktrace::Test {
            backtrace: Backtrace::capture(),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktrace::Test {
            backtrace: Some(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktrace::Test {
            backtrace: Arc::new(Backtrace::capture()),
        };
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = BacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = CombinedBacktraceFrom::from(InnerBacktrace {
            backtrace: Backtrace::capture(),
        });
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = OptBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());

        let error = ArcBacktraceFrom::from(Inner);
        assert!(any::request_ref::<Backtrace>(&error).is_some());
    }
}

#[test]
#[cfg_attr(not(thiserror_nightly_testing), ignore)]
fn test_backtrace() {}
<|endoftext|>


<commit_before>extern crate reqwest;
extern crate serde;
extern crate serde_json;
extern crate regex;

use std::io::Read;
use irc::client::prelude::*;
use irc::error::Error as IrcError;
use self::reqwest::Client;
use self::reqwest::header::Connection;
use self::serde_json::Value;

use plugin::*;

#[derive(PluginName, Debug)]
pub struct Currency;

struct ConvertionRequest<'a> {
    value: f64,
    source: &'a str,
    target: &'a str,
}

macro_rules! try_option {
    ($e:expr) => {
        match $e {
            Some(v) => v,
            None    => { return None; }
        }
    }
}

impl<'a> ConvertionRequest<'a> {
    fn send(&self) -> Option<f64> {

        let response = Client::new()
            .get("https://api.fixer.io/latest")
            .form(&[("base", self.source)])
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                try_option!(response.read_to_string(&mut body).ok());

                let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
                match convertion_rates {
                    Ok(convertion_rates) => {

                        let rates: &Value = try_option!(convertion_rates.get("rates"));
                        let target_rate: &Value =
                            try_option!(rates.get(self.target.to_uppercase()));
                        Some(self.value * try_option!(target_rate.as_f64()))
                    }
                    Err(_) => None,
                }
            }
            Err(_) => None,
        }
    }
}

impl Currency {
    pub fn new() -> Currency {
        Currency {}
    }

    fn eval_command<'a>(&self, tokens: &'a [String]) -> Option<ConvertionRequest<'a>> {
        let parsed = match tokens[0].parse() {
            Ok(v) => v,
            Err(_) => {
                return None;
            }
        };

        Some(ConvertionRequest {
                 value: parsed,
                 source: &tokens[1],
                 target: &tokens[2],
             })
    }

    fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let request = match self.eval_command(&command.tokens) {
            Some(request) => request,
            None => {
                return self.invalid_command(server, &command);
            }
        };

        match request.send() {
            Some(response) => {
                let response = format!("{} {} => {:.4} {}",
                                       request.value,
                                       request.source.to_lowercase(),
                                       response / 1.00000000,
                                       request.target.to_lowercase());

                server.send_privmsg(&command.target, &response)
            }
            None => server.send_notice(&command.source, "Error while converting given currency"),
        }
    }

    fn help(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let usage = format!("usage: {} currency value from_currency to_currency",
                            server.current_nickname());

        if let Err(e) = server.send_notice(&command.source, &usage) {
            return Err(e);
        }
        server.send_notice(&command.source, "example: 1.5 eur usd")
    }

    fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
        let help = format!("Incorrect value. \
                           Send \"{} help currency\" for help.",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }
}

impl Plugin for Currency {
    fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
        false
    }

    fn execute(&mut self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
        Ok(())
    }

    fn command(&mut self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        if command.tokens.is_empty() {
            self.invalid_command(server, &command)

        } else if command.tokens[0].to_lowercase() == "help" {
            self.help(server, command)

        } else if command.tokens.len() >= 3 {
            self.convert(server, command)

        } else {
            self.invalid_command(server, &command)
        }
    }
}

#[cfg(test)]
mod tests {}
<commit_msg>Improve currency help message<commit_after>extern crate reqwest;
extern crate serde;
extern crate serde_json;
extern crate regex;

use std::io::Read;
use irc::client::prelude::*;
use irc::error::Error as IrcError;
use self::reqwest::Client;
use self::reqwest::header::Connection;
use self::serde_json::Value;

use plugin::*;

#[derive(PluginName, Debug)]
pub struct Currency;

struct ConvertionRequest<'a> {
    value: f64,
    source: &'a str,
    target: &'a str,
}

macro_rules! try_option {
    ($e:expr) => {
        match $e {
            Some(v) => v,
            None    => { return None; }
        }
    }
}

impl<'a> ConvertionRequest<'a> {
    fn send(&self) -> Option<f64> {

        let response = Client::new()
            .get("https://api.fixer.io/latest")
            .form(&[("base", self.source)])
            .header(Connection::close())
            .send();

        match response {
            Ok(mut response) => {
                let mut body = String::new();
                try_option!(response.read_to_string(&mut body).ok());

                let convertion_rates: Result<Value, _> = serde_json::from_str(&body);
                match convertion_rates {
                    Ok(convertion_rates) => {

                        let rates: &Value = try_option!(convertion_rates.get("rates"));
                        let target_rate: &Value =
                            try_option!(rates.get(self.target.to_uppercase()));
                        Some(self.value * try_option!(target_rate.as_f64()))
                    }
                    Err(_) => None,
                }
            }
            Err(_) => None,
        }
    }
}

impl Currency {
    pub fn new() -> Currency {
        Currency {}
    }

    fn eval_command<'a>(&self, tokens: &'a [String]) -> Option<ConvertionRequest<'a>> {
        let parsed = match tokens[0].parse() {
            Ok(v) => v,
            Err(_) => {
                return None;
            }
        };

        Some(ConvertionRequest {
                 value: parsed,
                 source: &tokens[1],
                 target: &tokens[2],
             })
    }

    fn convert(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let request = match self.eval_command(&command.tokens) {
            Some(request) => request,
            None => {
                return self.invalid_command(server, &command);
            }
        };

        match request.send() {
            Some(response) => {
                let response = format!("{} {} => {:.4} {}",
                                       request.value,
                                       request.source.to_lowercase(),
                                       response / 1.00000000,
                                       request.target.to_lowercase());

                server.send_privmsg(&command.target, &response)
            }
            None => server.send_notice(&command.source, "Error while converting given currency"),
        }
    }

    fn help(&self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        let help = format!("usage: {} currency value from_currency to_currency\r\n\
                            example: 1.5 eur usd\r\n\
                            available currencies: AUD, BGN, BRL, CAD, \
                            CHF, CNY, CZK, DKK, GBP, HKD, HRK, HUF, \
                            IDR, ILS, INR, JPY, KRW, MXN, MYR, NOK, \
                            NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, \
                            TRY, USD, ZAR",
                            server.current_nickname());

        server.send_notice(&command.source, &help)
    }

    fn invalid_command(&self, server: &IrcServer, command: &PluginCommand) -> Result<(), IrcError> {
        let help = format!("Incorrect value. \
                           Send \"{} help currency\" for help.",
                           server.current_nickname());

        server.send_notice(&command.source, &help)
    }
}

impl Plugin for Currency {
    fn is_allowed(&self, _: &IrcServer, _: &Message) -> bool {
        false
    }

    fn execute(&mut self, _: &IrcServer, _: &Message) -> Result<(), IrcError> {
        Ok(())
    }

    fn command(&mut self, server: &IrcServer, command: PluginCommand) -> Result<(), IrcError> {
        if command.tokens.is_empty() {
            self.invalid_command(server, &command)

        } else if command.tokens[0].to_lowercase() == "help" {
            self.help(server, command)

        } else if command.tokens.len() >= 3 {
            self.convert(server, command)

        } else {
            self.invalid_command(server, &command)
        }
    }
}

#[cfg(test)]
mod tests {}
<|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 devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg};
use dom::bindings::inheritance::Castable;
use dom::bindings::str::DOMString;
use dom::globalscope::GlobalScope;
use dom::workerglobalscope::WorkerGlobalScope;

// https://developer.mozilla.org/en-US/docs/Web/API/Console
pub struct Console(());

impl Console {
    fn send_to_devtools(global: &GlobalScope, level: LogLevel, message: DOMString) {
        if let Some(chan) = global.devtools_chan() {
            let console_message = prepare_message(level, message);
            let worker_id = global.downcast::<WorkerGlobalScope>().map(|worker| {
                worker.get_worker_id()
            });
            let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI(
                global.pipeline_id(),
                console_message,
                worker_id);
            chan.send(devtools_message).unwrap();
        }
    }
}

impl Console {
    // https://developer.mozilla.org/en-US/docs/Web/API/Console/log
    pub fn Log(global: &GlobalScope, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Log, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console
    pub fn Debug(global: &GlobalScope, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Debug, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/info
    pub fn Info(global: &GlobalScope, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Info, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/warn
    pub fn Warn(global: &GlobalScope, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Warn, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/error
    pub fn Error(global: &GlobalScope, messages: Vec<DOMString>) {
        for message in messages {
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Error, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert
    pub fn Assert(global: &GlobalScope, condition: bool, message: Option<DOMString>) {
        if !condition {
            let message = message.unwrap_or_else(|| DOMString::from("no message"));
            println!("Assertion failed: {}", message);
            Self::send_to_devtools(global, LogLevel::Error, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/time
    pub fn Time(global: &GlobalScope, label: DOMString) {
        if let Ok(()) = global.time(label.clone()) {
            let message = DOMString::from(format!("{}: timer started", label));
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Log, message);
        }
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd
    pub fn TimeEnd(global: &GlobalScope, label: DOMString) {
        if let Ok(delta) = global.time_end(&label) {
            let message = DOMString::from(
                format!("{}: {}ms", label, delta)
            );
            println!("{}", message);
            Self::send_to_devtools(global, LogLevel::Log, message);
        };
    }
}

fn prepare_message(log_level: LogLevel, message: DOMString) -> ConsoleMessage {
    // TODO: Sending fake values for filename, lineNumber and columnNumber in LogMessage; adjust later
    ConsoleMessage {
        message: String::from(message),
        logLevel: log_level,
        filename: "test".to_owned(),
        lineNumber: 1,
        columnNumber: 1,
    }
}
<commit_msg>Hold stderr lock when using stdout through Console APIs.<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 devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg};
use dom::bindings::inheritance::Castable;
use dom::bindings::str::DOMString;
use dom::globalscope::GlobalScope;
use dom::workerglobalscope::WorkerGlobalScope;
use std::io;

// https://developer.mozilla.org/en-US/docs/Web/API/Console
pub struct Console(());

impl Console {
    fn send_to_devtools(global: &GlobalScope, level: LogLevel, message: DOMString) {
        if let Some(chan) = global.devtools_chan() {
            let console_message = prepare_message(level, message);
            let worker_id = global.downcast::<WorkerGlobalScope>().map(|worker| {
                worker.get_worker_id()
            });
            let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI(
                global.pipeline_id(),
                console_message,
                worker_id);
            chan.send(devtools_message).unwrap();
        }
    }
}

// In order to avoid interleaving the stdout output of the Console API methods
// with stderr that could be in use on other threads, we lock stderr until
// we're finished with stdout. Since the stderr lock is reentrant, there is
// no risk of deadlock if the callback ends up trying to write to stderr for
// any reason.
fn with_stderr_lock<F>(f: F) where F: FnOnce() {
    let stderr = io::stderr();
    let _handle = stderr.lock();
    f()
}

impl Console {
    // https://developer.mozilla.org/en-US/docs/Web/API/Console/log
    pub fn Log(global: &GlobalScope, messages: Vec<DOMString>) {
        with_stderr_lock(move || {
            for message in messages {
                println!("{}", message);
                Self::send_to_devtools(global, LogLevel::Log, message);
            }
        })
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console
    pub fn Debug(global: &GlobalScope, messages: Vec<DOMString>) {
        with_stderr_lock(move || {
            for message in messages {
                println!("{}", message);
                Self::send_to_devtools(global, LogLevel::Debug, message);
            }
        })
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/info
    pub fn Info(global: &GlobalScope, messages: Vec<DOMString>) {
        with_stderr_lock(move || {
            for message in messages {
                println!("{}", message);
                Self::send_to_devtools(global, LogLevel::Info, message);
            }
        })
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/warn
    pub fn Warn(global: &GlobalScope, messages: Vec<DOMString>) {
        with_stderr_lock(move || {
            for message in messages {
                println!("{}", message);
                Self::send_to_devtools(global, LogLevel::Warn, message);
            }
        })
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/error
    pub fn Error(global: &GlobalScope, messages: Vec<DOMString>) {
        with_stderr_lock(move || {
            for message in messages {
                println!("{}", message);
                Self::send_to_devtools(global, LogLevel::Error, message);
            }
        })
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert
    pub fn Assert(global: &GlobalScope, condition: bool, message: Option<DOMString>) {
        with_stderr_lock(move || {
            if !condition {
                let message = message.unwrap_or_else(|| DOMString::from("no message"));
                println!("Assertion failed: {}", message);
                Self::send_to_devtools(global, LogLevel::Error, message);
            }
        })
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/time
    pub fn Time(global: &GlobalScope, label: DOMString) {
        with_stderr_lock(move || {
            if let Ok(()) = global.time(label.clone()) {
                let message = DOMString::from(format!("{}: timer started", label));
                println!("{}", message);
                Self::send_to_devtools(global, LogLevel::Log, message);
            }
        })
    }

    // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd
    pub fn TimeEnd(global: &GlobalScope, label: DOMString) {
        with_stderr_lock(move || {
            if let Ok(delta) = global.time_end(&label) {
                let message = DOMString::from(
                    format!("{}: {}ms", label, delta)
                );
                println!("{}", message);
                Self::send_to_devtools(global, LogLevel::Log, message);
            };
        })
    }
}

fn prepare_message(log_level: LogLevel, message: DOMString) -> ConsoleMessage {
    // TODO: Sending fake values for filename, lineNumber and columnNumber in LogMessage; adjust later
    ConsoleMessage {
        message: String::from(message),
        logLevel: log_level,
        filename: "test".to_owned(),
        lineNumber: 1,
        columnNumber: 1,
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use crate::{db::RootDatabase, FilePosition};
use hir::db::AstDatabase;
use ra_db::SourceDatabase;
use rustc_hash::FxHashMap;

use ra_syntax::{
    algo::{find_node_at_offset, replace_descendants},
    ast::{self},
    AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent,
};

pub struct ExpandedMacro {
    pub name: String,
    pub expansion: String,
}

pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?;
    let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?;

    let source = hir::Source::new(position.file_id.into(), mac.syntax());
    let expanded = expand_macro_recur(db, source, &mac)?;

    // FIXME:
    // macro expansion may lose all white space information
    // But we hope someday we can use ra_fmt for that
    let expansion = insert_whitespaces(expanded);
    Some(ExpandedMacro { name: name_ref.text().to_string(), expansion })
}

fn expand_macro_recur(
    db: &RootDatabase,
    source: hir::Source<&SyntaxNode>,
    macro_call: &ast::MacroCall,
) -> Option<SyntaxNode> {
    let analyzer = hir::SourceAnalyzer::new(db, source, None);
    let expansion = analyzer.expand(db, &macro_call)?;
    let macro_file_id = expansion.file_id();
    let expanded: SyntaxNode = db.parse_or_expand(macro_file_id)?;

    let children = expanded.descendants().filter_map(ast::MacroCall::cast);
    let mut replaces = FxHashMap::default();

    for child in children.into_iter() {
        let source = hir::Source::new(macro_file_id, source.ast);
        let new_node = expand_macro_recur(db, source, &child)?;

        replaces.insert(child.syntax().clone().into(), new_node.into());
    }

    Some(replace_descendants(&expanded, &replaces))
}

fn insert_whitespaces(syn: SyntaxNode) -> String {
    let mut res = String::new();

    let mut token_iter = syn
        .preorder_with_tokens()
        .filter_map(|event| {
            if let WalkEvent::Enter(NodeOrToken::Token(token)) = event {
                Some(token)
            } else {
                None
            }
        })
        .peekable();

    while let Some(token) = token_iter.next() {
        res += &token.text().to_string();
        if token.kind().is_keyword()
            || token.kind().is_literal()
            || token.kind() == SyntaxKind::IDENT
        {
            if !token_iter.peek().map(|it| it.kind().is_punct()).unwrap_or(false) {
                res += " ";
            }
        }
    }

    res
}

#[cfg(test)]
mod tests {
    use crate::mock_analysis::analysis_and_position;

    fn check_expand_macro(fixture: &str, expected: (&str, &str)) {
        let (analysis, pos) = analysis_and_position(fixture);

        let result = analysis.expand_macro(pos).unwrap().unwrap();
        assert_eq!(result.name, expected.0.to_string());
        assert_eq!(result.expansion, expected.1.to_string());
    }

    #[test]
    fn macro_expand_recursive_expansion() {
        check_expand_macro(
            r#"
        //- /lib.rs
        macro_rules! bar {
            () => { fn  b() {} }
        }
        macro_rules! foo {
            () => { bar!(); }
        }
        macro_rules! baz {
            () => { foo!(); }
        }
        f<|>oo!();
        "#,
            ("foo", "fn b(){}"),
        );
    }
}
<commit_msg>Improve insert_whitespaces<commit_after>//! FIXME: write short doc here

use crate::{db::RootDatabase, FilePosition};
use hir::db::AstDatabase;
use ra_db::SourceDatabase;
use rustc_hash::FxHashMap;

use ra_syntax::{
    algo::{find_node_at_offset, replace_descendants},
    ast::{self},
    AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T,
};

pub struct ExpandedMacro {
    pub name: String,
    pub expansion: String,
}

pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset)?;
    let mac = name_ref.syntax().ancestors().find_map(ast::MacroCall::cast)?;

    let source = hir::Source::new(position.file_id.into(), mac.syntax());
    let expanded = expand_macro_recur(db, source, &mac)?;

    // FIXME:
    // macro expansion may lose all white space information
    // But we hope someday we can use ra_fmt for that
    let expansion = insert_whitespaces(expanded);
    Some(ExpandedMacro { name: name_ref.text().to_string(), expansion })
}

fn expand_macro_recur(
    db: &RootDatabase,
    source: hir::Source<&SyntaxNode>,
    macro_call: &ast::MacroCall,
) -> Option<SyntaxNode> {
    let analyzer = hir::SourceAnalyzer::new(db, source, None);
    let expansion = analyzer.expand(db, &macro_call)?;
    let macro_file_id = expansion.file_id();
    let expanded: SyntaxNode = db.parse_or_expand(macro_file_id)?;

    let children = expanded.descendants().filter_map(ast::MacroCall::cast);
    let mut replaces = FxHashMap::default();

    for child in children.into_iter() {
        let source = hir::Source::new(macro_file_id, source.ast);
        let new_node = expand_macro_recur(db, source, &child)?;

        replaces.insert(child.syntax().clone().into(), new_node.into());
    }

    Some(replace_descendants(&expanded, &replaces))
}

// FIXME: It would also be cool to share logic here and in the mbe tests,
// which are pretty unreadable at the moment.
fn insert_whitespaces(syn: SyntaxNode) -> String {
    use SyntaxKind::*;

    let mut res = String::new();
    let mut token_iter = syn
        .preorder_with_tokens()
        .filter_map(|event| {
            if let WalkEvent::Enter(NodeOrToken::Token(token)) = event {
                Some(token)
            } else {
                None
            }
        })
        .peekable();

    let mut indent = 0;
    let mut last: Option<SyntaxKind> = None;

    while let Some(token) = token_iter.next() {
        let mut is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
            token_iter.peek().map(|it| f(it.kind())).unwrap_or(default)
        };
        let is_last = |f: fn(SyntaxKind) -> bool, default| -> bool {
            last.map(|it| f(it)).unwrap_or(default)
        };

        res += &match token.kind() {
            k @ _
                if (k.is_keyword() || k.is_literal() || k == IDENT)
                    && is_next(|it| !it.is_punct(), true) =>
            {
                token.text().to_string() + " "
            }
            L_CURLY if is_next(|it| it != R_CURLY, true) => {
                indent += 1;
                format!(" {{\n{}", "  ".repeat(indent))
            }
            R_CURLY if is_last(|it| it != L_CURLY, true) => {
                indent = indent.checked_sub(1).unwrap_or(0);
                format!("\n}}{}", "  ".repeat(indent))
            }
            R_CURLY => {
                indent = indent.checked_sub(1).unwrap_or(0);
                format!("}}\n{}", "  ".repeat(indent))
            }
            T![;] => format!(";\n{}", "  ".repeat(indent)),
            T![->] => " -> ".to_string(),
            T![=] => " = ".to_string(),
            T![=>] => " => ".to_string(),
            _ => token.text().to_string(),
        };

        last = Some(token.kind());
    }

    res
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::mock_analysis::analysis_and_position;
    use insta::assert_snapshot;

    fn check_expand_macro(fixture: &str) -> ExpandedMacro {
        let (analysis, pos) = analysis_and_position(fixture);
        analysis.expand_macro(pos).unwrap().unwrap()
    }

    #[test]
    fn macro_expand_recursive_expansion() {
        let res = check_expand_macro(
            r#"
        //- /lib.rs
        macro_rules! bar {
            () => { fn  b() {} }
        }
        macro_rules! foo {
            () => { bar!(); }
        }
        macro_rules! baz {
            () => { foo!(); }
        }
        f<|>oo!();
        "#,
        );

        assert_eq!(res.name, "foo");
        assert_snapshot!(res.expansion, @r###"
fn b(){}
"###);
    }

    #[test]
    fn macro_expand_multiple_lines() {
        let res = check_expand_macro(
            r#"
        //- /lib.rs
        macro_rules! foo {
            () => {
                fn some_thing() -> u32 {
                    let a = 0;
                    a + 10
                }
            }
        }
        f<|>oo!();
        "#,
        );

        assert_eq!(res.name, "foo");
        assert_snapshot!(res.expansion, @r###"
fn some_thing() -> u32 {
  let a = 0;
  a+10
}
"###);
    }
}
<|endoftext|>