<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 app_units::Au;
use azure::azure_hl::BackendType;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
use azure::scaled_font::FontInfo;
use azure::scaled_font::ScaledFont;
use fnv::FnvHasher;
use font::{Font, FontGroup, FontHandleMethods};
use font_cache_thread::FontCacheThread;
use font_template::FontTemplateDescriptor;
use heapsize::HeapSizeOf;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use smallvec::SmallVec;
use std::cell::RefCell;
use std::collections::HashMap;
use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use string_cache::Atom;
use style::computed_values::{font_style, font_variant};
use style::properties::style_structs::ServoFont;
use webrender_traits;

#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    ScaledFont::new(BackendType::Skia, FontInfo::FontData(&template.bytes),
                    pt_size.to_f32_px())
}

#[cfg(target_os = "macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    let cgfont = template.ctfont(pt_size.to_f64_px()).as_ref().unwrap().copy_to_CGFont();
    ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_f32_px())
}

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

#[derive(Debug)]
struct LayoutFontCacheEntry {
    family: String,
    font: Option<Rc<RefCell<Font>>>,
}

#[derive(Debug)]
struct FallbackFontCacheEntry {
    font: Rc<RefCell<Font>>,
}

/// A cached azure font (per paint thread) that
/// can be shared by multiple text runs.
#[derive(Debug)]
struct PaintFontCacheEntry {
    pt_size: Au,
    identifier: Atom,
    font: Rc<RefCell<ScaledFont>>,
}

/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
/// this one.
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;

/// The FontContext represents the per-thread/thread state necessary for
/// working with fonts. It is the public API used by the layout and
/// paint code. It talks directly to the font cache thread where
/// required.
#[derive(Debug)]
pub struct FontContext {
    platform_handle: FontContextHandle,
    font_cache_thread: FontCacheThread,

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

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

    layout_font_group_cache:
        HashMap<LayoutFontGroupCacheKey, Rc<FontGroup>, BuildHasherDefault<FnvHasher>>,

    epoch: usize,
}

impl FontContext {
    pub fn new(font_cache_thread: FontCacheThread) -> FontContext {
        let handle = FontContextHandle::new();
        FontContext {
            platform_handle: handle,
            font_cache_thread: font_cache_thread,
            layout_font_cache: vec!(),
            fallback_font_cache: vec!(),
            paint_font_cache: vec!(),
            layout_font_group_cache: HashMap::with_hasher(Default::default()),
            epoch: 0,
        }
    }

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

        let handle: Result<FontHandle, _> =
            FontHandleMethods::new_from_template(&self.platform_handle, template,
                                                 Some(actual_pt_size));

        handle.map(|handle|
            Font::new(handle, variant, descriptor, pt_size, actual_pt_size, font_key))
    }

    fn expire_font_caches_if_necessary(&mut self) {
        let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
        if current_epoch == self.epoch {
            return
        }

        self.layout_font_cache.clear();
        self.fallback_font_cache.clear();
        self.paint_font_cache.clear();
        self.layout_font_group_cache.clear();
        self.epoch = current_epoch
    }

    /// 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 layout_font_group_for_style(&mut self, style: Arc<ServoFont>)
                                       -> Rc<FontGroup> {
        self.expire_font_caches_if_necessary();

        let layout_font_group_cache_key = LayoutFontGroupCacheKey {
            pointer: style.clone(),
            size: style.font_size,
        };
        if let Some(ref cached_font_group) = self.layout_font_group_cache.get(
                &layout_font_group_cache_key) {
            return (*cached_font_group).clone()
        }

        // TODO: The font context holds a strong ref to the cached fonts
        // so they will never be released. Find out a good time to drop them.

        let desc = FontTemplateDescriptor::new(style.font_weight,
                                               style.font_stretch,
                                               style.font_style == font_style::T::italic ||
                                                style.font_style == font_style::T::oblique);

        let mut fonts: SmallVec<[Rc<RefCell<Font>>; 8]> = SmallVec::new();

        for family in &style.font_family.0 {
            // 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 {
                if cached_font_entry.family == family.name() {
                    match cached_font_entry.font {
                        None => {
                            cache_hit = true;
                            break;
                        }
                        Some(ref cached_font_ref) => {
                            let cached_font = (*cached_font_ref).borrow();
                            if cached_font.descriptor == desc &&
                               cached_font.requested_pt_size == style.font_size &&
                               cached_font.variant == style.font_variant {
                                fonts.push((*cached_font_ref).clone());
                                cache_hit = true;
                                break;
                            }
                        }
                    }
                }
            }

            if !cache_hit {
                let template_info = self.font_cache_thread.find_font_template(family.clone(),
                                                                             desc.clone());
                match template_info {
                    Some(template_info) => {
                        let layout_font = self.create_layout_font(template_info.font_template,
                                                                  desc.clone(),
                                                                  style.font_size,
                                                                  style.font_variant,
                                                                  template_info.font_key);
                        let font = match layout_font {
                            Ok(layout_font) => {
                                let layout_font = Rc::new(RefCell::new(layout_font));
                                fonts.push(layout_font.clone());

                                Some(layout_font)
                            }
                            Err(_) => None
                        };

                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: font
                        });
                    }
                    None => {
                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: None,
                        });
                    }
                }
            }
        }

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

            if !cache_hit {
                let template_info = self.font_cache_thread.last_resort_font_template(desc.clone());
                let layout_font = self.create_layout_font(template_info.font_template,
                                                          desc.clone(),
                                                          style.font_size,
                                                          style.font_variant,
                                                          template_info.font_key);
                match layout_font {
                    Ok(layout_font) => {
                        let layout_font = Rc::new(RefCell::new(layout_font));
                        self.fallback_font_cache.push(FallbackFontCacheEntry {
                            font: layout_font.clone(),
                        });
                        fonts.push(layout_font);
                    }
                    Err(_) => debug!("Failed to create fallback layout font!")
                }
            }
        }

        let font_group = Rc::new(FontGroup::new(fonts));
        self.layout_font_group_cache.insert(layout_font_group_cache_key, font_group.clone());
        font_group
    }

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

        let paint_font = Rc::new(RefCell::new(create_scaled_font(template, pt_size)));
        self.paint_font_cache.push(PaintFontCacheEntry {
            font: paint_font.clone(),
            pt_size: pt_size,
            identifier: template.identifier.clone(),
        });
        paint_font
    }

    /// Returns a reference to the font cache thread.
    pub fn font_cache_thread(&self) -> FontCacheThread {
        self.font_cache_thread.clone()
    }
}

impl HeapSizeOf for FontContext {
    fn heap_size_of_children(&self) -> usize {
        // FIXME(njn): Measure other fields eventually.
        self.platform_handle.heap_size_of_children()
    }
}

#[derive(Debug)]
struct LayoutFontGroupCacheKey {
    pointer: Arc<ServoFont>,
    size: Au,
}

impl PartialEq for LayoutFontGroupCacheKey {
    fn eq(&self, other: &LayoutFontGroupCacheKey) -> bool {
        self.pointer == other.pointer && self.size == other.size
    }
}

impl Eq for LayoutFontGroupCacheKey {}

impl Hash for LayoutFontGroupCacheKey {
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
        self.pointer.hash.hash(hasher)
    }
}

#[inline]
pub fn invalidate_font_caches() {
    FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
}
<commit_msg>Simplify FontContext::create_layout_font.<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 app_units::Au;
use azure::azure_hl::BackendType;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
use azure::scaled_font::FontInfo;
use azure::scaled_font::ScaledFont;
use fnv::FnvHasher;
use font::{Font, FontGroup, FontHandleMethods};
use font_cache_thread::FontCacheThread;
use font_template::FontTemplateDescriptor;
use heapsize::HeapSizeOf;
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use smallvec::SmallVec;
use std::cell::RefCell;
use std::collections::HashMap;
use std::default::Default;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use string_cache::Atom;
use style::computed_values::{font_style, font_variant};
use style::properties::style_structs::ServoFont;
use webrender_traits;

#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    ScaledFont::new(BackendType::Skia, FontInfo::FontData(&template.bytes),
                    pt_size.to_f32_px())
}

#[cfg(target_os = "macos")]
fn create_scaled_font(template: &Arc<FontTemplateData>, pt_size: Au) -> ScaledFont {
    let cgfont = template.ctfont(pt_size.to_f64_px()).as_ref().unwrap().copy_to_CGFont();
    ScaledFont::new(BackendType::Skia, &cgfont, pt_size.to_f32_px())
}

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

#[derive(Debug)]
struct LayoutFontCacheEntry {
    family: String,
    font: Option<Rc<RefCell<Font>>>,
}

#[derive(Debug)]
struct FallbackFontCacheEntry {
    font: Rc<RefCell<Font>>,
}

/// A cached azure font (per paint thread) that
/// can be shared by multiple text runs.
#[derive(Debug)]
struct PaintFontCacheEntry {
    pt_size: Au,
    identifier: Atom,
    font: Rc<RefCell<ScaledFont>>,
}

/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
/// this one.
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;

/// The FontContext represents the per-thread/thread state necessary for
/// working with fonts. It is the public API used by the layout and
/// paint code. It talks directly to the font cache thread where
/// required.
#[derive(Debug)]
pub struct FontContext {
    platform_handle: FontContextHandle,
    font_cache_thread: FontCacheThread,

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

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

    layout_font_group_cache:
        HashMap<LayoutFontGroupCacheKey, Rc<FontGroup>, BuildHasherDefault<FnvHasher>>,

    epoch: usize,
}

impl FontContext {
    pub fn new(font_cache_thread: FontCacheThread) -> FontContext {
        let handle = FontContextHandle::new();
        FontContext {
            platform_handle: handle,
            font_cache_thread: font_cache_thread,
            layout_font_cache: vec!(),
            fallback_font_cache: vec!(),
            paint_font_cache: vec!(),
            layout_font_group_cache: HashMap::with_hasher(Default::default()),
            epoch: 0,
        }
    }

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

        let handle = try!(FontHandle::new_from_template(&self.platform_handle,
                                                        template,
                                                        Some(actual_pt_size)));

        Ok(Font::new(handle, variant, descriptor, pt_size, actual_pt_size, font_key))
    }

    fn expire_font_caches_if_necessary(&mut self) {
        let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
        if current_epoch == self.epoch {
            return
        }

        self.layout_font_cache.clear();
        self.fallback_font_cache.clear();
        self.paint_font_cache.clear();
        self.layout_font_group_cache.clear();
        self.epoch = current_epoch
    }

    /// 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 layout_font_group_for_style(&mut self, style: Arc<ServoFont>)
                                       -> Rc<FontGroup> {
        self.expire_font_caches_if_necessary();

        let layout_font_group_cache_key = LayoutFontGroupCacheKey {
            pointer: style.clone(),
            size: style.font_size,
        };
        if let Some(ref cached_font_group) = self.layout_font_group_cache.get(
                &layout_font_group_cache_key) {
            return (*cached_font_group).clone()
        }

        // TODO: The font context holds a strong ref to the cached fonts
        // so they will never be released. Find out a good time to drop them.

        let desc = FontTemplateDescriptor::new(style.font_weight,
                                               style.font_stretch,
                                               style.font_style == font_style::T::italic ||
                                                style.font_style == font_style::T::oblique);

        let mut fonts: SmallVec<[Rc<RefCell<Font>>; 8]> = SmallVec::new();

        for family in &style.font_family.0 {
            // 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 {
                if cached_font_entry.family == family.name() {
                    match cached_font_entry.font {
                        None => {
                            cache_hit = true;
                            break;
                        }
                        Some(ref cached_font_ref) => {
                            let cached_font = (*cached_font_ref).borrow();
                            if cached_font.descriptor == desc &&
                               cached_font.requested_pt_size == style.font_size &&
                               cached_font.variant == style.font_variant {
                                fonts.push((*cached_font_ref).clone());
                                cache_hit = true;
                                break;
                            }
                        }
                    }
                }
            }

            if !cache_hit {
                let template_info = self.font_cache_thread.find_font_template(family.clone(),
                                                                             desc.clone());
                match template_info {
                    Some(template_info) => {
                        let layout_font = self.create_layout_font(template_info.font_template,
                                                                  desc.clone(),
                                                                  style.font_size,
                                                                  style.font_variant,
                                                                  template_info.font_key);
                        let font = match layout_font {
                            Ok(layout_font) => {
                                let layout_font = Rc::new(RefCell::new(layout_font));
                                fonts.push(layout_font.clone());

                                Some(layout_font)
                            }
                            Err(_) => None
                        };

                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: font
                        });
                    }
                    None => {
                        self.layout_font_cache.push(LayoutFontCacheEntry {
                            family: family.name().to_owned(),
                            font: None,
                        });
                    }
                }
            }
        }

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

            if !cache_hit {
                let template_info = self.font_cache_thread.last_resort_font_template(desc.clone());
                let layout_font = self.create_layout_font(template_info.font_template,
                                                          desc.clone(),
                                                          style.font_size,
                                                          style.font_variant,
                                                          template_info.font_key);
                match layout_font {
                    Ok(layout_font) => {
                        let layout_font = Rc::new(RefCell::new(layout_font));
                        self.fallback_font_cache.push(FallbackFontCacheEntry {
                            font: layout_font.clone(),
                        });
                        fonts.push(layout_font);
                    }
                    Err(_) => debug!("Failed to create fallback layout font!")
                }
            }
        }

        let font_group = Rc::new(FontGroup::new(fonts));
        self.layout_font_group_cache.insert(layout_font_group_cache_key, font_group.clone());
        font_group
    }

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

        let paint_font = Rc::new(RefCell::new(create_scaled_font(template, pt_size)));
        self.paint_font_cache.push(PaintFontCacheEntry {
            font: paint_font.clone(),
            pt_size: pt_size,
            identifier: template.identifier.clone(),
        });
        paint_font
    }

    /// Returns a reference to the font cache thread.
    pub fn font_cache_thread(&self) -> FontCacheThread {
        self.font_cache_thread.clone()
    }
}

impl HeapSizeOf for FontContext {
    fn heap_size_of_children(&self) -> usize {
        // FIXME(njn): Measure other fields eventually.
        self.platform_handle.heap_size_of_children()
    }
}

#[derive(Debug)]
struct LayoutFontGroupCacheKey {
    pointer: Arc<ServoFont>,
    size: Au,
}

impl PartialEq for LayoutFontGroupCacheKey {
    fn eq(&self, other: &LayoutFontGroupCacheKey) -> bool {
        self.pointer == other.pointer && self.size == other.size
    }
}

impl Eq for LayoutFontGroupCacheKey {}

impl Hash for LayoutFontGroupCacheKey {
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
        self.pointer.hash.hash(hasher)
    }
}

#[inline]
pub fn invalidate_font_caches() {
    FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
}
<|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/. */

//! A task that sniffs data
use std::comm::{channel, Receiver, Sender, Disconnected};
use std::task::TaskBuilder;
use resource_task::{LoadResponse};

pub type SnifferTask = Sender<LoadResponse>;

pub fn new_sniffer_task(next_rx: Sender<LoadResponse>) -> SnifferTask {
    let (sen, rec) = channel();
    let builder = TaskBuilder::new().named("SnifferManager");
    builder.spawn(proc() {
        SnifferManager::new(rec).start(next_rx);
    });
    sen
}

struct SnifferManager {
    data_receiver: Receiver<LoadResponse>,
}

impl SnifferManager {
    fn new(data_receiver: Receiver<LoadResponse>) -> SnifferManager {
        SnifferManager {
            data_receiver: data_receiver,
        }
    }
}

impl SnifferManager {
    fn start(&self, next_rx: Sender<LoadResponse>) {
        loop {
            match self.data_receiver.try_recv() {
                Ok(snif_data) => next_rx.send(snif_data),
                Err(Disconnected) => break,
                Err(_) => (),
            }
        }
    }
}
<commit_msg>Break out of loop if can't send data<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/. */

//! A task that sniffs data
use std::comm::{channel, Receiver, Sender, Disconnected};
use std::task::TaskBuilder;
use resource_task::{LoadResponse};

pub type SnifferTask = Sender<LoadResponse>;

pub fn new_sniffer_task(next_rx: Sender<LoadResponse>) -> SnifferTask {
    let (sen, rec) = channel();
    let builder = TaskBuilder::new().named("SnifferManager");
    builder.spawn(proc() {
        SnifferManager::new(rec).start(next_rx);
    });
    sen
}

struct SnifferManager {
    data_receiver: Receiver<LoadResponse>,
}

impl SnifferManager {
    fn new(data_receiver: Receiver<LoadResponse>) -> SnifferManager {
        SnifferManager {
            data_receiver: data_receiver,
        }
    }
}

impl SnifferManager {
    fn start(self, next_rx: Sender<LoadResponse>) {
        loop {
            match self.data_receiver.try_recv() {
                Ok(snif_data) => {
                    let result = next_rx.send_opt(snif_data);
                    if result.is_err() {
                        break;
                    }
                }
                Err(Disconnected) => break,
                Err(_) => (),
            }
        }
    }
}
<|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/. */

//! Implements sequential traversal over the DOM tree.

use dom::{TElement, TNode};
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};

pub fn traverse_dom<N, D>(traversal: &D,
                          root: N::ConcreteElement,
                          token: PreTraverseToken)
    where N: TNode,
          D: DomTraversal<N>
{
    debug_assert!(token.should_traverse());

    fn doit<N, D>(traversal: &D, traversal_data: &mut PerLevelTraversalData,
                  thread_local: &mut D::ThreadLocalContext, node: N)
        where N: TNode,
              D: DomTraversal<N>
    {
        traversal.process_preorder(traversal_data, thread_local, node);
        if let Some(el) = node.as_element() {
            if let Some(ref mut depth) = traversal_data.current_dom_depth {
                *depth += 1;
            }

            D::traverse_children(el, |kid| doit(traversal, traversal_data, thread_local, kid));

            if let Some(ref mut depth) = traversal_data.current_dom_depth {
                *depth -= 1;
            }
        }

        if D::needs_postorder_traversal() {
            traversal.process_postorder(thread_local, node);
        }
    }

    let mut traversal_data = PerLevelTraversalData {
        current_dom_depth: None,
    };

    let mut tlc = traversal.create_thread_local_context();
    if token.traverse_unstyled_children_only() {
        for kid in root.as_node().children() {
            if kid.as_element().map_or(false, |el| el.get_data().is_none()) {
                doit(traversal, &mut traversal_data, &mut tlc, kid);
            }
        }
    } else {
        doit(traversal, &mut traversal_data, &mut tlc, root.as_node());
    }
}
<commit_msg>style: Do the proper thing for the sequential traversal.<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/. */

//! Implements sequential traversal over the DOM tree.

#![deny(missing_docs)]

use dom::{TElement, TNode};
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};

/// Do a sequential DOM traversal for layout or styling, generic over `D`.
pub fn traverse_dom<N, D>(traversal: &D,
                          root: N::ConcreteElement,
                          token: PreTraverseToken)
    where N: TNode,
          D: DomTraversal<N>,
{
    debug_assert!(token.should_traverse());

    fn doit<N, D>(traversal: &D, traversal_data: &mut PerLevelTraversalData,
                  thread_local: &mut D::ThreadLocalContext, node: N)
        where N: TNode,
              D: DomTraversal<N>
    {
        traversal.process_preorder(traversal_data, thread_local, node);
        if let Some(el) = node.as_element() {
            if let Some(ref mut depth) = traversal_data.current_dom_depth {
                *depth += 1;
            }

            D::traverse_children(el, |kid| doit(traversal, traversal_data, thread_local, kid));

            if let Some(ref mut depth) = traversal_data.current_dom_depth {
                *depth -= 1;
            }
        }

        if D::needs_postorder_traversal() {
            traversal.process_postorder(thread_local, node);
        }
    }

    let mut traversal_data = PerLevelTraversalData {
        current_dom_depth: None,
    };

    let mut tlc = traversal.create_thread_local_context();
    if token.traverse_unstyled_children_only() {
        for kid in root.as_node().children() {
            if kid.as_element().map_or(false, |el| el.get_data().is_none()) {
                doit(traversal, &mut traversal_data, &mut tlc, kid);
            }
        }
    } else {
        doit(traversal, &mut traversal_data, &mut tlc, root.as_node());
    }
}
<|endoftext|>


<commit_before>mod raft_log;
mod storage;
mod progress;
mod errors;
mod log_unstable;
<commit_msg>raft: export necessary unit for outer use.<commit_after>mod raft_log;
mod progress;
mod log_unstable;

pub mod storage;
pub mod errors;

pub use self::storage::{RaftState, Storage};

pub use self::errors::{Result, Error, StorageError};
<|endoftext|>


<commit_before>use crate::msg::actionlib_msgs::{GoalID, GoalStatusArray};
use crate::status_tracker::StatusTracker;
use crate::Action;
use std::sync::{Arc, Mutex};

pub struct ActionServer<T: Action> {
    result_pub: rosrust::Publisher<T::Result>,
    feedback_pub: rosrust::Publisher<T::Feedback>,
    goal_sub: rosrust::Subscriber,
    cancel_sub: rosrust::Subscriber,
    status_frequency: f64,
    status_list: Arc<Mutex<StatusList<T>>>,
    status_timer: std::thread::JoinHandle<()>,
}

fn decode_queue_size(param: &str, default: usize) -> usize {
    let param: Option<i32> = rosrust::param(param)
        .expect(UNEXPECTED_FAILED_NAME_RESOLVE)
        .get()
        .ok();
    match param {
        None => default,
        Some(v) if v < 0 => default,
        Some(v) => v as usize,
    }
}

fn get_status_frequency() -> Option<f64> {
    let key = rosrust::param("actionlib_status_frequency")
        .expect(UNEXPECTED_FAILED_NAME_RESOLVE)
        .search()
        .ok()?;
    rosrust::param(&key)?.get().ok()
}

fn create_status_publisher<F: Fn()>(frequency: f64, callback: F) -> impl Fn() {
    move || {
        let mut rate = rosrust::rate(frequency);
        rosrust::ros_debug!("Starting timer");
        while rosrust::is_ok() {
            rate.sleep();
            callback()
        }
    }
}

impl<T: Action> ActionServer<T> {
    pub fn new<Fg, Fc, Fs>(
        namespace: &str,
        on_goal: Fg,
        on_cancel: Fc,
    ) -> rosrust::error::Result<Self>
    where
        Fg: Fn(T::Goal) + Send + 'static,
        Fc: Fn(GoalID) + Send + 'static,
    {
        let pub_queue_size = decode_queue_size("actionlib_server_pub_queue_size", 50);
        let sub_queue_size = decode_queue_size("actionlib_server_sub_queue_size", 0);

        let status_pub = rosrust::publish(&format!("{}/status", namespace), pub_queue_size)?;
        let result_pub = rosrust::publish(&format!("{}/result", namespace), pub_queue_size)?;
        let feedback_pub = rosrust::publish(&format!("{}/feedback", namespace), pub_queue_size)?;

        let goal_sub = rosrust::subscribe(&format!("{}/goal", namespace), sub_queue_size, on_goal)?;
        let cancel_sub =
            rosrust::subscribe(&format!("{}/cancel", namespace), sub_queue_size, on_cancel)?;

        let status_frequency = get_status_frequency().unwrap_or(5.0);

        let status_list_timeout = rosrust::param(&format!("{}/status_list_timeout", namespace))
            .ok_or_else(|| "Bad actionlib namespace")?
            .get()
            .unwrap_or(5.0);
        let status_list_timeout =
            rosrust::Duration::from_nanos((status_list_timeout as i64) * 1_000_000_000);

        let status_list = Arc::new(Mutex::new(StatusList {
            publisher: status_pub,
            timeout: status_list_timeout,
            items: vec![],
        }));

        let on_status = {
            let status_list = Arc::clone(&status_list);
            move || {
                if let Err(err) = status_list.lock().expect("Failed to lock mutex").publish() {
                    rosrust::ros_err!("Failed to publish status: {}", err);
                }
            }
        };

        let status_timer = std::thread::spawn(create_status_publisher(status_frequency, on_status));

        Ok(Self {
            result_pub,
            feedback_pub,
            goal_sub,
            cancel_sub,
            status_frequency,
            status_list,
            status_timer,
        })
    }
}

struct StatusList<T> {
    publisher: rosrust::Publisher<GoalStatusArray>,
    timeout: rosrust::Duration,
    items: Vec<StatusTracker<T>>,
}

impl<T: Action> StatusList<T> {
    fn get_status_array(&mut self) -> GoalStatusArray {
        GoalStatusArray::default()
    }

    fn publish(&mut self) -> rosrust::error::Result<()> {
        let status_array = self.get_status_array();
        if !rosrust::is_ok() {
            return Ok(());
        }
        self.publisher.send(status_array)
    }
}

static UNEXPECTED_FAILED_NAME_RESOLVE: &str = "Resolving this parameter name should never fail";
<commit_msg>Group most handling logic<commit_after>use crate::msg::actionlib_msgs::{GoalID, GoalStatusArray};
use crate::status_tracker::StatusTracker;
use crate::Action;
use std::sync::{Arc, Mutex};

pub struct ActionServer<T: Action> {
    fields: Arc<Mutex<Fields<T>>>,
    _goal_sub: rosrust::Subscriber,
    _cancel_sub: rosrust::Subscriber,
    _status_timer: std::thread::JoinHandle<()>,
}

struct Fields<T: Action> {
    result_pub: rosrust::Publisher<T::Result>,
    feedback_pub: rosrust::Publisher<T::Feedback>,
    status_pub: rosrust::Publisher<GoalStatusArray>,
    status_list_timeout: i64,
    status_list: Vec<StatusTracker<T>>,
    status_frequency: f64,
    on_goal: Box<Fn(T::Goal) + Send + Sync>,
    on_cancel: Box<Fn(GoalID) + Send + Sync>,
}

fn decode_queue_size(param: &str, default: usize) -> usize {
    let param: Option<i32> = rosrust::param(param)
        .expect(UNEXPECTED_FAILED_NAME_RESOLVE)
        .get()
        .ok();
    match param {
        None => default,
        Some(v) if v < 0 => default,
        Some(v) => v as usize,
    }
}

fn get_status_frequency() -> Option<f64> {
    let key = rosrust::param("actionlib_status_frequency")
        .expect(UNEXPECTED_FAILED_NAME_RESOLVE)
        .search()
        .ok()?;
    rosrust::param(&key)?.get().ok()
}

fn create_status_publisher<F>(frequency: f64, callback: F) -> impl Fn()
where
    F: Fn() -> rosrust::error::Result<()>,
{
    move || {
        let mut rate = rosrust::rate(frequency);
        rosrust::ros_debug!("Starting timer");
        while rosrust::is_ok() {
            rate.sleep();
            if let Err(err) = callback() {
                rosrust::ros_err!("Failed to publish status: {}", err);
            }
        }
    }
}

impl<T: Action> ActionServer<T> {
    pub fn new(namespace: &str) -> rosrust::error::Result<Self> {
        let pub_queue_size = decode_queue_size("actionlib_server_pub_queue_size", 50);
        let sub_queue_size = decode_queue_size("actionlib_server_sub_queue_size", 0);

        let status_pub = rosrust::publish(&format!("{}/status", namespace), pub_queue_size)?;
        let result_pub = rosrust::publish(&format!("{}/result", namespace), pub_queue_size)?;
        let feedback_pub = rosrust::publish(&format!("{}/feedback", namespace), pub_queue_size)?;

        let status_frequency = get_status_frequency().unwrap_or(5.0);

        let status_list_timeout = rosrust::param(&format!("{}/status_list_timeout", namespace))
            .ok_or_else(|| "Bad actionlib namespace")?
            .get()
            .unwrap_or(5.0);
        let status_list_timeout = (status_list_timeout * 1_000_000_000.0) as i64;

        let fields = Arc::new(Mutex::new(Fields {
            result_pub,
            feedback_pub,
            status_pub,
            status_frequency,
            status_list: vec![],
            status_list_timeout,
            on_goal: Box::new(|_| {}),
            on_cancel: Box::new(|_| {}),
        }));

        let on_status = {
            let fields = Arc::clone(&fields);
            move || fields.lock().expect(MUTEX_LOCK_FAIL).publish_status()
        };

        let status_timer = std::thread::spawn(create_status_publisher(status_frequency, on_status));

        let internal_on_goal = {
            let fields = Arc::clone(&fields);
            move |goal| fields.lock().expect(MUTEX_LOCK_FAIL).handle_on_goal(goal)
        };

        let goal_sub = rosrust::subscribe(
            &format!("{}/goal", namespace),
            sub_queue_size,
            internal_on_goal,
        )?;

        let internal_on_cancel = {
            let fields = Arc::clone(&fields);
            move |goal_id| {
                fields
                    .lock()
                    .expect(MUTEX_LOCK_FAIL)
                    .handle_on_cancel(goal_id)
            }
        };

        let cancel_sub = rosrust::subscribe(
            &format!("{}/cancel", namespace),
            sub_queue_size,
            internal_on_cancel,
        )?;

        Ok(Self {
            fields,
            _goal_sub: goal_sub,
            _cancel_sub: cancel_sub,
            _status_timer: status_timer,
        })
    }

    pub fn set_on_goal<F>(&mut self, on_goal: F)
    where
        F: Fn(T::Goal) + Send + Sync + 'static,
    {
        self.fields.lock().expect(MUTEX_LOCK_FAIL).on_goal = Box::new(on_goal);
    }

    pub fn set_on_cancel<F>(&mut self, on_cancel: F)
    where
        F: Fn(GoalID) + Send + Sync + 'static,
    {
        self.fields.lock().expect(MUTEX_LOCK_FAIL).on_cancel = Box::new(on_cancel);
    }
}

impl<T: Action> Fields<T> {
    fn get_status_array(&mut self) -> GoalStatusArray {
        let now = rosrust::now();
        let now_nanos = now.nanos();
        let status_list_timeout = self.status_list_timeout;
        self.status_list.retain(|tracker| {
            let destruction_time = tracker.handle_destruction_time.nanos();
            if destruction_time == 0 {
                return true;
            }
            if destruction_time + status_list_timeout > now_nanos {
                return true;
            }
            rosrust::ros_debug!(
                "Item {} with destruction time of {} being removed from list.  Now = {}",
                tracker.status.goal_id.id,
                tracker.handle_destruction_time.seconds(),
                now.seconds()
            );
            return false;
        });
        let status_list = self
            .status_list
            .iter()
            .map(|tracker| tracker.status.clone())
            .collect();
        GoalStatusArray {
            header: Default::default(),
            status_list,
        }
    }

    fn publish_status(&mut self) -> rosrust::error::Result<()> {
        let status_array = self.get_status_array();
        if !rosrust::is_ok() {
            return Ok(());
        }
        self.status_pub.send(status_array)
    }

    fn handle_on_goal(&self, goal: T::Goal) {
        unimplemented!();
        (*self.on_goal)(goal);
    }

    fn handle_on_cancel(&self, goal_id: GoalID) {
        unimplemented!();
        (*self.on_cancel)(goal_id);
    }
}

static UNEXPECTED_FAILED_NAME_RESOLVE: &str = "Resolving this parameter name should never fail";
static MUTEX_LOCK_FAIL: &str = "Failed to lock mutex";
<|endoftext|>


<commit_before>// FIXME: add the support of transient objects

use any_cache::{Cache, HashCache};
use notify::{self, RecommendedWatcher, Watcher};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Class of types that can be loaded.
pub trait Load: Sized {
  /// Arguments passed at loading.
  type Args: Clone;

  /// Static string representing the name of the type (used in filesystem caching).
  const TY_STR: &'static str;

  fn load<P>(path: P, cache: &mut ResCache, args: Self::Args) -> Result<Self> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload: Load {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<T> Reload for T where T: Load<Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

pub type Result<T> = ::std::result::Result<T, LoadError>;

/// Resources are wrapped in this type.
#[derive(Debug)]
pub struct Res<T>(Rc<RefCell<T>>);

impl<T> Res<T> {
  pub fn new(resource: T) -> Self {
    Res(Rc::new(RefCell::new(resource)))
  }

  pub fn as_rc(&self) -> &Rc<RefCell<T>> {
    &self.0
  }
}

impl<T> Clone for Res<T> {
  fn clone(&self) -> Self {
    Res(self.0.clone())
  }
}

impl<T> Deref for Res<T> {
  type Target = RefCell<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T> From<Rc<RefCell<T>>> for Res<T> {
  fn from(rc: Rc<RefCell<T>>) -> Self {
    Res(rc)
  }
}

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource cache. Responsible for caching resource.
pub struct ResCache {
  // contains all the typed-erased Rc<RefCell<T>>
  cache: HashCache<PathBuf>,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // vector of pair (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

struct ResMetaData {
  on_reload: Box<Fn(&mut ResCache)>,
  last_update_instant: Instant
}

impl ResCache {
  /// Create a new cache.
  pub fn new<P>(root: P) -> Self where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let (wsx, wrx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(root);

      for event in wrx.iter() {
        if let notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } = event {
          dirty_.lock().unwrap().push((path.clone(), Instant::now()));
        }
      }
    });

    ResCache {
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    }
  }

  /// Inject a new resource in the cache.
  fn inject<T>(&mut self, path_buf: &PathBuf, resource: T, args: T::Args) -> Res<T> where T: 'static + Any + Reload {
    let res = Res(Rc::new(RefCell::new(resource)));
    let res_ = res.clone();

    let path_buf_ = path_buf.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut ResCache)> = Box::new(move |cache_| {
      match T::load(&path_buf_, cache_, args.clone()) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = new_resource;
          deb!("reloaded resource from {:?}", path_buf_);
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed:\n{:#?}", path_buf_, e);
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now()
    };

    // cache the resource and its meta data
    self.cache.save(path_buf.clone(), res.clone());
    self.metadata.insert(path_buf.clone(), metadata);

    res
  }

  /// Get a resource from the cache.
  pub fn get<T>(&mut self, key: &str, args: T::Args) -> Option<Res<T>> where T: 'static + Any + Reload {
    let path_str = format!("data/{}/{}", T::TY_STR, key);
    let path = Path::new(&path_str);
    let path_buf = path.to_owned();

    match self.cache.get::<Res<T>>(&path_buf).cloned() {
      r@Some(..) => {
        deb!("cache hit for {} ({})", key, path_str);
        r
      },
      None => {
        deb!("cache miss for {} ({})", key, path_str);

        // specific loading
        if path.exists() {
          match T::load(&path, self, args.clone()) {
            Ok(resource) => {
              Some(self.inject(&path_buf, resource, args))
            },
            Err(e) => {
              err!("unable to load resource from {}:\n{:#?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("resource at {} cannot be found", path_str);
          None
        }
      }
    }
  }

   /// Get a resource from the cache. If it fails, a proxy version is used, which will get replaced
   /// by the resource once it’s available.
   pub fn get_proxied<T, P>(&mut self, key: &str, args: T::Args, proxy: P) -> Option<Res<T>>
       where T: 'static + Any + Reload,
             P: FnOnce() -> T {
     // riterite
     self.get::<T>(key, args.clone()).or_else(move || {
       let path_str = format!("data/{}/{}", T::TY_STR, key);
       let path = Path::new(&path_str);
       let path_buf = path.to_owned();

       deb!("proxying resource {}", key);
       Some(self.inject(&path_buf, proxy(), args))
     })
   }

  /// Synchronize the cache by updating the resource that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let mut metadata = self.metadata.remove(path).unwrap();

      if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
        (metadata.on_reload)(self);
      }

      metadata.last_update_instant = *instant;
      self.metadata.insert(path.clone(), metadata);
    }

    dirty_.clear();
  }
}
<commit_msg>Formatting.<commit_after>// FIXME: add the support of transient objects

use any_cache::{Cache, HashCache};
use notify::{self, RecommendedWatcher, Watcher};
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Class of types that can be loaded.
pub trait Load: Sized {
  /// Arguments passed at loading.
  type Args: Clone;

  /// Static string representing the name of the type (used in filesystem caching).
  const TY_STR: &'static str;

  fn load<P>(path: P, cache: &mut ResCache, args: Self::Args) -> Result<Self> where P: AsRef<Path>;
}

/// Class of types that can be reloaded.
///
/// The idea is to simply recover the arguments used in `Load::load`.
pub trait Reload: Load {
  fn reload_args(&self) -> Self::Args;
}

/// Default implementation for types which are loaded without any arguments.
impl<T> Reload for T where T: Load<Args=()> {
  fn reload_args(&self) -> Self::Args {
    ()
  }
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  FileNotFound(PathBuf, String),
  ParseFailed(String),
  ConversionFailed(String)
}

pub type Result<T> = ::std::result::Result<T, LoadError>;

/// Resources are wrapped in this type.
#[derive(Debug)]
pub struct Res<T>(Rc<RefCell<T>>);

impl<T> Res<T> {
  pub fn new(resource: T) -> Self {
    Res(Rc::new(RefCell::new(resource)))
  }

  pub fn as_rc(&self) -> &Rc<RefCell<T>> {
    &self.0
  }
}

impl<T> Clone for Res<T> {
  fn clone(&self) -> Self {
    Res(self.0.clone())
  }
}

impl<T> Deref for Res<T> {
  type Target = RefCell<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T> From<Rc<RefCell<T>>> for Res<T> {
  fn from(rc: Rc<RefCell<T>>) -> Self {
    Res(rc)
  }
}

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource cache. Responsible for caching resource.
pub struct ResCache {
  // contains all the typed-erased Rc<RefCell<T>>
  cache: HashCache<PathBuf>,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // vector of pair (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

struct ResMetaData {
  on_reload: Box<Fn(&mut ResCache)>,
  last_update_instant: Instant
}

impl ResCache {
  /// Create a new cache.
  pub fn new<P>(root: P) -> Self where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let (wsx, wrx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(root);

      for event in wrx.iter() {
        if let notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } = event {
          dirty_.lock().unwrap().push((path.clone(), Instant::now()));
        }
      }
    });

    ResCache {
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    }
  }

  /// Inject a new resource in the cache.
  fn inject<T>(&mut self, path_buf: &PathBuf, resource: T, args: T::Args) -> Res<T> where T: 'static + Any + Reload {
    let res = Res(Rc::new(RefCell::new(resource)));
    let res_ = res.clone();

    let path_buf_ = path_buf.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut ResCache)> = Box::new(move |cache_| {
      match T::load(&path_buf_, cache_, args.clone()) {
        Ok(new_resource) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = new_resource;
          deb!("reloaded resource from {:?}", path_buf_);
        },
        Err(e) => {
          warn!("reloading resource from {:?} has failed:\n{:#?}", path_buf_, e);
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now()
    };

    // cache the resource and its meta data
    self.cache.save(path_buf.clone(), res.clone());
    self.metadata.insert(path_buf.clone(), metadata);

    res
  }

  /// Get a resource from the cache.
  pub fn get<T>(&mut self, key: &str, args: T::Args) -> Option<Res<T>> where T: 'static + Any + Reload {
    let path_str = format!("data/{}/{}", T::TY_STR, key);
    let path = Path::new(&path_str);
    let path_buf = path.to_owned();

    match self.cache.get::<Res<T>>(&path_buf).cloned() {
      r@Some(..) => {
        deb!("cache hit for {} ({})", key, path_str);
        r
      },
      None => {
        deb!("cache miss for {} ({})", key, path_str);

        // specific loading
        if path.exists() {
          match T::load(&path, self, args.clone()) {
            Ok(resource) => {
              Some(self.inject(&path_buf, resource, args))
            },
            Err(e) => {
              err!("unable to load resource from {}:\n{:#?}", path_str, e);
              None
            }
          }
        } else { // path doesn’t exist
          err!("resource at {} cannot be found", path_str);
          None
        }
      }
    }
  }

  /// Get a resource from the cache. If it fails, a proxy version is used, which will get replaced
  /// by the resource once it’s available.
  pub fn get_proxied<T, P>(&mut self, key: &str, args: T::Args, proxy: P) -> Option<Res<T>>
      where T: 'static + Any + Reload,
            P: FnOnce() -> T {
    self.get::<T>(key, args.clone()).or_else(move || {
      let path_str = format!("data/{}/{}", T::TY_STR, key);
      let path = Path::new(&path_str);
      let path_buf = path.to_owned();

      deb!("proxying resource {}", key);
      Some(self.inject(&path_buf, proxy(), args))
    })
  }

  /// Synchronize the cache by updating the resource that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let mut metadata = self.metadata.remove(path).unwrap();

      if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
        (metadata.on_reload)(self);
      }

      metadata.last_update_instant = *instant;
      self.metadata.insert(path.clone(), metadata);
    }

    dirty_.clear();
  }
}
<|endoftext|>


<commit_before>use std::mem;
use std::fmt;
use std::io::{self, Read};
use std::net::SocketAddr;
use std::time::Duration;
use std::borrow::Cow;

use encoding_rs::{Encoding, UTF_8};
use futures::{Async, Poll, Stream};
use http;
use mime::Mime;
use serde::de::DeserializeOwned;
use serde_json;

use cookie;
use client::KeepCoreThreadAlive;
use hyper::header::HeaderMap;
use {async_impl, StatusCode, Url, Version, wait};

/// A Response to a submitted `Request`.
pub struct Response {
    inner: async_impl::Response,
    body: async_impl::ReadableChunks<WaitBody>,
    content_length: Option<u64>,
    _thread_handle: KeepCoreThreadAlive,
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl Response {
    pub(crate) fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response {
        let body = mem::replace(res.body_mut(), async_impl::Decoder::empty());
        let len = body.content_length();
        let body = async_impl::ReadableChunks::new(WaitBody {
            inner: wait::stream(body, timeout)
        });

        Response {
            inner: res,
            body: body,
            content_length: len,
            _thread_handle: thread,
        }
    }

    /// Get the `StatusCode` of this `Response`.
    ///
    /// # Examples
    ///
    /// Checking for general status class:
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/get")?;
    /// if resp.status().is_success() {
    ///     println!("success!");
    /// } else if resp.status().is_server_error() {
    ///     println!("server error!");
    /// } else {
    ///     println!("Something else happened. Status: {:?}", resp.status());
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Checking for specific status codes:
    ///
    /// ```rust
    /// use reqwest::Client;
    /// use reqwest::StatusCode;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    ///
    /// let resp = client.post("http://httpbin.org/post")
    ///     .body("possibly too large")
    ///     .send()?;
    ///
    /// match resp.status() {
    ///     StatusCode::OK => println!("success!"),
    ///     StatusCode::PAYLOAD_TOO_LARGE => {
    ///         println!("Request payload is too large!");
    ///     }
    ///     s => println!("Received response status: {:?}", s),
    /// };
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn status(&self) -> StatusCode {
        self.inner.status()
    }

    /// Get the `Headers` of this `Response`.
    ///
    /// # Example
    ///
    /// Saving an etag when caching a file:
    ///
    /// ```
    /// use reqwest::Client;
    /// use reqwest::header::ETAG;
    ///
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    ///
    /// let mut resp = client.get("http://httpbin.org/cache").send()?;
    /// if resp.status().is_success() {
    ///     if let Some(etag) = resp.headers().get(ETAG) {
    ///         std::fs::write("etag", etag.as_bytes());
    ///     }
    ///     let mut file = std::fs::File::create("file")?;
    ///     resp.copy_to(&mut file)?;
    /// }
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn headers(&self) -> &HeaderMap {
        self.inner.headers()
    }

    /// Retrieve the cookies contained in the response.
    ///
    /// Note that invalid 'Set-Cookie' headers will be ignored.
    pub fn cookies<'a>(&'a self) -> impl Iterator< Item = cookie::Cookie<'a> > + 'a {
        cookie::extract_response_cookies(self.headers())
            .filter_map(Result::ok)
    }

    /// Get the HTTP `Version` of this `Response`.
    #[inline]
    pub fn version(&self) -> Version {
        self.inner.version()
    }

    /// Get the final `Url` of this `Response`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
    /// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn url(&self) -> &Url {
        self.inner.url()
    }

    /// Get the remote address used to get this `Response`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
    /// println!("httpbin.org address: {:?}", resp.remote_addr());
    /// # Ok(())
    /// # }
    /// ```
    pub fn remote_addr(&self) -> Option<SocketAddr> {
        self.inner.remote_addr()
    }

    /// Get the content-length of the response, if it is known.
    ///
    /// Reasons it may not be known:
    ///
    /// - The server didn't send a `content-length` header.
    /// - The response is gzipped and automatically decoded (thus changing
    ///   the actual decoded length).
    pub fn content_length(&self) -> Option<u64> {
        self.inner.content_length()
    }

    /// Try and deserialize the response body as JSON using `serde`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # #[macro_use] extern crate serde_derive;
    /// #
    /// # use reqwest::Error;
    /// #
    /// #[derive(Deserialize)]
    /// struct Ip {
    ///     origin: String,
    /// }
    ///
    /// # fn run() -> Result<(), Error> {
    /// let json: Ip = reqwest::get("http://httpbin.org/ip")?.json()?;
    /// # Ok(())
    /// # }
    /// #
    /// # fn main() { }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails whenever the response body is not in JSON format
    /// or it cannot be properly deserialized to target type `T`. For more
    /// details please see [`serde_json::from_reader`].
    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
    #[inline]
    pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
        // There's 2 ways we could implement this:
        //
        // 1. Just using from_reader(self), making use of our blocking read adapter
        // 2. Just use self.inner.json().wait()
        //
        // Doing 1 is pretty easy, but it means we have the `serde_json` code
        // in more than one place, doing basically the same thing.
        //
        // Doing 2 would mean `serde_json` is only in one place, but we'd
        // need to update the sync Response to lazily make a blocking read
        // adapter, so that our `inner` could possibly still have the original
        // body.
        //
        // Went for easier for now, just to get it working.
        serde_json::from_reader(self).map_err(::error::from)
    }

    /// Get the response text.
    ///
    /// This method decodes the response body with BOM sniffing
    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
    /// Encoding is determinated from the `charset` parameter of `Content-Type` header,
    /// and defaults to `utf-8` if not presented.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let content = reqwest::get("http://httpbin.org/range/26")?.text()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Note
    ///
    /// This consumes the body. Trying to read more, or use of `response.json()`
    /// will return empty values.
    pub fn text(&mut self) -> ::Result<String> {
        let len = self.content_length.unwrap_or(0);
        let mut content = Vec::with_capacity(len as usize);
        self.read_to_end(&mut content).map_err(::error::from)?;
        let content_type = self.headers().get(::header::CONTENT_TYPE)
            .and_then(|value| {
                value.to_str().ok()
            })
            .and_then(|value| {
                value.parse::<Mime>().ok()
            });
        let encoding_name = content_type
            .as_ref()
            .and_then(|mime| {
                mime
                    .get_param("charset")
                    .map(|charset| charset.as_str())
            })
            .unwrap_or("utf-8");
        let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
        // a block because of borrow checker
        {
            let (text, _, _) = encoding.decode(&content);
            match text {
                Cow::Owned(s) => return Ok(s),
                _ => (),
            }
        }
        unsafe {
            // decoding returned Cow::Borrowed, meaning these bytes
            // are already valid utf8
            Ok(String::from_utf8_unchecked(content))
        }
    }

    /// Copy the response body into a writer.
    ///
    /// This function internally uses [`std::io::copy`] and hence will continuously read data from
    /// the body and then write it into writer in a streaming fashion until EOF is met.
    ///
    /// On success, the total number of bytes that were copied to `writer` is returned.
    ///
    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let mut resp = reqwest::get("http://httpbin.org/range/5")?;
    /// let mut buf: Vec<u8> = vec![];
    /// resp.copy_to(&mut buf)?;
    /// assert_eq!(b"abcde", buf.as_slice());
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> ::Result<u64>
        where W: io::Write
    {
        io::copy(self, w).map_err(::error::from)
    }

    /// Turn a response into an error if the server returned an error.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let res = reqwest::get("http://httpbin.org/status/400")?
    ///     .error_for_status();
    /// if let Err(err) = res {
    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
    /// }
    /// # Ok(())
    /// # }
    /// # fn main() {}
    /// ```
    #[inline]
    pub fn error_for_status(self) -> ::Result<Self> {
        let Response { body, content_length, inner, _thread_handle } = self;
        inner.error_for_status().map(move |inner| {
            Response {
                inner,
                body,
                content_length,
                _thread_handle,
            }
        })
    }

    /// Turn a reference to a response into an error if the server returned an error.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let res = reqwest::get("http://httpbin.org/status/400")?;
    /// let res = res.error_for_status_ref();
    /// if let Err(err) = res {
    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
    /// }
    /// # Ok(())
    /// # }
    /// # fn main() {}
    /// ```
    #[inline]
    pub fn error_for_status_ref(&self) -> ::Result<&Self> {
        self.inner.error_for_status_ref().and_then(|_| Ok(self))
    }
}

impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.body.read(buf)
    }
}

struct WaitBody {
    inner: wait::WaitStream<async_impl::Decoder>
}

impl Stream for WaitBody {
    type Item = <async_impl::Decoder as Stream>::Item;
    type Error = <async_impl::Decoder as Stream>::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match self.inner.next() {
            Some(Ok(chunk)) => Ok(Async::Ready(Some(chunk))),
            Some(Err(e)) => {
                let req_err = match e {
                    wait::Waited::TimedOut => ::error::timedout(None),
                    wait::Waited::Err(e) => e,
                };

                Err(req_err)
            },
            None => Ok(Async::Ready(None)),
        }
    }
}

impl<T: Into<async_impl::body::Body>> From<http::Response<T>> for Response {
    fn from(r: http::Response<T>) -> Response {
        let response = async_impl::Response::from(r);
        Response::new(response, None, KeepCoreThreadAlive::empty())
    }
}
<commit_msg>Allow user to specify a default encoding when reading Response to text (#511)<commit_after>use std::mem;
use std::fmt;
use std::io::{self, Read};
use std::net::SocketAddr;
use std::time::Duration;
use std::borrow::Cow;

use encoding_rs::{Encoding, UTF_8};
use futures::{Async, Poll, Stream};
use http;
use mime::Mime;
use serde::de::DeserializeOwned;
use serde_json;

use cookie;
use client::KeepCoreThreadAlive;
use hyper::header::HeaderMap;
use {async_impl, StatusCode, Url, Version, wait};

/// A Response to a submitted `Request`.
pub struct Response {
    inner: async_impl::Response,
    body: async_impl::ReadableChunks<WaitBody>,
    content_length: Option<u64>,
    _thread_handle: KeepCoreThreadAlive,
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl Response {
    pub(crate) fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response {
        let body = mem::replace(res.body_mut(), async_impl::Decoder::empty());
        let len = body.content_length();
        let body = async_impl::ReadableChunks::new(WaitBody {
            inner: wait::stream(body, timeout)
        });

        Response {
            inner: res,
            body: body,
            content_length: len,
            _thread_handle: thread,
        }
    }

    /// Get the `StatusCode` of this `Response`.
    ///
    /// # Examples
    ///
    /// Checking for general status class:
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/get")?;
    /// if resp.status().is_success() {
    ///     println!("success!");
    /// } else if resp.status().is_server_error() {
    ///     println!("server error!");
    /// } else {
    ///     println!("Something else happened. Status: {:?}", resp.status());
    /// }
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Checking for specific status codes:
    ///
    /// ```rust
    /// use reqwest::Client;
    /// use reqwest::StatusCode;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    ///
    /// let resp = client.post("http://httpbin.org/post")
    ///     .body("possibly too large")
    ///     .send()?;
    ///
    /// match resp.status() {
    ///     StatusCode::OK => println!("success!"),
    ///     StatusCode::PAYLOAD_TOO_LARGE => {
    ///         println!("Request payload is too large!");
    ///     }
    ///     s => println!("Received response status: {:?}", s),
    /// };
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn status(&self) -> StatusCode {
        self.inner.status()
    }

    /// Get the `Headers` of this `Response`.
    ///
    /// # Example
    ///
    /// Saving an etag when caching a file:
    ///
    /// ```
    /// use reqwest::Client;
    /// use reqwest::header::ETAG;
    ///
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let client = Client::new();
    ///
    /// let mut resp = client.get("http://httpbin.org/cache").send()?;
    /// if resp.status().is_success() {
    ///     if let Some(etag) = resp.headers().get(ETAG) {
    ///         std::fs::write("etag", etag.as_bytes());
    ///     }
    ///     let mut file = std::fs::File::create("file")?;
    ///     resp.copy_to(&mut file)?;
    /// }
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn headers(&self) -> &HeaderMap {
        self.inner.headers()
    }

    /// Retrieve the cookies contained in the response.
    ///
    /// Note that invalid 'Set-Cookie' headers will be ignored.
    pub fn cookies<'a>(&'a self) -> impl Iterator< Item = cookie::Cookie<'a> > + 'a {
        cookie::extract_response_cookies(self.headers())
            .filter_map(Result::ok)
    }

    /// Get the HTTP `Version` of this `Response`.
    #[inline]
    pub fn version(&self) -> Version {
        self.inner.version()
    }

    /// Get the final `Url` of this `Response`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
    /// assert_eq!(resp.url().as_str(), "http://httpbin.org/get");
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn url(&self) -> &Url {
        self.inner.url()
    }

    /// Get the remote address used to get this `Response`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let resp = reqwest::get("http://httpbin.org/redirect/1")?;
    /// println!("httpbin.org address: {:?}", resp.remote_addr());
    /// # Ok(())
    /// # }
    /// ```
    pub fn remote_addr(&self) -> Option<SocketAddr> {
        self.inner.remote_addr()
    }

    /// Get the content-length of the response, if it is known.
    ///
    /// Reasons it may not be known:
    ///
    /// - The server didn't send a `content-length` header.
    /// - The response is gzipped and automatically decoded (thus changing
    ///   the actual decoded length).
    pub fn content_length(&self) -> Option<u64> {
        self.inner.content_length()
    }

    /// Try and deserialize the response body as JSON using `serde`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # #[macro_use] extern crate serde_derive;
    /// #
    /// # use reqwest::Error;
    /// #
    /// #[derive(Deserialize)]
    /// struct Ip {
    ///     origin: String,
    /// }
    ///
    /// # fn run() -> Result<(), Error> {
    /// let json: Ip = reqwest::get("http://httpbin.org/ip")?.json()?;
    /// # Ok(())
    /// # }
    /// #
    /// # fn main() { }
    /// ```
    ///
    /// # Errors
    ///
    /// This method fails whenever the response body is not in JSON format
    /// or it cannot be properly deserialized to target type `T`. For more
    /// details please see [`serde_json::from_reader`].
    /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html
    #[inline]
    pub fn json<T: DeserializeOwned>(&mut self) -> ::Result<T> {
        // There's 2 ways we could implement this:
        //
        // 1. Just using from_reader(self), making use of our blocking read adapter
        // 2. Just use self.inner.json().wait()
        //
        // Doing 1 is pretty easy, but it means we have the `serde_json` code
        // in more than one place, doing basically the same thing.
        //
        // Doing 2 would mean `serde_json` is only in one place, but we'd
        // need to update the sync Response to lazily make a blocking read
        // adapter, so that our `inner` could possibly still have the original
        // body.
        //
        // Went for easier for now, just to get it working.
        serde_json::from_reader(self).map_err(::error::from)
    }

    /// Get the response text.
    ///
    /// This method decodes the response body with BOM sniffing
    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
    /// Encoding is determinated from the `charset` parameter of `Content-Type` header,
    /// and defaults to `utf-8` if not presented.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let content = reqwest::get("http://httpbin.org/range/26")?.text()?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Note
    ///
    /// This consumes the body. Trying to read more, or use of `response.json()`
    /// will return empty values.
    pub fn text(&mut self) -> ::Result<String> {
        self.text_with_charset("utf-8")
    }

    /// Get the response text given a specific encoding.
    ///
    /// This method decodes the response body with BOM sniffing
    /// and with malformed sequences replaced with the REPLACEMENT CHARACTER.
    /// You can provide a default encoding for decoding the raw message, while the
    /// `charset` parameter of `Content-Type` header is still prioritized. For more information
    /// about the possible encoding name, please go to
    /// https://docs.rs/encoding_rs/0.8.17/encoding_rs/#relationship-with-windows-code-pages
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let content = reqwest::get("http://httpbin.org/range/26")?.text_with_charset("utf-8")?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// # Note
    ///
    /// This consumes the body. Trying to read more, or use of `response.json()`
    /// will return empty values.
    pub fn text_with_charset(&mut self, default_encoding: &str) -> ::Result<String> {
        let len = self.content_length.unwrap_or(0);
        let mut content = Vec::with_capacity(len as usize);
        self.read_to_end(&mut content).map_err(::error::from)?;
        let content_type = self.headers().get(::header::CONTENT_TYPE)
            .and_then(|value| {
                value.to_str().ok()
            })
            .and_then(|value| {
                value.parse::<Mime>().ok()
            });
        let encoding_name = content_type
            .as_ref()
            .and_then(|mime| {
                mime
                    .get_param("charset")
                    .map(|charset| charset.as_str())
            })
            .unwrap_or(default_encoding);
        let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
        // a block because of borrow checker
        {
            let (text, _, _) = encoding.decode(&content);
            match text {
                Cow::Owned(s) => return Ok(s),
                _ => (),
            }
        }
        unsafe {
            // decoding returned Cow::Borrowed, meaning these bytes
            // are already valid utf8
            Ok(String::from_utf8_unchecked(content))
        }
    }

    /// Copy the response body into a writer.
    ///
    /// This function internally uses [`std::io::copy`] and hence will continuously read data from
    /// the body and then write it into writer in a streaming fashion until EOF is met.
    ///
    /// On success, the total number of bytes that were copied to `writer` is returned.
    ///
    /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
    ///
    /// # Example
    ///
    /// ```rust
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let mut resp = reqwest::get("http://httpbin.org/range/5")?;
    /// let mut buf: Vec<u8> = vec![];
    /// resp.copy_to(&mut buf)?;
    /// assert_eq!(b"abcde", buf.as_slice());
    /// # Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> ::Result<u64>
        where W: io::Write
    {
        io::copy(self, w).map_err(::error::from)
    }

    /// Turn a response into an error if the server returned an error.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let res = reqwest::get("http://httpbin.org/status/400")?
    ///     .error_for_status();
    /// if let Err(err) = res {
    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
    /// }
    /// # Ok(())
    /// # }
    /// # fn main() {}
    /// ```
    #[inline]
    pub fn error_for_status(self) -> ::Result<Self> {
        let Response { body, content_length, inner, _thread_handle } = self;
        inner.error_for_status().map(move |inner| {
            Response {
                inner,
                body,
                content_length,
                _thread_handle,
            }
        })
    }

    /// Turn a reference to a response into an error if the server returned an error.
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # extern crate reqwest;
    /// # fn run() -> Result<(), Box<::std::error::Error>> {
    /// let res = reqwest::get("http://httpbin.org/status/400")?;
    /// let res = res.error_for_status_ref();
    /// if let Err(err) = res {
    ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
    /// }
    /// # Ok(())
    /// # }
    /// # fn main() {}
    /// ```
    #[inline]
    pub fn error_for_status_ref(&self) -> ::Result<&Self> {
        self.inner.error_for_status_ref().and_then(|_| Ok(self))
    }
}

impl Read for Response {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.body.read(buf)
    }
}

struct WaitBody {
    inner: wait::WaitStream<async_impl::Decoder>
}

impl Stream for WaitBody {
    type Item = <async_impl::Decoder as Stream>::Item;
    type Error = <async_impl::Decoder as Stream>::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        match self.inner.next() {
            Some(Ok(chunk)) => Ok(Async::Ready(Some(chunk))),
            Some(Err(e)) => {
                let req_err = match e {
                    wait::Waited::TimedOut => ::error::timedout(None),
                    wait::Waited::Err(e) => e,
                };

                Err(req_err)
            },
            None => Ok(Async::Ready(None)),
        }
    }
}

impl<T: Into<async_impl::body::Body>> From<http::Response<T>> for Response {
    fn from(r: http::Response<T>) -> Response {
        let response = async_impl::Response::from(r);
        Response::new(response, None, KeepCoreThreadAlive::empty())
    }
}
<|endoftext|>