<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 euclid::Point3D;
use geometry::ray_intersects_rect;
use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
use prim_store::GpuBlock32;
use renderer::VertexDataStore;
use spring::{DAMPING, STIFFNESS, Spring};
use tiling::PackedLayerIndex;
use util::TransformedRectKind;
use webrender_traits::{ClipId, ClipRegion, LayerPixel, LayerPoint, LayerRect, LayerSize};
use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, PipelineId};
use webrender_traits::{ScrollEventPhase, ScrollLayerRect, ScrollLocation, WorldPoint};
use webrender_traits::{DeviceIntRect, WorldPoint4D};

#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;

#[cfg(not(target_os = "macos"))]
const CAN_OVERSCROLL: bool = false;

#[derive(Clone, Debug)]
pub struct ClipInfo {
    /// The ClipSource for this node, which is used to generate mask_cache_info.
    pub clip_sources: Vec<ClipSource>,

    /// The MaskCacheInfo for this node, which is produced by processing the
    /// provided ClipSource.
    pub mask_cache_info: Option<MaskCacheInfo>,

    /// The packed layer index for this node, which is used to render a clip mask
    /// for it, if necessary.
    pub packed_layer_index: PackedLayerIndex,

    /// The final transformed rectangle of this clipping region for this node,
    /// which depends on the screen rectangle and the transformation of all of
    /// the parents.
    pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
}

impl ClipInfo {
    pub fn new(clip_region: &ClipRegion,
               clip_store: &mut VertexDataStore<GpuBlock32>,
               packed_layer_index: PackedLayerIndex,)
               -> ClipInfo {
        // We pass true here for the MaskCacheInfo because this type of
        // mask needs an extra clip for the clip rectangle.
        let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
        ClipInfo {
            mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
            clip_sources: clip_sources,
            packed_layer_index: packed_layer_index,
            screen_bounding_rect: None,
        }
    }

    pub fn is_masking(&self) -> bool {
        match self.mask_cache_info {
            Some(ref info) => info.is_masking(),
            _ => false,
        }
    }

}
#[derive(Clone, Debug)]
pub enum NodeType {
    /// Transform for this layer, relative to parent reference frame. A reference
    /// frame establishes a new coordinate space in the tree.
    ReferenceFrame(LayerToScrollTransform),

    /// Other nodes just do clipping, but no transformation.
    Clip(ClipInfo),
}

/// Contains scrolling and transform information stacking contexts.
#[derive(Clone, Debug)]
pub struct ClipScrollNode {
    /// Manages scrolling offset, overscroll state etc.
    pub scrolling: ScrollingState,

    /// Size of the content inside the scroll region (in logical pixels)
    pub content_size: LayerSize,

    /// Viewing rectangle in the coordinate system of the parent reference frame.
    pub local_viewport_rect: LayerRect,

    /// Clip rect of this node - typically the same as viewport rect, except
    /// in overscroll cases.
    pub local_clip_rect: LayerRect,

    /// Viewport rectangle clipped against parent layer(s) viewport rectangles.
    /// This is in the coordinate system of the parent reference frame.
    pub combined_local_viewport_rect: LayerRect,

    /// World transform for the viewport rect itself. This is the parent
    /// reference frame transformation plus the scrolling offsets provided by
    /// the nodes in between the reference frame and this node.
    pub world_viewport_transform: LayerToWorldTransform,

    /// World transform for content transformed by this node.
    pub world_content_transform: LayerToWorldTransform,

    /// Pipeline that this layer belongs to
    pub pipeline_id: PipelineId,

    /// Parent layer. If this is None, we are the root node.
    pub parent: Option<ClipId>,

    /// Child layers
    pub children: Vec<ClipId>,

    /// Whether or not this node is a reference frame.
    pub node_type: NodeType,
}

impl ClipScrollNode {
    pub fn new(pipeline_id: PipelineId,
               parent_id: ClipId,
               content_rect: &LayerRect,
               clip_rect: &LayerRect,
               clip_info: ClipInfo)
               -> ClipScrollNode {
        // FIXME(mrobinson): We don't yet handle clipping rectangles that don't start at the origin
        // of the node.
        let local_viewport_rect = LayerRect::new(content_rect.origin, clip_rect.size);
        ClipScrollNode {
            scrolling: ScrollingState::new(),
            content_size: content_rect.size,
            local_viewport_rect: local_viewport_rect,
            local_clip_rect: local_viewport_rect,
            combined_local_viewport_rect: LayerRect::zero(),
            world_viewport_transform: LayerToWorldTransform::identity(),
            world_content_transform: LayerToWorldTransform::identity(),
            parent: Some(parent_id),
            children: Vec::new(),
            pipeline_id: pipeline_id,
            node_type: NodeType::Clip(clip_info),
        }
    }

    pub fn new_reference_frame(parent_id: Option<ClipId>,
                               local_viewport_rect: &LayerRect,
                               content_size: LayerSize,
                               local_transform: &LayerToScrollTransform,
                               pipeline_id: PipelineId)
                               -> ClipScrollNode {
        ClipScrollNode {
            scrolling: ScrollingState::new(),
            content_size: content_size,
            local_viewport_rect: *local_viewport_rect,
            local_clip_rect: *local_viewport_rect,
            combined_local_viewport_rect: LayerRect::zero(),
            world_viewport_transform: LayerToWorldTransform::identity(),
            world_content_transform: LayerToWorldTransform::identity(),
            parent: parent_id,
            children: Vec::new(),
            pipeline_id: pipeline_id,
            node_type: NodeType::ReferenceFrame(*local_transform),
        }
    }

    pub fn add_child(&mut self, child: ClipId) {
        self.children.push(child);
    }

    pub fn finalize(&mut self, scrolling: &ScrollingState) {
        self.scrolling = *scrolling;
    }

    pub fn overscroll_amount(&self) -> LayerSize {
        let scrollable_width = self.scrollable_width();
        let overscroll_x = if self.scrolling.offset.x > 0.0 {
            -self.scrolling.offset.x
        } else if self.scrolling.offset.x < -scrollable_width {
            -scrollable_width - self.scrolling.offset.x
        } else {
            0.0
        };

        let scrollable_height = self.scrollable_height();
        let overscroll_y = if self.scrolling.offset.y > 0.0 {
            -self.scrolling.offset.y
        } else if self.scrolling.offset.y < -scrollable_height {
            -scrollable_height - self.scrolling.offset.y
        } else {
            0.0
        };

        LayerSize::new(overscroll_x, overscroll_y)
    }

    pub fn set_scroll_origin(&mut self, origin: &LayerPoint) -> bool {
        match self.node_type {
            NodeType::ReferenceFrame(_) => {
                warn!("Tried to scroll a reference frame.");
                return false;
            }
            NodeType::Clip(_) => {}
        };

        let scrollable_height = self.scrollable_height();
        let scrollable_width = self.scrollable_width();
        if scrollable_height <= 0. && scrollable_width <= 0. {
            return false;
        }

        let new_offset = LayerPoint::new((-origin.x).max(-scrollable_width).min(0.0).round(),
                                         (-origin.y).max(-scrollable_height).min(0.0).round());
        if new_offset == self.scrolling.offset {
            return false;
        }

        self.scrolling.offset = new_offset;
        self.scrolling.bouncing_back = false;
        self.scrolling.started_bouncing_back = false;
        true
    }

    pub fn update_transform(&mut self,
                            parent_reference_frame_transform: &LayerToWorldTransform,
                            parent_combined_viewport_rect: &ScrollLayerRect,
                            parent_scroll_offset: LayerPoint,
                            parent_accumulated_scroll_offset: LayerPoint) {
        let local_transform = match self.node_type {
            NodeType::ReferenceFrame(transform) => transform,
            NodeType::Clip(_) => LayerToScrollTransform::identity(),
        };

        let inv_transform = match local_transform.inverse() {
            Some(transform) => transform,
            None => {
                // If a transform function causes the current transformation matrix of an object
                // to be non-invertible, the object and its content do not get displayed.
                self.combined_local_viewport_rect = LayerRect::zero();
                return;
            }
        };

        // We are trying to move the combined viewport rectangle of our parent nodes into the
        // coordinate system of this node, so we must invert our transformation (only for
        // reference frames) and then apply the scroll offset the parent layer. The combined
        // local viewport rect doesn't include scrolling offsets so the only one that matters
        // is the relative offset between us and the parent.
        let parent_combined_viewport_in_local_space =
            inv_transform.pre_translated(-parent_scroll_offset.x, -parent_scroll_offset.y, 0.0)
                         .transform_rect(parent_combined_viewport_rect);

        // Now that we have the combined viewport rectangle of the parent nodes in local space,
        // we do the intersection and get our combined viewport rect in the coordinate system
        // starting from our origin.
        self.combined_local_viewport_rect = match self.node_type {
            NodeType::Clip(_) => {
                parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
                                                       .unwrap_or(LayerRect::zero())
            }
            NodeType::ReferenceFrame(_) => parent_combined_viewport_in_local_space,
        };

        // The transformation for this viewport in world coordinates is the transformation for
        // our parent reference frame, plus any accumulated scrolling offsets from nodes
        // between our reference frame and this node. For reference frames, we also include
        // whatever local transformation this reference frame provides. This can be combined
        // with the local_viewport_rect to get its position in world space.
        self.world_viewport_transform =
            parent_reference_frame_transform
                .pre_translated(parent_accumulated_scroll_offset.x,
                                parent_accumulated_scroll_offset.y,
                                0.0)
                .pre_mul(&local_transform.with_destination::<LayerPixel>());

        // The transformation for any content inside of us is the viewport transformation, plus
        // whatever scrolling offset we supply as well.
        self.world_content_transform =
            self.world_viewport_transform.pre_translated(self.scrolling.offset.x,
                                                         self.scrolling.offset.y,
                                                         0.0);
    }

    pub fn scrollable_height(&self) -> f32 {
        self.content_size.height - self.local_viewport_rect.size.height
    }

    pub fn scrollable_width(&self) -> f32 {
        self.content_size.width - self.local_viewport_rect.size.width
    }

    pub fn scroll(&mut self, scroll_location: ScrollLocation, phase: ScrollEventPhase) -> bool {
        if self.scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) {
            return false;
        }

        let mut delta = match scroll_location {
            ScrollLocation::Delta(delta) => delta,
            ScrollLocation::Start => {
                if self.scrolling.offset.y.round() >= 0.0 {
                    // Nothing to do on this layer.
                    return false;
                }

                self.scrolling.offset.y = 0.0;
                return true;
            },
            ScrollLocation::End => {
                let end_pos = self.local_viewport_rect.size.height - self.content_size.height;

                if self.scrolling.offset.y.round() <= end_pos {
                    // Nothing to do on this layer.
                    return false;
                }

                self.scrolling.offset.y = end_pos;
                return true;
            }
        };

        let overscroll_amount = self.overscroll_amount();
        let overscrolling = CAN_OVERSCROLL && (overscroll_amount.width != 0.0 ||
                                               overscroll_amount.height != 0.0);
        if overscrolling {
            if overscroll_amount.width != 0.0 {
                delta.x /= overscroll_amount.width.abs()
            }
            if overscroll_amount.height != 0.0 {
                delta.y /= overscroll_amount.height.abs()
            }
        }

        let scrollable_width = self.scrollable_width();
        let scrollable_height = self.scrollable_height();
        let is_unscrollable = scrollable_width <= 0. && scrollable_height <= 0.;
        let original_layer_scroll_offset = self.scrolling.offset;

        if scrollable_width > 0. {
            self.scrolling.offset.x = self.scrolling.offset.x + delta.x;
            if is_unscrollable || !CAN_OVERSCROLL {
                self.scrolling.offset.x =
                    self.scrolling.offset.x.min(0.0).max(-scrollable_width).round();
            }
        }

        if scrollable_height > 0. {
            self.scrolling.offset.y = self.scrolling.offset.y + delta.y;
            if is_unscrollable || !CAN_OVERSCROLL {
                self.scrolling.offset.y =
                    self.scrolling.offset.y.min(0.0).max(-scrollable_height).round();
            }
        }

        if phase == ScrollEventPhase::Start || phase == ScrollEventPhase::Move(true) {
            self.scrolling.started_bouncing_back = false
        } else if overscrolling &&
                ((delta.x < 1.0 && delta.y < 1.0) || phase == ScrollEventPhase::End) {
            self.scrolling.started_bouncing_back = true;
            self.scrolling.bouncing_back = true
        }

        if CAN_OVERSCROLL {
            self.stretch_overscroll_spring();
        }

        self.scrolling.offset != original_layer_scroll_offset ||
            self.scrolling.started_bouncing_back
    }

    pub fn stretch_overscroll_spring(&mut self) {
        let overscroll_amount = self.overscroll_amount();
        self.scrolling.spring.coords(self.scrolling.offset,
                                     self.scrolling.offset,
                                     self.scrolling.offset + overscroll_amount);
    }

    pub fn tick_scrolling_bounce_animation(&mut self) {
        let finished = self.scrolling.spring.animate();
        self.scrolling.offset = self.scrolling.spring.current();
        if finished {
            self.scrolling.bouncing_back = false
        }
    }

    pub fn ray_intersects_node(&self, cursor: &WorldPoint) -> bool {
        let inv = self.world_viewport_transform.inverse().unwrap();
        let z0 = -10000.0;
        let z1 =  10000.0;

        let p0 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z0, 1.0));
        let p0 = Point3D::new(p0.x / p0.w,
                              p0.y / p0.w,
                              p0.z / p0.w);
        let p1 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z1, 1.0));
        let p1 = Point3D::new(p1.x / p1.w,
                              p1.y / p1.w,
                              p1.z / p1.w);

        if self.scrollable_width() <= 0. && self.scrollable_height() <= 0. {
            return false;
        }
        ray_intersects_rect(p0, p1, self.local_viewport_rect.to_untyped())
    }
}

#[derive(Copy, Clone, Debug)]
pub struct ScrollingState {
    pub offset: LayerPoint,
    pub spring: Spring,
    pub started_bouncing_back: bool,
    pub bouncing_back: bool,
    pub should_handoff_scroll: bool
}

impl ScrollingState {
    pub fn new() -> ScrollingState {
        ScrollingState {
            offset: LayerPoint::zero(),
            spring: Spring::at(LayerPoint::zero(), STIFFNESS, DAMPING),
            started_bouncing_back: false,
            bouncing_back: false,
            should_handoff_scroll: false
        }
    }
}

<commit_msg>Combined clip rect workaround<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 euclid::Point3D;
use geometry::ray_intersects_rect;
use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
use prim_store::GpuBlock32;
use renderer::VertexDataStore;
use spring::{DAMPING, STIFFNESS, Spring};
use tiling::PackedLayerIndex;
use util::TransformedRectKind;
use webrender_traits::{ClipId, ClipRegion, LayerPixel, LayerPoint, LayerRect, LayerSize};
use webrender_traits::{LayerToScrollTransform, LayerToWorldTransform, PipelineId};
use webrender_traits::{ScrollEventPhase, ScrollLayerRect, ScrollLocation, WorldPoint};
use webrender_traits::{DeviceIntRect, WorldPoint4D};

#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;

#[cfg(not(target_os = "macos"))]
const CAN_OVERSCROLL: bool = false;

#[derive(Clone, Debug)]
pub struct ClipInfo {
    /// The ClipSource for this node, which is used to generate mask_cache_info.
    pub clip_sources: Vec<ClipSource>,

    /// The MaskCacheInfo for this node, which is produced by processing the
    /// provided ClipSource.
    pub mask_cache_info: Option<MaskCacheInfo>,

    /// The packed layer index for this node, which is used to render a clip mask
    /// for it, if necessary.
    pub packed_layer_index: PackedLayerIndex,

    /// The final transformed rectangle of this clipping region for this node,
    /// which depends on the screen rectangle and the transformation of all of
    /// the parents.
    pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
}

impl ClipInfo {
    pub fn new(clip_region: &ClipRegion,
               clip_store: &mut VertexDataStore<GpuBlock32>,
               packed_layer_index: PackedLayerIndex,)
               -> ClipInfo {
        // We pass true here for the MaskCacheInfo because this type of
        // mask needs an extra clip for the clip rectangle.
        let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
        ClipInfo {
            mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
            clip_sources: clip_sources,
            packed_layer_index: packed_layer_index,
            screen_bounding_rect: None,
        }
    }

    pub fn is_masking(&self) -> bool {
        match self.mask_cache_info {
            Some(ref info) => info.is_masking(),
            _ => false,
        }
    }

}
#[derive(Clone, Debug)]
pub enum NodeType {
    /// Transform for this layer, relative to parent reference frame. A reference
    /// frame establishes a new coordinate space in the tree.
    ReferenceFrame(LayerToScrollTransform),

    /// Other nodes just do clipping, but no transformation.
    Clip(ClipInfo),
}

/// Contains scrolling and transform information stacking contexts.
#[derive(Clone, Debug)]
pub struct ClipScrollNode {
    /// Manages scrolling offset, overscroll state etc.
    pub scrolling: ScrollingState,

    /// Size of the content inside the scroll region (in logical pixels)
    pub content_size: LayerSize,

    /// Viewing rectangle in the coordinate system of the parent reference frame.
    pub local_viewport_rect: LayerRect,

    /// Clip rect of this node - typically the same as viewport rect, except
    /// in overscroll cases.
    pub local_clip_rect: LayerRect,

    /// Viewport rectangle clipped against parent layer(s) viewport rectangles.
    /// This is in the coordinate system of the parent reference frame.
    pub combined_local_viewport_rect: LayerRect,

    /// World transform for the viewport rect itself. This is the parent
    /// reference frame transformation plus the scrolling offsets provided by
    /// the nodes in between the reference frame and this node.
    pub world_viewport_transform: LayerToWorldTransform,

    /// World transform for content transformed by this node.
    pub world_content_transform: LayerToWorldTransform,

    /// Pipeline that this layer belongs to
    pub pipeline_id: PipelineId,

    /// Parent layer. If this is None, we are the root node.
    pub parent: Option<ClipId>,

    /// Child layers
    pub children: Vec<ClipId>,

    /// Whether or not this node is a reference frame.
    pub node_type: NodeType,
}

impl ClipScrollNode {
    pub fn new(pipeline_id: PipelineId,
               parent_id: ClipId,
               content_rect: &LayerRect,
               clip_rect: &LayerRect,
               clip_info: ClipInfo)
               -> ClipScrollNode {
        // FIXME(mrobinson): We don't yet handle clipping rectangles that don't start at the origin
        // of the node.
        let local_viewport_rect = LayerRect::new(content_rect.origin, clip_rect.size);
        ClipScrollNode {
            scrolling: ScrollingState::new(),
            content_size: content_rect.size,
            local_viewport_rect: local_viewport_rect,
            local_clip_rect: local_viewport_rect,
            combined_local_viewport_rect: LayerRect::zero(),
            world_viewport_transform: LayerToWorldTransform::identity(),
            world_content_transform: LayerToWorldTransform::identity(),
            parent: Some(parent_id),
            children: Vec::new(),
            pipeline_id: pipeline_id,
            node_type: NodeType::Clip(clip_info),
        }
    }

    pub fn new_reference_frame(parent_id: Option<ClipId>,
                               local_viewport_rect: &LayerRect,
                               content_size: LayerSize,
                               local_transform: &LayerToScrollTransform,
                               pipeline_id: PipelineId)
                               -> ClipScrollNode {
        ClipScrollNode {
            scrolling: ScrollingState::new(),
            content_size: content_size,
            local_viewport_rect: *local_viewport_rect,
            local_clip_rect: *local_viewport_rect,
            combined_local_viewport_rect: LayerRect::zero(),
            world_viewport_transform: LayerToWorldTransform::identity(),
            world_content_transform: LayerToWorldTransform::identity(),
            parent: parent_id,
            children: Vec::new(),
            pipeline_id: pipeline_id,
            node_type: NodeType::ReferenceFrame(*local_transform),
        }
    }

    pub fn add_child(&mut self, child: ClipId) {
        self.children.push(child);
    }

    pub fn finalize(&mut self, scrolling: &ScrollingState) {
        self.scrolling = *scrolling;
    }

    pub fn overscroll_amount(&self) -> LayerSize {
        let scrollable_width = self.scrollable_width();
        let overscroll_x = if self.scrolling.offset.x > 0.0 {
            -self.scrolling.offset.x
        } else if self.scrolling.offset.x < -scrollable_width {
            -scrollable_width - self.scrolling.offset.x
        } else {
            0.0
        };

        let scrollable_height = self.scrollable_height();
        let overscroll_y = if self.scrolling.offset.y > 0.0 {
            -self.scrolling.offset.y
        } else if self.scrolling.offset.y < -scrollable_height {
            -scrollable_height - self.scrolling.offset.y
        } else {
            0.0
        };

        LayerSize::new(overscroll_x, overscroll_y)
    }

    pub fn set_scroll_origin(&mut self, origin: &LayerPoint) -> bool {
        match self.node_type {
            NodeType::ReferenceFrame(_) => {
                warn!("Tried to scroll a reference frame.");
                return false;
            }
            NodeType::Clip(_) => {}
        };

        let scrollable_height = self.scrollable_height();
        let scrollable_width = self.scrollable_width();
        if scrollable_height <= 0. && scrollable_width <= 0. {
            return false;
        }

        let new_offset = LayerPoint::new((-origin.x).max(-scrollable_width).min(0.0).round(),
                                         (-origin.y).max(-scrollable_height).min(0.0).round());
        if new_offset == self.scrolling.offset {
            return false;
        }

        self.scrolling.offset = new_offset;
        self.scrolling.bouncing_back = false;
        self.scrolling.started_bouncing_back = false;
        true
    }

    pub fn update_transform(&mut self,
                            parent_reference_frame_transform: &LayerToWorldTransform,
                            parent_combined_viewport_rect: &ScrollLayerRect,
                            parent_scroll_offset: LayerPoint,
                            parent_accumulated_scroll_offset: LayerPoint) {
        let local_transform = match self.node_type {
            NodeType::ReferenceFrame(transform) => transform,
            NodeType::Clip(_) => LayerToScrollTransform::identity(),
        };

        let inv_transform = match local_transform.inverse() {
            Some(transform) => transform,
            None => {
                // If a transform function causes the current transformation matrix of an object
                // to be non-invertible, the object and its content do not get displayed.
                self.combined_local_viewport_rect = LayerRect::zero();
                return;
            }
        };

        // We are trying to move the combined viewport rectangle of our parent nodes into the
        // coordinate system of this node, so we must invert our transformation (only for
        // reference frames) and then apply the scroll offset the parent layer. The combined
        // local viewport rect doesn't include scrolling offsets so the only one that matters
        // is the relative offset between us and the parent.
        let parent_combined_viewport_in_local_space =
            inv_transform.pre_translated(-parent_scroll_offset.x, -parent_scroll_offset.y, 0.0)
                         .transform_rect(parent_combined_viewport_rect);

        // Now that we have the combined viewport rectangle of the parent nodes in local space,
        // we do the intersection and get our combined viewport rect in the coordinate system
        // starting from our origin.
        self.combined_local_viewport_rect = match self.node_type {
            NodeType::Clip(_) => {
                parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
                                                       .unwrap_or(LayerRect::zero())
            }
            NodeType::ReferenceFrame(_) => parent_combined_viewport_in_local_space,
        };

        // HACK: prevent the code above for non-AA transforms, it's incorrect.
        if (local_transform.m13, local_transform.m23) != (0.0, 0.0) {
            self.combined_local_viewport_rect = self.local_clip_rect;
        }

        // The transformation for this viewport in world coordinates is the transformation for
        // our parent reference frame, plus any accumulated scrolling offsets from nodes
        // between our reference frame and this node. For reference frames, we also include
        // whatever local transformation this reference frame provides. This can be combined
        // with the local_viewport_rect to get its position in world space.
        self.world_viewport_transform =
            parent_reference_frame_transform
                .pre_translated(parent_accumulated_scroll_offset.x,
                                parent_accumulated_scroll_offset.y,
                                0.0)
                .pre_mul(&local_transform.with_destination::<LayerPixel>());

        // The transformation for any content inside of us is the viewport transformation, plus
        // whatever scrolling offset we supply as well.
        self.world_content_transform =
            self.world_viewport_transform.pre_translated(self.scrolling.offset.x,
                                                         self.scrolling.offset.y,
                                                         0.0);
    }

    pub fn scrollable_height(&self) -> f32 {
        self.content_size.height - self.local_viewport_rect.size.height
    }

    pub fn scrollable_width(&self) -> f32 {
        self.content_size.width - self.local_viewport_rect.size.width
    }

    pub fn scroll(&mut self, scroll_location: ScrollLocation, phase: ScrollEventPhase) -> bool {
        if self.scrolling.started_bouncing_back && phase == ScrollEventPhase::Move(false) {
            return false;
        }

        let mut delta = match scroll_location {
            ScrollLocation::Delta(delta) => delta,
            ScrollLocation::Start => {
                if self.scrolling.offset.y.round() >= 0.0 {
                    // Nothing to do on this layer.
                    return false;
                }

                self.scrolling.offset.y = 0.0;
                return true;
            },
            ScrollLocation::End => {
                let end_pos = self.local_viewport_rect.size.height - self.content_size.height;

                if self.scrolling.offset.y.round() <= end_pos {
                    // Nothing to do on this layer.
                    return false;
                }

                self.scrolling.offset.y = end_pos;
                return true;
            }
        };

        let overscroll_amount = self.overscroll_amount();
        let overscrolling = CAN_OVERSCROLL && (overscroll_amount.width != 0.0 ||
                                               overscroll_amount.height != 0.0);
        if overscrolling {
            if overscroll_amount.width != 0.0 {
                delta.x /= overscroll_amount.width.abs()
            }
            if overscroll_amount.height != 0.0 {
                delta.y /= overscroll_amount.height.abs()
            }
        }

        let scrollable_width = self.scrollable_width();
        let scrollable_height = self.scrollable_height();
        let is_unscrollable = scrollable_width <= 0. && scrollable_height <= 0.;
        let original_layer_scroll_offset = self.scrolling.offset;

        if scrollable_width > 0. {
            self.scrolling.offset.x = self.scrolling.offset.x + delta.x;
            if is_unscrollable || !CAN_OVERSCROLL {
                self.scrolling.offset.x =
                    self.scrolling.offset.x.min(0.0).max(-scrollable_width).round();
            }
        }

        if scrollable_height > 0. {
            self.scrolling.offset.y = self.scrolling.offset.y + delta.y;
            if is_unscrollable || !CAN_OVERSCROLL {
                self.scrolling.offset.y =
                    self.scrolling.offset.y.min(0.0).max(-scrollable_height).round();
            }
        }

        if phase == ScrollEventPhase::Start || phase == ScrollEventPhase::Move(true) {
            self.scrolling.started_bouncing_back = false
        } else if overscrolling &&
                ((delta.x < 1.0 && delta.y < 1.0) || phase == ScrollEventPhase::End) {
            self.scrolling.started_bouncing_back = true;
            self.scrolling.bouncing_back = true
        }

        if CAN_OVERSCROLL {
            self.stretch_overscroll_spring();
        }

        self.scrolling.offset != original_layer_scroll_offset ||
            self.scrolling.started_bouncing_back
    }

    pub fn stretch_overscroll_spring(&mut self) {
        let overscroll_amount = self.overscroll_amount();
        self.scrolling.spring.coords(self.scrolling.offset,
                                     self.scrolling.offset,
                                     self.scrolling.offset + overscroll_amount);
    }

    pub fn tick_scrolling_bounce_animation(&mut self) {
        let finished = self.scrolling.spring.animate();
        self.scrolling.offset = self.scrolling.spring.current();
        if finished {
            self.scrolling.bouncing_back = false
        }
    }

    pub fn ray_intersects_node(&self, cursor: &WorldPoint) -> bool {
        let inv = self.world_viewport_transform.inverse().unwrap();
        let z0 = -10000.0;
        let z1 =  10000.0;

        let p0 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z0, 1.0));
        let p0 = Point3D::new(p0.x / p0.w,
                              p0.y / p0.w,
                              p0.z / p0.w);
        let p1 = inv.transform_point4d(&WorldPoint4D::new(cursor.x, cursor.y, z1, 1.0));
        let p1 = Point3D::new(p1.x / p1.w,
                              p1.y / p1.w,
                              p1.z / p1.w);

        if self.scrollable_width() <= 0. && self.scrollable_height() <= 0. {
            return false;
        }
        ray_intersects_rect(p0, p1, self.local_viewport_rect.to_untyped())
    }
}

#[derive(Copy, Clone, Debug)]
pub struct ScrollingState {
    pub offset: LayerPoint,
    pub spring: Spring,
    pub started_bouncing_back: bool,
    pub bouncing_back: bool,
    pub should_handoff_scroll: bool
}

impl ScrollingState {
    pub fn new() -> ScrollingState {
        ScrollingState {
            offset: LayerPoint::zero(),
            spring: Spring::at(LayerPoint::zero(), STIFFNESS, DAMPING),
            started_bouncing_back: false,
            bouncing_back: false,
            should_handoff_scroll: false
        }
    }
}

<|endoftext|>


<commit_before>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{
        self, AppVec, ArcType, BuiltinType, Generic, NullInterner, Type, TypeExt, TypeInterner,
    },
};

use crate::{
    substitution::{is_variable_unified, Substitution},
    typ::RcType,
    typecheck::Typecheck,
};

pub fn generalize_from(tc: &mut Typecheck, level: u32, typ: &RcType) {
    let mut variable_generator = TypeVariableGenerator::new(&tc.subs, typ);
    for var in tc
        .subs
        .unbound_variables(level)
        .cloned()
        .collect::<Vec<_>>()
    {
        let var = match &*var {
            Type::Variable(var) => var,
            _ => unreachable!(),
        };
        // Create a prefix if none exists
        let id = variable_generator.next_variable(tc);

        let gen: RcType = Type::generic(Generic::new(id.clone(), var.kind.clone()));
        debug!("Gen {} to {}", var.id, gen);
        tc.subs.insert(var.id, gen.clone());
    }
}

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<Symbol, Generic<Symbol>>,
    variable_generator: TypeVariableGenerator,
    tc: &'a mut Typecheck<'b>,
    span: Span<BytePos>,
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeInterner<Symbol, RcType> for TypeGeneralizer<'a, 'b> {
    fn intern(&mut self, typ: Type<Symbol, RcType>) -> RcType {
        (&self.tc.subs).intern(typ)
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &RcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: FnvMap::default(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            span,
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.tc.type_variables.enter_scope();
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
                self.generalizer.tc.type_variables.exit_scope();
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                if let Type::Variable(var) = &**typ {
                    let ref typ = self.generalizer.tc.subs.arc_real(typ).clone();
                    {
                        let type_cache = &self.generalizer.tc.type_cache;
                        self.generalizer.tc.type_variables.extend(
                            typ.forall_params()
                                .map(|param| (param.id.clone(), type_cache.hole())),
                        );
                    }
                    if let Some(typ) = self.generalizer.generalize_type(typ) {
                        self.generalizer.tc.subs.replace(var.id, typ);
                    }
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut RcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(_, generic)| generic)
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            Type::forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &RcType) -> Option<RcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        trace!("GEN: {}", typ);

        if !typ.needs_generalize() {
            trace!("No need to generalize: {}", typ);
            return replacement;
        }

        match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                // Create a prefix if none exists
                let id = self.variable_generator.next_variable(self.tc);

                let gen: RcType = self.generic(Generic::new(id.clone(), var.kind.clone()));
                debug!("Gen {} to {}", var.id, gen);
                self.subs.insert(var.id, gen.clone());

                self.unbound_variables
                    .insert(id.clone(), Generic::new(id, var.kind.clone()));

                Some(gen)
            }

            Type::Forall(ref params, ref typ, Some(ref vars)) => {
                trace!("Generalize `{}` {:?}", typ, vars);

                let mut new_params = Vec::new();
                let typ = {
                    let subs = &self.tc.subs;
                    self.tc.named_variables.clear();
                    for (param, var) in params.iter().zip(vars) {
                        if is_variable_unified(subs, var) {
                            self.tc
                                .named_variables
                                .insert(param.id.clone(), var.clone());
                        } else {
                            new_params.push(param.clone());
                        }
                    }

                    if self.tc.named_variables.is_empty() {
                        typ.clone()
                    } else {
                        typ.instantiate_generics_(&mut NullInterner, &mut self.tc.named_variables)
                            .unwrap_or(typ.clone())
                    }
                };

                self.type_variables.enter_scope();
                self.type_variables.extend(
                    params
                        .iter()
                        .zip(vars)
                        .map(|(param, var)| (param.id.clone(), var.clone())),
                );

                trace!("Generalize forall => `{}`", typ);

                let new_type = self.generalize_type(&typ);
                self.type_variables.exit_scope();

                Some(self.forall(new_params, new_type.unwrap_or_else(|| typ.clone())))
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    };

                    if !in_scope {
                        self.unbound_variables
                            .insert(generic.id.clone(), generic.clone());
                    }

                    Some(self.generic(generic))
                } else {
                    replacement
                }
            }

            _ => {
                if let Type::Forall(ref params, _, None) = **typ {
                    let type_cache = &self.tc.type_cache;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                match **typ {
                    Type::Generic(ref generic)
                        if self.type_variables.get(&generic.id).is_none() =>
                    {
                        self.unbound_variables
                            .insert(generic.id.clone(), generic.clone());
                    }
                    _ => (),
                }

                types::walk_move_type_opt(
                    typ,
                    &mut types::InternerVisitor::control(self, |self_, typ: &RcType| {
                        self_.generalize_type(typ)
                    }),
                )
                .map(|t| unroll_typ(self.tc, &t).unwrap_or(t))
                .or_else(|| replacement.clone())
            }
        }
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<RcType>, typ: &RcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<RcType>, typ: &RcType) {
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_move_type_opt(
                typ,
                &mut types::ControlVisitation(|typ: &RcType| {
                    gather_foralls(map, subs, typ);
                    None
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        tc.type_variables.insert(symbol.clone(), Type::hole());
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(interner: &mut impl TypeInterner<Symbol, RcType>, typ: &RcType) -> Option<RcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(interner, typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(interner.function(args, ret))
            }
            _ => Some(interner.app(current.clone(), args)),
        }
    }
}

fn unroll_record(
    interner: &mut impl TypeInterner<Symbol, RcType>,
    typ: &Type<Symbol, RcType>,
) -> Option<RcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(interner.extend_row(new_types, new_fields, current.clone()))
    }
}

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

    use crate::base::types::SharedInterner;

    #[test]
    fn unroll_typ_test() {
        let mut interner = SharedInterner::default();

        let i: RcType = interner.int();
        let s: RcType = interner.string();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![s.clone()]),
                    collect![i.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: RcType = interner.function_builtin();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(f.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.function(vec![i.clone()], s.clone()))
        );
    }
}
<commit_msg>perf: Avoid recomputing generalization for the same type<commit_after>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{
        self, AppVec, ArcType, BuiltinType, Generic, NullInterner, Type, TypeExt, TypeInterner,
    },
};

use crate::{
    substitution::{is_variable_unified, Substitution},
    typ::RcType,
    typecheck::Typecheck,
};

pub fn generalize_from(tc: &mut Typecheck, level: u32, typ: &RcType) {
    let mut variable_generator = TypeVariableGenerator::new(&tc.subs, typ);
    for var in tc
        .subs
        .unbound_variables(level)
        .cloned()
        .collect::<Vec<_>>()
    {
        let var = match &*var {
            Type::Variable(var) => var,
            _ => unreachable!(),
        };
        // Create a prefix if none exists
        let id = variable_generator.next_variable(tc);

        let gen: RcType = Type::generic(Generic::new(id.clone(), var.kind.clone()));
        debug!("Gen {} to {}", var.id, gen);
        tc.subs.insert(var.id, gen.clone());
    }
}

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<Symbol, Generic<Symbol>>,
    variable_generator: TypeVariableGenerator,
    tc: &'a mut Typecheck<'b>,
    span: Span<BytePos>,
    visited: FnvMap<RcType, RcType>,
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeInterner<Symbol, RcType> for TypeGeneralizer<'a, 'b> {
    fn intern(&mut self, typ: Type<Symbol, RcType>) -> RcType {
        (&self.tc.subs).intern(typ)
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &RcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: FnvMap::default(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            span,
            visited: Default::default(),
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.tc.type_variables.enter_scope();
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
                self.generalizer.tc.type_variables.exit_scope();
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                if let Type::Variable(var) = &**typ {
                    let ref typ = self.generalizer.tc.subs.arc_real(typ).clone();
                    {
                        let type_cache = &self.generalizer.tc.type_cache;
                        self.generalizer.tc.type_variables.extend(
                            typ.forall_params()
                                .map(|param| (param.id.clone(), type_cache.hole())),
                        );
                    }
                    if let Some(typ) = self.generalizer.generalize_type(typ) {
                        self.generalizer.tc.subs.replace(var.id, typ);
                    }
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut RcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(_, generic)| generic)
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            Type::forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &RcType) -> Option<RcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        trace!("GEN: {}", typ);

        if !typ.needs_generalize() {
            trace!("No need to generalize: {}", typ);
            return replacement;
        }

        if RcType::strong_count(&typ) > 2 {
            if let Some(new_type) = self.visited.get(typ) {
                return Some(new_type.clone());
            }
        }

        let new_type = match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                // Create a prefix if none exists
                let id = self.variable_generator.next_variable(self.tc);

                let gen: RcType = self.generic(Generic::new(id.clone(), var.kind.clone()));
                debug!("Gen {} to {}", var.id, gen);
                self.subs.insert(var.id, gen.clone());

                self.unbound_variables
                    .insert(id.clone(), Generic::new(id, var.kind.clone()));

                Some(gen)
            }

            Type::Forall(ref params, ref typ, Some(ref vars)) => {
                trace!("Generalize `{}` {:?}", typ, vars);

                let mut new_params = Vec::new();
                let typ = {
                    let subs = &self.tc.subs;
                    self.tc.named_variables.clear();
                    for (param, var) in params.iter().zip(vars) {
                        if is_variable_unified(subs, var) {
                            self.tc
                                .named_variables
                                .insert(param.id.clone(), var.clone());
                        } else {
                            new_params.push(param.clone());
                        }
                    }

                    if self.tc.named_variables.is_empty() {
                        typ.clone()
                    } else {
                        typ.instantiate_generics_(&mut NullInterner, &mut self.tc.named_variables)
                            .unwrap_or(typ.clone())
                    }
                };

                self.type_variables.enter_scope();
                self.type_variables.extend(
                    params
                        .iter()
                        .zip(vars)
                        .map(|(param, var)| (param.id.clone(), var.clone())),
                );

                trace!("Generalize forall => `{}`", typ);

                let new_type = self.generalize_type(&typ);
                self.type_variables.exit_scope();

                Some(self.forall(new_params, new_type.unwrap_or_else(|| typ.clone())))
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    };

                    if !in_scope {
                        self.unbound_variables
                            .insert(generic.id.clone(), generic.clone());
                    }

                    Some(self.generic(generic))
                } else {
                    replacement.clone()
                }
            }

            _ => {
                if let Type::Forall(ref params, _, None) = **typ {
                    let type_cache = &self.tc.type_cache;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                match **typ {
                    Type::Generic(ref generic)
                        if self.type_variables.get(&generic.id).is_none() =>
                    {
                        self.unbound_variables
                            .insert(generic.id.clone(), generic.clone());
                    }
                    _ => (),
                }

                types::walk_move_type_opt(
                    typ,
                    &mut types::InternerVisitor::control(self, |self_, typ: &RcType| {
                        self_.generalize_type(typ)
                    }),
                )
                .map(|t| unroll_typ(self.tc, &t).unwrap_or(t))
                .or_else(|| replacement.clone())
            }
        };

        if let Some(new_type) = &new_type {
            if RcType::strong_count(&typ) > 2 {
                self.visited.insert(typ.clone(), new_type.clone());
            }
        }

        new_type
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<RcType>, typ: &RcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<RcType>, typ: &RcType) {
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_move_type_opt(
                typ,
                &mut types::ControlVisitation(|typ: &RcType| {
                    gather_foralls(map, subs, typ);
                    None
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        tc.type_variables.insert(symbol.clone(), Type::hole());
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(interner: &mut impl TypeInterner<Symbol, RcType>, typ: &RcType) -> Option<RcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(interner, typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(interner.function(args, ret))
            }
            _ => Some(interner.app(current.clone(), args)),
        }
    }
}

fn unroll_record(
    interner: &mut impl TypeInterner<Symbol, RcType>,
    typ: &Type<Symbol, RcType>,
) -> Option<RcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(interner.extend_row(new_types, new_fields, current.clone()))
    }
}

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

    use crate::base::types::SharedInterner;

    #[test]
    fn unroll_typ_test() {
        let mut interner = SharedInterner::default();

        let i: RcType = interner.int();
        let s: RcType = interner.string();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![s.clone()]),
                    collect![i.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(i.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: RcType = interner.function_builtin();
        assert_eq!(
            unroll_typ(
                &mut &interner,
                &(&interner).app(
                    (&interner).app(f.clone(), collect![i.clone()]),
                    collect![s.clone()]
                )
            ),
            Some(interner.function(vec![i.clone()], s.clone()))
        );
    }
}
<|endoftext|>


<commit_before>//! Parses "multi_match" queries

use rustc_serialize::json::Json;

use search::term::Term;
use search::analysis::Analyzer;

use search::query::Query;
use search::query::term_matcher::TermMatcher;
use search::query::term_scorer::TermScorer;
use search::query::parser::{QueryParseContext, QueryParseError};
use search::query::parser::utils::{parse_string, parse_float, Operator, parse_operator, parse_field_and_boost};

pub fn parse(context: &QueryParseContext, json: &Json) -> Result<Query, QueryParseError> {
    let object = try!(json.as_object().ok_or(QueryParseError::ExpectedObject));

    // Get configuration
    let mut fields_with_boosts = Vec::new();
    let mut query = String::new();
    let mut boost = 1.0f64;
    let mut operator = Operator::Or;

    let mut has_fields_key = false;
    let mut has_query_key = false;

    for (key, val) in object.iter() {
        match key.as_ref() {
            "fields" => {
                has_fields_key = true;

                match *val {
                    Json::Array(ref array) => {
                        for field in array.iter() {
                            fields_with_boosts.push(try!(parse_field_and_boost(field)));
                        }
                    }
                    _ => return Err(QueryParseError::ExpectedArray)
                }
            }
            "query" => {
                has_query_key = true;
                query = try!(parse_string(val));
            }
            "boost" => {
                boost = try!(parse_float(val));
            }
            "operator" => {
                operator = try!(parse_operator(val))
            }
            _ => return Err(QueryParseError::UnrecognisedKey(key.clone()))
        }
    }

    if !has_fields_key {
        return Err(QueryParseError::ExpectedKey("fields"))
    }

    if !has_query_key {
        return Err(QueryParseError::ExpectedKey("query"))
    }

    // Convert query string into term query objects
    let mut field_queries = Vec::new();
    for (field_name, field_boost) in fields_with_boosts {
        let mut term_queries = Vec::new();
        for token in Analyzer::Standard.run(query.clone()) {
            term_queries.push(Query::MatchTerm {
                field: field_name.clone(),
                term: token.term,
                matcher: TermMatcher::Exact,
                scorer: TermScorer::default(),
            });
        }

        let mut field_query = match operator {
            Operator::Or => {
                Query::new_disjunction(term_queries)
            }
            Operator::And => {
                Query::new_conjunction(term_queries)
            }
        };

        // Add boost
        field_query = Query::new_score(field_query, field_boost, 0.0f64);

        field_queries.push(field_query);
    }

    let mut query = Query::new_disjunction_max(field_queries);

    // Add boost
    query = Query::new_score(query, boost, 0.0f64);

    return Ok(query);
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::Json;

    use search::term::Term;
    use search::query::Query;
    use search::query::term_matcher::TermMatcher;
    use search::query::term_scorer::TermScorer;
    use search::query::parser::{QueryParseContext, QueryParseError};

    use super::parse;

    #[test]
    fn test_multi_match_query() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::MatchTerm {
                    field: "bar".to_string(),
                    term: Term::String("foo".to_string()),
                    matcher: TermMatcher::Exact,
                    scorer: TermScorer::default(),
                },
                Query::MatchTerm {
                    field: "baz".to_string(),
                    term: Term::String("foo".to_string()),
                    matcher: TermMatcher::Exact,
                    scorer: TermScorer::default(),
                }
            ],
        }));
    }

    #[test]
    fn test_multi_term_multi_match_query() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"hello world\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Disjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                },
                Query::Disjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_with_boost() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2.0
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::Score {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::MatchTerm {
                        field: "bar".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    },
                    Query::MatchTerm {
                        field: "baz".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    }
                ],
            }),
            mul: 2.0f64,
            add: 0.0f64,
        }));
    }

    #[test]
    fn test_with_boost_integer() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::Score {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::MatchTerm {
                        field: "bar".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    },
                    Query::MatchTerm {
                        field: "baz".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    }
                ],
            }),
            mul: 2.0f64,
            add: 0.0f64,
        }));
    }

    #[test]
    fn test_with_field_boost() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar^2\", \"baz^1.0\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Score {
                    query: Box::new(Query::MatchTerm {
                        field: "bar".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    }),
                    mul: 2.0f64,
                    add: 0.0f64,
                },
                Query::MatchTerm {
                    field: "baz".to_string(),
                    term: Term::String("foo".to_string()),
                    matcher: TermMatcher::Exact,
                    scorer: TermScorer::default(),
                }
            ],
        }));
    }

    #[test]
    fn test_with_and_operator() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo bar\",
            \"fields\": [\"baz\", \"quux\"],
            \"operator\": \"and\"
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Conjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("bar".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                },
                Query::Conjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "quux".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "quux".to_string(),
                            term: Term::String("bar".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_gives_error_for_incorrect_type() {
        // String
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        \"foo\"
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Array
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        [
            \"foo\"
        ]
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Integer
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        123
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Float
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        123.1234
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));
    }

    #[test]
    fn test_gives_error_for_incorrect_query_type() {
        // Object
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": {
                \"foo\": \"bar\"
            },
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Array
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": [\"foo\"],
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Integer
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": 123,
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Float
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": 123.456,
            \"fields\": [\"bar\", \"baz\"]
        }        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));
    }

    #[test]
    fn test_gives_error_for_incorrect_fields_type() {
        // String
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": \"bar\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Object
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": {
                \"value\": [\"bar\", \"baz\"]
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Integer
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Float
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123.456
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));
    }

    #[test]
    fn test_gives_error_for_incorrect_boost_type() {
        // String
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": \"2\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Array
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": [2]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Object
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": {
                \"value\": 2
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));
    }

    #[test]
    fn test_gives_error_for_missing_query() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("query")));
    }

    #[test]
    fn test_gives_error_for_missing_fields() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("fields")));
    }

    #[test]
    fn test_gives_error_for_extra_key() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"hello\": \"world\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::UnrecognisedKey("hello".to_string())));
    }
}
<commit_msg>Make multi-match query use correct query analyzer for field<commit_after>//! Parses "multi_match" queries

use rustc_serialize::json::Json;

use search::term::Term;
use search::analysis::Analyzer;
use search::mapping::FieldMapping;

use search::query::Query;
use search::query::term_matcher::TermMatcher;
use search::query::term_scorer::TermScorer;
use search::query::parser::{QueryParseContext, QueryParseError};
use search::query::parser::utils::{parse_string, parse_float, Operator, parse_operator, parse_field_and_boost};

pub fn parse(context: &QueryParseContext, json: &Json) -> Result<Query, QueryParseError> {
    let object = try!(json.as_object().ok_or(QueryParseError::ExpectedObject));

    // Get configuration
    let mut fields_with_boosts = Vec::new();
    let mut query = String::new();
    let mut boost = 1.0f64;
    let mut operator = Operator::Or;

    let mut has_fields_key = false;
    let mut has_query_key = false;

    for (key, val) in object.iter() {
        match key.as_ref() {
            "fields" => {
                has_fields_key = true;

                match *val {
                    Json::Array(ref array) => {
                        for field in array.iter() {
                            fields_with_boosts.push(try!(parse_field_and_boost(field)));
                        }
                    }
                    _ => return Err(QueryParseError::ExpectedArray)
                }
            }
            "query" => {
                has_query_key = true;
                query = try!(parse_string(val));
            }
            "boost" => {
                boost = try!(parse_float(val));
            }
            "operator" => {
                operator = try!(parse_operator(val))
            }
            _ => return Err(QueryParseError::UnrecognisedKey(key.clone()))
        }
    }

    if !has_fields_key {
        return Err(QueryParseError::ExpectedKey("fields"))
    }

    if !has_query_key {
        return Err(QueryParseError::ExpectedKey("query"))
    }

    // Convert query string into term query objects
    let mut field_queries = Vec::new();
    for (field_name, field_boost) in fields_with_boosts {
        // Get mapping for field
        let field_mapping = match context.mappings {
            Some(mappings) => mappings.get_field(&field_name),
            None => None,
        };

        // Tokenise query string
        let tokens = match field_mapping {
            Some(ref field_mapping) => {
                field_mapping.process_value_for_query(Json::String(query.clone()))
            }
            None => {
                // TODO: Raise error?
                warn!("Unknown field: {}", field_name);

                FieldMapping::default().process_value_for_query(Json::String(query.clone()))
            }
        };

        let tokens = match tokens {
            Some(tokens) => tokens,
            None => {
                // Couldn't convert the passed in value into tokens
                // TODO: Raise error
                warn!("Unprocessable query: {}", query);

                vec![]
            }
        };

        let mut term_queries = Vec::new();
        for token in tokens {
            term_queries.push(Query::MatchTerm {
                field: field_name.clone(),
                term: token.term,
                matcher: TermMatcher::Exact,
                scorer: TermScorer::default(),
            });
        }

        let mut field_query = match operator {
            Operator::Or => {
                Query::new_disjunction(term_queries)
            }
            Operator::And => {
                Query::new_conjunction(term_queries)
            }
        };

        // Add boost
        field_query = Query::new_score(field_query, field_boost, 0.0f64);

        field_queries.push(field_query);
    }

    let mut query = Query::new_disjunction_max(field_queries);

    // Add boost
    query = Query::new_score(query, boost, 0.0f64);

    return Ok(query);
}

#[cfg(test)]
mod tests {
    use rustc_serialize::json::Json;

    use search::term::Term;
    use search::query::Query;
    use search::query::term_matcher::TermMatcher;
    use search::query::term_scorer::TermScorer;
    use search::query::parser::{QueryParseContext, QueryParseError};

    use super::parse;

    #[test]
    fn test_multi_match_query() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::MatchTerm {
                    field: "bar".to_string(),
                    term: Term::String("foo".to_string()),
                    matcher: TermMatcher::Exact,
                    scorer: TermScorer::default(),
                },
                Query::MatchTerm {
                    field: "baz".to_string(),
                    term: Term::String("foo".to_string()),
                    matcher: TermMatcher::Exact,
                    scorer: TermScorer::default(),
                }
            ],
        }));
    }

    #[test]
    fn test_multi_term_multi_match_query() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"hello world\",
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Disjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "bar".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                },
                Query::Disjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("hello".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("world".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_with_boost() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2.0
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::Score {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::MatchTerm {
                        field: "bar".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    },
                    Query::MatchTerm {
                        field: "baz".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    }
                ],
            }),
            mul: 2.0f64,
            add: 0.0f64,
        }));
    }

    #[test]
    fn test_with_boost_integer() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": 2
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::Score {
            query: Box::new(Query::DisjunctionMax {
                queries: vec![
                    Query::MatchTerm {
                        field: "bar".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    },
                    Query::MatchTerm {
                        field: "baz".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    }
                ],
            }),
            mul: 2.0f64,
            add: 0.0f64,
        }));
    }

    #[test]
    fn test_with_field_boost() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar^2\", \"baz^1.0\"]
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Score {
                    query: Box::new(Query::MatchTerm {
                        field: "bar".to_string(),
                        term: Term::String("foo".to_string()),
                        matcher: TermMatcher::Exact,
                        scorer: TermScorer::default(),
                    }),
                    mul: 2.0f64,
                    add: 0.0f64,
                },
                Query::MatchTerm {
                    field: "baz".to_string(),
                    term: Term::String("foo".to_string()),
                    matcher: TermMatcher::Exact,
                    scorer: TermScorer::default(),
                }
            ],
        }));
    }

    #[test]
    fn test_with_and_operator() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo bar\",
            \"fields\": [\"baz\", \"quux\"],
            \"operator\": \"and\"
        }
        ").unwrap());

        assert_eq!(query, Ok(Query::DisjunctionMax {
            queries: vec![
                Query::Conjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "baz".to_string(),
                            term: Term::String("bar".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                },
                Query::Conjunction {
                    queries: vec![
                        Query::MatchTerm {
                            field: "quux".to_string(),
                            term: Term::String("foo".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        },
                        Query::MatchTerm {
                            field: "quux".to_string(),
                            term: Term::String("bar".to_string()),
                            matcher: TermMatcher::Exact,
                            scorer: TermScorer::default(),
                        }
                    ],
                }
            ],
        }));
    }

    #[test]
    fn test_gives_error_for_incorrect_type() {
        // String
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        \"foo\"
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Array
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        [
            \"foo\"
        ]
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Integer
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        123
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));

        // Float
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        123.1234
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedObject));
    }

    #[test]
    fn test_gives_error_for_incorrect_query_type() {
        // Object
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": {
                \"foo\": \"bar\"
            },
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Array
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": [\"foo\"],
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Integer
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": 123,
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));

        // Float
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": 123.456,
            \"fields\": [\"bar\", \"baz\"]
        }        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedString));
    }

    #[test]
    fn test_gives_error_for_incorrect_fields_type() {
        // String
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": \"bar\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Object
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": {
                \"value\": [\"bar\", \"baz\"]
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Integer
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));

        // Float
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": 123.456
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedArray));
    }

    #[test]
    fn test_gives_error_for_incorrect_boost_type() {
        // String
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": \"2\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Array
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": [2]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));

        // Object
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"boost\": {
                \"value\": 2
            }
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedFloat));
    }

    #[test]
    fn test_gives_error_for_missing_query() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"fields\": [\"bar\", \"baz\"]
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("query")));
    }

    #[test]
    fn test_gives_error_for_missing_fields() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::ExpectedKey("fields")));
    }

    #[test]
    fn test_gives_error_for_extra_key() {
        let query = parse(&QueryParseContext::new(), &Json::from_str("
        {
            \"query\": \"foo\",
            \"fields\": [\"bar\", \"baz\"],
            \"hello\": \"world\"
        }
        ").unwrap());

        assert_eq!(query, Err(QueryParseError::UnrecognisedKey("hello".to_string())));
    }
}
<|endoftext|>