<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use persistent_list::PersistentList;
use std::cmp::{max, min};
use std::fmt;
use std::i32;
use style::computed_values::float;
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};

/// The kind of float: left or right.
#[derive(Clone, RustcEncodable, Debug, Copy)]
pub enum FloatKind {
    Left,
    Right
}

impl FloatKind {
    pub fn from_property(property: float::T) -> Option<FloatKind> {
        match property {
            float::T::none => None,
            float::T::left => Some(FloatKind::Left),
            float::T::right => Some(FloatKind::Right),
        }
    }
}

/// The kind of clearance: left, right, or both.
#[derive(Copy, Clone)]
pub enum ClearType {
    Left,
    Right,
    Both,
}

/// Information about a single float.
#[derive(Clone, Copy)]
struct Float {
    /// The boundaries of this float.
    bounds: LogicalRect<Au>,
    /// The kind of float: left or right.
    kind: FloatKind,
}

impl fmt::Debug for Float {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "bounds={:?} kind={:?}", self.bounds, self.kind)
    }
}

/// Information about the floats next to a flow.
#[derive(Clone)]
struct FloatList {
    /// Information about each of the floats here.
    floats: PersistentList<Float>,
    /// Cached copy of the maximum block-start offset of the float.
    max_block_start: Au,
}

impl FloatList {
    fn new() -> FloatList {
        FloatList {
            floats: PersistentList::new(),
            max_block_start: Au(0),
        }
    }

    /// Returns true if the list is allocated and false otherwise. If false, there are guaranteed
    /// not to be any floats.
    fn is_present(&self) -> bool {
        self.floats.len() > 0
    }
}

impl fmt::Debug for FloatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "max_block_start={:?} floats={}", self.max_block_start, self.floats.len())
    }
}

/// All the information necessary to place a float.
pub struct PlacementInfo {
    /// The dimensions of the float.
    pub size: LogicalSize<Au>,
    /// The minimum block-start of the float, as determined by earlier elements.
    pub ceiling: Au,
    /// The maximum inline-end position of the float, generally determined by the containing block.
    pub max_inline_size: Au,
    /// The kind of float.
    pub kind: FloatKind
}

impl fmt::Debug for PlacementInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "size={:?} ceiling={:?} max_inline_size={:?} kind={:?}",
               self.size,
               self.ceiling,
               self.max_inline_size,
               self.kind)
    }
}

fn range_intersect(block_start_1: Au, block_end_1: Au, block_start_2: Au, block_end_2: Au) -> (Au, Au) {
    (max(block_start_1, block_start_2), min(block_end_1, block_end_2))
}

/// Encapsulates information about floats. This is optimized to avoid allocation if there are
/// no floats, and to avoid copying when translating the list of floats downward.
#[derive(Clone)]
pub struct Floats {
    /// The list of floats.
    list: FloatList,
    /// The offset of the flow relative to the first float.
    offset: LogicalSize<Au>,
    /// The writing mode of these floats.
    pub writing_mode: WritingMode,
}

impl fmt::Debug for Floats {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if !self.list.is_present() {
            write!(f, "[empty]")
        } else {
            write!(f, "offset={:?} floats={:?}", self.offset, self.list)
        }
    }
}

impl Floats {
    /// Creates a new `Floats` object.
    pub fn new(writing_mode: WritingMode) -> Floats {
        Floats {
            list: FloatList::new(),
            offset: LogicalSize::zero(writing_mode),
            writing_mode: writing_mode,
        }
    }

    /// Adjusts the recorded offset of the flow relative to the first float.
    pub fn translate(&mut self, delta: LogicalSize<Au>) {
        self.offset = self.offset + delta
    }

    /// Returns the position of the last float in flow coordinates.
    pub fn last_float_pos(&self) -> Option<LogicalRect<Au>> {
        match self.list.floats.front() {
            None => None,
            Some(float) => Some(float.bounds.translate_by_size(self.offset)),
        }
    }

    /// Returns a rectangle that encloses the region from block-start to block-start + block-size,
    /// with inline-size small enough that it doesn't collide with any floats. max_x is the
    /// inline-size beyond which floats have no effect. (Generally this is the containing block
    /// inline-size.)
    pub fn available_rect(&self, block_start: Au, block_size: Au, max_x: Au)
                          -> Option<LogicalRect<Au>> {
        let list = &self.list;
        let block_start = block_start - self.offset.block;

        debug!("available_rect: trying to find space at {:?}", block_start);

        // Relevant dimensions for the inline-end-most inline-start float
        let mut max_inline_start = Au(0) - self.offset.inline;
        let mut l_block_start = None;
        let mut l_block_end = None;
        // Relevant dimensions for the inline-start-most inline-end float
        let mut min_inline_end = max_x - self.offset.inline;
        let mut r_block_start = None;
        let mut r_block_end = None;

        // Find the float collisions for the given range in the block direction.
        for float in list.floats.iter() {
            debug!("available_rect: Checking for collision against float");
            let float_pos = float.bounds.start;
            let float_size = float.bounds.size;

            debug!("float_pos: {:?}, float_size: {:?}", float_pos, float_size);
            match float.kind {
                FloatKind::Left if float_pos.i + float_size.inline > max_inline_start &&
                        float_pos.b + float_size.block > block_start &&
                        float_pos.b < block_start + block_size => {
                    max_inline_start = float_pos.i + float_size.inline;

                    l_block_start = Some(float_pos.b);
                    l_block_end = Some(float_pos.b + float_size.block);

                    debug!("available_rect: collision with inline_start float: new \
                            max_inline_start is {:?}",
                           max_inline_start);
                }
                FloatKind::Right if float_pos.i < min_inline_end &&
                       float_pos.b + float_size.block > block_start &&
                       float_pos.b < block_start + block_size => {
                    min_inline_end = float_pos.i;

                    r_block_start = Some(float_pos.b);
                    r_block_end = Some(float_pos.b + float_size.block);
                    debug!("available_rect: collision with inline_end float: new min_inline_end \
                            is {:?}",
                            min_inline_end);
                }
                FloatKind::Left | FloatKind::Right => {}
            }
        }

        // Extend the vertical range of the rectangle to the closest floats.
        // If there are floats on both sides, take the intersection of the
        // two areas. Also make sure we never return a block-start smaller than the
        // given upper bound.
        let (block_start, block_end) = match (r_block_start,
                                              r_block_end,
                                              l_block_start,
                                              l_block_end) {
            (Some(r_block_start), Some(r_block_end), Some(l_block_start), Some(l_block_end)) => {
                range_intersect(max(block_start, r_block_start),
                                r_block_end,
                                max(block_start, l_block_start),
                                l_block_end)
            }
            (None, None, Some(l_block_start), Some(l_block_end)) => {
                (max(block_start, l_block_start), l_block_end)
            }
            (Some(r_block_start), Some(r_block_end), None, None) => {
                (max(block_start, r_block_start), r_block_end)
            }
            (None, None, None, None) => return None,
            _ => panic!("Reached unreachable state when computing float area")
        };

        // FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to
        // return negative inline-sizes since we check against that inline-end away, but we should
        // still understand why they occur and add a stronger assertion here.
        // assert!(max_inline-start < min_inline-end);

        assert!(block_start <= block_end, "Float position error");

        Some(LogicalRect::new(self.writing_mode,
                              max_inline_start + self.offset.inline,
                              block_start + self.offset.block,
                              min_inline_end - max_inline_start,
                              block_end - block_start))
    }

    /// Adds a new float to the list.
    pub fn add_float(&mut self, info: &PlacementInfo) {
        let new_info;
        {
            new_info = PlacementInfo {
                size: info.size,
                ceiling: max(info.ceiling, self.list.max_block_start + self.offset.block),
                max_inline_size: info.max_inline_size,
                kind: info.kind
            }
        }

        debug!("add_float: added float with info {:?}", new_info);

        let new_float = Float {
            bounds: LogicalRect::from_point_size(
                self.writing_mode,
                self.place_between_floats(&new_info).start - self.offset,
                info.size,
            ),
            kind: info.kind
        };

        self.list.floats = self.list.floats.prepend_elem(new_float);
        self.list.max_block_start = max(self.list.max_block_start, new_float.bounds.start.b);
    }

    /// Given the three sides of the bounding rectangle in the block-start direction, finds the
    /// largest block-size that will result in the rectangle not colliding with any floats. Returns
    /// `None` if that block-size is infinite.
    fn max_block_size_for_bounds(&self, inline_start: Au, block_start: Au, inline_size: Au)
                                 -> Option<Au> {
        let list = &self.list;

        let block_start = block_start - self.offset.block;
        let inline_start = inline_start - self.offset.inline;
        let mut max_block_size = None;

        for float in list.floats.iter() {
            if float.bounds.start.b + float.bounds.size.block > block_start &&
                   float.bounds.start.i + float.bounds.size.inline > inline_start &&
                   float.bounds.start.i < inline_start + inline_size {
               let new_y = float.bounds.start.b;
               max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
            }
        }

        max_block_size.map(|h| h + self.offset.block)
    }

    /// Given placement information, finds the closest place a fragment can be positioned without
    /// colliding with any floats.
    pub fn place_between_floats(&self, info: &PlacementInfo) -> LogicalRect<Au> {
        debug!("place_between_floats: Placing object with {:?}", info.size);

        // If no floats, use this fast path.
        if !self.list.is_present() {
            match info.kind {
                FloatKind::Left => {
                    return LogicalRect::new(
                        self.writing_mode,
                        Au(0),
                        info.ceiling,
                        info.max_inline_size,
                        Au(i32::MAX))
                }
                FloatKind::Right => {
                    return LogicalRect::new(
                        self.writing_mode,
                        info.max_inline_size - info.size.inline,
                        info.ceiling,
                        info.max_inline_size,
                        Au(i32::MAX))
                }
            }
        }

        // Can't go any higher than previous floats or previous elements in the document.
        let mut float_b = info.ceiling;
        loop {
            let maybe_location = self.available_rect(float_b,
                                                     info.size.block,
                                                     info.max_inline_size);
            debug!("place_float: got available rect: {:?} for block-pos: {:?}",
                   maybe_location,
                   float_b);
            match maybe_location {
                // If there are no floats blocking us, return the current location
                // TODO(eatkinson): integrate with overflow
                None => {
                    return match info.kind {
                        FloatKind::Left => {
                            LogicalRect::new(
                                self.writing_mode,
                                Au(0),
                                float_b,
                                info.max_inline_size,
                                Au(i32::MAX))
                        }
                        FloatKind::Right => {
                            LogicalRect::new(
                                self.writing_mode,
                                info.max_inline_size - info.size.inline,
                                float_b,
                                info.max_inline_size,
                                Au(i32::MAX))
                        }
                    }
                }
                Some(rect) => {
                    assert!(rect.start.b + rect.size.block != float_b,
                            "Non-terminating float placement");

                    // Place here if there is enough room
                    if rect.size.inline >= info.size.inline {
                        let block_size = self.max_block_size_for_bounds(rect.start.i,
                                                                        rect.start.b,
                                                                        rect.size.inline);
                        let block_size = block_size.unwrap_or(Au(i32::MAX));
                        return match info.kind {
                            FloatKind::Left => {
                                LogicalRect::new(
                                    self.writing_mode,
                                    rect.start.i,
                                    float_b,
                                    rect.size.inline,
                                    block_size)
                            }
                            FloatKind::Right => {
                                LogicalRect::new(
                                    self.writing_mode,
                                    rect.start.i + rect.size.inline - info.size.inline,
                                    float_b,
                                    rect.size.inline,
                                    block_size)
                            }
                        }
                    }

                    // Try to place at the next-lowest location.
                    // Need to be careful of fencepost errors.
                    float_b = rect.start.b + rect.size.block;
                }
            }
        }
    }

    pub fn clearance(&self, clear: ClearType) -> Au {
        let list = &self.list;
        let mut clearance = Au(0);
        for float in list.floats.iter() {
            match (clear, float.kind) {
                (ClearType::Left, FloatKind::Left) |
                (ClearType::Right, FloatKind::Right) |
                (ClearType::Both, _) => {
                    let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
                    clearance = max(clearance, b);
                }
                _ => {}
            }
        }
        clearance
    }
}
<commit_msg>layout: Allow floats to have negative ceilings due to negative margins.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use persistent_list::PersistentList;
use std::cmp::{max, min};
use std::fmt;
use std::i32;
use style::computed_values::float;
use style::logical_geometry::{LogicalRect, LogicalSize, WritingMode};

/// The kind of float: left or right.
#[derive(Clone, RustcEncodable, Debug, Copy)]
pub enum FloatKind {
    Left,
    Right
}

impl FloatKind {
    pub fn from_property(property: float::T) -> Option<FloatKind> {
        match property {
            float::T::none => None,
            float::T::left => Some(FloatKind::Left),
            float::T::right => Some(FloatKind::Right),
        }
    }
}

/// The kind of clearance: left, right, or both.
#[derive(Copy, Clone)]
pub enum ClearType {
    Left,
    Right,
    Both,
}

/// Information about a single float.
#[derive(Clone, Copy)]
struct Float {
    /// The boundaries of this float.
    bounds: LogicalRect<Au>,
    /// The kind of float: left or right.
    kind: FloatKind,
}

impl fmt::Debug for Float {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "bounds={:?} kind={:?}", self.bounds, self.kind)
    }
}

/// Information about the floats next to a flow.
#[derive(Clone)]
struct FloatList {
    /// Information about each of the floats here.
    floats: PersistentList<Float>,
    /// Cached copy of the maximum block-start offset of the float.
    max_block_start: Option<Au>,
}

impl FloatList {
    fn new() -> FloatList {
        FloatList {
            floats: PersistentList::new(),
            max_block_start: None,
        }
    }

    /// Returns true if the list is allocated and false otherwise. If false, there are guaranteed
    /// not to be any floats.
    fn is_present(&self) -> bool {
        self.floats.len() > 0
    }
}

impl fmt::Debug for FloatList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "max_block_start={:?} floats={}", self.max_block_start, self.floats.len())
    }
}

/// All the information necessary to place a float.
pub struct PlacementInfo {
    /// The dimensions of the float.
    pub size: LogicalSize<Au>,
    /// The minimum block-start of the float, as determined by earlier elements.
    pub ceiling: Au,
    /// The maximum inline-end position of the float, generally determined by the containing block.
    pub max_inline_size: Au,
    /// The kind of float.
    pub kind: FloatKind
}

impl fmt::Debug for PlacementInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "size={:?} ceiling={:?} max_inline_size={:?} kind={:?}",
               self.size,
               self.ceiling,
               self.max_inline_size,
               self.kind)
    }
}

fn range_intersect(block_start_1: Au, block_end_1: Au, block_start_2: Au, block_end_2: Au) -> (Au, Au) {
    (max(block_start_1, block_start_2), min(block_end_1, block_end_2))
}

/// Encapsulates information about floats. This is optimized to avoid allocation if there are
/// no floats, and to avoid copying when translating the list of floats downward.
#[derive(Clone)]
pub struct Floats {
    /// The list of floats.
    list: FloatList,
    /// The offset of the flow relative to the first float.
    offset: LogicalSize<Au>,
    /// The writing mode of these floats.
    pub writing_mode: WritingMode,
}

impl fmt::Debug for Floats {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if !self.list.is_present() {
            write!(f, "[empty]")
        } else {
            write!(f, "offset={:?} floats={:?}", self.offset, self.list)
        }
    }
}

impl Floats {
    /// Creates a new `Floats` object.
    pub fn new(writing_mode: WritingMode) -> Floats {
        Floats {
            list: FloatList::new(),
            offset: LogicalSize::zero(writing_mode),
            writing_mode: writing_mode,
        }
    }

    /// Adjusts the recorded offset of the flow relative to the first float.
    pub fn translate(&mut self, delta: LogicalSize<Au>) {
        self.offset = self.offset + delta
    }

    /// Returns the position of the last float in flow coordinates.
    pub fn last_float_pos(&self) -> Option<LogicalRect<Au>> {
        match self.list.floats.front() {
            None => None,
            Some(float) => Some(float.bounds.translate_by_size(self.offset)),
        }
    }

    /// Returns a rectangle that encloses the region from block-start to block-start + block-size,
    /// with inline-size small enough that it doesn't collide with any floats. max_x is the
    /// inline-size beyond which floats have no effect. (Generally this is the containing block
    /// inline-size.)
    pub fn available_rect(&self, block_start: Au, block_size: Au, max_x: Au)
                          -> Option<LogicalRect<Au>> {
        let list = &self.list;
        let block_start = block_start - self.offset.block;

        debug!("available_rect: trying to find space at {:?}", block_start);

        // Relevant dimensions for the inline-end-most inline-start float
        let mut max_inline_start = Au(0) - self.offset.inline;
        let mut l_block_start = None;
        let mut l_block_end = None;
        // Relevant dimensions for the inline-start-most inline-end float
        let mut min_inline_end = max_x - self.offset.inline;
        let mut r_block_start = None;
        let mut r_block_end = None;

        // Find the float collisions for the given range in the block direction.
        for float in list.floats.iter() {
            debug!("available_rect: Checking for collision against float");
            let float_pos = float.bounds.start;
            let float_size = float.bounds.size;

            debug!("float_pos: {:?}, float_size: {:?}", float_pos, float_size);
            match float.kind {
                FloatKind::Left if float_pos.i + float_size.inline > max_inline_start &&
                        float_pos.b + float_size.block > block_start &&
                        float_pos.b < block_start + block_size => {
                    max_inline_start = float_pos.i + float_size.inline;

                    l_block_start = Some(float_pos.b);
                    l_block_end = Some(float_pos.b + float_size.block);

                    debug!("available_rect: collision with inline_start float: new \
                            max_inline_start is {:?}",
                           max_inline_start);
                }
                FloatKind::Right if float_pos.i < min_inline_end &&
                       float_pos.b + float_size.block > block_start &&
                       float_pos.b < block_start + block_size => {
                    min_inline_end = float_pos.i;

                    r_block_start = Some(float_pos.b);
                    r_block_end = Some(float_pos.b + float_size.block);
                    debug!("available_rect: collision with inline_end float: new min_inline_end \
                            is {:?}",
                            min_inline_end);
                }
                FloatKind::Left | FloatKind::Right => {}
            }
        }

        // Extend the vertical range of the rectangle to the closest floats.
        // If there are floats on both sides, take the intersection of the
        // two areas. Also make sure we never return a block-start smaller than the
        // given upper bound.
        let (block_start, block_end) = match (r_block_start,
                                              r_block_end,
                                              l_block_start,
                                              l_block_end) {
            (Some(r_block_start), Some(r_block_end), Some(l_block_start), Some(l_block_end)) => {
                range_intersect(max(block_start, r_block_start),
                                r_block_end,
                                max(block_start, l_block_start),
                                l_block_end)
            }
            (None, None, Some(l_block_start), Some(l_block_end)) => {
                (max(block_start, l_block_start), l_block_end)
            }
            (Some(r_block_start), Some(r_block_end), None, None) => {
                (max(block_start, r_block_start), r_block_end)
            }
            (None, None, None, None) => return None,
            _ => panic!("Reached unreachable state when computing float area")
        };

        // FIXME(eatkinson): This assertion is too strong and fails in some cases. It is OK to
        // return negative inline-sizes since we check against that inline-end away, but we should
        // still understand why they occur and add a stronger assertion here.
        // assert!(max_inline-start < min_inline-end);

        assert!(block_start <= block_end, "Float position error");

        Some(LogicalRect::new(self.writing_mode,
                              max_inline_start + self.offset.inline,
                              block_start + self.offset.block,
                              min_inline_end - max_inline_start,
                              block_end - block_start))
    }

    /// Adds a new float to the list.
    pub fn add_float(&mut self, info: &PlacementInfo) {
        let new_info = PlacementInfo {
            size: info.size,
            ceiling: match self.list.max_block_start {
                None => info.ceiling,
                Some(max_block_start) => max(info.ceiling, max_block_start + self.offset.block),
            },
            max_inline_size: info.max_inline_size,
            kind: info.kind
        };

        debug!("add_float: added float with info {:?}", new_info);

        let new_float = Float {
            bounds: LogicalRect::from_point_size(
                self.writing_mode,
                self.place_between_floats(&new_info).start - self.offset,
                info.size,
            ),
            kind: info.kind
        };

        self.list.floats = self.list.floats.prepend_elem(new_float);
        self.list.max_block_start = match self.list.max_block_start {
            None => Some(new_float.bounds.start.b),
            Some(max_block_start) => Some(max(max_block_start, new_float.bounds.start.b)),
        }
    }

    /// Given the three sides of the bounding rectangle in the block-start direction, finds the
    /// largest block-size that will result in the rectangle not colliding with any floats. Returns
    /// `None` if that block-size is infinite.
    fn max_block_size_for_bounds(&self, inline_start: Au, block_start: Au, inline_size: Au)
                                 -> Option<Au> {
        let list = &self.list;

        let block_start = block_start - self.offset.block;
        let inline_start = inline_start - self.offset.inline;
        let mut max_block_size = None;

        for float in list.floats.iter() {
            if float.bounds.start.b + float.bounds.size.block > block_start &&
                   float.bounds.start.i + float.bounds.size.inline > inline_start &&
                   float.bounds.start.i < inline_start + inline_size {
               let new_y = float.bounds.start.b;
               max_block_size = Some(min(max_block_size.unwrap_or(new_y), new_y));
            }
        }

        max_block_size.map(|h| h + self.offset.block)
    }

    /// Given placement information, finds the closest place a fragment can be positioned without
    /// colliding with any floats.
    pub fn place_between_floats(&self, info: &PlacementInfo) -> LogicalRect<Au> {
        debug!("place_between_floats: Placing object with {:?}", info.size);

        // If no floats, use this fast path.
        if !self.list.is_present() {
            match info.kind {
                FloatKind::Left => {
                    return LogicalRect::new(
                        self.writing_mode,
                        Au(0),
                        info.ceiling,
                        info.max_inline_size,
                        Au(i32::MAX))
                }
                FloatKind::Right => {
                    return LogicalRect::new(
                        self.writing_mode,
                        info.max_inline_size - info.size.inline,
                        info.ceiling,
                        info.max_inline_size,
                        Au(i32::MAX))
                }
            }
        }

        // Can't go any higher than previous floats or previous elements in the document.
        let mut float_b = info.ceiling;
        loop {
            let maybe_location = self.available_rect(float_b,
                                                     info.size.block,
                                                     info.max_inline_size);
            debug!("place_float: got available rect: {:?} for block-pos: {:?}",
                   maybe_location,
                   float_b);
            match maybe_location {
                // If there are no floats blocking us, return the current location
                // TODO(eatkinson): integrate with overflow
                None => {
                    return match info.kind {
                        FloatKind::Left => {
                            LogicalRect::new(
                                self.writing_mode,
                                Au(0),
                                float_b,
                                info.max_inline_size,
                                Au(i32::MAX))
                        }
                        FloatKind::Right => {
                            LogicalRect::new(
                                self.writing_mode,
                                info.max_inline_size - info.size.inline,
                                float_b,
                                info.max_inline_size,
                                Au(i32::MAX))
                        }
                    }
                }
                Some(rect) => {
                    assert!(rect.start.b + rect.size.block != float_b,
                            "Non-terminating float placement");

                    // Place here if there is enough room
                    if rect.size.inline >= info.size.inline {
                        let block_size = self.max_block_size_for_bounds(rect.start.i,
                                                                        rect.start.b,
                                                                        rect.size.inline);
                        let block_size = block_size.unwrap_or(Au(i32::MAX));
                        return match info.kind {
                            FloatKind::Left => {
                                LogicalRect::new(
                                    self.writing_mode,
                                    rect.start.i,
                                    float_b,
                                    rect.size.inline,
                                    block_size)
                            }
                            FloatKind::Right => {
                                LogicalRect::new(
                                    self.writing_mode,
                                    rect.start.i + rect.size.inline - info.size.inline,
                                    float_b,
                                    rect.size.inline,
                                    block_size)
                            }
                        }
                    }

                    // Try to place at the next-lowest location.
                    // Need to be careful of fencepost errors.
                    float_b = rect.start.b + rect.size.block;
                }
            }
        }
    }

    pub fn clearance(&self, clear: ClearType) -> Au {
        let list = &self.list;
        let mut clearance = Au(0);
        for float in list.floats.iter() {
            match (clear, float.kind) {
                (ClearType::Left, FloatKind::Left) |
                (ClearType::Right, FloatKind::Right) |
                (ClearType::Both, _) => {
                    let b = self.offset.block + float.bounds.start.b + float.bounds.size.block;
                    clearance = max(clearance, b);
                }
                _ => {}
            }
        }
        clearance
    }
}
<|endoftext|>


<commit_before>extern crate thunderdome;

use thunderdome::graph::Graph;
use thunderdome::vertex::Vertex;
use thunderdome::path::Element;
use thunderdome::treepath::TreePath;

fn simple_graph() -> Box<Graph> {
    let mut g = Graph::new();
    let mut jon = g.add_vertex();
    let mut blake = g.add_vertex();
    let mut caleb = g.add_vertex();
    let mut mike = g.add_vertex();

    jon.add_edge(&mut blake, "friend");
    jon.add_edge(&mut caleb, "friend");
    blake.add_edge(&mut caleb, "friend");
    blake.add_edge(&mut jon, "tried_to_murder");

    jon.add_edge(&mut mike, "enemy");
    g
}

#[test]
fn test_create_tree() {

    let mut g = Graph::new();
    let v = g.add_vertex();
    let v2 = g.add_vertex();
    let v3 = g.add_vertex();

    let mut t = TreePath::from_vertex(v);
    t.add_child(Element::Vertex(v2));
    assert_eq!(1, t.element_count);

    t.add_child(Element::Vertex(v3));

    assert_eq!(2, t.child_count());

}

#[test]
fn test_iterate_over_leaves() {
    // make sure we can iterate over the tree's leaves and remove sub trees
    let g = simple_graph();
}
<commit_msg>fixed treepath bug<commit_after>extern crate thunderdome;

use thunderdome::graph::Graph;
use thunderdome::vertex::Vertex;
use thunderdome::path::Element;
use thunderdome::treepath::TreePath;

fn simple_graph() -> Box<Graph> {
    let mut g = Graph::new();
    let mut jon = g.add_vertex();
    let mut blake = g.add_vertex();
    let mut caleb = g.add_vertex();
    let mut mike = g.add_vertex();

    jon.add_edge(&mut blake, "friend");
    jon.add_edge(&mut caleb, "friend");
    blake.add_edge(&mut caleb, "friend");
    blake.add_edge(&mut jon, "tried_to_murder");

    jon.add_edge(&mut mike, "enemy");
    g
}

fn sample_tree() -> TreePath {
    let t = TreePath::new();
    // t.add_child()
    t
}

#[test]
fn test_create_tree() {

    let mut g = Graph::new();
    let v = g.add_vertex();
    let v2 = g.add_vertex();
    let v3 = g.add_vertex();

    let mut t = TreePath::from_vertex(v);
    t.add_child(Element::Vertex(v2));
    assert_eq!(1, t.element_count);

    t.add_child(Element::Vertex(v3));

    assert_eq!(2, t.child_count());

}

#[test]
fn test_iterate_over_leaves() {
    // make sure we can iterate over the tree's leaves and remove sub trees
}
<|endoftext|>


<commit_before>//! Flycheck provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.

#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]

use std::{
    fmt, io,
    process::{ChildStderr, ChildStdout, Command, Stdio},
    time::Duration,
};

use command_group::{CommandGroup, GroupChild};
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;
use stdx::process::streaming_output;

pub use cargo_metadata::diagnostic::{
    Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
    DiagnosticSpanMacroExpansion,
};

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationStrategy {
    Once,
    #[default]
    PerWorkspace,
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationLocation {
    Root(AbsPathBuf),
    #[default]
    Workspace,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig {
    CargoCommand {
        command: String,
        target_triples: Vec<String>,
        all_targets: bool,
        no_default_features: bool,
        all_features: bool,
        features: Vec<String>,
        extra_args: Vec<String>,
        extra_env: FxHashMap<String, String>,
    },
    CustomCommand {
        command: String,
        args: Vec<String>,
        extra_env: FxHashMap<String, String>,
        invocation_strategy: InvocationStrategy,
        invocation_location: InvocationLocation,
    },
}

impl fmt::Display for FlycheckConfig {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
            FlycheckConfig::CustomCommand { command, args, .. } => {
                write!(f, "{} {}", command, args.join(" "))
            }
        }
    }
}

/// Flycheck wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct FlycheckHandle {
    // XXX: drop order is significant
    sender: Sender<Restart>,
    _thread: jod_thread::JoinHandle,
    id: usize,
}

impl FlycheckHandle {
    pub fn spawn(
        id: usize,
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: AbsPathBuf,
    ) -> FlycheckHandle {
        let actor = FlycheckActor::new(id, sender, config, workspace_root);
        let (sender, receiver) = unbounded::<Restart>();
        let thread = jod_thread::Builder::new()
            .name("Flycheck".to_owned())
            .spawn(move || actor.run(receiver))
            .expect("failed to spawn thread");
        FlycheckHandle { id, sender, _thread: thread }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn restart(&self) {
        self.sender.send(Restart::Yes).unwrap();
    }

    /// Stop this cargo check worker.
    pub fn cancel(&self) {
        self.sender.send(Restart::No).unwrap();
    }

    pub fn id(&self) -> usize {
        self.id
    }
}

pub enum Message {
    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic },

    /// Request check progress notification to client
    Progress {
        /// Flycheck instance ID
        id: usize,
        progress: Progress,
    },
}

impl fmt::Debug for Message {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Message::AddDiagnostic { id, workspace_root, diagnostic } => f
                .debug_struct("AddDiagnostic")
                .field("id", id)
                .field("workspace_root", workspace_root)
                .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code))
                .finish(),
            Message::Progress { id, progress } => {
                f.debug_struct("Progress").field("id", id).field("progress", progress).finish()
            }
        }
    }
}

#[derive(Debug)]
pub enum Progress {
    DidStart,
    DidCheckCrate(String),
    DidFinish(io::Result<()>),
    DidCancel,
    DidFailToRestart(String),
}

enum Restart {
    Yes,
    No,
}

/// A [`FlycheckActor`] is a single check instance of a workspace.
struct FlycheckActor {
    /// The workspace id of this flycheck instance.
    id: usize,
    sender: Box<dyn Fn(Message) + Send>,
    config: FlycheckConfig,
    /// Either the workspace root of the workspace we are flychecking,
    /// or the project root of the project.
    root: AbsPathBuf,
    /// CargoHandle exists to wrap around the communication needed to be able to
    /// run `cargo check` without blocking. Currently the Rust standard library
    /// doesn't provide a way to read sub-process output without blocking, so we
    /// have to wrap sub-processes output handling in a thread and pass messages
    /// back over a channel.
    cargo_handle: Option<CargoHandle>,
}

enum Event {
    Restart(Restart),
    CheckEvent(Option<CargoMessage>),
}

impl FlycheckActor {
    fn new(
        id: usize,
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: AbsPathBuf,
    ) -> FlycheckActor {
        tracing::info!(%id, ?workspace_root, "Spawning flycheck");
        FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None }
    }

    fn report_progress(&self, progress: Progress) {
        self.send(Message::Progress { id: self.id, progress });
    }

    fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
        let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
        if let Ok(msg) = inbox.try_recv() {
            // give restarts a preference so check outputs don't block a restart or stop
            return Some(Event::Restart(msg));
        }
        select! {
            recv(inbox) -> msg => msg.ok().map(Event::Restart),
            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
        }
    }

    fn run(mut self, inbox: Receiver<Restart>) {
        'event: while let Some(event) = self.next_event(&inbox) {
            match event {
                Event::Restart(Restart::No) => {
                    self.cancel_check_process();
                }
                Event::Restart(Restart::Yes) => {
                    // Cancel the previously spawned process
                    self.cancel_check_process();
                    while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
                        // restart chained with a stop, so just cancel
                        if let Restart::No = restart {
                            continue 'event;
                        }
                    }

                    let command = self.check_command();
                    tracing::debug!(?command, "will restart flycheck");
                    match CargoHandle::spawn(command) {
                        Ok(cargo_handle) => {
                            tracing::debug!(
                                command = ?self.check_command(),
                                "did  restart flycheck"
                            );
                            self.cargo_handle = Some(cargo_handle);
                            self.report_progress(Progress::DidStart);
                        }
                        Err(error) => {
                            self.report_progress(Progress::DidFailToRestart(format!(
                                "Failed to run the following command: {:?} error={}",
                                self.check_command(),
                                error
                            )));
                        }
                    }
                }
                Event::CheckEvent(None) => {
                    tracing::debug!(flycheck_id = self.id, "flycheck finished");

                    // Watcher finished
                    let cargo_handle = self.cargo_handle.take().unwrap();
                    let res = cargo_handle.join();
                    if res.is_err() {
                        tracing::error!(
                            "Flycheck failed to run the following command: {:?}",
                            self.check_command()
                        );
                    }
                    self.report_progress(Progress::DidFinish(res));
                }
                Event::CheckEvent(Some(message)) => match message {
                    CargoMessage::CompilerArtifact(msg) => {
                        self.report_progress(Progress::DidCheckCrate(msg.target.name));
                    }

                    CargoMessage::Diagnostic(msg) => {
                        self.send(Message::AddDiagnostic {
                            id: self.id,
                            workspace_root: self.root.clone(),
                            diagnostic: msg,
                        });
                    }
                },
            }
        }
        // If we rerun the thread, we need to discard the previous check results first
        self.cancel_check_process();
    }

    fn cancel_check_process(&mut self) {
        if let Some(cargo_handle) = self.cargo_handle.take() {
            tracing::debug!(
                command = ?self.check_command(),
                "did  cancel flycheck"
            );
            cargo_handle.cancel();
            self.report_progress(Progress::DidCancel);
        }
    }

    fn check_command(&self) -> Command {
        let (mut cmd, args) = match &self.config {
            FlycheckConfig::CargoCommand {
                command,
                target_triples,
                no_default_features,
                all_targets,
                all_features,
                extra_args,
                features,
                extra_env,
            } => {
                let mut cmd = Command::new(toolchain::cargo());
                cmd.arg(command);
                cmd.current_dir(&self.root);
                cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
                    .arg(self.root.join("Cargo.toml").as_os_str());

                for target in target_triples {
                    cmd.args(&["--target", target.as_str()]);
                }
                if *all_targets {
                    cmd.arg("--all-targets");
                }
                if *all_features {
                    cmd.arg("--all-features");
                } else {
                    if *no_default_features {
                        cmd.arg("--no-default-features");
                    }
                    if !features.is_empty() {
                        cmd.arg("--features");
                        cmd.arg(features.join(" "));
                    }
                }
                cmd.envs(extra_env);
                (cmd, extra_args)
            }
            FlycheckConfig::CustomCommand {
                command,
                args,
                extra_env,
                invocation_strategy,
                invocation_location,
            } => {
                let mut cmd = Command::new(command);
                cmd.envs(extra_env);

                match invocation_location {
                    InvocationLocation::Workspace => {
                        match invocation_strategy {
                            InvocationStrategy::Once => {
                                cmd.current_dir(&self.root);
                            }
                            InvocationStrategy::PerWorkspace => {
                                // FIXME: cmd.current_dir(&affected_workspace);
                                cmd.current_dir(&self.root);
                            }
                        }
                    }
                    InvocationLocation::Root(root) => {
                        cmd.current_dir(root);
                    }
                }

                (cmd, args)
            }
        };

        cmd.args(args);
        cmd
    }

    fn send(&self, check_task: Message) {
        (self.sender)(check_task);
    }
}

struct JodChild(GroupChild);

/// A handle to a cargo process used for fly-checking.
struct CargoHandle {
    /// The handle to the actual cargo process. As we cannot cancel directly from with
    /// a read syscall dropping and therefore terminating the process is our best option.
    child: JodChild,
    thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
    receiver: Receiver<CargoMessage>,
}

impl CargoHandle {
    fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
        command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
        let mut child = command.group_spawn().map(JodChild)?;

        let stdout = child.0.inner().stdout.take().unwrap();
        let stderr = child.0.inner().stderr.take().unwrap();

        let (sender, receiver) = unbounded();
        let actor = CargoActor::new(sender, stdout, stderr);
        let thread = jod_thread::Builder::new()
            .name("CargoHandle".to_owned())
            .spawn(move || actor.run())
            .expect("failed to spawn thread");
        Ok(CargoHandle { child, thread, receiver })
    }

    fn cancel(mut self) {
        let _ = self.child.0.kill();
        let _ = self.child.0.wait();
    }

    fn join(mut self) -> io::Result<()> {
        let _ = self.child.0.kill();
        let exit_status = self.child.0.wait()?;
        let (read_at_least_one_message, error) = self.thread.join()?;
        if read_at_least_one_message || exit_status.success() {
            Ok(())
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!(
                "Cargo watcher failed, the command produced no valid metadata (exit code: {:?}):\n{}",
                exit_status, error
            )))
        }
    }
}

struct CargoActor {
    sender: Sender<CargoMessage>,
    stdout: ChildStdout,
    stderr: ChildStderr,
}

impl CargoActor {
    fn new(sender: Sender<CargoMessage>, stdout: ChildStdout, stderr: ChildStderr) -> CargoActor {
        CargoActor { sender, stdout, stderr }
    }

    fn run(self) -> io::Result<(bool, String)> {
        // We manually read a line at a time, instead of using serde's
        // stream deserializers, because the deserializer cannot recover
        // from an error, resulting in it getting stuck, because we try to
        // be resilient against failures.
        //
        // Because cargo only outputs one JSON object per line, we can
        // simply skip a line if it doesn't parse, which just ignores any
        // erroneous output.

        let mut error = String::new();
        let mut read_at_least_one_message = false;
        let output = streaming_output(
            self.stdout,
            self.stderr,
            &mut |line| {
                read_at_least_one_message = true;

                // Try to deserialize a message from Cargo or Rustc.
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
                    match message {
                        // Skip certain kinds of messages to only spend time on what's useful
                        JsonMessage::Cargo(message) => match message {
                            cargo_metadata::Message::CompilerArtifact(artifact)
                                if !artifact.fresh =>
                            {
                                self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap();
                            }
                            cargo_metadata::Message::CompilerMessage(msg) => {
                                self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
                            }
                            _ => (),
                        },
                        JsonMessage::Rustc(message) => {
                            self.sender.send(CargoMessage::Diagnostic(message)).unwrap();
                        }
                    }
                }
            },
            &mut |line| {
                error.push_str(line);
                error.push('\n');
            },
        );
        match output {
            Ok(_) => Ok((read_at_least_one_message, error)),
            Err(e) => Err(io::Error::new(e.kind(), format!("{:?}: {}", e, error))),
        }
    }
}

enum CargoMessage {
    CompilerArtifact(cargo_metadata::Artifact),
    Diagnostic(Diagnostic),
}

#[derive(Deserialize)]
#[serde(untagged)]
enum JsonMessage {
    Cargo(cargo_metadata::Message),
    Rustc(Diagnostic),
}
<commit_msg>Properly implement Drop for JodGroupChild<commit_after>//! Flycheck provides the functionality needed to run `cargo check` or
//! another compatible command (f.x. clippy) in a background thread and provide
//! LSP diagnostics based on the output of the command.

#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]

use std::{
    fmt, io,
    process::{ChildStderr, ChildStdout, Command, Stdio},
    time::Duration,
};

use command_group::{CommandGroup, GroupChild};
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use serde::Deserialize;
use stdx::process::streaming_output;

pub use cargo_metadata::diagnostic::{
    Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
    DiagnosticSpanMacroExpansion,
};

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationStrategy {
    Once,
    #[default]
    PerWorkspace,
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationLocation {
    Root(AbsPathBuf),
    #[default]
    Workspace,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig {
    CargoCommand {
        command: String,
        target_triples: Vec<String>,
        all_targets: bool,
        no_default_features: bool,
        all_features: bool,
        features: Vec<String>,
        extra_args: Vec<String>,
        extra_env: FxHashMap<String, String>,
    },
    CustomCommand {
        command: String,
        args: Vec<String>,
        extra_env: FxHashMap<String, String>,
        invocation_strategy: InvocationStrategy,
        invocation_location: InvocationLocation,
    },
}

impl fmt::Display for FlycheckConfig {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
            FlycheckConfig::CustomCommand { command, args, .. } => {
                write!(f, "{} {}", command, args.join(" "))
            }
        }
    }
}

/// Flycheck wraps the shared state and communication machinery used for
/// running `cargo check` (or other compatible command) and providing
/// diagnostics based on the output.
/// The spawned thread is shut down when this struct is dropped.
#[derive(Debug)]
pub struct FlycheckHandle {
    // XXX: drop order is significant
    sender: Sender<Restart>,
    _thread: jod_thread::JoinHandle,
    id: usize,
}

impl FlycheckHandle {
    pub fn spawn(
        id: usize,
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: AbsPathBuf,
    ) -> FlycheckHandle {
        let actor = FlycheckActor::new(id, sender, config, workspace_root);
        let (sender, receiver) = unbounded::<Restart>();
        let thread = jod_thread::Builder::new()
            .name("Flycheck".to_owned())
            .spawn(move || actor.run(receiver))
            .expect("failed to spawn thread");
        FlycheckHandle { id, sender, _thread: thread }
    }

    /// Schedule a re-start of the cargo check worker.
    pub fn restart(&self) {
        self.sender.send(Restart::Yes).unwrap();
    }

    /// Stop this cargo check worker.
    pub fn cancel(&self) {
        self.sender.send(Restart::No).unwrap();
    }

    pub fn id(&self) -> usize {
        self.id
    }
}

pub enum Message {
    /// Request adding a diagnostic with fixes included to a file
    AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic },

    /// Request check progress notification to client
    Progress {
        /// Flycheck instance ID
        id: usize,
        progress: Progress,
    },
}

impl fmt::Debug for Message {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Message::AddDiagnostic { id, workspace_root, diagnostic } => f
                .debug_struct("AddDiagnostic")
                .field("id", id)
                .field("workspace_root", workspace_root)
                .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code))
                .finish(),
            Message::Progress { id, progress } => {
                f.debug_struct("Progress").field("id", id).field("progress", progress).finish()
            }
        }
    }
}

#[derive(Debug)]
pub enum Progress {
    DidStart,
    DidCheckCrate(String),
    DidFinish(io::Result<()>),
    DidCancel,
    DidFailToRestart(String),
}

enum Restart {
    Yes,
    No,
}

/// A [`FlycheckActor`] is a single check instance of a workspace.
struct FlycheckActor {
    /// The workspace id of this flycheck instance.
    id: usize,
    sender: Box<dyn Fn(Message) + Send>,
    config: FlycheckConfig,
    /// Either the workspace root of the workspace we are flychecking,
    /// or the project root of the project.
    root: AbsPathBuf,
    /// CargoHandle exists to wrap around the communication needed to be able to
    /// run `cargo check` without blocking. Currently the Rust standard library
    /// doesn't provide a way to read sub-process output without blocking, so we
    /// have to wrap sub-processes output handling in a thread and pass messages
    /// back over a channel.
    cargo_handle: Option<CargoHandle>,
}

enum Event {
    Restart(Restart),
    CheckEvent(Option<CargoMessage>),
}

impl FlycheckActor {
    fn new(
        id: usize,
        sender: Box<dyn Fn(Message) + Send>,
        config: FlycheckConfig,
        workspace_root: AbsPathBuf,
    ) -> FlycheckActor {
        tracing::info!(%id, ?workspace_root, "Spawning flycheck");
        FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None }
    }

    fn report_progress(&self, progress: Progress) {
        self.send(Message::Progress { id: self.id, progress });
    }

    fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
        let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
        if let Ok(msg) = inbox.try_recv() {
            // give restarts a preference so check outputs don't block a restart or stop
            return Some(Event::Restart(msg));
        }
        select! {
            recv(inbox) -> msg => msg.ok().map(Event::Restart),
            recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
        }
    }

    fn run(mut self, inbox: Receiver<Restart>) {
        'event: while let Some(event) = self.next_event(&inbox) {
            match event {
                Event::Restart(Restart::No) => {
                    self.cancel_check_process();
                }
                Event::Restart(Restart::Yes) => {
                    // Cancel the previously spawned process
                    self.cancel_check_process();
                    while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
                        // restart chained with a stop, so just cancel
                        if let Restart::No = restart {
                            continue 'event;
                        }
                    }

                    let command = self.check_command();
                    tracing::debug!(?command, "will restart flycheck");
                    match CargoHandle::spawn(command) {
                        Ok(cargo_handle) => {
                            tracing::debug!(
                                command = ?self.check_command(),
                                "did  restart flycheck"
                            );
                            self.cargo_handle = Some(cargo_handle);
                            self.report_progress(Progress::DidStart);
                        }
                        Err(error) => {
                            self.report_progress(Progress::DidFailToRestart(format!(
                                "Failed to run the following command: {:?} error={}",
                                self.check_command(),
                                error
                            )));
                        }
                    }
                }
                Event::CheckEvent(None) => {
                    tracing::debug!(flycheck_id = self.id, "flycheck finished");

                    // Watcher finished
                    let cargo_handle = self.cargo_handle.take().unwrap();
                    let res = cargo_handle.join();
                    if res.is_err() {
                        tracing::error!(
                            "Flycheck failed to run the following command: {:?}",
                            self.check_command()
                        );
                    }
                    self.report_progress(Progress::DidFinish(res));
                }
                Event::CheckEvent(Some(message)) => match message {
                    CargoMessage::CompilerArtifact(msg) => {
                        self.report_progress(Progress::DidCheckCrate(msg.target.name));
                    }

                    CargoMessage::Diagnostic(msg) => {
                        self.send(Message::AddDiagnostic {
                            id: self.id,
                            workspace_root: self.root.clone(),
                            diagnostic: msg,
                        });
                    }
                },
            }
        }
        // If we rerun the thread, we need to discard the previous check results first
        self.cancel_check_process();
    }

    fn cancel_check_process(&mut self) {
        if let Some(cargo_handle) = self.cargo_handle.take() {
            tracing::debug!(
                command = ?self.check_command(),
                "did  cancel flycheck"
            );
            cargo_handle.cancel();
            self.report_progress(Progress::DidCancel);
        }
    }

    fn check_command(&self) -> Command {
        let (mut cmd, args) = match &self.config {
            FlycheckConfig::CargoCommand {
                command,
                target_triples,
                no_default_features,
                all_targets,
                all_features,
                extra_args,
                features,
                extra_env,
            } => {
                let mut cmd = Command::new(toolchain::cargo());
                cmd.arg(command);
                cmd.current_dir(&self.root);
                cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
                    .arg(self.root.join("Cargo.toml").as_os_str());

                for target in target_triples {
                    cmd.args(&["--target", target.as_str()]);
                }
                if *all_targets {
                    cmd.arg("--all-targets");
                }
                if *all_features {
                    cmd.arg("--all-features");
                } else {
                    if *no_default_features {
                        cmd.arg("--no-default-features");
                    }
                    if !features.is_empty() {
                        cmd.arg("--features");
                        cmd.arg(features.join(" "));
                    }
                }
                cmd.envs(extra_env);
                (cmd, extra_args)
            }
            FlycheckConfig::CustomCommand {
                command,
                args,
                extra_env,
                invocation_strategy,
                invocation_location,
            } => {
                let mut cmd = Command::new(command);
                cmd.envs(extra_env);

                match invocation_location {
                    InvocationLocation::Workspace => {
                        match invocation_strategy {
                            InvocationStrategy::Once => {
                                cmd.current_dir(&self.root);
                            }
                            InvocationStrategy::PerWorkspace => {
                                // FIXME: cmd.current_dir(&affected_workspace);
                                cmd.current_dir(&self.root);
                            }
                        }
                    }
                    InvocationLocation::Root(root) => {
                        cmd.current_dir(root);
                    }
                }

                (cmd, args)
            }
        };

        cmd.args(args);
        cmd
    }

    fn send(&self, check_task: Message) {
        (self.sender)(check_task);
    }
}

struct JodGroupChild(GroupChild);

impl Drop for JodGroupChild {
    fn drop(&mut self) {
        _ = self.0.kill();
        _ = self.0.wait();
    }
}

/// A handle to a cargo process used for fly-checking.
struct CargoHandle {
    /// The handle to the actual cargo process. As we cannot cancel directly from with
    /// a read syscall dropping and therefore terminating the process is our best option.
    child: JodGroupChild,
    thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
    receiver: Receiver<CargoMessage>,
}

impl CargoHandle {
    fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
        command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
        let mut child = command.group_spawn().map(JodGroupChild)?;

        let stdout = child.0.inner().stdout.take().unwrap();
        let stderr = child.0.inner().stderr.take().unwrap();

        let (sender, receiver) = unbounded();
        let actor = CargoActor::new(sender, stdout, stderr);
        let thread = jod_thread::Builder::new()
            .name("CargoHandle".to_owned())
            .spawn(move || actor.run())
            .expect("failed to spawn thread");
        Ok(CargoHandle { child, thread, receiver })
    }

    fn cancel(mut self) {
        let _ = self.child.0.kill();
        let _ = self.child.0.wait();
    }

    fn join(mut self) -> io::Result<()> {
        let _ = self.child.0.kill();
        let exit_status = self.child.0.wait()?;
        let (read_at_least_one_message, error) = self.thread.join()?;
        if read_at_least_one_message || exit_status.success() {
            Ok(())
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!(
                "Cargo watcher failed, the command produced no valid metadata (exit code: {:?}):\n{}",
                exit_status, error
            )))
        }
    }
}

struct CargoActor {
    sender: Sender<CargoMessage>,
    stdout: ChildStdout,
    stderr: ChildStderr,
}

impl CargoActor {
    fn new(sender: Sender<CargoMessage>, stdout: ChildStdout, stderr: ChildStderr) -> CargoActor {
        CargoActor { sender, stdout, stderr }
    }

    fn run(self) -> io::Result<(bool, String)> {
        // We manually read a line at a time, instead of using serde's
        // stream deserializers, because the deserializer cannot recover
        // from an error, resulting in it getting stuck, because we try to
        // be resilient against failures.
        //
        // Because cargo only outputs one JSON object per line, we can
        // simply skip a line if it doesn't parse, which just ignores any
        // erroneous output.

        let mut error = String::new();
        let mut read_at_least_one_message = false;
        let output = streaming_output(
            self.stdout,
            self.stderr,
            &mut |line| {
                read_at_least_one_message = true;

                // Try to deserialize a message from Cargo or Rustc.
                let mut deserializer = serde_json::Deserializer::from_str(line);
                deserializer.disable_recursion_limit();
                if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
                    match message {
                        // Skip certain kinds of messages to only spend time on what's useful
                        JsonMessage::Cargo(message) => match message {
                            cargo_metadata::Message::CompilerArtifact(artifact)
                                if !artifact.fresh =>
                            {
                                self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap();
                            }
                            cargo_metadata::Message::CompilerMessage(msg) => {
                                self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap();
                            }
                            _ => (),
                        },
                        JsonMessage::Rustc(message) => {
                            self.sender.send(CargoMessage::Diagnostic(message)).unwrap();
                        }
                    }
                }
            },
            &mut |line| {
                error.push_str(line);
                error.push('\n');
            },
        );
        match output {
            Ok(_) => Ok((read_at_least_one_message, error)),
            Err(e) => Err(io::Error::new(e.kind(), format!("{:?}: {}", e, error))),
        }
    }
}

enum CargoMessage {
    CompilerArtifact(cargo_metadata::Artifact),
    Diagnostic(Diagnostic),
}

#[derive(Deserialize)]
#[serde(untagged)]
enum JsonMessage {
    Cargo(cargo_metadata::Message),
    Rustc(Diagnostic),
}
<|endoftext|>


<commit_before>use std::{
    iter,
    sync::Arc,
    path::{Path, PathBuf},
};

use relative_path::{ RelativePath, RelativePathBuf};
use ra_arena::{impl_arena_id, Arena, RawId};

/// VfsRoot identifies a watched directory on the file system.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VfsRoot(pub RawId);
impl_arena_id!(VfsRoot);

/// Describes the contents of a single source root.
///
/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which
/// specifies the source root or as a function which takes a `PathBuf` and
/// returns `true` iff path belongs to the source root
struct RootData {
    path: PathBuf,
    // result of `root.canonicalize()` if that differs from `root`; `None` otherwise.
    canonical_path: Option<PathBuf>,
    excluded_dirs: Vec<RelativePathBuf>,
}

pub(crate) struct Roots {
    roots: Arena<VfsRoot, Arc<RootData>>,
}

impl Roots {
    pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots {
        let mut roots = Arena::default();
        // A hack to make nesting work.
        paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len()));
        paths.dedup();
        for (i, path) in paths.iter().enumerate() {
            let nested_roots =
                paths[..i].iter().filter_map(|it| rel_path(path, it)).collect::<Vec<_>>();

            let config = Arc::new(RootData::new(path.clone(), nested_roots));

            roots.alloc(config.clone());
        }
        Roots { roots }
    }
    pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> {
        self.iter().find_map(|root| {
            let rel_path = self.contains(root, path)?;
            Some((root, rel_path))
        })
    }
    pub(crate) fn len(&self) -> usize {
        self.roots.len()
    }
    pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = VfsRoot> + 'a {
        self.roots.iter().map(|(id, _)| id)
    }
    pub(crate) fn path(&self, root: VfsRoot) -> &Path {
        self.roots[root].path.as_path()
    }
    /// Checks if root contains a path and returns a root-relative path.
    pub(crate) fn contains(&self, root: VfsRoot, path: &Path) -> Option<RelativePathBuf> {
        let data = &self.roots[root];
        iter::once(&data.path)
            .chain(data.canonical_path.as_ref().into_iter())
            .find_map(|base| rel_path(base, path))
            .filter(|path| !data.excluded_dirs.contains(path))
            .filter(|path| !data.is_excluded(path))
    }
}

impl RootData {
    fn new(path: PathBuf, excluded_dirs: Vec<RelativePathBuf>) -> RootData {
        let mut canonical_path = path.canonicalize().ok();
        if Some(&path) == canonical_path.as_ref() {
            canonical_path = None;
        }
        RootData { path, canonical_path, excluded_dirs }
    }

    fn is_excluded(&self, path: &RelativePath) -> bool {
        if self.excluded_dirs.iter().any(|it| it == path) {
            return true;
        }
        // Ignore some common directories.
        //
        // FIXME: don't hard-code, specify at source-root creation time using
        // gitignore
        for (i, c) in path.components().enumerate() {
            if let relative_path::Component::Normal(c) = c {
                if (i == 0 && c == "target") || c == ".git" || c == "node_modules" {
                    return true;
                }
            }
        }

        match path.extension() {
            None | Some("rs") => false,
            _ => true,
        }
    }
}

fn rel_path(base: &Path, path: &Path) -> Option<RelativePathBuf> {
    let path = path.strip_prefix(base).ok()?;
    let path = RelativePathBuf::from_path(path).unwrap();
    Some(path)
}
<commit_msg>remove useless Arc<commit_after>use std::{
    iter,
    path::{Path, PathBuf},
};

use relative_path::{ RelativePath, RelativePathBuf};
use ra_arena::{impl_arena_id, Arena, RawId};

/// VfsRoot identifies a watched directory on the file system.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VfsRoot(pub RawId);
impl_arena_id!(VfsRoot);

/// Describes the contents of a single source root.
///
/// `RootConfig` can be thought of as a glob pattern like `src/**.rs` which
/// specifies the source root or as a function which takes a `PathBuf` and
/// returns `true` iff path belongs to the source root
struct RootData {
    path: PathBuf,
    // result of `root.canonicalize()` if that differs from `root`; `None` otherwise.
    canonical_path: Option<PathBuf>,
    excluded_dirs: Vec<RelativePathBuf>,
}

pub(crate) struct Roots {
    roots: Arena<VfsRoot, RootData>,
}

impl Roots {
    pub(crate) fn new(mut paths: Vec<PathBuf>) -> Roots {
        let mut roots = Arena::default();
        // A hack to make nesting work.
        paths.sort_by_key(|it| std::cmp::Reverse(it.as_os_str().len()));
        paths.dedup();
        for (i, path) in paths.iter().enumerate() {
            let nested_roots =
                paths[..i].iter().filter_map(|it| rel_path(path, it)).collect::<Vec<_>>();

            roots.alloc(RootData::new(path.clone(), nested_roots));
        }
        Roots { roots }
    }
    pub(crate) fn find(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf)> {
        self.iter().find_map(|root| {
            let rel_path = self.contains(root, path)?;
            Some((root, rel_path))
        })
    }
    pub(crate) fn len(&self) -> usize {
        self.roots.len()
    }
    pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = VfsRoot> + 'a {
        self.roots.iter().map(|(id, _)| id)
    }
    pub(crate) fn path(&self, root: VfsRoot) -> &Path {
        self.roots[root].path.as_path()
    }
    /// Checks if root contains a path and returns a root-relative path.
    pub(crate) fn contains(&self, root: VfsRoot, path: &Path) -> Option<RelativePathBuf> {
        let data = &self.roots[root];
        iter::once(&data.path)
            .chain(data.canonical_path.as_ref().into_iter())
            .find_map(|base| rel_path(base, path))
            .filter(|path| !data.excluded_dirs.contains(path))
            .filter(|path| !data.is_excluded(path))
    }
}

impl RootData {
    fn new(path: PathBuf, excluded_dirs: Vec<RelativePathBuf>) -> RootData {
        let mut canonical_path = path.canonicalize().ok();
        if Some(&path) == canonical_path.as_ref() {
            canonical_path = None;
        }
        RootData { path, canonical_path, excluded_dirs }
    }

    fn is_excluded(&self, path: &RelativePath) -> bool {
        if self.excluded_dirs.iter().any(|it| it == path) {
            return true;
        }
        // Ignore some common directories.
        //
        // FIXME: don't hard-code, specify at source-root creation time using
        // gitignore
        for (i, c) in path.components().enumerate() {
            if let relative_path::Component::Normal(c) = c {
                if (i == 0 && c == "target") || c == ".git" || c == "node_modules" {
                    return true;
                }
            }
        }

        match path.extension() {
            None | Some("rs") => false,
            _ => true,
        }
    }
}

fn rel_path(base: &Path, path: &Path) -> Option<RelativePathBuf> {
    let path = path.strip_prefix(base).ok()?;
    let path = RelativePathBuf::from_path(path).unwrap();
    Some(path)
}
<|endoftext|>


<commit_before>use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::Duration;

use super::*;

#[derive(Debug, Clone)]
pub(crate) struct Flusher {
    shutdown: Arc<Mutex<bool>>,
    sc: Arc<Condvar>,
    join_handle: Arc<Mutex<Option<std::thread::JoinHandle<()>>>>,
}

impl Flusher {
    /// Spawns a thread that periodically calls `callback` until dropped.
    pub(crate) fn new(
        name: String,
        context: Arc<Context>,
        flush_every_ms: u64,
    ) -> Flusher {
        #[allow(clippy::mutex_atomic)] // mutex used in CondVar below
        let shutdown = Arc::new(Mutex::new(false));
        let sc = Arc::new(Condvar::new());

        let join_handle = thread::Builder::new()
            .name(name)
            .spawn({
                let shutdown = shutdown.clone();
                let sc = sc.clone();
                move || run(shutdown, sc, context, flush_every_ms)
            })
            .unwrap();

        Flusher {
            shutdown,
            sc,
            join_handle: Arc::new(Mutex::new(Some(join_handle))),
        }
    }
}

fn run(
    shutdown: Arc<Mutex<bool>>,
    sc: Arc<Condvar>,
    context: Arc<Context>,
    flush_every_ms: u64,
) {
    let flush_every = Duration::from_millis(flush_every_ms);
    let mut shutdown = shutdown.lock().unwrap();
    while !*shutdown {
        let before = std::time::Instant::now();
        match context.pagecache.flush() {
            Ok(0) => {
                // we had no dirty data to flush,
                // so we can spend a little effort
                // cleaning up the file. try not to
                // spend more than half of our sleep
                // time rewriting pages though.
                while before.elapsed() < flush_every / 2 {
                    match context.pagecache.attempt_gc() {
                        Err(e) => {
                            error!(
                                "failed to clean file from async flush thread: {}",
                                e
                            );
                            return;
                        }
                        Ok(false) => break,
                        Ok(true) => {}
                    }
                }
            }
            Ok(_) => {
                // at some point, we may want to
                // put adaptive logic here to tune
                // sleeps based on how much work
                // we accomplished
            }
            Err(e) => {
                error!("failed to flush from periodic flush thread: {}", e);

                #[cfg(feature = "failpoints")]
                context.pagecache.set_failpoint(e);

                return;
            }
        }

        let sleep_duration = flush_every
            .checked_sub(before.elapsed())
            .unwrap_or(Duration::from_millis(1));

        shutdown = sc.wait_timeout(shutdown, sleep_duration).unwrap().0;
    }
}

impl Drop for Flusher {
    fn drop(&mut self) {
        let mut shutdown = self.shutdown.lock().unwrap();
        *shutdown = true;
        self.sc.notify_all();
        drop(shutdown);

        let mut join_handle_opt = self.join_handle.lock().unwrap();
        if let Some(join_handle) = join_handle_opt.take() {
            if let Err(e) = join_handle.join() {
                error!("error joining Periodic thread: {:?}", e);
            }
        }
    }
}
<commit_msg>Propagate failpoints when encountered while GC'ing pages<commit_after>use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::Duration;

use super::*;

#[derive(Debug, Clone)]
pub(crate) struct Flusher {
    shutdown: Arc<Mutex<bool>>,
    sc: Arc<Condvar>,
    join_handle: Arc<Mutex<Option<std::thread::JoinHandle<()>>>>,
}

impl Flusher {
    /// Spawns a thread that periodically calls `callback` until dropped.
    pub(crate) fn new(
        name: String,
        context: Arc<Context>,
        flush_every_ms: u64,
    ) -> Flusher {
        #[allow(clippy::mutex_atomic)] // mutex used in CondVar below
        let shutdown = Arc::new(Mutex::new(false));
        let sc = Arc::new(Condvar::new());

        let join_handle = thread::Builder::new()
            .name(name)
            .spawn({
                let shutdown = shutdown.clone();
                let sc = sc.clone();
                move || run(shutdown, sc, context, flush_every_ms)
            })
            .unwrap();

        Flusher {
            shutdown,
            sc,
            join_handle: Arc::new(Mutex::new(Some(join_handle))),
        }
    }
}

fn run(
    shutdown: Arc<Mutex<bool>>,
    sc: Arc<Condvar>,
    context: Arc<Context>,
    flush_every_ms: u64,
) {
    let flush_every = Duration::from_millis(flush_every_ms);
    let mut shutdown = shutdown.lock().unwrap();
    while !*shutdown {
        let before = std::time::Instant::now();
        match context.pagecache.flush() {
            Ok(0) => {
                // we had no dirty data to flush,
                // so we can spend a little effort
                // cleaning up the file. try not to
                // spend more than half of our sleep
                // time rewriting pages though.
                while before.elapsed() < flush_every / 2 {
                    match context.pagecache.attempt_gc() {
                        Err(e) => {
                            error!(
                                "failed to clean file from async flush thread: {}",
                                e
                            );

                            #[cfg(feature = "failpoints")]
                            context.pagecache.set_failpoint(e);

                            return;
                        }
                        Ok(false) => break,
                        Ok(true) => {}
                    }
                }
            }
            Ok(_) => {
                // at some point, we may want to
                // put adaptive logic here to tune
                // sleeps based on how much work
                // we accomplished
            }
            Err(e) => {
                error!("failed to flush from periodic flush thread: {}", e);

                #[cfg(feature = "failpoints")]
                context.pagecache.set_failpoint(e);

                return;
            }
        }

        let sleep_duration = flush_every
            .checked_sub(before.elapsed())
            .unwrap_or(Duration::from_millis(1));

        shutdown = sc.wait_timeout(shutdown, sleep_duration).unwrap().0;
    }
}

impl Drop for Flusher {
    fn drop(&mut self) {
        let mut shutdown = self.shutdown.lock().unwrap();
        *shutdown = true;
        self.sc.notify_all();
        drop(shutdown);

        let mut join_handle_opt = self.join_handle.lock().unwrap();
        if let Some(join_handle) = join_handle_opt.take() {
            if let Err(e) = join_handle.join() {
                error!("error joining Periodic thread: {:?}", e);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;

#[cfg(target_os="windows")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "mingw"], Some(&build_dir().join("lua-5.3.0")))
}

/// The comand to build lua, specialized for different OSes.
#[cfg(target_os="macos")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "macosx"], Some(&build_dir().join("lua-5.3.0")))
}

#[cfg(target_os="linux")]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    run_command_in_dir(&["make", "linux", "MYCFLAGS=-fPIC"], Some(&dir))
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    try!(run_command_in_dir(&["sed", "-e", "s/^MYCFLAGS=.*/MYCFLAGS=-fPIC/g", "-i", "bak", "src/Makefile"],
                            Some(&dir)));
    if cfg!(target_os = "freebsd") {
        run_command_in_dir(&["make", "freebsd"], Some(&dir))
    } else {
        run_command_in_dir(&["make", "bsd"], Some(&dir))
    }
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["wget", url], cwd)
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["fetch", url], cwd)
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["curl", "-O", url], cwd)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// current directory.
fn run_command(all_args: &[&str]) -> io::Result<()> {
    run_command_in_dir(all_args, None)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// directory 'cwd' or the current directory.
fn run_command_in_dir(all_args: &[&str], cwd: Option<&Path>) -> io::Result<()> {
    let command_name = all_args[0];
    let args = &all_args[1..];
    let mut command = Command::new(command_name);
    command.args(args);
    if let Some(cwd) = cwd {
        command.current_dir(cwd);
    }
    let status = try!(command.status());
    if !status.success() {
        return Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
        \t{}\n\
        did not run successfully.", all_args.connect(" "))));
    }
    Ok(())
}

/// The cargo output directory for builds from this.
fn build_dir() -> PathBuf {
    PathBuf::from(env::var("OUT_DIR").unwrap())
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() ->io::Result<()> {
    if !fs::metadata(
        &build_dir().join("lua-5.3.0").join("src").join("liblua.a")
    ).is_ok() {
        try!(fs::create_dir_all(&build_dir()));

        // Compile Lua.
        try!(fetch_in_dir("http://www.lua.org/ftp/lua-5.3.0.tar.gz",
                          Some(&build_dir())));
        try!(run_command_in_dir(&["tar", "xzvf", "lua-5.3.0.tar.gz"],
                                Some(&build_dir())));

        try!(build_lua());

        // Compile and run glue.c.
        let glue = build_dir().join("glue").to_string_lossy().into_owned();
        try!(run_command(&["gcc",
                         "-I", &build_dir().join("lua-5.3.0").join("src").to_string_lossy(),
                         &PathBuf::from("src").join("glue").join("glue.c").to_string_lossy(),
                         "-o", &glue]));
        try!(run_command(&[&glue, "src/ffi/glue.rs"]));
    }

    let mut build_dir_absolute = try!(env::current_dir());
    build_dir_absolute.push(&build_dir());
    build_dir_absolute.push("lua-5.3.0");
    build_dir_absolute.push("src");

    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}", build_dir_absolute.to_string_lossy());

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<commit_msg>Pass MYCFLAGS to make on bsd<commit_after>use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::process::Command;

#[cfg(target_os="windows")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "mingw"], Some(&build_dir().join("lua-5.3.0")))
}

/// The comand to build lua, specialized for different OSes.
#[cfg(target_os="macos")]
fn build_lua() -> io::Result<()> {
    run_command_in_dir(&["make", "macosx"], Some(&build_dir().join("lua-5.3.0")))
}

#[cfg(target_os="linux")]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    run_command_in_dir(&["make", "linux", "MYCFLAGS=-fPIC"], Some(&dir))
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn build_lua() -> io::Result<()> {
    let dir = build_dir().join("lua-5.3.0");
    if cfg!(target_os = "freebsd") {
        run_command_in_dir(&["make", "freebsd", "MYCFLAGS=-fPIC"], Some(&dir))
    } else {
        run_command_in_dir(&["make", "bsd", "MYCFLAGS=-fPIC"], Some(&dir))
    }
}

/// The command to fetch a URL (e.g. with wget) specialized for different
/// OSes.
#[cfg(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos")))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["wget", url], cwd)
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["fetch", url], cwd)
}

#[cfg(target_os = "macos")]
fn fetch_in_dir(url: &str, cwd: Option<&Path>) -> io::Result<()> {
    run_command_in_dir(&["curl", "-O", url], cwd)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// current directory.
fn run_command(all_args: &[&str]) -> io::Result<()> {
    run_command_in_dir(all_args, None)
}

/// Runs the command 'all_args[0]' with the arguments 'all_args[1..]' in the
/// directory 'cwd' or the current directory.
fn run_command_in_dir(all_args: &[&str], cwd: Option<&Path>) -> io::Result<()> {
    let command_name = all_args[0];
    let args = &all_args[1..];
    let mut command = Command::new(command_name);
    command.args(args);
    if let Some(cwd) = cwd {
        command.current_dir(cwd);
    }
    let status = try!(command.status());
    if !status.success() {
        return Err(io::Error::new(io::ErrorKind::Other, format!("The command\n\
        \t{}\n\
        did not run successfully.", all_args.connect(" "))));
    }
    Ok(())
}

/// The cargo output directory for builds from this.
fn build_dir() -> PathBuf {
    PathBuf::from(env::var("OUT_DIR").unwrap())
}

/// If a static Lua is not yet available from a prior run of this script, this
/// will download Lua and build it. The cargo configuration text to link
/// statically against lua.a is then printed to stdout.
fn prebuild() ->io::Result<()> {
    if !fs::metadata(
        &build_dir().join("lua-5.3.0").join("src").join("liblua.a")
    ).is_ok() {
        try!(fs::create_dir_all(&build_dir()));

        // Compile Lua.
        try!(fetch_in_dir("http://www.lua.org/ftp/lua-5.3.0.tar.gz",
                          Some(&build_dir())));
        try!(run_command_in_dir(&["tar", "xzvf", "lua-5.3.0.tar.gz"],
                                Some(&build_dir())));

        try!(build_lua());

        // Compile and run glue.c.
        let glue = build_dir().join("glue").to_string_lossy().into_owned();
        try!(run_command(&["gcc",
                         "-I", &build_dir().join("lua-5.3.0").join("src").to_string_lossy(),
                         &PathBuf::from("src").join("glue").join("glue.c").to_string_lossy(),
                         "-o", &glue]));
        try!(run_command(&[&glue, "src/ffi/glue.rs"]));
    }

    let mut build_dir_absolute = try!(env::current_dir());
    build_dir_absolute.push(&build_dir());
    build_dir_absolute.push("lua-5.3.0");
    build_dir_absolute.push("src");

    println!("cargo:rustc-link-lib=static=lua");
    println!("cargo:rustc-link-search=native={}", build_dir_absolute.to_string_lossy());

    Ok(())
}

fn main() {
    match prebuild() {
        Err(e) => panic!("Error: {}", e),
        Ok(()) => (),
    }
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::env;
use std::path::Path;

fn main() {
    Command::new("protoc")
        .args(&["--rust_out", "src/messages/", "src/messages/protobufs.proto"])
        .status()
        .unwrap();
    Command::new("protoc")
        .args(&["--rust_out", "src/client/messages/", "src/client/messages/protobufs.proto"])
        .status()
        .unwrap();
}
<commit_msg>Remove unused imports in build script<commit_after>use std::process::Command;

fn main() {
    Command::new("protoc")
        .args(&["--rust_out", "src/messages/", "src/messages/protobufs.proto"])
        .status()
        .unwrap();
    Command::new("protoc")
        .args(&["--rust_out", "src/client/messages/", "src/client/messages/protobufs.proto"])
        .status()
        .unwrap();
}
<|endoftext|>


<commit_before>//! Contains functions for computing SCCs and serializing graphs.
use std::io::{stderr, Read, Write, BufRead, BufReader, BufWriter};
use petgraph::{Graph};
use petgraph::algo::scc;
use petgraph::graph::NodeIndex;
use std::collections::HashMap;

fn load_graph<R>(source: &mut BufReader<R>) -> (Graph<u64, ()>, HashMap<u64, NodeIndex>) where R: Read {
    // read graph size
    let mut size = String::new();
    source.read_line(&mut size).ok().expect("failed to read graph size");

    let sz = size.trim().parse::<i64>().unwrap();
    writeln!(&mut stderr(), "Graph size is {} edges. Loading...", sz).unwrap();

    let mut graph = Graph::<u64, ()>::new();
    let mut nodes = HashMap::new();

    for line in source.lines() {
        let l = line.unwrap();
        let (s, e, o) = scan_fmt!(&l, "{} {} {}", u64, u64, u8);
        let (start, end, oneway) = (s.unwrap(), e.unwrap(), o.unwrap());

        if start == end {
            continue
        }

        // adding a node erases the index, so for each unique index we need to keep
        // a map of the corresponding node <-> internal node id mappings. shitty,
        // but it seems to work.
        if !nodes.contains_key(&start) {
            let sn = graph.add_node(0);
            nodes.insert(start, sn);
        }

        if !nodes.contains_key(&end) {
            let en = graph.add_node(0);
            nodes.insert(end, en);
        }

        let si = nodes.get(&start).unwrap();
        let ei = nodes.get(&end).unwrap();

        graph.update_edge(*si, *ei, ());

        if oneway != 1 {
            graph.update_edge(*ei, *si, ());
        }
    }

    (graph, nodes)
}

/// Computes the strongly connected components. Takes a graph stream as an
/// input where the format is as follows:
///
/// ```ignore
/// num_edges
/// id1 id2 oneway
/// id1 id2 oneway
/// ...
/// id1 id2 oneway
/// ```
///
/// where `id1` is the start of the edge, `id2` is the end of the edge,
/// and `oneway` is **0** if the edge is bidirectional (id2->id1 is permitted)
/// or **1** if `id2` -> `id1` is not allowed. `num_edges` is the number of edges in the
/// file.
pub fn compute<R>(source: &mut BufReader<R>) -> Vec<Vec<u64>> where R: Read {
    let (graph, nodes) = load_graph(source);
    writeln!(&mut stderr(), "Graph loaded. Computing SCCs.").unwrap();
    let mut comps: Vec<Vec<_>> = scc(&graph);
    writeln!(&mut stderr(), "Calculated {} components.", comps.len()).unwrap();

    // sort by size, and reverse
    comps.sort_by(|v, w| v.len().cmp(&w.len()));
    comps.reverse();
    // find out the real nodes
    let inverse_nodes: HashMap<_,_> = nodes.into_iter().map(|(k, v)| (v, k)).collect();
    let mut real_comps = Vec::new();
    for comp in comps {
        let real_nodes = comp.into_iter().map(|n| *inverse_nodes.get(&n).unwrap()).collect();
        real_comps.push(real_nodes);
    }
    real_comps
}

/// Serializes the contents of the components into a stream where each node is
/// separated by `delim`. Panics if target cannot be opened.
pub fn serialize<W>(comps: Vec<Vec<u64>>, target: &mut BufWriter<W>, delim: u8, include_biggest: bool) where W: Write {
    let sccs = match include_biggest {
        true => comps.iter(),
        false => comps[1..].iter(),
    };
    for comp in sccs {
        for node in comp {
            target.write(node.to_string().as_bytes()).unwrap();
            target.write(&[delim]).unwrap();
        }
    }
}
<commit_msg>Make the code a bit cleaner.<commit_after>//! Contains functions for computing SCCs and serializing graphs.
use std::io::{stderr, Read, Write, BufRead, BufReader, BufWriter};
use petgraph::{Graph};
use petgraph::algo::scc;
use petgraph::graph::NodeIndex;
use std::collections::HashMap;

fn load_graph<R>(source: &mut BufReader<R>) -> (Graph<u64, ()>, HashMap<u64, NodeIndex>) where R: Read {
    // read graph size
    let mut size = String::new();
    source.read_line(&mut size).ok().expect("failed to read graph size");

    let sz = size.trim().parse::<i64>().unwrap();
    writeln!(&mut stderr(), "Graph size is {} edges. Loading...", sz).unwrap();

    let mut graph = Graph::<u64, ()>::new();
    let mut nodes = HashMap::new();

    for line in source.lines() {
        let l = line.unwrap();
        let (s, e, o) = scan_fmt!(&l, "{} {} {}", u64, u64, u8);
        let (start, end, oneway) = (s.unwrap(), e.unwrap(), o.unwrap());

        if start == end {
            continue
        }

        // adding a node erases the index, so for each unique index we need to keep
        // a map of the corresponding node <-> internal node id mappings. shitty,
        // but it seems to work.
        nodes.entry(start).or_insert_with(|| { graph.add_node(0) });
        nodes.entry(end).or_insert_with(|| { graph.add_node(0) });

        let si = nodes.get(&start).unwrap();
        let ei = nodes.get(&end).unwrap();

        graph.update_edge(*si, *ei, ());

        if oneway != 1 {
            graph.update_edge(*ei, *si, ());
        }
    }

    (graph, nodes)
}

/// Computes the strongly connected components. Takes a graph stream as an
/// input where the format is as follows:
///
/// ```ignore
/// num_edges
/// id1 id2 oneway
/// id1 id2 oneway
/// ...
/// id1 id2 oneway
/// ```
///
/// where `id1` is the start of the edge, `id2` is the end of the edge,
/// and `oneway` is **0** if the edge is bidirectional (id2->id1 is permitted)
/// or **1** if `id2` -> `id1` is not allowed. `num_edges` is the number of edges in the
/// file.
pub fn compute<R>(source: &mut BufReader<R>) -> Vec<Vec<u64>> where R: Read {
    let (graph, nodes) = load_graph(source);
    writeln!(&mut stderr(), "Graph loaded. Computing SCCs.").unwrap();
    let mut comps: Vec<Vec<_>> = scc(&graph);
    writeln!(&mut stderr(), "Calculated {} components.", comps.len()).unwrap();

    // sort by size, and reverse
    comps.sort_by(|v, w| v.len().cmp(&w.len()));
    comps.reverse();
    // find out the real nodes
    let inverse_nodes: HashMap<_,_> = nodes.into_iter().map(|(k, v)| (v, k)).collect();
    let mut real_comps = Vec::new();
    for comp in comps {
        let real_nodes = comp.into_iter().map(|n| *inverse_nodes.get(&n).unwrap()).collect();
        real_comps.push(real_nodes);
    }
    real_comps
}

/// Serializes the contents of the components into a stream where each node is
/// separated by `delim`. Panics if target cannot be opened.
pub fn serialize<W>(comps: Vec<Vec<u64>>, target: &mut BufWriter<W>, delim: u8, include_biggest: bool) where W: Write {
    let sccs = match include_biggest {
        true => comps.iter(),
        false => comps[1..].iter(),
    };
    for comp in sccs {
        for node in comp {
            target.write(node.to_string().as_bytes()).unwrap();
            target.write(&[delim]).unwrap();
        }
    }
}
<|endoftext|>