<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;

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 std::rc::Rc;
use std::cell::RefCell;
use sync::Arc;

use azure::AzFloat;
use azure::azure_hl::BackendType;
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(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
    ScaledFont::new(backend, FontData(&template.bytes), pt_size as AzFloat)
}

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

/// 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<Rc<RefCell<Font>>>,

    /// 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!(),
            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) -> Font {

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

        Font {
            handle: handle,
            shaper: None,
            descriptor: descriptor,
            pt_size: 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 mut fonts: Vec<Rc<RefCell<Font>>> = vec!();

        for family in style.families.iter() {
            let desc = FontTemplateDescriptor::new(style.weight, style.style == font_style::italic);

            // GWTODO: Check on real pages if this is faster as Vec() or HashMap().
            let mut cache_hit = false;
            for cached_font in self.layout_font_cache.iter() {
                if cached_font.borrow().descriptor == desc &&
                   cached_font.borrow().pt_size == style.pt_size {
                    fonts.push(cached_font.clone());
                    cache_hit = true;
                    break;
                }
            }

            if !cache_hit {
                let font_template = self.font_cache_task.get_font_template(family.clone(), desc.clone());
                let layout_font = Rc::new(RefCell::new(self.create_layout_font(font_template, desc.clone(), style.pt_size)));
                self.layout_font_cache.push(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, backend: BackendType) -> 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(backend, 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>Fix font flickering due to missed check in font cache.<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;

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 std::rc::Rc;
use std::cell::RefCell;
use sync::Arc;

use azure::AzFloat;
use azure::azure_hl::BackendType;
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(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont {
    ScaledFont::new(backend, FontData(&template.bytes), pt_size as AzFloat)
}

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

struct LayoutFontCacheEntry {
    family: String,
    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>,

    /// 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!(),
            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) -> Font {

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

        Font {
            handle: handle,
            shaper: None,
            descriptor: descriptor,
            pt_size: 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 mut fonts: Vec<Rc<RefCell<Font>>> = vec!();

        for family in style.families.iter() {
            let desc = FontTemplateDescriptor::new(style.weight, style.style == font_style::italic);

            // 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 == *family {
                    let cached_font = cached_font_entry.font.borrow();
                    if cached_font.descriptor == desc &&
                       cached_font.pt_size == style.pt_size {
                        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.clone(), desc.clone());
                let layout_font = Rc::new(RefCell::new(self.create_layout_font(font_template, desc.clone(), style.pt_size)));
                self.layout_font_cache.push(LayoutFontCacheEntry {
                    family: family.clone(),
                    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, backend: BackendType) -> 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(backend, 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 clock_ticks;
use flow::{self, Flow};
use gfx::display_list::OpaqueNode;
use incremental::{self, RestyleDamage};
use layout_task::{LayoutTask, LayoutTaskData};
use msg::constellation_msg::{AnimationState, Msg, PipelineId};
use script::layout_interface::Animation;
use script_traits::ConstellationControlMsg;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use style::animation::{GetMod, PropertyAnimation};
use style::properties::ComputedValues;

/// Inserts transitions into the queue of running animations as applicable for the given style
/// difference. This is called from the layout worker threads.
pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>,
                                       node: OpaqueNode,
                                       old_style: &ComputedValues,
                                       new_style: &mut ComputedValues) {
    for i in 0..new_style.get_animation().transition_property.0.len() {
        // Create any property animations, if applicable.
        let property_animations = PropertyAnimation::from_transition(i, old_style, new_style);
        for property_animation in property_animations {
            // Set the property to the initial value.
            property_animation.update(new_style, 0.0);

            // Kick off the animation.
            let now = clock_ticks::precise_time_s();
            let animation_style = new_style.get_animation();
            let start_time =
                now + (animation_style.transition_delay.0.get_mod(i).seconds() as f64);
            new_animations_sender.send(Animation {
                node: node.id(),
                property_animation: property_animation,
                start_time: start_time,
                end_time: start_time +
                    (animation_style.transition_duration.0.get_mod(i).seconds() as f64),
            }).unwrap()
        }
    }
}

/// Processes any new animations that were discovered after style recalculation.
/// Also expire any old animations that have completed.
pub fn update_animation_state(rw_data: &mut LayoutTaskData, pipeline_id: PipelineId) {
    let mut new_running_animations = Vec::new();
    while let Ok(animation) = rw_data.new_animations_receiver.try_recv() {
        new_running_animations.push(animation)
    }

    let mut running_animations_hash = (*rw_data.running_animations).clone();

    // Expire old running animations.
    let now = clock_ticks::precise_time_s();
    let mut keys_to_remove = Vec::new();
    for (key, running_animations) in &mut running_animations_hash {
        running_animations.retain(|running_animation| {
            now < running_animation.end_time
        });
        if running_animations.len() == 0 {
            keys_to_remove.push(*key);
        }
    }
    for key in keys_to_remove {
        running_animations_hash.remove(&key).unwrap();
    }

    // Add new running animations.
    for new_running_animation in new_running_animations {
        match running_animations_hash.entry(OpaqueNode(new_running_animation.node)) {
            Entry::Vacant(entry) => {
                entry.insert(vec![new_running_animation]);
            }
            Entry::Occupied(mut entry) => entry.get_mut().push(new_running_animation),
        }
    }

    rw_data.running_animations = Arc::new(running_animations_hash);

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

    rw_data.constellation_chan
           .0
           .send(Msg::ChangeRunningAnimationsState(pipeline_id, animation_state))
           .unwrap();

}

/// Recalculates style for a set of animations. This does *not* run with the DOM lock held.
pub fn recalc_style_for_animations(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(&OpaqueNode(fragment.node.id())) {
            for animation in *animations {
                let now = clock_ticks::precise_time_s();
                let mut progress = (now - animation.start_time) / animation.duration();
                if progress > 1.0 {
                    progress = 1.0
                }
                if progress <= 0.0 {
                    continue
                }

                let mut new_style = fragment.style.clone();
                animation.property_animation.update(&mut *Arc::make_mut(&mut new_style),
                                                    progress);
                damage.insert(incremental::compute_damage(&Some(fragment.style.clone()),
                                                          &new_style));
                fragment.style = new_style
            }
        }
    });

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

/// Handles animation updates.
pub fn tick_all_animations(layout_task: &LayoutTask, rw_data: &mut LayoutTaskData) {
    layout_task.tick_animations(rw_data);

    layout_task.script_chan
               .send(ConstellationControlMsg::TickAllAnimations(layout_task.id))
               .unwrap();
}
<commit_msg>layout: Avoid flooding the compositor with animation state changes if there are no animations running and no new animations were added.<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 clock_ticks;
use flow::{self, Flow};
use gfx::display_list::OpaqueNode;
use incremental::{self, RestyleDamage};
use layout_task::{LayoutTask, LayoutTaskData};
use msg::constellation_msg::{AnimationState, Msg, PipelineId};
use script::layout_interface::Animation;
use script_traits::ConstellationControlMsg;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use style::animation::{GetMod, PropertyAnimation};
use style::properties::ComputedValues;

/// Inserts transitions into the queue of running animations as applicable for the given style
/// difference. This is called from the layout worker threads.
pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>,
                                       node: OpaqueNode,
                                       old_style: &ComputedValues,
                                       new_style: &mut ComputedValues) {
    for i in 0..new_style.get_animation().transition_property.0.len() {
        // Create any property animations, if applicable.
        let property_animations = PropertyAnimation::from_transition(i, old_style, new_style);
        for property_animation in property_animations {
            // Set the property to the initial value.
            property_animation.update(new_style, 0.0);

            // Kick off the animation.
            let now = clock_ticks::precise_time_s();
            let animation_style = new_style.get_animation();
            let start_time =
                now + (animation_style.transition_delay.0.get_mod(i).seconds() as f64);
            new_animations_sender.send(Animation {
                node: node.id(),
                property_animation: property_animation,
                start_time: start_time,
                end_time: start_time +
                    (animation_style.transition_duration.0.get_mod(i).seconds() as f64),
            }).unwrap()
        }
    }
}

/// Processes any new animations that were discovered after style recalculation.
/// Also expire any old animations that have completed.
pub fn update_animation_state(rw_data: &mut LayoutTaskData, pipeline_id: PipelineId) {
    let mut new_running_animations = Vec::new();
    while let Ok(animation) = rw_data.new_animations_receiver.try_recv() {
        new_running_animations.push(animation)
    }

    let mut running_animations_hash = (*rw_data.running_animations).clone();

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

    // Expire old running animations.
    let now = clock_ticks::precise_time_s();
    let mut keys_to_remove = Vec::new();
    for (key, running_animations) in &mut running_animations_hash {
        running_animations.retain(|running_animation| {
            now < running_animation.end_time
        });
        if running_animations.len() == 0 {
            keys_to_remove.push(*key);
        }
    }
    for key in keys_to_remove {
        running_animations_hash.remove(&key).unwrap();
    }

    // Add new running animations.
    for new_running_animation in new_running_animations {
        match running_animations_hash.entry(OpaqueNode(new_running_animation.node)) {
            Entry::Vacant(entry) => {
                entry.insert(vec![new_running_animation]);
            }
            Entry::Occupied(mut entry) => entry.get_mut().push(new_running_animation),
        }
    }

    rw_data.running_animations = Arc::new(running_animations_hash);

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

    rw_data.constellation_chan
           .0
           .send(Msg::ChangeRunningAnimationsState(pipeline_id, animation_state))
           .unwrap();

}

/// Recalculates style for a set of animations. This does *not* run with the DOM lock held.
pub fn recalc_style_for_animations(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(&OpaqueNode(fragment.node.id())) {
            for animation in *animations {
                let now = clock_ticks::precise_time_s();
                let mut progress = (now - animation.start_time) / animation.duration();
                if progress > 1.0 {
                    progress = 1.0
                }
                if progress <= 0.0 {
                    continue
                }

                let mut new_style = fragment.style.clone();
                animation.property_animation.update(&mut *Arc::make_mut(&mut new_style),
                                                    progress);
                damage.insert(incremental::compute_damage(&Some(fragment.style.clone()),
                                                          &new_style));
                fragment.style = new_style
            }
        }
    });

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

/// Handles animation updates.
pub fn tick_all_animations(layout_task: &LayoutTask, rw_data: &mut LayoutTaskData) {
    layout_task.tick_animations(rw_data);

    layout_task.script_chan
               .send(ConstellationControlMsg::TickAllAnimations(layout_task.id))
               .unwrap();
}
<|endoftext|>


<commit_before>extern crate security_framework;
extern crate security_framework_sys;

use self::security_framework::base;
use self::security_framework::certificate::SecCertificate;
use self::security_framework::identity::SecIdentity;
use self::security_framework::import_export::Pkcs12ImportOptions;
use self::security_framework::secure_transport::{self, SslContext, ProtocolSide, ConnectionType,
                                                 SslProtocol, ClientBuilder};
use std::fmt;
use std::io;
use std::error;

use Protocol;

fn convert_protocol(protocol: Protocol) -> SslProtocol {
    match protocol {
        Protocol::Sslv3 => SslProtocol::Ssl3,
        Protocol::Tlsv10 => SslProtocol::Tls1,
        Protocol::Tlsv11 => SslProtocol::Tls11,
        Protocol::Tlsv12 => SslProtocol::Tls12,
        Protocol::__NonExhaustive => unreachable!(),
    }
}

fn protocol_min_max(protocols: &[Protocol]) -> (SslProtocol, SslProtocol) {
    let mut min = Protocol::Tlsv12;
    let mut max = Protocol::Sslv3;
    for protocol in protocols {
        if (*protocol as usize) < (min as usize) {
            min = *protocol;
        }
        if (*protocol as usize) > (max as usize) {
            max = *protocol;
        }
    }
    (convert_protocol(min), convert_protocol(max))
}

pub struct Error(base::Error);

impl error::Error for Error {
    fn description(&self) -> &str {
        error::Error::description(&self.0)
    }

    fn cause(&self) -> Option<&error::Error> {
        error::Error::cause(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<base::Error> for Error {
    fn from(error: base::Error) -> Error {
        Error(error)
    }
}

pub struct Pkcs12 {
    identity: SecIdentity,
    chain: Vec<SecCertificate>,
}

impl Pkcs12 {
    pub fn from_der(buf: &[u8], pass: &str) -> Result<Pkcs12, Error> {
        let mut options = Pkcs12ImportOptions::new();
        options.passphrase(pass);

        let mut imported_identities = options.import(buf)?;
        imported_identities.truncate(1);
        let imported_identity = imported_identities.pop().unwrap();

        // FIXME: Compare the certificates for equality using CFEqual
        let identity_cert = imported_identity.identity.certificate()?.to_der();

        Ok(Pkcs12{
            identity: imported_identity.identity,
            chain: imported_identity.cert_chain
                .into_iter()
                .filter(|c| c.to_der() != identity_cert)
                .collect(),})
    }
}

pub enum HandshakeError<S> {
    Interrupted(MidHandshakeTlsStream<S>),
    Failure(Error),
}

impl<S> From<secure_transport::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::HandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Server(s))
            }
        }
    }
}

impl<S> From<secure_transport::ClientHandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::ClientHandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::ClientHandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::ClientHandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Client(s))
            }
        }
    }
}

impl<S> From<base::Error> for HandshakeError<S> {
    fn from(e: base::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

pub enum MidHandshakeTlsStream<S> {
    Server(secure_transport::MidHandshakeSslStream<S>),
    Client(secure_transport::MidHandshakeClientBuilder<S>),
}

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
    where S: fmt::Debug
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.fmt(fmt),
            MidHandshakeTlsStream::Client(ref s) => s.fmt(fmt),
        }
    }
}

impl<S> MidHandshakeTlsStream<S>
    where S: io::Read + io::Write
{
    pub fn get_ref(&self) -> &S {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.get_ref(),
            MidHandshakeTlsStream::Client(ref s) => s.get_ref(),
        }
    }

    pub fn get_mut(&mut self) -> &mut S {
        match *self {
            MidHandshakeTlsStream::Server(ref mut s) => s.get_mut(),
            MidHandshakeTlsStream::Client(ref mut s) => s.get_mut(),
        }
    }

    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self {
            MidHandshakeTlsStream::Server(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
            MidHandshakeTlsStream::Client(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
        }
    }
}

pub struct TlsConnectorBuilder(TlsConnector);

impl TlsConnectorBuilder {
    pub fn identity(&mut self, pkcs12: Pkcs12) -> Result<(), Error> {
        self.0.pkcs12 = Some(pkcs12);
        Ok(())
    }

    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsConnector, Error> {
        Ok(self.0)
    }
}

pub struct TlsConnector {
    pkcs12: Option<Pkcs12>,
    protocols: Vec<Protocol>,
    anchor_certificates: Option<Vec<SecCertificate>>,
}

impl TlsConnector {
    pub fn builder() -> Result<TlsConnectorBuilder, Error> {
        Ok(TlsConnectorBuilder(TlsConnector {
            pkcs12: None,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
            anchor_certificates: None,
        }))
    }

    pub fn connect<S>(&self,
                      domain: &str,
                      stream: S)
                      -> Result<TlsStream<S>, HandshakeError<S>>
        where S: io::Read + io::Write
    {
        let mut builder = ClientBuilder::new();
        let (min, max) = protocol_min_max(&self.protocols);
        builder.protocol_min(min);
        builder.protocol_max(max);
        if let Some(pkcs12) = self.pkcs12.as_ref() {
            builder.identity(&pkcs12.identity, &pkcs12.chain);
        }
        if let Some(anchors) = self.anchor_certificates.as_ref() {
            builder.anchor_certificates(anchors);
        }
        match builder.handshake2(domain, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsAcceptorBuilder(TlsAcceptor);

impl TlsAcceptorBuilder {
    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsAcceptor, Error> {
        Ok(self.0)
    }
}

pub struct TlsAcceptor {
    pkcs12: Pkcs12,
    protocols: Vec<Protocol>,
}

impl TlsAcceptor {
    pub fn builder(pkcs12: Pkcs12) -> Result<TlsAcceptorBuilder, Error> {
        Ok(TlsAcceptorBuilder(TlsAcceptor {
            pkcs12: pkcs12,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
        }))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
        where S: io::Read + io::Write
    {
        let mut ctx = try!(SslContext::new(ProtocolSide::Server, ConnectionType::Stream));

        let (min, max) = protocol_min_max(&self.protocols);
        try!(ctx.set_protocol_version_min(min));
        try!(ctx.set_protocol_version_max(max));
        try!(ctx.set_certificate(&self.pkcs12.identity, &self.pkcs12.chain));
        match ctx.handshake(stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(secure_transport::SslStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }

    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(try!(self.0.context().buffered_read_size()))
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        try!(self.0.close());
        Ok(())
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// Security Framework-specific extensions to `TlsStream`.
pub trait TlsStreamExt<S> {
    /// Returns a shared reference to the Security Framework `SslStream`.
    fn raw_stream(&self) -> &secure_transport::SslStream<S>;

    /// Returns a mutable reference to the Security Framework `SslStream`.
    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S>;
}

impl<S> TlsStreamExt<S> for ::TlsStream<S> {
    fn raw_stream(&self) -> &secure_transport::SslStream<S> {
        &(self.0).0
    }

    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S> {
        &mut (self.0).0
    }
}

/// Security Framework-specific extensions to `TlsConnectorBuilder`.
pub trait TlsConnectorBuilderExt {
    /// Specifies the set of additional root certificates to trust when
    /// verifying the server's certificate.
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self;
}

impl TlsConnectorBuilderExt for ::TlsConnectorBuilder {
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self {
        (self.0).0.anchor_certificates = Some(certs.to_owned());
        self
    }
}

/// Security Framework-specific extensions to `Error`
pub trait ErrorExt {
    /// Extract the underlying Security Framework error for inspection.
    fn security_framework_error(&self) -> &base::Error;
}

impl ErrorExt for ::Error {
    fn security_framework_error(&self) -> &base::Error {
        &(self.0).0
    }
}
<commit_msg>Use try! macro instead of ?<commit_after>extern crate security_framework;
extern crate security_framework_sys;

use self::security_framework::base;
use self::security_framework::certificate::SecCertificate;
use self::security_framework::identity::SecIdentity;
use self::security_framework::import_export::Pkcs12ImportOptions;
use self::security_framework::secure_transport::{self, SslContext, ProtocolSide, ConnectionType,
                                                 SslProtocol, ClientBuilder};
use std::fmt;
use std::io;
use std::error;

use Protocol;

fn convert_protocol(protocol: Protocol) -> SslProtocol {
    match protocol {
        Protocol::Sslv3 => SslProtocol::Ssl3,
        Protocol::Tlsv10 => SslProtocol::Tls1,
        Protocol::Tlsv11 => SslProtocol::Tls11,
        Protocol::Tlsv12 => SslProtocol::Tls12,
        Protocol::__NonExhaustive => unreachable!(),
    }
}

fn protocol_min_max(protocols: &[Protocol]) -> (SslProtocol, SslProtocol) {
    let mut min = Protocol::Tlsv12;
    let mut max = Protocol::Sslv3;
    for protocol in protocols {
        if (*protocol as usize) < (min as usize) {
            min = *protocol;
        }
        if (*protocol as usize) > (max as usize) {
            max = *protocol;
        }
    }
    (convert_protocol(min), convert_protocol(max))
}

pub struct Error(base::Error);

impl error::Error for Error {
    fn description(&self) -> &str {
        error::Error::description(&self.0)
    }

    fn cause(&self) -> Option<&error::Error> {
        error::Error::cause(&self.0)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, fmt)
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<base::Error> for Error {
    fn from(error: base::Error) -> Error {
        Error(error)
    }
}

pub struct Pkcs12 {
    identity: SecIdentity,
    chain: Vec<SecCertificate>,
}

impl Pkcs12 {
    pub fn from_der(buf: &[u8], pass: &str) -> Result<Pkcs12, Error> {
        let mut options = Pkcs12ImportOptions::new();
        options.passphrase(pass);

        let mut imported_identities = try!(options.import(buf));
        imported_identities.truncate(1);
        let imported_identity = imported_identities.pop().unwrap();

        // FIXME: Compare the certificates for equality using CFEqual
        let identity_cert = try!(imported_identity.identity.certificate()).to_der();

        Ok(Pkcs12{
            identity: imported_identity.identity,
            chain: imported_identity.cert_chain
                .into_iter()
                .filter(|c| c.to_der() != identity_cert)
                .collect(),})
    }
}

pub enum HandshakeError<S> {
    Interrupted(MidHandshakeTlsStream<S>),
    Failure(Error),
}

impl<S> From<secure_transport::HandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::HandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::HandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::HandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Server(s))
            }
        }
    }
}

impl<S> From<secure_transport::ClientHandshakeError<S>> for HandshakeError<S> {
    fn from(e: secure_transport::ClientHandshakeError<S>) -> HandshakeError<S> {
        match e {
            secure_transport::ClientHandshakeError::Failure(e) => HandshakeError::Failure(e.into()),
            secure_transport::ClientHandshakeError::Interrupted(s) => {
                HandshakeError::Interrupted(MidHandshakeTlsStream::Client(s))
            }
        }
    }
}

impl<S> From<base::Error> for HandshakeError<S> {
    fn from(e: base::Error) -> HandshakeError<S> {
        HandshakeError::Failure(e.into())
    }
}

pub enum MidHandshakeTlsStream<S> {
    Server(secure_transport::MidHandshakeSslStream<S>),
    Client(secure_transport::MidHandshakeClientBuilder<S>),
}

impl<S> fmt::Debug for MidHandshakeTlsStream<S>
    where S: fmt::Debug
{
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.fmt(fmt),
            MidHandshakeTlsStream::Client(ref s) => s.fmt(fmt),
        }
    }
}

impl<S> MidHandshakeTlsStream<S>
    where S: io::Read + io::Write
{
    pub fn get_ref(&self) -> &S {
        match *self {
            MidHandshakeTlsStream::Server(ref s) => s.get_ref(),
            MidHandshakeTlsStream::Client(ref s) => s.get_ref(),
        }
    }

    pub fn get_mut(&mut self) -> &mut S {
        match *self {
            MidHandshakeTlsStream::Server(ref mut s) => s.get_mut(),
            MidHandshakeTlsStream::Client(ref mut s) => s.get_mut(),
        }
    }

    pub fn handshake(self) -> Result<TlsStream<S>, HandshakeError<S>> {
        match self {
            MidHandshakeTlsStream::Server(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
            MidHandshakeTlsStream::Client(s) => {
                match s.handshake() {
                    Ok(s) => Ok(TlsStream(s)),
                    Err(e) => Err(e.into()),
                }
            }
        }
    }
}

pub struct TlsConnectorBuilder(TlsConnector);

impl TlsConnectorBuilder {
    pub fn identity(&mut self, pkcs12: Pkcs12) -> Result<(), Error> {
        self.0.pkcs12 = Some(pkcs12);
        Ok(())
    }

    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsConnector, Error> {
        Ok(self.0)
    }
}

pub struct TlsConnector {
    pkcs12: Option<Pkcs12>,
    protocols: Vec<Protocol>,
    anchor_certificates: Option<Vec<SecCertificate>>,
}

impl TlsConnector {
    pub fn builder() -> Result<TlsConnectorBuilder, Error> {
        Ok(TlsConnectorBuilder(TlsConnector {
            pkcs12: None,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
            anchor_certificates: None,
        }))
    }

    pub fn connect<S>(&self,
                      domain: &str,
                      stream: S)
                      -> Result<TlsStream<S>, HandshakeError<S>>
        where S: io::Read + io::Write
    {
        let mut builder = ClientBuilder::new();
        let (min, max) = protocol_min_max(&self.protocols);
        builder.protocol_min(min);
        builder.protocol_max(max);
        if let Some(pkcs12) = self.pkcs12.as_ref() {
            builder.identity(&pkcs12.identity, &pkcs12.chain);
        }
        if let Some(anchors) = self.anchor_certificates.as_ref() {
            builder.anchor_certificates(anchors);
        }
        match builder.handshake2(domain, stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsAcceptorBuilder(TlsAcceptor);

impl TlsAcceptorBuilder {
    pub fn supported_protocols(&mut self, protocols: &[Protocol]) -> Result<(), Error> {
        self.0.protocols = protocols.to_vec();
        Ok(())
    }

    pub fn build(self) -> Result<TlsAcceptor, Error> {
        Ok(self.0)
    }
}

pub struct TlsAcceptor {
    pkcs12: Pkcs12,
    protocols: Vec<Protocol>,
}

impl TlsAcceptor {
    pub fn builder(pkcs12: Pkcs12) -> Result<TlsAcceptorBuilder, Error> {
        Ok(TlsAcceptorBuilder(TlsAcceptor {
            pkcs12: pkcs12,
            protocols: vec![Protocol::Tlsv10, Protocol::Tlsv11, Protocol::Tlsv12],
        }))
    }

    pub fn accept<S>(&self, stream: S) -> Result<TlsStream<S>, HandshakeError<S>>
        where S: io::Read + io::Write
    {
        let mut ctx = try!(SslContext::new(ProtocolSide::Server, ConnectionType::Stream));

        let (min, max) = protocol_min_max(&self.protocols);
        try!(ctx.set_protocol_version_min(min));
        try!(ctx.set_protocol_version_max(max));
        try!(ctx.set_certificate(&self.pkcs12.identity, &self.pkcs12.chain));
        match ctx.handshake(stream) {
            Ok(s) => Ok(TlsStream(s)),
            Err(e) => Err(e.into()),
        }
    }
}

pub struct TlsStream<S>(secure_transport::SslStream<S>);

impl<S: fmt::Debug> fmt::Debug for TlsStream<S> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl<S: io::Read + io::Write> TlsStream<S> {
    pub fn get_ref(&self) -> &S {
        self.0.get_ref()
    }

    pub fn get_mut(&mut self) -> &mut S {
        self.0.get_mut()
    }

    pub fn buffered_read_size(&self) -> Result<usize, Error> {
        Ok(try!(self.0.context().buffered_read_size()))
    }

    pub fn shutdown(&mut self) -> io::Result<()> {
        try!(self.0.close());
        Ok(())
    }
}

impl<S: io::Read + io::Write> io::Read for TlsStream<S> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

/// Security Framework-specific extensions to `TlsStream`.
pub trait TlsStreamExt<S> {
    /// Returns a shared reference to the Security Framework `SslStream`.
    fn raw_stream(&self) -> &secure_transport::SslStream<S>;

    /// Returns a mutable reference to the Security Framework `SslStream`.
    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S>;
}

impl<S> TlsStreamExt<S> for ::TlsStream<S> {
    fn raw_stream(&self) -> &secure_transport::SslStream<S> {
        &(self.0).0
    }

    fn raw_stream_mut(&mut self) -> &mut secure_transport::SslStream<S> {
        &mut (self.0).0
    }
}

/// Security Framework-specific extensions to `TlsConnectorBuilder`.
pub trait TlsConnectorBuilderExt {
    /// Specifies the set of additional root certificates to trust when
    /// verifying the server's certificate.
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self;
}

impl TlsConnectorBuilderExt for ::TlsConnectorBuilder {
    fn anchor_certificates(&mut self, certs: &[SecCertificate]) -> &mut Self {
        (self.0).0.anchor_certificates = Some(certs.to_owned());
        self
    }
}

/// Security Framework-specific extensions to `Error`
pub trait ErrorExt {
    /// Extract the underlying Security Framework error for inspection.
    fn security_framework_error(&self) -> &base::Error;
}

impl ErrorExt for ::Error {
    fn security_framework_error(&self) -> &base::Error {
        &(self.0).0
    }
}
<|endoftext|>


<commit_before>use std::ops::Deref;

use node::Value;
use parser::{Error, Reader, Result};
use super::{Command, Parameters, Position};

/// A [data][1] attribute.
///
/// [1]: http://www.w3.org/TR/SVG/paths.html#PathData
#[derive(Clone, Default)]
pub struct Data(Vec<Command>);

struct Parser<'l> {
    reader: Reader<'l>,
}

impl Data {
    /// Create a data attribute.
    #[inline]
    pub fn new() -> Self {
        Default::default()
    }

    /// Parse a data attribute.
    #[inline]
    pub fn parse(content: &str) -> Result<Self> {
        Parser::new(content).process()
    }
}

macro_rules! implement {
    (@one #[$doc:meta] fn $method:ident($command:ident, $position:ident)) => (
        #[$doc]
        pub fn $method<T>(mut self, parameters: T) -> Self where T: Into<Parameters> {
            self.0.push(Command::$command(Position::$position, parameters.into()));
            self
        }
    );
    (@one #[$doc:meta] fn $method:ident($command:ident)) => (
        #[$doc]
        pub fn $method(mut self) -> Self {
            self.0.push(Command::$command);
            self
        }
    );
    ($(#[$doc:meta] fn $method:ident($($argument:tt)*))*) => (
        impl Data {
            $(implement! { @one #[$doc] fn $method($($argument)*) })*
        }
    );
}

implement! {
    #[doc = "Add an absolute `Command::Move` command."]
    fn move_to(Move, Absolute)

    #[doc = "Add a relative `Command::Move` command."]
    fn move_by(Move, Relative)

    #[doc = "Add an absolute `Command::Line` command."]
    fn line_to(Line, Absolute)

    #[doc = "Add a relative `Command::Line` command."]
    fn line_by(Line, Relative)

    #[doc = "Add an absolute `Command::HorizontalLine` command."]
    fn horizontal_line_to(HorizontalLine, Absolute)

    #[doc = "Add a relative `Command::HorizontalLine` command."]
    fn horizontal_line_by(HorizontalLine, Relative)

    #[doc = "Add an absolute `Command::VerticalLine` command."]
    fn vertical_line_to(VerticalLine, Absolute)

    #[doc = "Add a relative `Command::VerticalLine` command."]
    fn vertical_line_by(VerticalLine, Relative)

    #[doc = "Add an absolute `Command::QuadraticCurve` command."]
    fn quadratic_curve_to(QuadraticCurve, Absolute)

    #[doc = "Add a relative `Command::QuadraticCurve` command."]
    fn quadratic_curve_by(QuadraticCurve, Relative)

    #[doc = "Add an absolute `Command::SmoothQuadraticCurve` command."]
    fn smooth_quadratic_curve_to(SmoothQuadraticCurve, Absolute)

    #[doc = "Add a relative `Command::SmoothQuadraticCurve` command."]
    fn smooth_quadratic_curve_by(SmoothQuadraticCurve, Relative)

    #[doc = "Add an absolute `Command::CubicCurve` command."]
    fn cubic_curve_to(CubicCurve, Absolute)

    #[doc = "Add a relative `Command::CubicCurve` command."]
    fn cubic_curve_by(CubicCurve, Relative)

    #[doc = "Add an absolute `Command::SmoothCubicCurve` command."]
    fn smooth_cubic_curve_to(SmoothCubicCurve, Absolute)

    #[doc = "Add a relative `Command::SmoothCubicCurve` command."]
    fn smooth_cubic_curve_by(SmoothCubicCurve, Relative)

    #[doc = "Add an absolute `Command::EllipticalArc` command."]
    fn elliptical_arc_to(EllipticalArc, Absolute)

    #[doc = "Add a relative `Command::EllipticalArc` command."]
    fn elliptical_arc_by(EllipticalArc, Relative)

    #[doc = "Add a `Command::Close` command."]
    fn close(Close)
}

impl Deref for Data {
    type Target = [Command];

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<Vec<Command>> for Data {
    #[inline]
    fn from(commands: Vec<Command>) -> Self {
        Data(commands)
    }
}

impl From<Data> for Vec<Command> {
    #[inline]
    fn from(Data(commands): Data) -> Self {
        commands
    }
}

impl From<Data> for Value {
    #[inline]
    fn from(Data(mut inner): Data) -> Self {
        inner.drain(..).map(|value| String::from(value)).collect::<Vec<_>>().join(" ").into()
    }
}

macro_rules! raise(
    ($parser:expr, $($argument:tt)*) => (
        return Err(Error::new($parser.reader.position(), format!($($argument)*)));
    );
);

impl<'l> Parser<'l> {
    #[inline]
    fn new(content: &'l str) -> Self {
        Parser { reader: Reader::new(content) }
    }

    fn process(&mut self) -> Result<Data> {
        let mut commands = Vec::new();
        loop {
            self.reader.consume_whitespace();
            match try!(self.read_command()) {
                Some(command) => commands.push(command),
                _ => break,
            }
        }
        Ok(Data(commands))
    }

    fn read_command(&mut self) -> Result<Option<Command>> {
        use super::Command::*;
        use super::Position::*;

        let name = match self.reader.next() {
            Some(name) => match name {
                'A'...'Z' | 'a'...'z' => name,
                _ => raise!(self, "expected a path command"),
            },
            _ => return Ok(None),
        };
        self.reader.consume_whitespace();
        let parameters = try!(self.read_parameters()).into();
        Ok(Some(match name {
            'M' => Move(Absolute, parameters),
            'm' => Move(Relative, parameters),

            'L' => Line(Absolute, parameters),
            'l' => Line(Relative, parameters),

            'H' => HorizontalLine(Absolute, parameters),
            'h' => HorizontalLine(Relative, parameters),

            'V' => VerticalLine(Absolute, parameters),
            'v' => VerticalLine(Relative, parameters),

            'Q' => QuadraticCurve(Absolute, parameters),
            'q' => QuadraticCurve(Relative, parameters),

            'T' => SmoothQuadraticCurve(Absolute, parameters),
            't' => SmoothQuadraticCurve(Relative, parameters),

            'C' => CubicCurve(Absolute, parameters),
            'c' => CubicCurve(Relative, parameters),

            'S' => SmoothCubicCurve(Absolute, parameters),
            's' => SmoothCubicCurve(Relative, parameters),

            'A' => EllipticalArc(Absolute, parameters),
            'a' => EllipticalArc(Relative, parameters),

            'Z' | 'z' => Close,

            _ => raise!(self, "found an unknown path command '{}'", name),
        }))
    }

    fn read_parameters(&mut self) -> Result<Vec<f32>> {
        let mut parameters = Vec::new();
        loop {
            match try!(self.read_number()) {
                Some(number) => parameters.push(number),
                _ => break,
            }
            self.reader.consume_whitespace();
            self.reader.consume_any(",");
        }
        Ok(parameters)
    }

    pub fn read_number(&mut self) -> Result<Option<f32>> {
        self.reader.consume_whitespace();
        let number = self.reader.capture(|reader| {
            reader.consume_char('-');
            reader.consume_digits();
            reader.consume_char('.');
            reader.consume_digits();
        }).and_then(|number| Some(String::from(number)));
        match number {
            Some(number) => match (&number).parse() {
                Ok(number) => Ok(Some(number)),
                Err(_) => raise!(self, "failed to parse a number '{}'", number),
            },
            _ => Ok(None),
        }
    }
}

#[cfg(test)]
mod tests {
    use node::Value;
    use super::{Data, Parser};
    use super::super::Command::*;
    use super::super::Position::*;

    #[test]
    fn data_into_value() {
        let data = Data::new().line_to((1, 2)).cubic_curve_by((1, 2.5, 3, 4, 5, 6)).close();
        assert_eq!(Value::from(data).to_string(), "L1,2 c1,2.5,3,4,5,6 z");
    }

    #[test]
    fn data_parse() {
        let data = Data::parse("M1,2 l3,4").unwrap();
        assert_eq!(data.len(), 2);
        match data[0] {
            Move(Absolute, ref parameters) => assert_eq!(&parameters[..], &[1.0, 2.0]),
            _ => unreachable!(),
        }
        match data[1] {
            Line(Relative, ref parameters) => assert_eq!(&parameters[..], &[3.0, 4.0]),
            _ => unreachable!(),
        }
    }

    #[test]
    fn parser_read_command() {
        macro_rules! run(
            ($content:expr) => ({
                let mut parser = Parser::new($content);
                parser.read_command().unwrap().unwrap()
            });
        );

        macro_rules! test(
            ($content:expr, $command:ident, $position:ident, $parameters:expr) => (
                match run!($content) {
                    $command($position, parameters) => assert_eq!(&parameters[..], $parameters),
                    _ => unreachable!(),
                }
            );
            ($content:expr, $command:ident) => (
                match run!($content) {
                    $command => {},
                    _ => unreachable!(),
                }
            );
        );

        test!("M4,2", Move, Absolute, &[4.0, 2.0]);
        test!("m4,\n2", Move, Relative, &[4.0, 2.0]);

        test!("L7, 8  9", Line, Absolute, &[7.0, 8.0, 9.0]);
        test!("l 7,8 \n9", Line, Relative, &[7.0, 8.0, 9.0]);

        test!("H\t6,9", HorizontalLine, Absolute, &[6.0, 9.0]);
        test!("h6,  \t9", HorizontalLine, Relative, &[6.0, 9.0]);

        test!("V2.1,-3", VerticalLine, Absolute, &[2.1, -3.0]);
        test!("v\n2.1 -3", VerticalLine, Relative, &[2.1, -3.0]);

        test!("Q90.5 0", QuadraticCurve, Absolute, &[90.5, 0.0]);
        test!("q90.5\n, 0", QuadraticCurve, Relative, &[90.5, 0.0]);

        test!("T-1", SmoothQuadraticCurve, Absolute, &[-1.0]);
        test!("t -1", SmoothQuadraticCurve, Relative, &[-1.0]);

        test!("C0,1 0,2", CubicCurve, Absolute, &[0.0, 1.0, 0.0, 2.0]);
        test!("c0 ,1 0,  2", CubicCurve, Relative, &[0.0, 1.0, 0.0, 2.0]);

        test!("S42,0", SmoothCubicCurve, Absolute, &[42.0, 0.0]);
        test!("s \t 42,0", SmoothCubicCurve, Relative, &[42.0, 0.0]);

        test!("A2.6,0 -7", EllipticalArc, Absolute, &[2.6, 0.0, -7.0]);
        test!("a 2.6 ,0 -7", EllipticalArc, Relative, &[2.6, 0.0, -7.0]);

        test!("Z", Close);
        test!("z", Close);
    }

    #[test]
    fn parser_read_parameters() {
        let mut parser = Parser::new("1,2 3,4 5 6.7");
        let parameters = parser.read_parameters().unwrap();
        assert_eq!(&parameters[..], &[1.0, 2.0, 3.0, 4.0, 5.0, 6.7]);
    }

    #[test]
    fn parser_read_number() {
        let texts = vec!["-1", "3", "3.14"];
        let numbers = vec![-1.0, 3.0, 3.14];
        for (&content, &number) in texts.iter().zip(numbers.iter()) {
            let mut parser = Parser::new(content);
            assert_eq!(parser.read_number().unwrap().unwrap(), number);
        }
    }
}
<commit_msg>Add a test for double precision numbers<commit_after>use std::ops::Deref;

use node::Value;
use parser::{Error, Reader, Result};
use super::{Command, Parameters, Position};

/// A [data][1] attribute.
///
/// [1]: http://www.w3.org/TR/SVG/paths.html#PathData
#[derive(Clone, Default)]
pub struct Data(Vec<Command>);

struct Parser<'l> {
    reader: Reader<'l>,
}

impl Data {
    /// Create a data attribute.
    #[inline]
    pub fn new() -> Self {
        Default::default()
    }

    /// Parse a data attribute.
    #[inline]
    pub fn parse(content: &str) -> Result<Self> {
        Parser::new(content).process()
    }
}

macro_rules! implement {
    (@one #[$doc:meta] fn $method:ident($command:ident, $position:ident)) => (
        #[$doc]
        pub fn $method<T>(mut self, parameters: T) -> Self where T: Into<Parameters> {
            self.0.push(Command::$command(Position::$position, parameters.into()));
            self
        }
    );
    (@one #[$doc:meta] fn $method:ident($command:ident)) => (
        #[$doc]
        pub fn $method(mut self) -> Self {
            self.0.push(Command::$command);
            self
        }
    );
    ($(#[$doc:meta] fn $method:ident($($argument:tt)*))*) => (
        impl Data {
            $(implement! { @one #[$doc] fn $method($($argument)*) })*
        }
    );
}

implement! {
    #[doc = "Add an absolute `Command::Move` command."]
    fn move_to(Move, Absolute)

    #[doc = "Add a relative `Command::Move` command."]
    fn move_by(Move, Relative)

    #[doc = "Add an absolute `Command::Line` command."]
    fn line_to(Line, Absolute)

    #[doc = "Add a relative `Command::Line` command."]
    fn line_by(Line, Relative)

    #[doc = "Add an absolute `Command::HorizontalLine` command."]
    fn horizontal_line_to(HorizontalLine, Absolute)

    #[doc = "Add a relative `Command::HorizontalLine` command."]
    fn horizontal_line_by(HorizontalLine, Relative)

    #[doc = "Add an absolute `Command::VerticalLine` command."]
    fn vertical_line_to(VerticalLine, Absolute)

    #[doc = "Add a relative `Command::VerticalLine` command."]
    fn vertical_line_by(VerticalLine, Relative)

    #[doc = "Add an absolute `Command::QuadraticCurve` command."]
    fn quadratic_curve_to(QuadraticCurve, Absolute)

    #[doc = "Add a relative `Command::QuadraticCurve` command."]
    fn quadratic_curve_by(QuadraticCurve, Relative)

    #[doc = "Add an absolute `Command::SmoothQuadraticCurve` command."]
    fn smooth_quadratic_curve_to(SmoothQuadraticCurve, Absolute)

    #[doc = "Add a relative `Command::SmoothQuadraticCurve` command."]
    fn smooth_quadratic_curve_by(SmoothQuadraticCurve, Relative)

    #[doc = "Add an absolute `Command::CubicCurve` command."]
    fn cubic_curve_to(CubicCurve, Absolute)

    #[doc = "Add a relative `Command::CubicCurve` command."]
    fn cubic_curve_by(CubicCurve, Relative)

    #[doc = "Add an absolute `Command::SmoothCubicCurve` command."]
    fn smooth_cubic_curve_to(SmoothCubicCurve, Absolute)

    #[doc = "Add a relative `Command::SmoothCubicCurve` command."]
    fn smooth_cubic_curve_by(SmoothCubicCurve, Relative)

    #[doc = "Add an absolute `Command::EllipticalArc` command."]
    fn elliptical_arc_to(EllipticalArc, Absolute)

    #[doc = "Add a relative `Command::EllipticalArc` command."]
    fn elliptical_arc_by(EllipticalArc, Relative)

    #[doc = "Add a `Command::Close` command."]
    fn close(Close)
}

impl Deref for Data {
    type Target = [Command];

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<Vec<Command>> for Data {
    #[inline]
    fn from(commands: Vec<Command>) -> Self {
        Data(commands)
    }
}

impl From<Data> for Vec<Command> {
    #[inline]
    fn from(Data(commands): Data) -> Self {
        commands
    }
}

impl From<Data> for Value {
    #[inline]
    fn from(Data(mut inner): Data) -> Self {
        inner.drain(..).map(|value| String::from(value)).collect::<Vec<_>>().join(" ").into()
    }
}

macro_rules! raise(
    ($parser:expr, $($argument:tt)*) => (
        return Err(Error::new($parser.reader.position(), format!($($argument)*)));
    );
);

impl<'l> Parser<'l> {
    #[inline]
    fn new(content: &'l str) -> Self {
        Parser { reader: Reader::new(content) }
    }

    fn process(&mut self) -> Result<Data> {
        let mut commands = Vec::new();
        loop {
            self.reader.consume_whitespace();
            match try!(self.read_command()) {
                Some(command) => commands.push(command),
                _ => break,
            }
        }
        Ok(Data(commands))
    }

    fn read_command(&mut self) -> Result<Option<Command>> {
        use super::Command::*;
        use super::Position::*;

        let name = match self.reader.next() {
            Some(name) => match name {
                'A'...'Z' | 'a'...'z' => name,
                _ => raise!(self, "expected a path command"),
            },
            _ => return Ok(None),
        };
        self.reader.consume_whitespace();
        let parameters = try!(self.read_parameters()).into();
        Ok(Some(match name {
            'M' => Move(Absolute, parameters),
            'm' => Move(Relative, parameters),

            'L' => Line(Absolute, parameters),
            'l' => Line(Relative, parameters),

            'H' => HorizontalLine(Absolute, parameters),
            'h' => HorizontalLine(Relative, parameters),

            'V' => VerticalLine(Absolute, parameters),
            'v' => VerticalLine(Relative, parameters),

            'Q' => QuadraticCurve(Absolute, parameters),
            'q' => QuadraticCurve(Relative, parameters),

            'T' => SmoothQuadraticCurve(Absolute, parameters),
            't' => SmoothQuadraticCurve(Relative, parameters),

            'C' => CubicCurve(Absolute, parameters),
            'c' => CubicCurve(Relative, parameters),

            'S' => SmoothCubicCurve(Absolute, parameters),
            's' => SmoothCubicCurve(Relative, parameters),

            'A' => EllipticalArc(Absolute, parameters),
            'a' => EllipticalArc(Relative, parameters),

            'Z' | 'z' => Close,

            _ => raise!(self, "found an unknown path command '{}'", name),
        }))
    }

    fn read_parameters(&mut self) -> Result<Vec<f32>> {
        let mut parameters = Vec::new();
        loop {
            match try!(self.read_number()) {
                Some(number) => parameters.push(number),
                _ => break,
            }
            self.reader.consume_whitespace();
            self.reader.consume_any(",");
        }
        Ok(parameters)
    }

    pub fn read_number(&mut self) -> Result<Option<f32>> {
        self.reader.consume_whitespace();
        let number = self.reader.capture(|reader| {
            reader.consume_char('-');
            reader.consume_digits();
            reader.consume_char('.');
            reader.consume_digits();
        }).and_then(|number| Some(String::from(number)));
        match number {
            Some(number) => match (&number).parse() {
                Ok(number) => Ok(Some(number)),
                Err(_) => raise!(self, "failed to parse a number '{}'", number),
            },
            _ => Ok(None),
        }
    }
}

#[cfg(test)]
mod tests {
    use node::Value;
    use super::{Data, Parser};
    use super::super::Command::*;
    use super::super::Position::*;

    #[test]
    fn data_into_value() {
        let data = Data::new().line_to((1, 2)).cubic_curve_by((1, 2.5, 3, 4, 5, 6)).close();
        assert_eq!(Value::from(data).to_string(), "L1,2 c1,2.5,3,4,5,6 z");
    }

    #[test]
    fn data_parse() {
        let data = Data::parse("M1,2 l3,4").unwrap();
        assert_eq!(data.len(), 2);
        match data[0] {
            Move(Absolute, ref parameters) => assert_eq!(&parameters[..], &[1.0, 2.0]),
            _ => unreachable!(),
        }
        match data[1] {
            Line(Relative, ref parameters) => assert_eq!(&parameters[..], &[3.0, 4.0]),
            _ => unreachable!(),
        }
    }

    #[test]
    fn parser_read_command() {
        macro_rules! run(
            ($content:expr) => ({
                let mut parser = Parser::new($content);
                parser.read_command().unwrap().unwrap()
            });
        );

        macro_rules! test(
            ($content:expr, $command:ident, $position:ident, $parameters:expr) => (
                match run!($content) {
                    $command($position, parameters) => assert_eq!(&parameters[..], $parameters),
                    _ => unreachable!(),
                }
            );
            ($content:expr, $command:ident) => (
                match run!($content) {
                    $command => {},
                    _ => unreachable!(),
                }
            );
        );

        test!("M4,2", Move, Absolute, &[4.0, 2.0]);
        test!("m4,\n2", Move, Relative, &[4.0, 2.0]);

        test!("L7, 8  9", Line, Absolute, &[7.0, 8.0, 9.0]);
        test!("l 7,8 \n9", Line, Relative, &[7.0, 8.0, 9.0]);

        test!("H\t6,9", HorizontalLine, Absolute, &[6.0, 9.0]);
        test!("h6,  \t9", HorizontalLine, Relative, &[6.0, 9.0]);

        test!("V2.1,-3", VerticalLine, Absolute, &[2.1, -3.0]);
        test!("v\n2.1 -3", VerticalLine, Relative, &[2.1, -3.0]);

        test!("Q90.5 0", QuadraticCurve, Absolute, &[90.5, 0.0]);
        test!("q90.5\n, 0", QuadraticCurve, Relative, &[90.5, 0.0]);

        test!("T-1", SmoothQuadraticCurve, Absolute, &[-1.0]);
        test!("t -1", SmoothQuadraticCurve, Relative, &[-1.0]);

        test!("C0,1 0,2", CubicCurve, Absolute, &[0.0, 1.0, 0.0, 2.0]);
        test!("c0 ,1 0,  2", CubicCurve, Relative, &[0.0, 1.0, 0.0, 2.0]);

        test!("S42,0", SmoothCubicCurve, Absolute, &[42.0, 0.0]);
        test!("s \t 42,0", SmoothCubicCurve, Relative, &[42.0, 0.0]);

        test!("A2.6,0 -7", EllipticalArc, Absolute, &[2.6, 0.0, -7.0]);
        test!("a 2.6 ,0 -7", EllipticalArc, Relative, &[2.6, 0.0, -7.0]);

        test!("Z", Close);
        test!("z", Close);
    }

    #[test]
    fn parser_read_parameters() {
        let mut parser = Parser::new("1,2 3,4 5 6.7");
        let parameters = parser.read_parameters().unwrap();
        assert_eq!(&parameters[..], &[1.0, 2.0, 3.0, 4.0, 5.0, 6.7]);
    }

    #[test]
    fn parser_read_number() {
        let contents = vec!["-1", "3", "3.14"];
        let numbers = vec![-1.0, 3.0, 3.14];
        for (&content, &number) in contents.iter().zip(numbers.iter()) {
            let mut parser = Parser::new(content);
            assert_eq!(parser.read_number().unwrap().unwrap(), number);
        }
    }

    #[test]
    fn parser_read_number_double() {
        let mut parser = Parser::new("0.30000000000000004");
        assert_eq!(parser.read_number().unwrap().unwrap(), 0.3);
    }
}
<|endoftext|>


<commit_before><commit_msg>Remove some stray debug code<commit_after><|endoftext|>


<commit_before>use std::env;
use std::process;
use std::fs::File;
use std::io::prelude::*;
// use std::io::Read;   // will include this

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);

    let mut f = File::open(config.filename).expect("file not found");

    let mut contents = String::new();
    f.read_to_string(&mut contents)
        .expect("somthing went wrong reading the file");

    println!("With text:\n{}", contents);
}

struct Config {
    query: String,
    filename: String,
}

impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }
        let query = args[1].clone();
        let filename = args[2].clone();

        Ok(Config {
            query: query,
            filename: filename,
        })
    }
}
<commit_msg>On io refactor: abstract main logic and err handling to `run`.<commit_after>use std::env;
use std::process;
use std::fs::File;
use std::error::Error;
use std::io::prelude::*;
// use std::io::Read;   // will include this

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    println!("Searching for {}", config.query);
    println!("In file {}", config.filename);

    if let Err(e) = run(config) {
        println!("Application error: {}", e);

        process::exit(1);
    }
}

fn run(config: Config) -> Result<(), Box<Error>> {  // trait obj, a type that impl Error
    let mut f = File::open(config.filename)?;

    let mut contents = String::new();
    f.read_to_string(&mut contents)?;

    println!("With text:\n{}", contents);

    Ok(())  // idiomatic way to indicate `run` for side effect
}

struct Config {
    query: String,
    filename: String,
}

impl Config {
    fn new(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }
        let query = args[1].clone();
        let filename = args[2].clone();

        Ok(Config {
            query: query,
            filename: filename,
        })
    }
}
<|endoftext|>


<commit_before>use filetime::FileTime;
use serde::{Deserialize, Serialize};

use std::{
    collections::{HashMap, HashSet},
    fs, io,
    path::{Path, PathBuf},
    vec,
};

/// Helps identify changes in a set of files.
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
#[serde(from = "DepGraphSerdeRepr", into = "DepGraphSerdeRepr")]
pub struct DepGraph {
    entries: HashMap<PathBuf, PathState>,
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum PathState {
    Seen { mtime: FileTime, size: u64 },
    Unknown,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct DepGraphSerdeRepr {
    entries: Vec<PathStateSerdeRepr>,
}

impl From<DepGraphSerdeRepr> for DepGraph {
    fn from(from: DepGraphSerdeRepr) -> Self {
        DepGraph {
            entries: from
                .entries
                .iter()
                .map(|d| {
                    (
                        PathBuf::from(&d.path),
                        if d.has_graph_entry() {
                            PathState::Seen {
                                mtime: FileTime::from_unix_time(d.unix_seconds, d.nanos),
                                size: d.size,
                            }
                        } else {
                            PathState::Unknown
                        },
                    )
                })
                .collect(),
        }
    }
}

impl From<DepGraph> for DepGraphSerdeRepr {
    fn from(dg: DepGraph) -> Self {
        DepGraphSerdeRepr {
            entries: dg
                .entries
                .iter()
                .map(|e| {
                    let path = e.0.to_str().expect("Only UTF names please").to_string();
                    match e.1 {
                        PathState::Seen { mtime, size } => PathStateSerdeRepr {
                            path,
                            unix_seconds: mtime.unix_seconds(),
                            nanos: mtime.nanoseconds(),
                            size: *size,
                        },
                        PathState::Unknown => PathStateSerdeRepr {
                            path,
                            unix_seconds: 0,
                            nanos: 0,
                            size: 0,
                        },
                    }
                })
                .collect(),
        }
    }
}

/// The serde-friendly representation of a DepGraphEntry.
///
/// SystemTime lacks a platform independent representation we can
/// depend on so use FileTime's unix_seconds,nanos.
/// unix_seconds = nanos = size = 0 is used to represent lack of a DepGraphEntry.
#[derive(Serialize, Deserialize, Debug, Clone)]
struct PathStateSerdeRepr {
    path: String,
    unix_seconds: i64,
    nanos: u32,
    size: u64,
}

impl PathStateSerdeRepr {
    fn has_graph_entry(&self) -> bool {
        !(self.unix_seconds == 0 && self.nanos == 0 && self.size == 0)
    }
}

/// A new snapshot of the state of a file.
///
/// Always contains PathState::Seen.
pub struct Change {
    path: PathBuf,
    path_state: PathState,
}

pub enum InitialState {
    NotChanged,
    Changed,
}

impl PathState {
    fn of(path: &Path) -> Result<PathState, io::Error> {
        let metadata = path.metadata()?;
        Ok(PathState::Seen {
            mtime: FileTime::from_system_time(metadata.modified()?),
            size: metadata.len(),
        })
    }
}

impl DepGraph {
    pub fn new() -> DepGraph {
        Default::default()
    }
}

impl DepGraph {
    pub fn contains(&self, path: &PathBuf) -> bool {
        self.entries.contains_key(path)
    }

    pub fn track(&mut self, path: &PathBuf, default_state: InitialState) -> Result<(), io::Error> {
        if !self.entries.contains_key(path) {
            self.entries.insert(
                path.clone(),
                match default_state {
                    InitialState::NotChanged => PathState::of(path)?,
                    InitialState::Changed => PathState::Unknown,
                },
            );

            // For a dir to register unchanged we need to add it's current contents
            if let InitialState::NotChanged = default_state {
                if path.is_dir() {
                    let mut dirs_visited = HashSet::new();
                    if let Some(new_paths) = self.new_files(&mut dirs_visited, path) {
                        for new_path in new_paths {
                            self.entries
                                .insert(new_path.clone(), PathState::of(&new_path)?);
                        }
                    }
                }
            }
        }
        Ok(())
    }

    pub fn has_changed(&self, change: &Change) -> bool {
        // If we are tracking with different state or we're not tracking at all then you've changed
        let current_state = self
            .entries
            .get(&change.path)
            .unwrap_or(&PathState::Unknown);
        *current_state != change.path_state
    }

    pub fn update(&mut self, changes: &[Change]) {
        for change in changes {
            if self.has_changed(change) {
                self.entries.insert(change.path.clone(), change.path_state);
            }
        }
    }

    fn new_files(
        &self,
        dirs_visited: &mut HashSet<PathBuf>,
        dir: &PathBuf,
    ) -> Option<Vec<PathBuf>> {
        assert!(dir.is_dir());
        if dirs_visited.contains(dir) {
            return None;
        }

        let mut frontier = vec![dir.to_owned()];
        let mut results: Option<Vec<PathBuf>> = None;

        while let Some(dir) = frontier.pop() {
            let dir_entries = fs::read_dir(&dir).expect("Unable to iterate directory");
            for dir_entry in dir_entries {
                let dir_entry = dir_entry.expect("Cannot read dir entry").path();

                // Queue subdir for processing. Bravely assume lack of cycles.
                if dir_entry.is_dir() && !dirs_visited.contains(&dir_entry) {
                    frontier.push(dir_entry.to_owned());
                }

                // A file we've never seen before?!
                if !dir_entry.is_dir() && !self.entries.contains_key(&dir_entry) {
                    let new_files = results.get_or_insert_with(Vec::new);
                    new_files.push(dir_entry);
                }
            }
            dirs_visited.insert(dir.to_owned());
        }
        results
    }

    /// Set of files that have either changed themselves or had
    /// something they depend on change.
    ///
    /// Directories can be tracked but never report as changed.
    pub fn changed(&self) -> Result<Vec<Change>, io::Error> {
        let mut paths: Vec<&PathBuf> = self.entries.iter().map(|(k, _)| k).collect();

        let mut changes: Vec<Change> = Vec::new();

        // Add any new files in tracked dirs
        let mut dirs_visited: HashSet<PathBuf> = HashSet::new();
        let new_files: Vec<PathBuf> = paths
            .iter()
            .filter(|p| p.is_dir())
            .flat_map(|p| self.new_files(&mut dirs_visited, p).unwrap_or_default())
            .collect();
        new_files.iter().for_each(|p| paths.push(p));

        // Anything change 'round here?
        for path in paths {
            let path_state = PathState::of(path)?;
            let has_changed = self
                .entries
                .get(path)
                .map(|prior_state| *prior_state != path_state)
                .unwrap_or(true);

            if has_changed {
                changes.push(Change {
                    path: path.to_owned(),
                    path_state,
                });
            }
        }

        Ok(changes)
    }
}

#[cfg(test)]
mod tests {
    use std::{fs, ops::Add, path::PathBuf, time::Duration};

    use filetime::set_file_mtime;
    use tempfile::{tempdir, TempDir};

    use super::DepGraph;

    fn assert_no_changes(dg: &DepGraph) {
        assert_eq!(
            Vec::<&PathBuf>::new(),
            dg.changed()
                .unwrap()
                .iter()
                .map(|c| &c.path)
                .collect::<Vec<&PathBuf>>()
        )
    }

    #[test]
    fn detect_file_change() {
        let temp_dir = tempdir().unwrap();

        let file = temp_dir.path().join("a");
        fs::write(&file, "eh").unwrap();

        let mut dg = DepGraph::new();
        dg.track(&file, super::InitialState::NotChanged).unwrap();

        assert_no_changes(&dg);

        // Detect changed size
        fs::write(&file, "whoa").unwrap();
        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&file],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );

        // We're happy with that change
        dg.update(&changes);
        assert_no_changes(&dg);

        // Detect changed mtime
        let new_mtime = file
            .metadata()
            .unwrap()
            .modified()
            .unwrap()
            .add(Duration::from_secs(1));
        set_file_mtime(&file, new_mtime.into()).unwrap();
        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&file],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );
    }

    #[test]
    fn detect_dir_change() {
        let temp_dir = tempdir().unwrap();

        let subdir = temp_dir.path().join("glif");
        fs::create_dir(&subdir).unwrap();

        let f1 = temp_dir.path().join("fileA");
        let f2 = subdir.join("fileB");
        fs::write(&f1, "eh").unwrap();
        fs::write(&f2, "eh").unwrap();

        // Track the parent dir from it's current state
        let mut dg = DepGraph::new();
        dg.track(
            &temp_dir.path().to_path_buf(),
            super::InitialState::NotChanged,
        )
        .unwrap();

        // Notably, we should NOT report the files temp dir as changed
        // because we added the entire directory as unchanged and nothing
        // in it has changed since then
        assert_no_changes(&dg);

        // If we change or add files in the tracked dir that should count
        let f3 = subdir.join("fileC");
        fs::write(&f2, "eh+").unwrap();
        fs::write(&f3, "eh").unwrap();

        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&f2, &f3],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );
    }

    fn one_changed_file_one_not(temp_dir: &TempDir) -> (PathBuf, PathBuf, DepGraph) {
        let unchanged = temp_dir.path().join("a");
        let changed = temp_dir.path().join("b");

        fs::write(&unchanged, "eh").unwrap();
        fs::write(&changed, "eh").unwrap();

        let mut dg = DepGraph::new();
        dg.track(&unchanged, super::InitialState::NotChanged)
            .unwrap();
        dg.track(&changed, super::InitialState::Changed).unwrap();

        assert!(dg.contains(&changed));
        assert!(dg.contains(&unchanged));

        (changed, unchanged, dg)
    }

    #[test]
    fn track_with_initial_state() {
        let temp_dir = tempdir().unwrap();

        let (changed, _, mut dg) = one_changed_file_one_not(&temp_dir);

        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&changed],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );

        dg.update(&changes);
        let changes = dg.changed().unwrap();
        assert!(changes.is_empty());
    }

    #[test]
    fn read_write_bincode_entry() {}

    #[test]
    fn read_write_toml() {
        let temp_dir = tempdir().unwrap();

        let (changed, _, dg) = one_changed_file_one_not(&temp_dir);

        let toml = toml::ser::to_string_pretty(&dg).unwrap();
        let dg: DepGraph = toml::from_str(&toml).unwrap();

        assert_eq!(
            vec![&changed],
            dg.changed()
                .unwrap()
                .iter()
                .map(|c| &c.path)
                .collect::<Vec<&PathBuf>>()
        );
    }

    #[test]
    fn read_write_bincode() {
        let temp_dir = tempdir().unwrap();

        let (changed, _, dg) = one_changed_file_one_not(&temp_dir);

        let bc = bincode::serialize(&dg).unwrap();
        let dg: DepGraph = bincode::deserialize(&bc).unwrap();

        assert_eq!(
            vec![&changed],
            dg.changed()
                .unwrap()
                .iter()
                .map(|c| &c.path)
                .collect::<Vec<&PathBuf>>()
        );
    }
}
<commit_msg>Remove empty test that will never be populated<commit_after>use filetime::FileTime;
use serde::{Deserialize, Serialize};

use std::{
    collections::{HashMap, HashSet},
    fs, io,
    path::{Path, PathBuf},
    vec,
};

/// Helps identify changes in a set of files.
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
#[serde(from = "DepGraphSerdeRepr", into = "DepGraphSerdeRepr")]
pub struct DepGraph {
    entries: HashMap<PathBuf, PathState>,
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum PathState {
    Seen { mtime: FileTime, size: u64 },
    Unknown,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
struct DepGraphSerdeRepr {
    entries: Vec<PathStateSerdeRepr>,
}

impl From<DepGraphSerdeRepr> for DepGraph {
    fn from(from: DepGraphSerdeRepr) -> Self {
        DepGraph {
            entries: from
                .entries
                .iter()
                .map(|d| {
                    (
                        PathBuf::from(&d.path),
                        if d.has_graph_entry() {
                            PathState::Seen {
                                mtime: FileTime::from_unix_time(d.unix_seconds, d.nanos),
                                size: d.size,
                            }
                        } else {
                            PathState::Unknown
                        },
                    )
                })
                .collect(),
        }
    }
}

impl From<DepGraph> for DepGraphSerdeRepr {
    fn from(dg: DepGraph) -> Self {
        DepGraphSerdeRepr {
            entries: dg
                .entries
                .iter()
                .map(|e| {
                    let path = e.0.to_str().expect("Only UTF names please").to_string();
                    match e.1 {
                        PathState::Seen { mtime, size } => PathStateSerdeRepr {
                            path,
                            unix_seconds: mtime.unix_seconds(),
                            nanos: mtime.nanoseconds(),
                            size: *size,
                        },
                        PathState::Unknown => PathStateSerdeRepr {
                            path,
                            unix_seconds: 0,
                            nanos: 0,
                            size: 0,
                        },
                    }
                })
                .collect(),
        }
    }
}

/// The serde-friendly representation of a DepGraphEntry.
///
/// SystemTime lacks a platform independent representation we can
/// depend on so use FileTime's unix_seconds,nanos.
/// unix_seconds = nanos = size = 0 is used to represent lack of a DepGraphEntry.
#[derive(Serialize, Deserialize, Debug, Clone)]
struct PathStateSerdeRepr {
    path: String,
    unix_seconds: i64,
    nanos: u32,
    size: u64,
}

impl PathStateSerdeRepr {
    fn has_graph_entry(&self) -> bool {
        !(self.unix_seconds == 0 && self.nanos == 0 && self.size == 0)
    }
}

/// A new snapshot of the state of a file.
///
/// Always contains PathState::Seen.
pub struct Change {
    path: PathBuf,
    path_state: PathState,
}

pub enum InitialState {
    NotChanged,
    Changed,
}

impl PathState {
    fn of(path: &Path) -> Result<PathState, io::Error> {
        let metadata = path.metadata()?;
        Ok(PathState::Seen {
            mtime: FileTime::from_system_time(metadata.modified()?),
            size: metadata.len(),
        })
    }
}

impl DepGraph {
    pub fn new() -> DepGraph {
        Default::default()
    }
}

impl DepGraph {
    pub fn contains(&self, path: &PathBuf) -> bool {
        self.entries.contains_key(path)
    }

    pub fn track(&mut self, path: &PathBuf, default_state: InitialState) -> Result<(), io::Error> {
        if !self.entries.contains_key(path) {
            self.entries.insert(
                path.clone(),
                match default_state {
                    InitialState::NotChanged => PathState::of(path)?,
                    InitialState::Changed => PathState::Unknown,
                },
            );

            // For a dir to register unchanged we need to add it's current contents
            if let InitialState::NotChanged = default_state {
                if path.is_dir() {
                    let mut dirs_visited = HashSet::new();
                    if let Some(new_paths) = self.new_files(&mut dirs_visited, path) {
                        for new_path in new_paths {
                            self.entries
                                .insert(new_path.clone(), PathState::of(&new_path)?);
                        }
                    }
                }
            }
        }
        Ok(())
    }

    pub fn has_changed(&self, change: &Change) -> bool {
        // If we are tracking with different state or we're not tracking at all then you've changed
        let current_state = self
            .entries
            .get(&change.path)
            .unwrap_or(&PathState::Unknown);
        *current_state != change.path_state
    }

    pub fn update(&mut self, changes: &[Change]) {
        for change in changes {
            if self.has_changed(change) {
                self.entries.insert(change.path.clone(), change.path_state);
            }
        }
    }

    fn new_files(
        &self,
        dirs_visited: &mut HashSet<PathBuf>,
        dir: &PathBuf,
    ) -> Option<Vec<PathBuf>> {
        assert!(dir.is_dir());
        if dirs_visited.contains(dir) {
            return None;
        }

        let mut frontier = vec![dir.to_owned()];
        let mut results: Option<Vec<PathBuf>> = None;

        while let Some(dir) = frontier.pop() {
            let dir_entries = fs::read_dir(&dir).expect("Unable to iterate directory");
            for dir_entry in dir_entries {
                let dir_entry = dir_entry.expect("Cannot read dir entry").path();

                // Queue subdir for processing. Bravely assume lack of cycles.
                if dir_entry.is_dir() && !dirs_visited.contains(&dir_entry) {
                    frontier.push(dir_entry.to_owned());
                }

                // A file we've never seen before?!
                if !dir_entry.is_dir() && !self.entries.contains_key(&dir_entry) {
                    let new_files = results.get_or_insert_with(Vec::new);
                    new_files.push(dir_entry);
                }
            }
            dirs_visited.insert(dir.to_owned());
        }
        results
    }

    /// Set of files that have either changed themselves or had
    /// something they depend on change.
    ///
    /// Directories can be tracked but never report as changed.
    pub fn changed(&self) -> Result<Vec<Change>, io::Error> {
        let mut paths: Vec<&PathBuf> = self.entries.iter().map(|(k, _)| k).collect();

        let mut changes: Vec<Change> = Vec::new();

        // Add any new files in tracked dirs
        let mut dirs_visited: HashSet<PathBuf> = HashSet::new();
        let new_files: Vec<PathBuf> = paths
            .iter()
            .filter(|p| p.is_dir())
            .flat_map(|p| self.new_files(&mut dirs_visited, p).unwrap_or_default())
            .collect();
        new_files.iter().for_each(|p| paths.push(p));

        // Anything change 'round here?
        for path in paths {
            let path_state = PathState::of(path)?;
            let has_changed = self
                .entries
                .get(path)
                .map(|prior_state| *prior_state != path_state)
                .unwrap_or(true);

            if has_changed {
                changes.push(Change {
                    path: path.to_owned(),
                    path_state,
                });
            }
        }

        Ok(changes)
    }
}

#[cfg(test)]
mod tests {
    use std::{fs, ops::Add, path::PathBuf, time::Duration};

    use filetime::set_file_mtime;
    use tempfile::{tempdir, TempDir};

    use super::DepGraph;

    fn assert_no_changes(dg: &DepGraph) {
        assert_eq!(
            Vec::<&PathBuf>::new(),
            dg.changed()
                .unwrap()
                .iter()
                .map(|c| &c.path)
                .collect::<Vec<&PathBuf>>()
        )
    }

    #[test]
    fn detect_file_change() {
        let temp_dir = tempdir().unwrap();

        let file = temp_dir.path().join("a");
        fs::write(&file, "eh").unwrap();

        let mut dg = DepGraph::new();
        dg.track(&file, super::InitialState::NotChanged).unwrap();

        assert_no_changes(&dg);

        // Detect changed size
        fs::write(&file, "whoa").unwrap();
        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&file],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );

        // We're happy with that change
        dg.update(&changes);
        assert_no_changes(&dg);

        // Detect changed mtime
        let new_mtime = file
            .metadata()
            .unwrap()
            .modified()
            .unwrap()
            .add(Duration::from_secs(1));
        set_file_mtime(&file, new_mtime.into()).unwrap();
        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&file],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );
    }

    #[test]
    fn detect_dir_change() {
        let temp_dir = tempdir().unwrap();

        let subdir = temp_dir.path().join("glif");
        fs::create_dir(&subdir).unwrap();

        let f1 = temp_dir.path().join("fileA");
        let f2 = subdir.join("fileB");
        fs::write(&f1, "eh").unwrap();
        fs::write(&f2, "eh").unwrap();

        // Track the parent dir from it's current state
        let mut dg = DepGraph::new();
        dg.track(
            &temp_dir.path().to_path_buf(),
            super::InitialState::NotChanged,
        )
        .unwrap();

        // Notably, we should NOT report the files temp dir as changed
        // because we added the entire directory as unchanged and nothing
        // in it has changed since then
        assert_no_changes(&dg);

        // If we change or add files in the tracked dir that should count
        let f3 = subdir.join("fileC");
        fs::write(&f2, "eh+").unwrap();
        fs::write(&f3, "eh").unwrap();

        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&f2, &f3],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );
    }

    fn one_changed_file_one_not(temp_dir: &TempDir) -> (PathBuf, PathBuf, DepGraph) {
        let unchanged = temp_dir.path().join("a");
        let changed = temp_dir.path().join("b");

        fs::write(&unchanged, "eh").unwrap();
        fs::write(&changed, "eh").unwrap();

        let mut dg = DepGraph::new();
        dg.track(&unchanged, super::InitialState::NotChanged)
            .unwrap();
        dg.track(&changed, super::InitialState::Changed).unwrap();

        assert!(dg.contains(&changed));
        assert!(dg.contains(&unchanged));

        (changed, unchanged, dg)
    }

    #[test]
    fn track_with_initial_state() {
        let temp_dir = tempdir().unwrap();

        let (changed, _, mut dg) = one_changed_file_one_not(&temp_dir);

        let changes = dg.changed().unwrap();
        assert_eq!(
            vec![&changed],
            changes.iter().map(|c| &c.path).collect::<Vec<&PathBuf>>()
        );

        dg.update(&changes);
        let changes = dg.changed().unwrap();
        assert!(changes.is_empty());
    }

    #[test]
    fn read_write_toml() {
        let temp_dir = tempdir().unwrap();

        let (changed, _, dg) = one_changed_file_one_not(&temp_dir);

        let toml = toml::ser::to_string_pretty(&dg).unwrap();
        let dg: DepGraph = toml::from_str(&toml).unwrap();

        assert_eq!(
            vec![&changed],
            dg.changed()
                .unwrap()
                .iter()
                .map(|c| &c.path)
                .collect::<Vec<&PathBuf>>()
        );
    }

    #[test]
    fn read_write_bincode() {
        let temp_dir = tempdir().unwrap();

        let (changed, _, dg) = one_changed_file_one_not(&temp_dir);

        let bc = bincode::serialize(&dg).unwrap();
        let dg: DepGraph = bincode::deserialize(&bc).unwrap();

        assert_eq!(
            vec![&changed],
            dg.changed()
                .unwrap()
                .iter()
                .map(|c| &c.path)
                .collect::<Vec<&PathBuf>>()
        );
    }
}
<|endoftext|>