<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 font::{Font, FontGroup};
use font::SpecifiedFontStyle;
use platform::font_context::FontContextHandle;
use style::computed_values::{font_style, font_variant};

use font_cache_task::FontCacheTask;
use font_template::FontTemplateDescriptor;
use platform::font_template::FontTemplateData;
use font::FontHandleMethods;
use platform::font::FontHandle;
use servo_util::cache::HashCache;
use servo_util::smallvec::{SmallVec, SmallVec1};

use std::rc::Rc;
use std::cell::RefCell;
use sync::Arc;

use azure::AzFloat;
use azure::azure_hl::SkiaBackend;
use azure::scaled_font::ScaledFont;

#[cfg(target_os="linux")]
#[cfg(target_os="android")]
use azure::scaled_font::FontData;

#[cfg(target_os="linux")]
#[cfg(target_os="android")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
    ScaledFont::new(SkiaBackend, FontData(&template.bytes), pt_size as AzFloat)
}

#[cfg(target_os="macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
    let cgfont = template.ctfont.as_ref().unwrap().copy_to_CGFont();
    ScaledFont::new(SkiaBackend, &cgfont, pt_size as AzFloat)
}

static SMALL_CAPS_SCALE_FACTOR: f64 = 0.8;      // Matches FireFox (see gfxFont.h)

struct LayoutFontCacheEntry {
    family: String,
    font: Rc<RefCell<Font>>,
}

struct FallbackFontCacheEntry {
    font: Rc<RefCell<Font>>,
}

/// A cached azure font (per render task) that
/// can be shared by multiple text runs.
struct RenderFontCacheEntry {
    pt_size: f64,
    identifier: String,
    font: Rc<RefCell<ScaledFont>>,
}

/// The FontContext represents the per-thread/task state necessary for
/// working with fonts. It is the public API used by the layout and
/// render code. It talks directly to the font cache task where
/// required.
pub struct FontContext {
    platform_handle: FontContextHandle,
    font_cache_task: FontCacheTask,

    /// TODO: See bug https://github.com/servo/servo/issues/3300.
    layout_font_cache: Vec<LayoutFontCacheEntry>,
    fallback_font_cache: Vec<FallbackFontCacheEntry>,

    /// Strong reference as the render FontContext is (for now) recycled
    /// per frame. TODO: Make this weak when incremental redraw is done.
    render_font_cache: Vec<RenderFontCacheEntry>,
}

impl FontContext {
    pub fn new(font_cache_task: FontCacheTask) -> FontContext {
        let handle = FontContextHandle::new();
        FontContext {
            platform_handle: handle,
            font_cache_task: font_cache_task,
            layout_font_cache: vec!(),
            fallback_font_cache: vec!(),
            render_font_cache: vec!(),
        }
    }

    /// Create a font for use in layout calculations.
    fn create_layout_font(&self, template: Arc<FontTemplateData>,
                            descriptor: FontTemplateDescriptor, pt_size: f64,
                            variant: font_variant::T) -> Font {
        // TODO: (Bug #3463): Currently we only support fake small-caps
        // rendering. We should also support true small-caps (where the
        // font supports it) in the future.
        let actual_pt_size = match variant {
            font_variant::small_caps => pt_size * SMALL_CAPS_SCALE_FACTOR,
            font_variant::normal => pt_size,
        };

        let handle: FontHandle = FontHandleMethods::new_from_template(&self.platform_handle,
                                    template, Some(actual_pt_size)).unwrap();
        let metrics = handle.get_metrics();

        Font {
            handle: handle,
            shaper: None,
            variant: variant,
            descriptor: descriptor,
            requested_pt_size: pt_size,
            actual_pt_size: actual_pt_size,
            metrics: metrics,
            shape_cache: HashCache::new(),
            glyph_advance_cache: HashCache::new(),
        }
    }

    /// Create a group of fonts for use in layout calculations. May return
    /// a cached font if this font instance has already been used by
    /// this context.
    pub fn get_layout_font_group_for_style(&mut self, style: &SpecifiedFontStyle) -> FontGroup {
        // TODO: The font context holds a strong ref to the cached fonts
        // so they will never be released. Find out a good time to drop them.

        let desc = FontTemplateDescriptor::new(style.font_weight,
                                               style.font_style == font_style::italic);
        let mut fonts: SmallVec1<Rc<RefCell<Font>>> = SmallVec1::new();

        for family in style.font_family.iter() {
            // GWTODO: Check on real pages if this is faster as Vec() or HashMap().
            let mut cache_hit = false;
            for cached_font_entry in self.layout_font_cache.iter() {
                if cached_font_entry.family.as_slice() == family.name() {
                    let cached_font = cached_font_entry.font.borrow();
                    if cached_font.descriptor == desc &&
                       cached_font.requested_pt_size == style.font_size.to_subpx() &&
                       cached_font.variant == style.font_variant {
                        fonts.push(cached_font_entry.font.clone());
                        cache_hit = true;
                        break;
                    }
                }
            }

            if !cache_hit {
                let font_template = self.font_cache_task.get_font_template(family.name()
                                                                                .to_string(),
                                                                           desc.clone());
                match font_template {
                    Some(font_template) => {
                        let layout_font = self.create_layout_font(font_template,
                                                                  desc.clone(),
                                                                  style.font_size.to_subpx(),
                                                                  style.font_variant);
                        let layout_font = Rc::new(RefCell::new(layout_font));
                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_string(),
                            font: layout_font.clone(),
                        });
                        fonts.push(layout_font);
                    }
                    None => {}
                }
            }
        }

        // If unable to create any of the specified fonts, create one from the
        // list of last resort fonts for this platform.
        if fonts.len() == 0 {
            let mut cache_hit = false;
            for cached_font_entry in self.fallback_font_cache.iter() {
                let cached_font = cached_font_entry.font.borrow();
                if cached_font.descriptor == desc &&
                            cached_font.requested_pt_size == style.font_size.to_subpx() &&
                            cached_font.variant == style.font_variant {
                    fonts.push(cached_font_entry.font.clone());
                    cache_hit = true;
                    break;
                }
            }

            if !cache_hit {
                let font_template = self.font_cache_task.get_last_resort_font_template(desc.clone());
                let layout_font = self.create_layout_font(font_template,
                                                          desc.clone(),
                                                          style.font_size.to_subpx(),
                                                          style.font_variant);
                let layout_font = Rc::new(RefCell::new(layout_font));
                self.fallback_font_cache.push(FallbackFontCacheEntry {
                    font: layout_font.clone(),
                });
                fonts.push(layout_font);
            }
        }

        FontGroup::new(fonts)
    }

    /// Create a render font for use with azure. May return a cached
    /// reference if already used by this font context.
    pub fn get_render_font_from_template(&mut self,
                                         template: &Arc<FontTemplateData>,
                                         pt_size: f64) -> Rc<RefCell<ScaledFont>> {
        for cached_font in self.render_font_cache.iter() {
            if cached_font.pt_size == pt_size &&
               cached_font.identifier == template.identifier {
                return cached_font.font.clone();
            }
        }

        let render_font = Rc::new(RefCell::new(create_scaled_font(template, pt_size)));
        self.render_font_cache.push(RenderFontCacheEntry{
            font: render_font.clone(),
            pt_size: pt_size,
            identifier: template.identifier.clone(),
        });
        render_font
    }
}
<commit_msg>gfx: Make fonts that fail to match hit the cache too.<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 font::{Font, FontGroup};
use font::SpecifiedFontStyle;
use platform::font_context::FontContextHandle;
use style::computed_values::{font_style, font_variant};

use font_cache_task::FontCacheTask;
use font_template::FontTemplateDescriptor;
use platform::font_template::FontTemplateData;
use font::FontHandleMethods;
use platform::font::FontHandle;
use servo_util::cache::HashCache;
use servo_util::smallvec::{SmallVec, SmallVec1};

use std::rc::Rc;
use std::cell::RefCell;
use sync::Arc;

use azure::AzFloat;
use azure::azure_hl::SkiaBackend;
use azure::scaled_font::ScaledFont;

#[cfg(target_os="linux")]
#[cfg(target_os="android")]
use azure::scaled_font::FontData;

#[cfg(target_os="linux")]
#[cfg(target_os="android")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
    ScaledFont::new(SkiaBackend, FontData(&template.bytes), pt_size as AzFloat)
}

#[cfg(target_os="macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
    let cgfont = template.ctfont.as_ref().unwrap().copy_to_CGFont();
    ScaledFont::new(SkiaBackend, &cgfont, pt_size as AzFloat)
}

static SMALL_CAPS_SCALE_FACTOR: f64 = 0.8;      // Matches FireFox (see gfxFont.h)

struct LayoutFontCacheEntry {
    family: String,
    font: Option<Rc<RefCell<Font>>>,
}

struct FallbackFontCacheEntry {
    font: Rc<RefCell<Font>>,
}

/// A cached azure font (per render task) that
/// can be shared by multiple text runs.
struct RenderFontCacheEntry {
    pt_size: f64,
    identifier: String,
    font: Rc<RefCell<ScaledFont>>,
}

/// The FontContext represents the per-thread/task state necessary for
/// working with fonts. It is the public API used by the layout and
/// render code. It talks directly to the font cache task where
/// required.
pub struct FontContext {
    platform_handle: FontContextHandle,
    font_cache_task: FontCacheTask,

    /// TODO: See bug https://github.com/servo/servo/issues/3300.
    layout_font_cache: Vec<LayoutFontCacheEntry>,
    fallback_font_cache: Vec<FallbackFontCacheEntry>,

    /// Strong reference as the render FontContext is (for now) recycled
    /// per frame. TODO: Make this weak when incremental redraw is done.
    render_font_cache: Vec<RenderFontCacheEntry>,
}

impl FontContext {
    pub fn new(font_cache_task: FontCacheTask) -> FontContext {
        let handle = FontContextHandle::new();
        FontContext {
            platform_handle: handle,
            font_cache_task: font_cache_task,
            layout_font_cache: vec!(),
            fallback_font_cache: vec!(),
            render_font_cache: vec!(),
        }
    }

    /// Create a font for use in layout calculations.
    fn create_layout_font(&self, template: Arc<FontTemplateData>,
                            descriptor: FontTemplateDescriptor, pt_size: f64,
                            variant: font_variant::T) -> Font {
        // TODO: (Bug #3463): Currently we only support fake small-caps
        // rendering. We should also support true small-caps (where the
        // font supports it) in the future.
        let actual_pt_size = match variant {
            font_variant::small_caps => pt_size * SMALL_CAPS_SCALE_FACTOR,
            font_variant::normal => pt_size,
        };

        let handle: FontHandle = FontHandleMethods::new_from_template(&self.platform_handle,
                                    template, Some(actual_pt_size)).unwrap();
        let metrics = handle.get_metrics();

        Font {
            handle: handle,
            shaper: None,
            variant: variant,
            descriptor: descriptor,
            requested_pt_size: pt_size,
            actual_pt_size: actual_pt_size,
            metrics: metrics,
            shape_cache: HashCache::new(),
            glyph_advance_cache: HashCache::new(),
        }
    }

    /// Create a group of fonts for use in layout calculations. May return
    /// a cached font if this font instance has already been used by
    /// this context.
    pub fn get_layout_font_group_for_style(&mut self, style: &SpecifiedFontStyle) -> FontGroup {
        // TODO: The font context holds a strong ref to the cached fonts
        // so they will never be released. Find out a good time to drop them.

        let desc = FontTemplateDescriptor::new(style.font_weight,
                                               style.font_style == font_style::italic);
        let mut fonts: SmallVec1<Rc<RefCell<Font>>> = SmallVec1::new();

        for family in style.font_family.iter() {
            // GWTODO: Check on real pages if this is faster as Vec() or HashMap().
            let mut cache_hit = false;
            for cached_font_entry in self.layout_font_cache.iter() {
                if cached_font_entry.family.as_slice() == family.name() {
                    match cached_font_entry.font {
                        None => {
                            cache_hit = true;
                            break;
                        }
                        Some(ref cached_font_ref) => {
                            let cached_font = cached_font_ref.borrow();
                            if cached_font.descriptor == desc &&
                               cached_font.requested_pt_size == style.font_size.to_subpx() &&
                               cached_font.variant == style.font_variant {
                                fonts.push((*cached_font_ref).clone());
                                cache_hit = true;
                                break;
                            }
                        }
                    }
                }
            }

            if !cache_hit {
                let font_template = self.font_cache_task.get_font_template(family.name()
                                                                                .to_string(),
                                                                           desc.clone());
                match font_template {
                    Some(font_template) => {
                        let layout_font = self.create_layout_font(font_template,
                                                                  desc.clone(),
                                                                  style.font_size.to_subpx(),
                                                                  style.font_variant);
                        let layout_font = Rc::new(RefCell::new(layout_font));
                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_string(),
                            font: Some(layout_font.clone()),
                        });
                        fonts.push(layout_font);
                    }
                    None => {
                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_string(),
                            font: None,
                        });
                    }
                }
            }
        }

        // If unable to create any of the specified fonts, create one from the
        // list of last resort fonts for this platform.
        if fonts.len() == 0 {
            let mut cache_hit = false;
            for cached_font_entry in self.fallback_font_cache.iter() {
                let cached_font = cached_font_entry.font.borrow();
                if cached_font.descriptor == desc &&
                            cached_font.requested_pt_size == style.font_size.to_subpx() &&
                            cached_font.variant == style.font_variant {
                    fonts.push(cached_font_entry.font.clone());
                    cache_hit = true;
                    break;
                }
            }

            if !cache_hit {
                let font_template = self.font_cache_task.get_last_resort_font_template(desc.clone());
                let layout_font = self.create_layout_font(font_template,
                                                          desc.clone(),
                                                          style.font_size.to_subpx(),
                                                          style.font_variant);
                let layout_font = Rc::new(RefCell::new(layout_font));
                self.fallback_font_cache.push(FallbackFontCacheEntry {
                    font: layout_font.clone(),
                });
                fonts.push(layout_font);
            }
        }

        FontGroup::new(fonts)
    }

    /// Create a render font for use with azure. May return a cached
    /// reference if already used by this font context.
    pub fn get_render_font_from_template(&mut self,
                                         template: &Arc<FontTemplateData>,
                                         pt_size: f64) -> Rc<RefCell<ScaledFont>> {
        for cached_font in self.render_font_cache.iter() {
            if cached_font.pt_size == pt_size &&
               cached_font.identifier == template.identifier {
                return cached_font.font.clone();
            }
        }

        let render_font = Rc::new(RefCell::new(create_scaled_font(template, pt_size)));
        self.render_font_cache.push(RenderFontCacheEntry{
            font: render_font.clone(),
            pt_size: pt_size,
            identifier: template.identifier.clone(),
        });
        render_font
    }
}
<|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/. */

//! CSS transitions and animations.

use context::LayoutContext;
use flow::{self, Flow};
use gfx::display_list::OpaqueNode;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
use opaque_node::OpaqueNodeMethods;
use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg};
use script_traits::UntrustedNodeAddress;
use std::collections::HashMap;
use std::sync::mpsc::Receiver;
use style::animation::{Animation, update_style_for_animation};
use style::font_metrics::ServoMetricsProvider;
use style::selector_parser::RestyleDamage;
use style::timer::Timer;

/// Processes any new animations that were discovered after style recalculation.
/// Also expire any old animations that have completed, inserting them into
/// `expired_animations`.
pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
                              script_chan: &IpcSender<ConstellationControlMsg>,
                              running_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
                              expired_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
                              mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>,
                              new_animations_receiver: &Receiver<Animation>,
                              pipeline_id: PipelineId,
                              timer: &Timer) {
    let mut new_running_animations = vec![];
    while let Ok(animation) = new_animations_receiver.try_recv() {
        let mut should_push = true;
        if let Animation::Keyframes(ref node, ref name, ref state) = animation {
            // If the animation was already present in the list for the
            // node, just update its state, else push the new animation to
            // run.
            if let Some(ref mut animations) = running_animations.get_mut(node) {
                // TODO: This being linear is probably not optimal.
                for mut anim in animations.iter_mut() {
                    if let Animation::Keyframes(_, ref anim_name, ref mut anim_state) = *anim {
                        if *name == *anim_name {
                            debug!("update_animation_state: Found other animation {}", name);
                            anim_state.update_from_other(&state, timer);
                            should_push = false;
                            break;
                        }
                    }
                }
            }
        }

        if should_push {
            new_running_animations.push(animation);
        }
    }

    if running_animations.is_empty() && new_running_animations.is_empty() {
        // Nothing to do. Return early so we don't flood the compositor with
        // `ChangeRunningAnimationsState` messages.
        return
    }

    let now = timer.seconds();
    // Expire old running animations.
    //
    // TODO: Do not expunge Keyframes animations, since we need that state if
    // the animation gets re-triggered. Probably worth splitting in two
    // different maps, or at least using a linked list?
    let mut keys_to_remove = vec![];
    for (key, running_animations) in running_animations.iter_mut() {
        let mut animations_still_running = vec![];
        for mut running_animation in running_animations.drain(..) {
            let still_running = !running_animation.is_expired() && match running_animation {
                Animation::Transition(_, started_at, ref frame, _expired) => {
                    now < started_at + frame.duration
                }
                Animation::Keyframes(_, _, ref mut state) => {
                    // This animation is still running, or we need to keep
                    // iterating.
                    now < state.started_at + state.duration || state.tick()
                }
            };

            if still_running {
                animations_still_running.push(running_animation);
                continue
            }

            if let Animation::Transition(node, _, ref frame, _) = running_animation {
                script_chan.send(ConstellationControlMsg::TransitionEnd(node.to_untrusted_node_address(),
                                                                        frame.property_animation
                                                                             .property_name().into(),
                                                                        frame.duration))
                           .unwrap();
            }

            expired_animations.entry(*key)
                              .or_insert_with(Vec::new)
                              .push(running_animation);
        }

        if animations_still_running.is_empty() {
            keys_to_remove.push(*key);
        } else {
            *running_animations = animations_still_running
        }
    }

    for key in keys_to_remove {
        running_animations.remove(&key).unwrap();
    }

    // Add new running animations.
    for new_running_animation in new_running_animations {
        match newly_transitioning_nodes {
            Some(ref mut nodes) if new_running_animation.is_transition() => {
                nodes.push(new_running_animation.node().to_untrusted_node_address());
            }
            _ => ()
        }

        running_animations.entry(*new_running_animation.node())
                          .or_insert_with(Vec::new)
                          .push(new_running_animation)
    }

    let animation_state = if running_animations.is_empty() {
        AnimationState::NoAnimationsPresent
    } else {
        AnimationState::AnimationsPresent
    };

    constellation_chan.send(ConstellationMsg::ChangeRunningAnimationsState(pipeline_id,
                                                                           animation_state))
                      .unwrap();
}

/// Recalculates style for a set of animations. This does *not* run with the DOM
/// lock held.
// NB: This is specific for SelectorImpl, since the layout context and the
// flows are SelectorImpl specific too. If that goes away at some point,
// this should be made generic.
pub fn recalc_style_for_animations(context: &LayoutContext,
                                   flow: &mut Flow,
                                   animations: &HashMap<OpaqueNode,
                                                        Vec<Animation>>) {
    let mut damage = RestyleDamage::empty();
    flow.mutate_fragments(&mut |fragment| {
        if let Some(ref animations) = animations.get(&fragment.node) {
            for animation in animations.iter() {
                let old_style = fragment.style.clone();
                update_style_for_animation(&context.style_context,
                                           animation,
                                           &mut fragment.style,
                                           &ServoMetricsProvider);
                damage |= RestyleDamage::compute(&old_style, &fragment.style);
            }
        }
    });

    let base = flow::mut_base(flow);
    base.restyle_damage.insert(damage);
    for kid in base.children.iter_mut() {
        recalc_style_for_animations(context, kid, animations)
    }
}
<commit_msg>Warn about ignored transitions which can't be rooted.<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/. */

//! CSS transitions and animations.

use context::LayoutContext;
use flow::{self, Flow};
use gfx::display_list::OpaqueNode;
use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId;
use opaque_node::OpaqueNodeMethods;
use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg};
use script_traits::UntrustedNodeAddress;
use std::collections::HashMap;
use std::sync::mpsc::Receiver;
use style::animation::{Animation, update_style_for_animation};
use style::font_metrics::ServoMetricsProvider;
use style::selector_parser::RestyleDamage;
use style::timer::Timer;

/// Processes any new animations that were discovered after style recalculation.
/// Also expire any old animations that have completed, inserting them into
/// `expired_animations`.
pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
                              script_chan: &IpcSender<ConstellationControlMsg>,
                              running_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
                              expired_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
                              mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>,
                              new_animations_receiver: &Receiver<Animation>,
                              pipeline_id: PipelineId,
                              timer: &Timer) {
    let mut new_running_animations = vec![];
    while let Ok(animation) = new_animations_receiver.try_recv() {
        let mut should_push = true;
        if let Animation::Keyframes(ref node, ref name, ref state) = animation {
            // If the animation was already present in the list for the
            // node, just update its state, else push the new animation to
            // run.
            if let Some(ref mut animations) = running_animations.get_mut(node) {
                // TODO: This being linear is probably not optimal.
                for mut anim in animations.iter_mut() {
                    if let Animation::Keyframes(_, ref anim_name, ref mut anim_state) = *anim {
                        if *name == *anim_name {
                            debug!("update_animation_state: Found other animation {}", name);
                            anim_state.update_from_other(&state, timer);
                            should_push = false;
                            break;
                        }
                    }
                }
            }
        }

        if should_push {
            new_running_animations.push(animation);
        }
    }

    if running_animations.is_empty() && new_running_animations.is_empty() {
        // Nothing to do. Return early so we don't flood the compositor with
        // `ChangeRunningAnimationsState` messages.
        return
    }

    let now = timer.seconds();
    // Expire old running animations.
    //
    // TODO: Do not expunge Keyframes animations, since we need that state if
    // the animation gets re-triggered. Probably worth splitting in two
    // different maps, or at least using a linked list?
    let mut keys_to_remove = vec![];
    for (key, running_animations) in running_animations.iter_mut() {
        let mut animations_still_running = vec![];
        for mut running_animation in running_animations.drain(..) {
            let still_running = !running_animation.is_expired() && match running_animation {
                Animation::Transition(_, started_at, ref frame, _expired) => {
                    now < started_at + frame.duration
                }
                Animation::Keyframes(_, _, ref mut state) => {
                    // This animation is still running, or we need to keep
                    // iterating.
                    now < state.started_at + state.duration || state.tick()
                }
            };

            if still_running {
                animations_still_running.push(running_animation);
                continue
            }

            if let Animation::Transition(node, _, ref frame, _) = running_animation {
                script_chan.send(ConstellationControlMsg::TransitionEnd(node.to_untrusted_node_address(),
                                                                        frame.property_animation
                                                                             .property_name().into(),
                                                                        frame.duration))
                           .unwrap();
            }

            expired_animations.entry(*key)
                              .or_insert_with(Vec::new)
                              .push(running_animation);
        }

        if animations_still_running.is_empty() {
            keys_to_remove.push(*key);
        } else {
            *running_animations = animations_still_running
        }
    }

    for key in keys_to_remove {
        running_animations.remove(&key).unwrap();
    }

    // Add new running animations.
    for new_running_animation in new_running_animations {
        if new_running_animation.is_transition() {
            match newly_transitioning_nodes {
                Some(ref mut nodes) => {
                    nodes.push(new_running_animation.node().to_untrusted_node_address());
                }
                None => {
                    warn!("New transition encountered from compositor-initiated layout.");
                }
            }
        }

        running_animations.entry(*new_running_animation.node())
                          .or_insert_with(Vec::new)
                          .push(new_running_animation)
    }

    let animation_state = if running_animations.is_empty() {
        AnimationState::NoAnimationsPresent
    } else {
        AnimationState::AnimationsPresent
    };

    constellation_chan.send(ConstellationMsg::ChangeRunningAnimationsState(pipeline_id,
                                                                           animation_state))
                      .unwrap();
}

/// Recalculates style for a set of animations. This does *not* run with the DOM
/// lock held.
// NB: This is specific for SelectorImpl, since the layout context and the
// flows are SelectorImpl specific too. If that goes away at some point,
// this should be made generic.
pub fn recalc_style_for_animations(context: &LayoutContext,
                                   flow: &mut Flow,
                                   animations: &HashMap<OpaqueNode,
                                                        Vec<Animation>>) {
    let mut damage = RestyleDamage::empty();
    flow.mutate_fragments(&mut |fragment| {
        if let Some(ref animations) = animations.get(&fragment.node) {
            for animation in animations.iter() {
                let old_style = fragment.style.clone();
                update_style_for_animation(&context.style_context,
                                           animation,
                                           &mut fragment.style,
                                           &ServoMetricsProvider);
                damage |= RestyleDamage::compute(&old_style, &fragment.style);
            }
        }
    });

    let base = flow::mut_base(flow);
    base.restyle_damage.insert(damage);
    for kid in base.children.iter_mut() {
        recalc_style_for_animations(context, kid, animations)
    }
}
<|endoftext|>


<commit_before>use leap;

fn process_leapyear_case(year: u64, expected: bool) {
    assert_eq!(leap::is_leap_year(year), expected);
}

#[test]
fn test_year_not_divisible_by_4_common_year() {
    process_leapyear_case(2015, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_2_not_divisible_by_4_in_common_year() {
    process_leapyear_case(1970, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_4_not_divisible_by_100_leap_year() {
    process_leapyear_case(1996, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_4_and_5_is_still_a_leap_year() {
    process_leapyear_case(1960, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_100_not_divisible_by_400_common_year() {
    process_leapyear_case(2100, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_100_but_not_by_3_is_still_not_a_leap_year() {
    process_leapyear_case(1900, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_400_leap_year() {
    process_leapyear_case(2000, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_400_but_not_by_125_is_still_a_leap_year() {
    process_leapyear_case(2400, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_200_not_divisible_by_400_common_year() {
    process_leapyear_case(1800, false);
}

#[test]
#[ignore]
fn test_any_old_year() {
    process_leapyear_case(1997, false);
}

#[test]
#[ignore]
fn test_early_years() {
    process_leapyear_case(1, false);
    process_leapyear_case(4, true);
    process_leapyear_case(100, false);
    process_leapyear_case(400, true);
    process_leapyear_case(900, false);
}

#[test]
#[ignore]
fn test_century() {
    process_leapyear_case(1700, false);
    process_leapyear_case(1800, false);
    process_leapyear_case(1900, false);
}

#[test]
#[ignore]
fn test_exceptional_centuries() {
    process_leapyear_case(1600, true);
    process_leapyear_case(2000, true);
    process_leapyear_case(2400, true);
}

#[test]
#[ignore]
fn test_years_1600_to_1699() {
    let incorrect_years = (1600..1700)
        .filter(|&year| leap::is_leap_year(year) != (year % 4 == 0))
        .collect::<Vec<_>>();

    if !incorrect_years.is_empty() {
        panic!("incorrect result for years: {:?}", incorrect_years);
    }
}
<commit_msg>Clippy says to remove `use leap;` at the beginning (#987)<commit_after>fn process_leapyear_case(year: u64, expected: bool) {
    assert_eq!(leap::is_leap_year(year), expected);
}

#[test]
fn test_year_not_divisible_by_4_common_year() {
    process_leapyear_case(2015, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_2_not_divisible_by_4_in_common_year() {
    process_leapyear_case(1970, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_4_not_divisible_by_100_leap_year() {
    process_leapyear_case(1996, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_4_and_5_is_still_a_leap_year() {
    process_leapyear_case(1960, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_100_not_divisible_by_400_common_year() {
    process_leapyear_case(2100, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_100_but_not_by_3_is_still_not_a_leap_year() {
    process_leapyear_case(1900, false);
}

#[test]
#[ignore]
fn test_year_divisible_by_400_leap_year() {
    process_leapyear_case(2000, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_400_but_not_by_125_is_still_a_leap_year() {
    process_leapyear_case(2400, true);
}

#[test]
#[ignore]
fn test_year_divisible_by_200_not_divisible_by_400_common_year() {
    process_leapyear_case(1800, false);
}

#[test]
#[ignore]
fn test_any_old_year() {
    process_leapyear_case(1997, false);
}

#[test]
#[ignore]
fn test_early_years() {
    process_leapyear_case(1, false);
    process_leapyear_case(4, true);
    process_leapyear_case(100, false);
    process_leapyear_case(400, true);
    process_leapyear_case(900, false);
}

#[test]
#[ignore]
fn test_century() {
    process_leapyear_case(1700, false);
    process_leapyear_case(1800, false);
    process_leapyear_case(1900, false);
}

#[test]
#[ignore]
fn test_exceptional_centuries() {
    process_leapyear_case(1600, true);
    process_leapyear_case(2000, true);
    process_leapyear_case(2400, true);
}

#[test]
#[ignore]
fn test_years_1600_to_1699() {
    let incorrect_years = (1600..1700)
        .filter(|&year| leap::is_leap_year(year) != (year % 4 == 0))
        .collect::<Vec<_>>();

    if !incorrect_years.is_empty() {
        panic!("incorrect result for years: {:?}", incorrect_years);
    }
}
<|endoftext|>


<commit_before>use rustfmt_nightly::{Config, Edition, EmitMode, Input, Session};
use serde::Deserialize;
use serde_json::Value;
use std::path::PathBuf;
use std::{
    collections::{BTreeMap, HashSet},
    fs::{read_dir, File},
    hash::{Hash, Hasher},
    io::{prelude::*, Read},
};

mod code_gen;

/// A complete API specification parsed from the REST API specs
pub struct Api {
    pub commit: String,
    /// parameters that are common to all API methods
    pub common_params: BTreeMap<String, Type>,
    /// root API methods
    pub root: BTreeMap<String, ApiEndpoint>,
    /// namespace client methods
    pub namespaces: BTreeMap<String, BTreeMap<String, ApiEndpoint>>,
    /// enums in parameters
    pub enums: Vec<ApiEnum>,
}

#[derive(Debug, PartialEq, Deserialize, Clone, Copy)]
pub enum HttpMethod {
    #[serde(rename = "HEAD")]
    Head,
    #[serde(rename = "GET")]
    Get,
    #[serde(rename = "POST")]
    Post,
    #[serde(rename = "PUT")]
    Put,
    #[serde(rename = "PATCH")]
    Patch,
    #[serde(rename = "DELETE")]
    Delete,
}

impl quote::ToTokens for HttpMethod {
    fn to_tokens(&self, tokens: &mut quote::Tokens) {
        tokens.append("HttpMethod");
        tokens.append("::");
        match *self {
            HttpMethod::Head => tokens.append("Head"),
            HttpMethod::Get => tokens.append("Get"),
            HttpMethod::Post => tokens.append("Post"),
            HttpMethod::Put => tokens.append("Put"),
            HttpMethod::Patch => tokens.append("Patch"),
            HttpMethod::Delete => tokens.append("Delete"),
        }
    }
}

/// A type defined in the REST API spec
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Type {
    #[serde(rename = "type", default)]
    pub ty: TypeKind,
    pub description: Option<String>,
    #[serde(default = "Vec::new")]
    pub options: Vec<Value>,
    #[serde(default)]
    pub default: Option<Value>,
}

/// The kind of type
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub enum TypeKind {
    None,
    #[serde(rename = "list")]
    List,
    #[serde(rename = "enum")]
    Enum,
    #[serde(rename = "string")]
    String,
    #[serde(rename = "text")]
    Text,
    #[serde(rename = "boolean")]
    Boolean,
    #[serde(rename = "number")]
    Number,
    #[serde(rename = "float")]
    Float,
    #[serde(rename = "double")]
    Double,
    #[serde(rename = "integer", alias = "int")]
    Integer,
    #[serde(rename = "long")]
    Long,
    #[serde(rename = "date")]
    Date,
    #[serde(rename = "time")]
    Time,
    #[serde(rename = "duration")]
    Duration,
}

impl Default for TypeKind {
    fn default() -> Self {
        TypeKind::None
    }
}

#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Url {
    pub paths: Vec<String>,
    #[serde(default = "BTreeMap::new")]
    pub parts: BTreeMap<String, Type>,
    #[serde(default = "BTreeMap::new")]
    pub params: BTreeMap<String, Type>,
}

#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Body {
    pub description: String,
}

/// An API endpoint defined in the REST API specs
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct ApiEndpoint {
    documentation: Option<String>,
    stability: String,
    methods: Vec<HttpMethod>,
    url: Url,
    body: Option<Body>,
}

impl ApiEndpoint {
    /// Whether the endpoint supports sending a body
    pub fn supports_body(&self) -> bool {
        self.methods
            .iter()
            .any(|m| m == &HttpMethod::Post || m == &HttpMethod::Put)
            || self.body.is_some()
    }
}

/// Common parameters accepted by all API endpoints
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Common {
    description: String,
    documentation: String,
    params: BTreeMap<String, Type>,
}

/// An enum defined in the REST API specs
pub struct ApiEnum {
    pub name: String,
    pub values: Vec<String>,
}

impl Hash for ApiEnum {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
    }
}

impl PartialEq for ApiEnum {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
    }
}

impl Eq for ApiEnum {}

pub fn generate(
    branch: &str,
    download_dir: &PathBuf,
    generated_dir: &PathBuf,
) -> Result<(), failure::Error> {
    let api = read_api(branch, download_dir)?;

    let enums = code_gen::enums::generate(&api)?;
    write_file(enums, generated_dir, "enums.rs")?;

    let namespace_clients = code_gen::namespace_clients::generate(&api)?;
    let mut namespace_clients_dir = generated_dir.clone();
    namespace_clients_dir.push("namespace_clients");
    std::fs::create_dir_all(&namespace_clients_dir)?;

    let modules = namespace_clients
        .iter()
        .map(|(name, _)| format!("pub mod {};", name))
        .collect::<Vec<_>>()
        .join("\n");

    write_file(modules, &namespace_clients_dir, "mod.rs")?;

    for namespace_client in namespace_clients {
        write_file(
            namespace_client.1,
            &namespace_clients_dir,
            format!("{}.rs", namespace_client.0).as_str(),
        )?;
    }

    let root = code_gen::root::generate(&api)?;
    write_file(root, generated_dir, "root.rs")?;

    Ok(())
}

fn write_file(input: String, dir: &PathBuf, file: &str) -> Result<(), failure::Error> {
    let mut generated_path = dir.clone();
    generated_path.push(file);
    let path = generated_path.to_string_lossy().into_owned();

    let mut file = File::create(&path)?;
    file.write_all(input.as_bytes())?;

    Ok(())
}

fn read_api(branch: &str, download_dir: &PathBuf) -> Result<Api, failure::Error> {
    let paths = read_dir(download_dir).unwrap();
    let mut namespaces = BTreeMap::new();
    let mut enums: HashSet<ApiEnum> = HashSet::new();
    let mut common_params = BTreeMap::new();
    let root_key = "root";

    for path in paths {
        let path = path?.path();
        let name = path.file_name().map(|path| path.to_str());
        let display = path.to_string_lossy().into_owned();

        if name
            .unwrap()
            .map(|name| !name.starts_with("_"))
            .unwrap_or(true)
        {
            let mut file = File::open(&path)?;
            let (name, api_endpoint) = endpoint_from_file(display, &mut file)?;

            let name_parts: Vec<&str> = name.splitn(2, '.').collect();
            let (namespace, method_name) = match name_parts.len() {
                len if len > 1 => (name_parts[0].to_string(), name_parts[1].to_string()),
                _ => (root_key.to_string(), name),
            };

            // collect unique enum values
            for param in api_endpoint
                .url
                .params
                .iter()
                .filter(|p| p.1.ty == TypeKind::Enum)
            {
                let options: Vec<String> = param
                    .1
                    .options
                    .iter()
                    .map(|v| v.as_str().unwrap().to_string())
                    .collect();

                enums.insert(ApiEnum {
                    name: param.0.to_string(),
                    values: options,
                });
            }

            // collect api endpoints into namespaces
            if !namespaces.contains_key(&namespace) {
                let mut api_endpoints = BTreeMap::new();
                api_endpoints.insert(method_name, api_endpoint);
                namespaces.insert(namespace.to_string(), api_endpoints);
            } else {
                namespaces
                    .get_mut(&namespace)
                    .unwrap()
                    .insert(method_name, api_endpoint);
            }
        } else if name
            .map(|name| name == Some("_common.json"))
            .unwrap_or(true)
        {
            let mut file = File::open(&path)?;
            let common = common_params_from_file(display, &mut file)?;
            common_params = common.params;
        }
    }

    // extract the root methods
    let root = namespaces.remove(root_key).unwrap();

    let mut sorted_enums = enums.into_iter().collect::<Vec<_>>();
    sorted_enums.sort_by(|a, b| a.name.cmp(&b.name));

    Ok(Api {
        commit: branch.to_string(),
        common_params,
        root,
        namespaces,
        enums: sorted_enums,
    })
}

/// deserializes an ApiEndpoint from a file
fn endpoint_from_file<R>(
    name: String,
    reader: &mut R,
) -> Result<(String, ApiEndpoint), failure::Error>
where
    R: Read,
{
    let endpoint: BTreeMap<String, ApiEndpoint> =
        serde_json::from_reader(reader).map_err(|e| super::error::ParseError {
            message: format!("Failed to parse {} because: {}", name, e),
        })?;

    // get the first (and only) endpoint name and endpoint body
    Ok(endpoint.into_iter().next().unwrap())
}

/// deserializes Common from a file
fn common_params_from_file<R>(name: String, reader: &mut R) -> Result<Common, failure::Error>
where
    R: Read,
{
    let common: Common = serde_json::from_reader(reader).map_err(|e| super::error::ParseError {
        message: format!("Failed to parse {} because: {}", name, e),
    })?;

    Ok(common)
}

/// formats tokens using rustfmt
/// https://github.com/bcmyers/num-format/blob/b7a99480b8087924d291887b13d8c38b7ce43a36/num-format-dev/src/rustfmt.rs
fn rust_fmt<S>(module: S) -> Result<String, failure::Error>
where
    S: Into<String>,
{
    let input = Input::Text(module.into());
    let mut config = Config::default();
    config.set().edition(Edition::Edition2018);
    config.set().emit_mode(EmitMode::Stdout);
    let mut output = Vec::new();
    {
        let mut session = Session::new(config, Some(&mut output));
        let _format_report = session.format(input)?;
    }

    // remove stdin: from start of output
    let b = "stdin:".as_bytes();
    if output.starts_with(b) {
        output.drain(0..b.len());
    }

    let s = String::from_utf8(output)?;

    // trim whitespace
    Ok(s.trim().to_owned())
}
<commit_msg>Additional comments<commit_after>use rustfmt_nightly::{Config, Edition, EmitMode, Input, Session};
use serde::Deserialize;
use serde_json::Value;
use std::path::PathBuf;
use std::{
    collections::{BTreeMap, HashSet},
    fs::{read_dir, File},
    hash::{Hash, Hasher},
    io::{prelude::*, Read},
};

mod code_gen;

/// A complete API specification parsed from the REST API specs
pub struct Api {
    pub commit: String,
    /// parameters that are common to all API methods
    pub common_params: BTreeMap<String, Type>,
    /// root API methods
    pub root: BTreeMap<String, ApiEndpoint>,
    /// namespace client methods
    pub namespaces: BTreeMap<String, BTreeMap<String, ApiEndpoint>>,
    /// enums in parameters
    pub enums: Vec<ApiEnum>,
}

#[derive(Debug, PartialEq, Deserialize, Clone, Copy)]
pub enum HttpMethod {
    #[serde(rename = "HEAD")]
    Head,
    #[serde(rename = "GET")]
    Get,
    #[serde(rename = "POST")]
    Post,
    #[serde(rename = "PUT")]
    Put,
    #[serde(rename = "PATCH")]
    Patch,
    #[serde(rename = "DELETE")]
    Delete,
}

impl quote::ToTokens for HttpMethod {
    fn to_tokens(&self, tokens: &mut quote::Tokens) {
        tokens.append("HttpMethod");
        tokens.append("::");
        match *self {
            HttpMethod::Head => tokens.append("Head"),
            HttpMethod::Get => tokens.append("Get"),
            HttpMethod::Post => tokens.append("Post"),
            HttpMethod::Put => tokens.append("Put"),
            HttpMethod::Patch => tokens.append("Patch"),
            HttpMethod::Delete => tokens.append("Delete"),
        }
    }
}

/// A type defined in the REST API spec
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Type {
    #[serde(rename = "type", default)]
    pub ty: TypeKind,
    pub description: Option<String>,
    #[serde(default = "Vec::new")]
    pub options: Vec<Value>,
    #[serde(default)]
    pub default: Option<Value>,
}

/// The kind of type
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub enum TypeKind {
    None,
    #[serde(rename = "list")]
    List,
    #[serde(rename = "enum")]
    Enum,
    #[serde(rename = "string")]
    String,
    #[serde(rename = "text")]
    Text,
    #[serde(rename = "boolean")]
    Boolean,
    #[serde(rename = "number")]
    Number,
    #[serde(rename = "float")]
    Float,
    #[serde(rename = "double")]
    Double,
    #[serde(rename = "integer", alias = "int")]
    Integer,
    #[serde(rename = "long")]
    Long,
    #[serde(rename = "date")]
    Date,
    #[serde(rename = "time")]
    Time,
    #[serde(rename = "duration")]
    Duration,
}

impl Default for TypeKind {
    fn default() -> Self {
        TypeKind::None
    }
}

#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Url {
    pub paths: Vec<String>,
    #[serde(default = "BTreeMap::new")]
    pub parts: BTreeMap<String, Type>,
    #[serde(default = "BTreeMap::new")]
    pub params: BTreeMap<String, Type>,
}

#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Body {
    pub description: String,
}

/// An API endpoint defined in the REST API specs
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct ApiEndpoint {
    documentation: Option<String>,
    stability: String,
    methods: Vec<HttpMethod>,
    url: Url,
    body: Option<Body>,
}

impl ApiEndpoint {
    /// Whether the endpoint supports sending a body
    pub fn supports_body(&self) -> bool {
        self.methods
            .iter()
            .any(|m| m == &HttpMethod::Post || m == &HttpMethod::Put)
            || self.body.is_some()
    }
}

/// Common parameters accepted by all API endpoints
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Common {
    description: String,
    documentation: String,
    params: BTreeMap<String, Type>,
}

/// An enum defined in the REST API specs
pub struct ApiEnum {
    pub name: String,
    pub values: Vec<String>,
}

impl Hash for ApiEnum {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
    }
}

impl PartialEq for ApiEnum {
    fn eq(&self, other: &Self) -> bool {
        self.name == other.name
    }
}

impl Eq for ApiEnum {}

/// Generates all client source from the REST API spec
pub fn generate(
    branch: &str,
    download_dir: &PathBuf,
    generated_dir: &PathBuf,
) -> Result<(), failure::Error> {
    // read the Api from file
    let api = read_api(branch, download_dir)?;

    // generate enums
    let enums = code_gen::enums::generate(&api)?;
    write_file(enums, generated_dir, "enums.rs")?;

    // generate namespace clients
    let namespace_clients = code_gen::namespace_clients::generate(&api)?;
    let mut namespace_clients_dir = generated_dir.clone();
    namespace_clients_dir.push("namespace_clients");
    std::fs::create_dir_all(&namespace_clients_dir)?;

    let modules = namespace_clients
        .iter()
        .map(|(name, _)| format!("pub mod {};", name))
        .collect::<Vec<_>>()
        .join("\n");

    write_file(modules, &namespace_clients_dir, "mod.rs")?;

    for namespace_client in namespace_clients {
        write_file(
            namespace_client.1,
            &namespace_clients_dir,
            format!("{}.rs", namespace_client.0).as_str(),
        )?;
    }

    // generate functions on root of client
    let root = code_gen::root::generate(&api)?;
    write_file(root, generated_dir, "root.rs")?;

    Ok(())
}

fn write_file(input: String, dir: &PathBuf, file: &str) -> Result<(), failure::Error> {
    let mut generated_path = dir.clone();
    generated_path.push(file);
    let path = generated_path.to_string_lossy().into_owned();

    let mut file = File::create(&path)?;
    file.write_all(input.as_bytes())?;

    Ok(())
}

/// Reads Api from a directory of REST Api specs
fn read_api(branch: &str, download_dir: &PathBuf) -> Result<Api, failure::Error> {
    let paths = read_dir(download_dir)?;
    let mut namespaces = BTreeMap::new();
    let mut enums: HashSet<ApiEnum> = HashSet::new();
    let mut common_params = BTreeMap::new();
    let root_key = "root";

    for path in paths {
        let path = path?.path();
        let name = path.file_name().map(|path| path.to_str());
        let display = path.to_string_lossy().into_owned();

        if name
            .unwrap()
            .map(|name| !name.starts_with("_"))
            .unwrap_or(true)
        {
            let mut file = File::open(&path)?;
            let (name, api_endpoint) = endpoint_from_file(display, &mut file)?;

            let name_parts: Vec<&str> = name.splitn(2, '.').collect();
            let (namespace, method_name) = match name_parts.len() {
                len if len > 1 => (name_parts[0].to_string(), name_parts[1].to_string()),
                _ => (root_key.to_string(), name),
            };

            // collect unique enum values
            for param in api_endpoint
                .url
                .params
                .iter()
                .filter(|p| p.1.ty == TypeKind::Enum)
            {
                let options: Vec<String> = param
                    .1
                    .options
                    .iter()
                    .map(|v| v.as_str().unwrap().to_string())
                    .collect();

                enums.insert(ApiEnum {
                    name: param.0.to_string(),
                    values: options,
                });
            }

            // collect api endpoints into namespaces
            if !namespaces.contains_key(&namespace) {
                let mut api_endpoints = BTreeMap::new();
                api_endpoints.insert(method_name, api_endpoint);
                namespaces.insert(namespace.to_string(), api_endpoints);
            } else {
                namespaces
                    .get_mut(&namespace)
                    .unwrap()
                    .insert(method_name, api_endpoint);
            }
        } else if name
            .map(|name| name == Some("_common.json"))
            .unwrap_or(true)
        {
            let mut file = File::open(&path)?;
            let common = common_params_from_file(display, &mut file)?;
            common_params = common.params;
        }
    }

    // extract the root methods
    let root = namespaces.remove(root_key).unwrap();

    let mut sorted_enums = enums.into_iter().collect::<Vec<_>>();
    sorted_enums.sort_by(|a, b| a.name.cmp(&b.name));

    Ok(Api {
        commit: branch.to_string(),
        common_params,
        root,
        namespaces,
        enums: sorted_enums,
    })
}

/// deserializes an ApiEndpoint from a file
fn endpoint_from_file<R>(
    name: String,
    reader: &mut R,
) -> Result<(String, ApiEndpoint), failure::Error>
where
    R: Read,
{
    let endpoint: BTreeMap<String, ApiEndpoint> =
        serde_json::from_reader(reader).map_err(|e| super::error::ParseError {
            message: format!("Failed to parse {} because: {}", name, e),
        })?;

    // get the first (and only) endpoint name and endpoint body
    Ok(endpoint.into_iter().next().unwrap())
}

/// deserializes Common from a file
fn common_params_from_file<R>(name: String, reader: &mut R) -> Result<Common, failure::Error>
where
    R: Read,
{
    let common: Common = serde_json::from_reader(reader).map_err(|e| super::error::ParseError {
        message: format!("Failed to parse {} because: {}", name, e),
    })?;

    Ok(common)
}

/// formats tokens using rustfmt
/// https://github.com/bcmyers/num-format/blob/b7a99480b8087924d291887b13d8c38b7ce43a36/num-format-dev/src/rustfmt.rs
fn rust_fmt<S>(module: S) -> Result<String, failure::Error>
where
    S: Into<String>,
{
    let input = Input::Text(module.into());
    let mut config = Config::default();
    config.set().edition(Edition::Edition2018);
    config.set().emit_mode(EmitMode::Stdout);
    let mut output = Vec::new();
    {
        let mut session = Session::new(config, Some(&mut output));
        let _format_report = session.format(input)?;
    }

    // remove stdin: from start of output
    let b = "stdin:".as_bytes();
    if output.starts_with(b) {
        output.drain(0..b.len());
    }

    let s = String::from_utf8(output)?;

    // trim whitespace
    Ok(s.trim().to_owned())
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::io;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::cmp::min;

pub use mio::{EventSet, PollOpt, Token};
use mio::Timeout as MioTimeout;
use mio::tcp::{TcpListener, TcpStream};
use mio::util::Slab;

use super::connection::{IncomingConnection, OutgoingConnection};
use super::{Timeout, InternalTimeout, EventLoop};

use super::super::messages::{MessageBuffer, RawMessage};

pub type PeerId = Token;

const SERVER_ID: PeerId = Token(1);
const RECONNECT_GROW_FACTOR: f32 = 2.0;

#[derive(Debug)]
pub enum Output {
    Data(MessageBuffer),
    Connected(SocketAddr),
    Disconnected(SocketAddr),
}

#[derive(Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub listen_address: SocketAddr,
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u32>,
    pub tcp_reconnect_timeout: u64,
    pub tcp_reconnect_timeout_max: u64,
}

pub struct Network {
    config: NetworkConfiguration,
    listener: Option<TcpListener>,

    incoming: Slab<IncomingConnection>,
    outgoing: Slab<OutgoingConnection>,
    addresses: HashMap<SocketAddr, PeerId>,

    reconnects: HashMap<SocketAddr, MioTimeout>,
}

enum PeerKind {
    Server,
    Incoming,
    Outgoing,
}

fn make_error<T: Borrow<str>>(s: T) -> io::Error {
    io::Error::new(io::ErrorKind::Other, s.borrow())
}

impl Network {
    pub fn with_config(config: NetworkConfiguration) -> Network {
        Network {
            config: config,
            listener: None,

            incoming: Slab::new_starting_at(Token(2), config.max_incoming_connections),
            outgoing: Slab::new_starting_at(Token(2 + config.max_incoming_connections),
                                            config.max_outgoing_connections),
            addresses: HashMap::new(),

            reconnects: HashMap::new(),
        }
    }

    pub fn address(&self) -> &SocketAddr {
        &self.config.listen_address
    }

    // TODO use error trait
    pub fn bind(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
        if let Some(_) = self.listener {
            return Err(make_error("Already binded"));
        }
        let listener = TcpListener::bind(&self.config.listen_address)?;
        let r = event_loop.register(&listener, SERVER_ID, EventSet::readable(), PollOpt::edge());
        match r {
            Ok(()) => {
                self.listener = Some(listener);
                Ok(())
            }
            Err(e) => Err(e),
        }
    }

    // TODO: Use ticks for fast reregistering sockets
    // TODO: Implement Connections collection with (re)registering
    pub fn io(&mut self,
              event_loop: &mut EventLoop,
              id: PeerId,
              set: EventSet)
              -> io::Result<Option<Output>> {

        match self.peer_kind(id) {
            PeerKind::Server => {
                // Accept new connections
                // FIXME: Fail-safe accepting of new connections?
                let pair = match self.listener {
                    Some(ref listener) => listener.accept()?,
                    None => None,
                };
                if let Some((socket, address)) = pair {
                    let peer = IncomingConnection::new(socket, address);
                    self.add_incoming_connection(event_loop, peer)?;

                    debug!("{}: Accepted incoming connection from {} id: {}",
                           self.address(),
                           address,
                           id.0);
                }
                return Ok(None);
            }
            PeerKind::Incoming => {
                if set.is_hup() | set.is_error() {
                    debug!("{}: incoming connection with addr {} closed",
                           self.address(),
                           self.incoming[id].address());

                    self.remove_incoming_connection(event_loop, id);
                    return Ok(None);
                }

                if set.is_readable() {
                    let address = *self.incoming[id].address();

                    trace!("{}: Socket is readable {} id: {}",
                           self.address(),
                           address,
                           id.0);

                    return match self.incoming[id].try_read() {
                        Ok(Some(buffer)) => Ok(Some(Output::Data(buffer))),
                        Ok(None) => Ok(None),
                        Err(e) => {
                            self.remove_incoming_connection(event_loop, id);
                            Err(e)
                        }
                    };
                }
            }
            PeerKind::Outgoing => {
                if set.is_hup() | set.is_error() {
                    let address = *self.outgoing[id].address();

                    debug!("{}: outgoing connection with addr {} closed",
                           self.address(),
                           self.outgoing[id].address());

                    self.remove_outgoing_connection(event_loop, id);
                    if self.reconnects.contains_key(&address) {
                        return Ok(None);
                    }
                    return Ok(Some(Output::Disconnected(address)));
                }

                if set.is_writable() {
                    let address = *self.outgoing[id].address();

                    trace!("{}: Socket is writable {} id: {}",
                           self.address(),
                           address,
                           id.0);

                    let r = {
                        self.outgoing[id].try_write()?;
                        event_loop.reregister(self.outgoing[id].socket(),
                                        id,
                                        self.outgoing[id].interest(),
                                        PollOpt::edge())?;
                        Ok(())
                    };

                    // Write data into socket
                    if let Err(e) = r {
                        self.remove_outgoing_connection(event_loop, id);
                        return Err(e);
                    }
                    return Ok(self.mark_connected(event_loop, id));
                }
            }
        }
        // FIXME: Can we be here?
        Ok(None)
    }

    pub fn tick(&mut self, _: &mut EventLoop) {}

    pub fn send_to(&mut self,
                   event_loop: &mut EventLoop,
                   address: &SocketAddr,
                   message: RawMessage)
                   -> io::Result<()> {
        let r = match self.get_outgoing_peer(address) {
            Ok(id) => {
                self.outgoing[id]
                    .send(message)
                    .and_then(|_| {
                        event_loop.reregister(self.outgoing[id].socket(),
                                        id,
                                        self.outgoing[id].interest(),
                                        PollOpt::edge())?;
                        self.mark_connected(event_loop, id);
                        Ok(())
                    })
                    .or_else(|e| {
                        self.remove_outgoing_connection(event_loop, id);
                        Err(e)
                    })
            }
            Err(e) => Err(e),
        };
        r
    }

    pub fn connect(&mut self, event_loop: &mut EventLoop, address: &SocketAddr) -> io::Result<()> {
        if !self.addresses.contains_key(address) {
            let peer = OutgoingConnection::new(TcpStream::connect(address)?, *address);
            let id = self.add_outgoing_connection(event_loop, peer)?;
            self.try_reconnect_addr(event_loop, *address)?;

            debug!("{}: Establish connection with {}, id: {}",
                   self.address(),
                   address,
                   id.0);
        }
        Ok(())
    }

    pub fn handle_timeout(&mut self, event_loop: &mut EventLoop, timeout: InternalTimeout) {
        match timeout {
            InternalTimeout::Reconnect(addr, delay) => {
                if self.reconnects.contains_key(&addr) {
                    debug!("Try to reconnect with delay {}", delay);

                    if let Err(e) = self.connect(event_loop, &addr) {
                        error!("{}: Unable to create connection to addr {}, error: {:?}",
                               self.address(),
                               addr,
                               e);
                    }

                    let delay = min((delay as f32 * RECONNECT_GROW_FACTOR) as u64,
                                    self.config.tcp_reconnect_timeout_max);

                    if let Err(e) = self.add_reconnect_timeout(event_loop, addr, delay) {
                        error!("{}: Unable to add timeout, error: {:?}", self.address(), e);
                    }
                }
            }
        }
    }

    fn peer_kind(&self, id: PeerId) -> PeerKind {
        if id == SERVER_ID {
            PeerKind::Server
        } else if id.0 >= (2 + self.config.max_incoming_connections) {
            PeerKind::Outgoing
        } else {
            PeerKind::Incoming
        }
    }

    fn get_outgoing_peer(&self, addr: &SocketAddr) -> io::Result<PeerId> {
        if let Some(id) = self.addresses.get(addr) {
            return Ok(*id);
        };
        Err(make_error(format!("{}: Outgoing peer not found {}", self.address(), addr)))
    }

    fn add_incoming_connection(&mut self,
                               event_loop: &mut EventLoop,
                               mut connection: IncomingConnection)
                               -> io::Result<PeerId> {
        self.configure_stream(connection.socket_mut())?;
        let address = *connection.address();
        let id = self.incoming
            .insert(connection)
            .map_err(|_| make_error("Maximum incoming onnections"))?;
        self.addresses.insert(address, id);

        let r = event_loop.register(self.incoming[id].socket(),
                                    id,
                                    self.incoming[id].interest(),
                                    PollOpt::edge());

        if let Err(e) = r {
            self.remove_incoming_connection(event_loop, id);
            return Err(e);
        }
        Ok(id)
    }

    fn add_outgoing_connection(&mut self,
                               event_loop: &mut EventLoop,
                               connection: OutgoingConnection)
                               -> io::Result<PeerId> {
        let address = *connection.address();
        let id = self.outgoing
            .insert(connection)
            .map_err(|_| make_error("Maximum outgoing onnections"))?;
        self.addresses.insert(address, id);

        let r = event_loop.register(self.outgoing[id].socket(),
                                    id,
                                    self.outgoing[id].interest() | EventSet::writable(),
                                    PollOpt::edge());

        if let Err(e) = r {
            self.remove_outgoing_connection(event_loop, id);
            return Err(e);
        }
        Ok(id)
    }

    fn remove_incoming_connection(&mut self, event_loop: &mut EventLoop, id: PeerId) {
        let addr = *self.incoming[id].address();
        self.addresses.remove(&addr);
        if let Some(connection) = self.incoming.remove(id) {
            if let Err(e) = event_loop.deregister(connection.socket()) {
                error!("{}: Unable to deregister incoming connection, id: {}, error: {:?}",
                       self.address(),
                       id.0,
                       e);
            }
        }
    }

    fn remove_outgoing_connection(&mut self, event_loop: &mut EventLoop, id: PeerId) {
        let addr = *self.outgoing[id].address();
        self.addresses.remove(&addr);
        if let Some(connection) = self.outgoing.remove(id) {
            if let Err(e) = event_loop.deregister(connection.socket()) {
                error!("{}: Unable to deregister outgoing connection, id: {}, error: {:?}",
                       self.address(),
                       id.0,
                       e);
            }
        }
    }

    fn configure_stream(&self, stream: &mut TcpStream) -> io::Result<()> {
        stream.set_keepalive(self.config.tcp_keep_alive)?;
        stream.set_nodelay(self.config.tcp_nodelay)?;
        Ok(())
    }

    fn try_reconnect_addr(&mut self,
                          event_loop: &mut EventLoop,
                          address: SocketAddr)
                          -> io::Result<()> {
        if !self.reconnects.contains_key(&address) {
            let delay = self.config.tcp_reconnect_timeout;
            return self.add_reconnect_timeout(event_loop, address, delay);
        }
        Ok(())
    }

    fn add_reconnect_timeout(&mut self,
                             event_loop: &mut EventLoop,
                             address: SocketAddr,
                             delay: u64)
                             -> io::Result<()> {
        let reconnect = Timeout::Internal(InternalTimeout::Reconnect(address, delay));
        let timeout = event_loop.timeout_ms(reconnect, delay)
            .map_err(|e| make_error(format!("A mio error occured {:?}", e)))?;
        self.reconnects.insert(address, timeout);
        Ok(())
    }

    fn mark_connected(&mut self, event_loop: &mut EventLoop, id: Token) -> Option<Output> {
        let address = *self.outgoing[id].address();
        self.clear_reconnect_request(event_loop, &address)
    }

    fn clear_reconnect_request(&mut self,
                               event_loop: &mut EventLoop,
                               addr: &SocketAddr)
                               -> Option<Output> {
        if let Some(timeout) = self.reconnects.remove(addr) {
            event_loop.clear_timeout(timeout);
            return Some(Output::Connected(*addr));
        }
        None
    }
}

#[cfg(test)]
mod tests {}
<commit_msg>Fix network on linux<commit_after>use std::borrow::Borrow;
use std::io;
use std::collections::HashMap;
use std::net::SocketAddr;
use std::cmp::min;

pub use mio::{EventSet, PollOpt, Token};
use mio::Timeout as MioTimeout;
use mio::tcp::{TcpListener, TcpStream};
use mio::util::Slab;

use super::connection::{IncomingConnection, OutgoingConnection};
use super::{Timeout, InternalTimeout, EventLoop};

use super::super::messages::{MessageBuffer, RawMessage};

pub type PeerId = Token;

const SERVER_ID: PeerId = Token(1);
const RECONNECT_GROW_FACTOR: f32 = 2.0;

#[derive(Debug)]
pub enum Output {
    Data(MessageBuffer),
    Connected(SocketAddr),
    Disconnected(SocketAddr),
}

#[derive(Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub listen_address: SocketAddr,
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u32>,
    pub tcp_reconnect_timeout: u64,
    pub tcp_reconnect_timeout_max: u64,
}

pub struct Network {
    config: NetworkConfiguration,
    listener: Option<TcpListener>,

    incoming: Slab<IncomingConnection>,
    outgoing: Slab<OutgoingConnection>,
    addresses: HashMap<SocketAddr, PeerId>,

    reconnects: HashMap<SocketAddr, MioTimeout>,
}

enum PeerKind {
    Server,
    Incoming,
    Outgoing,
}

fn make_error<T: Borrow<str>>(s: T) -> io::Error {
    io::Error::new(io::ErrorKind::Other, s.borrow())
}

impl Network {
    pub fn with_config(config: NetworkConfiguration) -> Network {
        Network {
            config: config,
            listener: None,

            incoming: Slab::new_starting_at(Token(2), config.max_incoming_connections),
            outgoing: Slab::new_starting_at(Token(2 + config.max_incoming_connections),
                                            config.max_outgoing_connections),
            addresses: HashMap::new(),

            reconnects: HashMap::new(),
        }
    }

    pub fn address(&self) -> &SocketAddr {
        &self.config.listen_address
    }

    // TODO use error trait
    pub fn bind(&mut self, event_loop: &mut EventLoop) -> io::Result<()> {
        if let Some(_) = self.listener {
            return Err(make_error("Already binded"));
        }
        let listener = TcpListener::bind(&self.config.listen_address)?;
        let r = event_loop.register(&listener, SERVER_ID, EventSet::readable(), PollOpt::edge());
        match r {
            Ok(()) => {
                self.listener = Some(listener);
                Ok(())
            }
            Err(e) => Err(e),
        }
    }

    // TODO: Use ticks for fast reregistering sockets
    // TODO: Implement Connections collection with (re)registering
    pub fn io(&mut self,
              event_loop: &mut EventLoop,
              id: PeerId,
              set: EventSet)
              -> io::Result<Option<Output>> {

        match self.peer_kind(id) {
            PeerKind::Server => {
                // Accept new connections
                // FIXME: Fail-safe accepting of new connections?
                let pair = match self.listener {
                    Some(ref listener) => listener.accept()?,
                    None => None,
                };
                if let Some((socket, address)) = pair {
                    let peer = IncomingConnection::new(socket, address);
                    self.add_incoming_connection(event_loop, peer)?;

                    debug!("{}: Accepted incoming connection from {} id: {}",
                           self.address(),
                           address,
                           id.0);
                }
                return Ok(None);
            }
            PeerKind::Incoming => {
                if !self.incoming.contains(id) {
                    return Ok(None)
                }

                if set.is_hup() | set.is_error() {
                    debug!("{}: incoming connection with addr {} closed",
                           self.address(),
                           self.incoming[id].address());

                    self.remove_incoming_connection(event_loop, id);
                    return Ok(None);
                }

                if set.is_readable() {
                    let address = *self.incoming[id].address();

                    trace!("{}: Socket is readable {} id: {}",
                           self.address(),
                           address,
                           id.0);

                    return match self.incoming[id].try_read() {
                        Ok(Some(buffer)) => Ok(Some(Output::Data(buffer))),
                        Ok(None) => Ok(None),
                        Err(e) => {
                            self.remove_incoming_connection(event_loop, id);
                            Err(e)
                        }
                    };
                }
            }
            PeerKind::Outgoing => {
                if !self.outgoing.contains(id) {
                    return Ok(None)
                }

                if set.is_hup() | set.is_error() {
                    let address = *self.outgoing[id].address();

                    debug!("{}: outgoing connection with addr {} closed",
                           self.address(),
                           self.outgoing[id].address());

                    self.remove_outgoing_connection(event_loop, id);
                    if self.reconnects.contains_key(&address) {
                        return Ok(None);
                    }
                    return Ok(Some(Output::Disconnected(address)));
                }

                if set.is_writable() {
                    let address = *self.outgoing[id].address();

                    trace!("{}: Socket is writable {} id: {}",
                           self.address(),
                           address,
                           id.0);

                    let r = {
                        self.outgoing[id].try_write()?;
                        event_loop.reregister(self.outgoing[id].socket(),
                                        id,
                                        self.outgoing[id].interest(),
                                        PollOpt::edge())?;
                        Ok(())
                    };

                    // Write data into socket
                    if let Err(e) = r {
                        self.remove_outgoing_connection(event_loop, id);
                        return Err(e);
                    }
                    return Ok(self.mark_connected(event_loop, id));
                }
            }
        }
        // FIXME: Can we be here?
        Ok(None)
    }

    pub fn tick(&mut self, _: &mut EventLoop) {}

    pub fn send_to(&mut self,
                   event_loop: &mut EventLoop,
                   address: &SocketAddr,
                   message: RawMessage)
                   -> io::Result<()> {
        let r = match self.get_outgoing_peer(address) {
            Ok(id) => {
                self.outgoing[id]
                    .send(message)
                    .and_then(|_| {
                        event_loop.reregister(self.outgoing[id].socket(),
                                        id,
                                        self.outgoing[id].interest(),
                                        PollOpt::edge())?;
                        self.mark_connected(event_loop, id);
                        Ok(())
                    })
                    .or_else(|e| {
                        self.remove_outgoing_connection(event_loop, id);
                        Err(e)
                    })
            }
            Err(e) => Err(e),
        };
        r
    }

    pub fn connect(&mut self, event_loop: &mut EventLoop, address: &SocketAddr) -> io::Result<()> {
        if !self.addresses.contains_key(address) {
            let peer = OutgoingConnection::new(TcpStream::connect(address)?, *address);
            let id = self.add_outgoing_connection(event_loop, peer)?;
            self.try_reconnect_addr(event_loop, *address)?;

            debug!("{}: Establish connection with {}, id: {}",
                   self.address(),
                   address,
                   id.0);
        }
        Ok(())
    }

    pub fn handle_timeout(&mut self, event_loop: &mut EventLoop, timeout: InternalTimeout) {
        match timeout {
            InternalTimeout::Reconnect(addr, delay) => {
                if self.reconnects.contains_key(&addr) {
                    debug!("Try to reconnect with delay {}", delay);

                    if let Err(e) = self.connect(event_loop, &addr) {
                        error!("{}: Unable to create connection to addr {}, error: {:?}",
                               self.address(),
                               addr,
                               e);
                    }

                    let delay = min((delay as f32 * RECONNECT_GROW_FACTOR) as u64,
                                    self.config.tcp_reconnect_timeout_max);

                    if let Err(e) = self.add_reconnect_timeout(event_loop, addr, delay) {
                        error!("{}: Unable to add timeout, error: {:?}", self.address(), e);
                    }
                }
            }
        }
    }

    fn peer_kind(&self, id: PeerId) -> PeerKind {
        if id == SERVER_ID {
            PeerKind::Server
        } else if id.0 >= (2 + self.config.max_incoming_connections) {
            PeerKind::Outgoing
        } else {
            PeerKind::Incoming
        }
    }

    fn get_outgoing_peer(&self, addr: &SocketAddr) -> io::Result<PeerId> {
        if let Some(id) = self.addresses.get(addr) {
            return Ok(*id);
        };
        Err(make_error(format!("{}: Outgoing peer not found {}", self.address(), addr)))
    }

    fn add_incoming_connection(&mut self,
                               event_loop: &mut EventLoop,
                               mut connection: IncomingConnection)
                               -> io::Result<PeerId> {
        self.configure_stream(connection.socket_mut())?;
        let address = *connection.address();
        let id = self.incoming
            .insert(connection)
            .map_err(|_| make_error("Maximum incoming onnections"))?;
        self.addresses.insert(address, id);

        let r = event_loop.register(self.incoming[id].socket(),
                                    id,
                                    self.incoming[id].interest(),
                                    PollOpt::edge());

        if let Err(e) = r {
            self.remove_incoming_connection(event_loop, id);
            return Err(e);
        }
        Ok(id)
    }

    fn add_outgoing_connection(&mut self,
                               event_loop: &mut EventLoop,
                               connection: OutgoingConnection)
                               -> io::Result<PeerId> {
        let address = *connection.address();
        let id = self.outgoing
            .insert(connection)
            .map_err(|_| make_error("Maximum outgoing onnections"))?;
        self.addresses.insert(address, id);

        let r = event_loop.register(self.outgoing[id].socket(),
                                    id,
                                    self.outgoing[id].interest() | EventSet::writable(),
                                    PollOpt::edge());

        if let Err(e) = r {
            self.remove_outgoing_connection(event_loop, id);
            return Err(e);
        }
        Ok(id)
    }

    fn remove_incoming_connection(&mut self, event_loop: &mut EventLoop, id: PeerId) {
        let addr = *self.incoming[id].address();
        self.addresses.remove(&addr);
        if let Some(connection) = self.incoming.remove(id) {
            if let Err(e) = event_loop.deregister(connection.socket()) {
                error!("{}: Unable to deregister incoming connection, id: {}, error: {:?}",
                       self.address(),
                       id.0,
                       e);
            }
        }
    }

    fn remove_outgoing_connection(&mut self, event_loop: &mut EventLoop, id: PeerId) {
        let addr = *self.outgoing[id].address();
        self.addresses.remove(&addr);
        if let Some(connection) = self.outgoing.remove(id) {
            if let Err(e) = event_loop.deregister(connection.socket()) {
                error!("{}: Unable to deregister outgoing connection, id: {}, error: {:?}",
                       self.address(),
                       id.0,
                       e);
            }
        }
    }

    fn configure_stream(&self, stream: &mut TcpStream) -> io::Result<()> {
        stream.set_keepalive(self.config.tcp_keep_alive)?;
        stream.set_nodelay(self.config.tcp_nodelay)?;
        Ok(())
    }

    fn try_reconnect_addr(&mut self,
                          event_loop: &mut EventLoop,
                          address: SocketAddr)
                          -> io::Result<()> {
        if !self.reconnects.contains_key(&address) {
            let delay = self.config.tcp_reconnect_timeout;
            return self.add_reconnect_timeout(event_loop, address, delay);
        }
        Ok(())
    }

    fn add_reconnect_timeout(&mut self,
                             event_loop: &mut EventLoop,
                             address: SocketAddr,
                             delay: u64)
                             -> io::Result<()> {
        let reconnect = Timeout::Internal(InternalTimeout::Reconnect(address, delay));
        let timeout = event_loop.timeout_ms(reconnect, delay)
            .map_err(|e| make_error(format!("A mio error occured {:?}", e)))?;
        self.reconnects.insert(address, timeout);
        Ok(())
    }

    fn mark_connected(&mut self, event_loop: &mut EventLoop, id: Token) -> Option<Output> {
        let address = *self.outgoing[id].address();
        self.clear_reconnect_request(event_loop, &address)
    }

    fn clear_reconnect_request(&mut self,
                               event_loop: &mut EventLoop,
                               addr: &SocketAddr)
                               -> Option<Output> {
        if let Some(timeout) = self.reconnects.remove(addr) {
            event_loop.clear_timeout(timeout);
            return Some(Output::Connected(*addr));
        }
        None
    }
}

#[cfg(test)]
mod tests {}
<|endoftext|>


<commit_before>// Eet Rust bindings for EFL.
// Copyright (C) 2014  Luis Araujo <araujoc.luisf@gmail.com>

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

extern crate libc;
extern crate core;

use eet::libc::{c_int, c_uint, c_char, c_void, free};
use eet::core::num::FromPrimitive;
use std::mem::transmute;
use std::fmt::{Show, Formatter, Result};

/// EetFile object.
pub struct EetFile {
    _eo: *_EetFile
}

/// EetValue object.
/// This object is a convenient wrapper around values returned by 'read'.
pub struct EetValue<T> {
    _value: *T
}

impl<T> EetValue<T> {
    pub fn getVal(&self) -> *T { unsafe { self._value } }
}

#[unsafe_destructor]
impl<T> Drop for EetValue<T> {
    fn drop(&mut self) {
        unsafe { free(transmute(self._value)) }
    }
}

impl<T: Show> Show for EetValue<T> {
    fn fmt(&self, _fmt: &mut Formatter) -> Result {
        unsafe { write!(_fmt.buf, "{}", *((*self)._value)) }
    }
}

/// Internal representation of an EetFile object.
pub enum _EetFile {}

/// Modes that a file can be opened.
pub enum EetFileMode {
    /// File is read-only.
    EetFileModeRead,
    /// File is write-only.
    EetFileModeWrite,
    /// File is for both read and write.
    EetFileModeReadWrite
}

#[deriving(Show, FromPrimitive)]
pub enum EetError {
    EetErrorNone,
    EetErrorBadObject,
    EetErrorEmpty,
    EetErrorNotWritable,
    EetErrorOutOfMemory,
    EetErrorWriteError,
    EetErrorWriteErrorFileTooBig,
    EetErrorWriteErrorIOError,
    EetErrorWriteErrorOutOfSpace,
    EetErrorWriteErrorFileClosed,
    EetErrorMmapFailed,
    EetErrorX509EncodingFailed,
    EetErrorSignatureFailed,
    EetErrorInvalidSignature,
    EetErrorNotSigned,
    EetErrorNotImplemented,
    EetErrorPrngNotSeeded,
    EetErrorEncryptFailed,
    EetErrorDecryptFailed
}

#[link(name = "eet")]
extern "C" {
    fn eet_init() -> c_int;
    fn eet_shutdown() -> c_int;
    fn eet_clearcache();
    fn eet_open(file: *c_char, mode: c_uint) -> *_EetFile;
    fn eet_read(ef: *_EetFile, name: *c_char, size_ret: *mut c_int) -> *c_void;
    fn eet_write(ef: *_EetFile, name: *c_char, data: *c_void,
                 size: c_uint, compress: c_int) -> c_int;
    fn eet_close(ef: *_EetFile) -> c_uint;
    fn eet_sync(ef: *_EetFile) -> c_uint;
}

/// Initialize the EET library.
pub fn init() -> int { unsafe { eet_init() as int } }

/// Shut down the EET library.
pub fn shutdown() -> int { unsafe { eet_shutdown() as int } }

/// Clear eet cache.
pub fn clearcache() { unsafe { eet_clearcache() } }

/// Open an eet file on disk, and returns a handle to it.
pub fn open(file: &str, mode: EetFileMode) -> EetFile {
    file.with_c_str(|c_file| unsafe {
        EetFile { _eo: eet_open(c_file, mode as c_uint) }
    })
}

/// Read a specified entry from an eet file and return data.
pub fn read<T>(ef: EetFile, name: &str, size_ret: &mut i32) -> EetValue<T> {
    name.with_c_str(|c_name| unsafe {
        EetValue {
            _value: transmute::<*c_void,*T>(eet_read(ef._eo, c_name, size_ret))
        }
    })
}

/// Write a specified entry to an eet file handle.
pub fn write<T>(ef: EetFile, name: &str, data: &T,
                size: uint, compress: int) -> int {
    name.with_c_str(|c_name| unsafe {
        eet_write(ef._eo, c_name, transmute(data), size as c_uint, compress as c_int) as int
    })
}

//// Close an eet file handle and flush pending writes.
pub fn close(ef: EetFile) -> EetError {
    let v: Option<EetError> = FromPrimitive::from_u32(unsafe { eet_close(ef._eo) });
    v.unwrap()
}

/// Sync content of an eet file handle, flushing pending writes.
pub fn sync(ef: EetFile) -> EetError {
    let v: Option<EetError> = FromPrimitive::from_u32(unsafe { eet_sync(ef._eo) });
    v.unwrap()
}
<commit_msg>Remove unnecessary unsafe block<commit_after>// Eet Rust bindings for EFL.
// Copyright (C) 2014  Luis Araujo <araujoc.luisf@gmail.com>

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

extern crate libc;
extern crate core;

use eet::libc::{c_int, c_uint, c_char, c_void, free};
use eet::core::num::FromPrimitive;
use std::mem::transmute;
use std::fmt::{Show, Formatter, Result};

/// EetFile object.
pub struct EetFile {
    _eo: *_EetFile
}

/// EetValue object.
/// This object is a convenient wrapper around values returned by 'read'.
pub struct EetValue<T> {
    _value: *T
}

impl<T> EetValue<T> {
    pub fn getVal(&self) -> *T { self._value }
}

#[unsafe_destructor]
impl<T> Drop for EetValue<T> {
    fn drop(&mut self) {
        unsafe { free(transmute(self._value)) }
    }
}

impl<T: Show> Show for EetValue<T> {
    fn fmt(&self, _fmt: &mut Formatter) -> Result {
        unsafe { write!(_fmt.buf, "{}", *((*self)._value)) }
    }
}

/// Internal representation of an EetFile object.
pub enum _EetFile {}

/// Modes that a file can be opened.
pub enum EetFileMode {
    /// File is read-only.
    EetFileModeRead,
    /// File is write-only.
    EetFileModeWrite,
    /// File is for both read and write.
    EetFileModeReadWrite
}

#[deriving(Show, FromPrimitive)]
pub enum EetError {
    EetErrorNone,
    EetErrorBadObject,
    EetErrorEmpty,
    EetErrorNotWritable,
    EetErrorOutOfMemory,
    EetErrorWriteError,
    EetErrorWriteErrorFileTooBig,
    EetErrorWriteErrorIOError,
    EetErrorWriteErrorOutOfSpace,
    EetErrorWriteErrorFileClosed,
    EetErrorMmapFailed,
    EetErrorX509EncodingFailed,
    EetErrorSignatureFailed,
    EetErrorInvalidSignature,
    EetErrorNotSigned,
    EetErrorNotImplemented,
    EetErrorPrngNotSeeded,
    EetErrorEncryptFailed,
    EetErrorDecryptFailed
}

#[link(name = "eet")]
extern "C" {
    fn eet_init() -> c_int;
    fn eet_shutdown() -> c_int;
    fn eet_clearcache();
    fn eet_open(file: *c_char, mode: c_uint) -> *_EetFile;
    fn eet_read(ef: *_EetFile, name: *c_char, size_ret: *mut c_int) -> *c_void;
    fn eet_write(ef: *_EetFile, name: *c_char, data: *c_void,
                 size: c_uint, compress: c_int) -> c_int;
    fn eet_close(ef: *_EetFile) -> c_uint;
    fn eet_sync(ef: *_EetFile) -> c_uint;
}

/// Initialize the EET library.
pub fn init() -> int { unsafe { eet_init() as int } }

/// Shut down the EET library.
pub fn shutdown() -> int { unsafe { eet_shutdown() as int } }

/// Clear eet cache.
pub fn clearcache() { unsafe { eet_clearcache() } }

/// Open an eet file on disk, and returns a handle to it.
pub fn open(file: &str, mode: EetFileMode) -> EetFile {
    file.with_c_str(|c_file| unsafe {
        EetFile { _eo: eet_open(c_file, mode as c_uint) }
    })
}

/// Read a specified entry from an eet file and return data.
pub fn read<T>(ef: EetFile, name: &str, size_ret: &mut i32) -> EetValue<T> {
    name.with_c_str(|c_name| unsafe {
        EetValue {
            _value: transmute::<*c_void,*T>(eet_read(ef._eo, c_name, size_ret))
        }
    })
}

/// Write a specified entry to an eet file handle.
pub fn write<T>(ef: EetFile, name: &str, data: &T,
                size: uint, compress: int) -> int {
    name.with_c_str(|c_name| unsafe {
        eet_write(ef._eo, c_name, transmute(data), size as c_uint, compress as c_int) as int
    })
}

//// Close an eet file handle and flush pending writes.
pub fn close(ef: EetFile) -> EetError {
    let v: Option<EetError> = FromPrimitive::from_u32(unsafe { eet_close(ef._eo) });
    v.unwrap()
}

/// Sync content of an eet file handle, flushing pending writes.
pub fn sync(ef: EetFile) -> EetError {
    let v: Option<EetError> = FromPrimitive::from_u32(unsafe { eet_sync(ef._eo) });
    v.unwrap()
}
<|endoftext|>