<commit_before>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterOutput, FilterResult};

mod error;
use self::error::FilterError;

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<(Primitive, PrimitiveParams), FilterError>;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

/// The base filter primitive node containing common properties.
#[derive(Default, Clone)]
pub struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

pub struct ResolvedPrimitive {
    x: Option<f64>,
    y: Option<f64>,
    width: Option<f64>,
    height: Option<f64>,
    result: Option<CustomIdent>,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

enum_default!(Input, Input::Unspecified);

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

impl Primitive {
    fn resolve(
        &self,
        ctx: &FilterContext,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> ResolvedPrimitive {
        let params = draw_ctx.push_coord_units(ctx.primitive_units());

        let x = self.x.map(|l| l.normalize(values, &params));
        let y = self.y.map(|l| l.normalize(values, &params));
        let width = self.width.map(|l| l.normalize(values, &params));
        let height = self.height.map(|l| l.normalize(values, &params));

        ResolvedPrimitive {
            x,
            y,
            width,
            height,
            result: self.result.clone(),
        }
    }
}

impl ResolvedPrimitive {
    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> BoundsBuilder {
        BoundsBuilder::new(self.x, self.y, self.width, self.height, ctx.paffine())
    }
}

impl Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    stroke_paint_source: UserSpacePaintSource,
    fill_paint_source: UserSpacePaintSource,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let filter_element = filter_node.borrow_element();

    if filter_element.is_in_error() {
        return Ok(source_surface);
    }

    let filter_values = filter_element.get_computed_values();

    let filter = borrow_element_as!(filter_node, Filter);

    let resolved_filter = {
        // This is in a temporary scope so we don't leave the coord_units pushed during
        // the execution of all the filter primitives.
        let params = draw_ctx.push_coord_units(filter.get_filter_units());
        filter.resolve(filter_values, &params)
    };

    if let Ok(mut filter_ctx) = FilterContext::new(
        &resolved_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    ) {
        let primitives = filter_node
            .children()
            .filter(|c| c.is_element())
            // Skip nodes in error.
            .filter(|c| {
                let in_error = c.borrow_element().is_in_error();

                if in_error {
                    rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
                }

                !in_error
            })
            // Keep only filter primitives (those that implement the Filter trait)
            .filter(|c| c.borrow_element().as_filter_effect().is_some());

        for primitive_node in primitives {
            let elt = primitive_node.borrow_element();
            let filter = elt.as_filter_effect().unwrap();

            let primitive_values = elt.get_computed_values();

            let start = Instant::now();

            if let Err(err) = filter
                .resolve(&primitive_node)
                .and_then(|(primitive, params)| {
                    let resolved_primitive =
                        primitive.resolve(&filter_ctx, primitive_values, draw_ctx);

                    let output = render_primitive(
                        &resolved_primitive,
                        &params,
                        &filter_ctx,
                        acquired_nodes,
                        draw_ctx,
                    )?;

                    Ok(FilterResult {
                        name: resolved_primitive.result,
                        output,
                    })
                })
                .and_then(|result| filter_ctx.store_result(result))
            {
                rsvg_log!(
                    "(filter primitive {} returned an error: {})",
                    primitive_node,
                    err
                );

                // Exit early on Cairo errors. Continue rendering otherwise.
                if let FilterError::CairoError(status) = err {
                    return Err(RenderingError::from(status));
                }
            }

            let elapsed = start.elapsed();
            rsvg_log!(
                "(rendered filter primitive {} in\n    {} seconds)",
                primitive_node,
                elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
            );
        }

        Ok(filter_ctx.into_output()?)
    } else {
        // Ignore errors that happened when creating the FilterContext
        Ok(SharedImageSurface::empty(
            source_surface.width(),
            source_surface.height(),
            SurfaceType::AlphaOnly,
        )?)
    }
}

#[rustfmt::skip]
fn render_primitive(
    primitive: &ResolvedPrimitive,
    params: &PrimitiveParams,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterOutput, FilterError> {
    use PrimitiveParams::*;

    let bounds_builder = primitive.get_bounds(ctx);

    match params {
        Blend(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
    }
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<commit_msg>Rename ResolvedPrimitive to UserSpacePrimitive<commit_after>//! Entry point for the CSS filters infrastructure.

use cssparser::{BasicParseError, Parser};
use markup5ever::{expanded_name, local_name, namespace_url, ns};
use std::time::Instant;

use crate::bbox::BoundingBox;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::element::{Draw, ElementResult, SetAttributes};
use crate::error::{ElementError, ParseError, RenderingError};
use crate::length::*;
use crate::node::{Node, NodeBorrow};
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::{CustomIdent, Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::property_defs::ColorInterpolationFilters;
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;
use crate::xml::Attributes;

mod bounds;
use self::bounds::BoundsBuilder;

pub mod context;
use self::context::{FilterContext, FilterOutput, FilterResult};

mod error;
use self::error::FilterError;

/// A filter primitive interface.
pub trait FilterEffect: SetAttributes + Draw {
    fn resolve(&self, node: &Node) -> Result<(Primitive, PrimitiveParams), FilterError>;
}

// Filter Effects do not need to draw themselves
impl<T: FilterEffect> Draw for T {}

pub mod blend;
pub mod color_matrix;
pub mod component_transfer;
pub mod composite;
pub mod convolve_matrix;
pub mod displacement_map;
pub mod flood;
pub mod gaussian_blur;
pub mod image;
pub mod lighting;
pub mod merge;
pub mod morphology;
pub mod offset;
pub mod tile;
pub mod turbulence;

/// Resolved parameters for each filter primitive.
///
/// These gather all the data that a primitive may need during rendering:
/// the `feFoo` element's attributes, any computed values from its properties,
/// and parameters extracted from the element's children (for example,
/// `feMerge` gathers info from its `feMergNode` children).
pub enum PrimitiveParams {
    Blend(blend::Blend),
    ColorMatrix(color_matrix::ColorMatrix),
    ComponentTransfer(component_transfer::ComponentTransfer),
    Composite(composite::Composite),
    ConvolveMatrix(convolve_matrix::ConvolveMatrix),
    DiffuseLighting(lighting::DiffuseLighting),
    DisplacementMap(displacement_map::DisplacementMap),
    Flood(flood::Flood),
    GaussianBlur(gaussian_blur::GaussianBlur),
    Image(image::Image),
    Merge(merge::Merge),
    Morphology(morphology::Morphology),
    Offset(offset::Offset),
    SpecularLighting(lighting::SpecularLighting),
    Tile(tile::Tile),
    Turbulence(turbulence::Turbulence),
}

/// The base filter primitive node containing common properties.
#[derive(Default, Clone)]
pub struct Primitive {
    x: Option<Length<Horizontal>>,
    y: Option<Length<Vertical>>,
    width: Option<ULength<Horizontal>>,
    height: Option<ULength<Vertical>>,
    result: Option<CustomIdent>,
}

/// Common properties for filter primitives, with all lengths in user space coordinates.
pub struct UserSpacePrimitive {
    x: Option<f64>,
    y: Option<f64>,
    width: Option<f64>,
    height: Option<f64>,
    result: Option<CustomIdent>,
}

/// An enumeration of possible inputs for a filter primitive.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Input {
    Unspecified,
    SourceGraphic,
    SourceAlpha,
    BackgroundImage,
    BackgroundAlpha,
    FillPaint,
    StrokePaint,
    FilterOutput(CustomIdent),
}

enum_default!(Input, Input::Unspecified);

impl Parse for Input {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        parser
            .try_parse(|p| {
                parse_identifiers!(
                    p,
                    "SourceGraphic" => Input::SourceGraphic,
                    "SourceAlpha" => Input::SourceAlpha,
                    "BackgroundImage" => Input::BackgroundImage,
                    "BackgroundAlpha" => Input::BackgroundAlpha,
                    "FillPaint" => Input::FillPaint,
                    "StrokePaint" => Input::StrokePaint,
                )
            })
            .or_else(|_: BasicParseError<'_>| {
                let ident = CustomIdent::parse(parser)?;
                Ok(Input::FilterOutput(ident))
            })
    }
}

impl Primitive {
    fn to_user_space(
        &self,
        ctx: &FilterContext,
        values: &ComputedValues,
        draw_ctx: &DrawingCtx,
    ) -> UserSpacePrimitive {
        let params = draw_ctx.push_coord_units(ctx.primitive_units());

        let x = self.x.map(|l| l.normalize(values, &params));
        let y = self.y.map(|l| l.normalize(values, &params));
        let width = self.width.map(|l| l.normalize(values, &params));
        let height = self.height.map(|l| l.normalize(values, &params));

        UserSpacePrimitive {
            x,
            y,
            width,
            height,
            result: self.result.clone(),
        }
    }
}

impl UserSpacePrimitive {
    /// Validates attributes and returns the `BoundsBuilder` for bounds computation.
    #[inline]
    fn get_bounds(&self, ctx: &FilterContext) -> BoundsBuilder {
        BoundsBuilder::new(self.x, self.y, self.width, self.height, ctx.paffine())
    }
}

impl Primitive {
    fn parse_standard_attributes(
        &mut self,
        attrs: &Attributes,
    ) -> Result<(Input, Input), ElementError> {
        let mut input_1 = Input::Unspecified;
        let mut input_2 = Input::Unspecified;

        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => self.x = attr.parse(value)?,
                expanded_name!("", "y") => self.y = attr.parse(value)?,
                expanded_name!("", "width") => self.width = attr.parse(value)?,
                expanded_name!("", "height") => self.height = attr.parse(value)?,
                expanded_name!("", "result") => self.result = attr.parse(value)?,
                expanded_name!("", "in") => input_1 = attr.parse(value)?,
                expanded_name!("", "in2") => input_2 = attr.parse(value)?,
                _ => (),
            }
        }

        Ok((input_1, input_2))
    }

    pub fn parse_no_inputs(&mut self, attrs: &Attributes) -> ElementResult {
        let (_, _) = self.parse_standard_attributes(attrs)?;
        Ok(())
    }

    pub fn parse_one_input(&mut self, attrs: &Attributes) -> Result<Input, ElementError> {
        let (input_1, _) = self.parse_standard_attributes(attrs)?;
        Ok(input_1)
    }

    pub fn parse_two_inputs(&mut self, attrs: &Attributes) -> Result<(Input, Input), ElementError> {
        self.parse_standard_attributes(attrs)
    }
}

/// Applies a filter and returns the resulting surface.
pub fn render(
    filter_node: &Node,
    stroke_paint_source: UserSpacePaintSource,
    fill_paint_source: UserSpacePaintSource,
    source_surface: SharedImageSurface,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
    transform: Transform,
    node_bbox: BoundingBox,
) -> Result<SharedImageSurface, RenderingError> {
    let filter_node = &*filter_node;
    assert!(is_element_of_type!(filter_node, Filter));

    let filter_element = filter_node.borrow_element();

    if filter_element.is_in_error() {
        return Ok(source_surface);
    }

    let filter_values = filter_element.get_computed_values();

    let filter = borrow_element_as!(filter_node, Filter);

    let resolved_filter = {
        // This is in a temporary scope so we don't leave the coord_units pushed during
        // the execution of all the filter primitives.
        let params = draw_ctx.push_coord_units(filter.get_filter_units());
        filter.resolve(filter_values, &params)
    };

    if let Ok(mut filter_ctx) = FilterContext::new(
        &resolved_filter,
        stroke_paint_source,
        fill_paint_source,
        &source_surface,
        transform,
        node_bbox,
    ) {
        let primitives = filter_node
            .children()
            .filter(|c| c.is_element())
            // Skip nodes in error.
            .filter(|c| {
                let in_error = c.borrow_element().is_in_error();

                if in_error {
                    rsvg_log!("(ignoring filter primitive {} because it is in error)", c);
                }

                !in_error
            })
            // Keep only filter primitives (those that implement the Filter trait)
            .filter(|c| c.borrow_element().as_filter_effect().is_some());

        for primitive_node in primitives {
            let elt = primitive_node.borrow_element();
            let filter = elt.as_filter_effect().unwrap();

            let primitive_values = elt.get_computed_values();

            let start = Instant::now();

            if let Err(err) = filter
                .resolve(&primitive_node)
                .and_then(|(primitive, params)| {
                    let user_space_primitive =
                        primitive.to_user_space(&filter_ctx, primitive_values, draw_ctx);

                    let output = render_primitive(
                        &user_space_primitive,
                        &params,
                        &filter_ctx,
                        acquired_nodes,
                        draw_ctx,
                    )?;

                    Ok(FilterResult {
                        name: user_space_primitive.result,
                        output,
                    })
                })
                .and_then(|result| filter_ctx.store_result(result))
            {
                rsvg_log!(
                    "(filter primitive {} returned an error: {})",
                    primitive_node,
                    err
                );

                // Exit early on Cairo errors. Continue rendering otherwise.
                if let FilterError::CairoError(status) = err {
                    return Err(RenderingError::from(status));
                }
            }

            let elapsed = start.elapsed();
            rsvg_log!(
                "(rendered filter primitive {} in\n    {} seconds)",
                primitive_node,
                elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9
            );
        }

        Ok(filter_ctx.into_output()?)
    } else {
        // Ignore errors that happened when creating the FilterContext
        Ok(SharedImageSurface::empty(
            source_surface.width(),
            source_surface.height(),
            SurfaceType::AlphaOnly,
        )?)
    }
}

#[rustfmt::skip]
fn render_primitive(
    primitive: &UserSpacePrimitive,
    params: &PrimitiveParams,
    ctx: &FilterContext,
    acquired_nodes: &mut AcquiredNodes<'_>,
    draw_ctx: &mut DrawingCtx,
) -> Result<FilterOutput, FilterError> {
    use PrimitiveParams::*;

    let bounds_builder = primitive.get_bounds(ctx);

    match params {
        Blend(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ColorMatrix(p)       => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ComponentTransfer(p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Composite(p)         => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        ConvolveMatrix(p)    => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DiffuseLighting(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        DisplacementMap(p)   => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Flood(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        GaussianBlur(p)      => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Image(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Merge(p)             => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Morphology(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Offset(p)            => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        SpecularLighting(p)  => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Tile(p)              => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
        Turbulence(p)        => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx),
    }
}

impl From<ColorInterpolationFilters> for SurfaceType {
    fn from(c: ColorInterpolationFilters) -> Self {
        match c {
            ColorInterpolationFilters::LinearRgb => SurfaceType::LinearRgb,
            _ => SurfaceType::SRgb,
        }
    }
}
<|endoftext|>


<commit_before>/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

/// Audio frame that contains one sample for each channel.
pub struct Frame {
  /// Information regarding the current audio frame.
  pub header: Header,
  /// CRC-16 of all frame bytes before this footer.
  pub footer: Footer,
}

/// Channel assignment order.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  /// Independent channels, from one up to eight.
  Independent,
  /// Left and side stereo.
  LeftSide,
  /// Right and side stereo.
  RightSide,
  /// Middle and side stereo.
  MiddleSide,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  Frame(u32),
  Sample(u64),
}

/// Information regarding the current audio frame.
#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  pub block_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub channel_assignment: ChannelAssignment,
  pub bits_per_sample: usize,
  pub number: NumberType,
  pub crc: u8,
}

/// End of the audio frame.
///
/// Contains a value that represents the CRC-16 of everything inside the
/// frame before the footer.
#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<commit_msg>Add documentation for NumberType fields<commit_after>/// Maximum number of channels supported in the FLAC format.
pub const MAX_CHANNELS: usize = 8;

/// Audio frame that contains one sample for each channel.
pub struct Frame {
  /// Information regarding the current audio frame.
  pub header: Header,
  /// CRC-16 of all frame bytes before this footer.
  pub footer: Footer,
}

/// Channel assignment order.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ChannelAssignment {
  /// Independent channels, from one up to eight.
  Independent,
  /// Left and side stereo.
  LeftSide,
  /// Right and side stereo.
  RightSide,
  /// Middle and side stereo.
  MiddleSide,
}

/// Numbering scheme used from the frame.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NumberType {
  /// Frame number of first sample in frame.
  Frame(u32),
  /// Sample number of first sample in frame.
  Sample(u64),
}

/// Information regarding the current audio frame.
#[derive(Debug, PartialEq, Eq)]
pub struct Header {
  pub block_size: u32,
  pub sample_rate: u32,
  pub channels: u8,
  pub channel_assignment: ChannelAssignment,
  pub bits_per_sample: usize,
  pub number: NumberType,
  pub crc: u8,
}

/// End of the audio frame.
///
/// Contains a value that represents the CRC-16 of everything inside the
/// frame before the footer.
#[derive(Debug, PartialEq, Eq)]
pub struct Footer(pub u16);
<|endoftext|>


<commit_before>use std::io;
use std::iter::repeat;
use std::borrow::Cow;
use std::cell::RefCell;
use std::time::{Duration, Instant};
use std::sync::mpsc::{channel, Sender, Receiver};

use parking_lot::RwLock;

use term::{Term, terminal_size};
use utils::expand_template;

/// Controls the rendering style of progress bars.
pub struct Style {
    pub tick_chars: Vec<char>,
    pub progress_chars: Vec<char>,
    pub bar_template: Cow<'static, str>,
    pub spinner_template: Cow<'static, str>,
}

/// The drawn state of an element.
#[derive(Clone)]
pub struct DrawState {
    /// The lines to print (can contain ANSI codes)
    pub lines: Vec<String>,
    /// True if the bar no longer needs drawing.
    pub finished: bool,
    /// Time when the draw state was created.
    pub ts: Instant,
}

enum Status {
    InProgress,
    DoneVisible,
    DoneHidden,
}

/// Target for draw operations
pub enum DrawTarget {
    /// Draws into a terminal
    Term(Term, Option<DrawState>),
    /// Draws to a remote receiver
    Remote(usize, Sender<(usize, DrawState)>),
    /// Do not draw at all
    Hidden,
}

impl DrawTarget {
    /// Draw to a buffered stdout terminal
    pub fn stdout() -> DrawTarget {
        DrawTarget::Term(Term::buffered_stdout(), None)
    }

    /// Draw to a buffered stderr terminal
    pub fn stderr() -> DrawTarget {
        DrawTarget::Term(Term::buffered_stderr(), None)
    }

    /// Given the state of a progress bar, draw to a draw state.
    fn get_draw_state(&self, state: &ProgressState) -> DrawState {
        DrawState {
            lines: if state.should_render() {
                state.style.format_state(state)
            } else {
                vec![]
            },
            finished: state.is_finished(),
            ts: Instant::now(),
        }
    }

    /// Apply the given draw state (draws it).
    pub fn update(&mut self, draw_state: DrawState) -> io::Result<()> {
        match *self {
            DrawTarget::Term(ref term, ref mut last_state) => {
                if let Some(ref last_state) = *last_state {
                    last_state.clear_term(term)?;
                }
                draw_state.draw_to_term(term)?;
                term.flush()?;
                *last_state = Some(draw_state);
            }
            DrawTarget::Remote(idx, ref chan) => {
                chan.send((idx, draw_state)).unwrap();
            }
            DrawTarget::Hidden => {}
        }
        Ok(())
    }
}

impl DrawState {
    pub fn clear_term(&self, term: &Term) -> io::Result<()> {
        term.clear_last_lines(self.lines.len())
    }

    pub fn draw_to_term(&self, term: &Term) -> io::Result<()> {
        for line in &self.lines {
            term.write_line(line)?;
        }
        Ok(())
    }
}

impl Default for Style {
    fn default() -> Style {
        Style {
            tick_chars: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ ".chars().collect(),
            progress_chars: "██░".chars().collect(),
            bar_template: Cow::Borrowed("{msg}\n{wide_bar} {pos}/{len}"),
            spinner_template: Cow::Borrowed("{spinner} {msg}"),
        }
    }
}

impl Style {

    /// Returns the tick char for a given number.
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.tick_chars[(idx as usize) % (self.tick_chars.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    pub fn get_final_tick_char(&self) -> char {
        self.tick_chars[self.tick_chars.len() - 1]
    }

    pub fn format_bar(&self, state: &ProgressState, width: usize) -> String {
        let pct = state.percent();
        let fill = (pct * width as f32) as usize;
        let bar = repeat(state.style.progress_chars[0]).take(fill).collect::<String>();
        let rest = repeat(state.style.progress_chars[2]).take(width - fill).collect::<String>();
        format!("{}{}", bar, rest)
    }

    pub fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.bar_template.lines() {
            let need_wide_bar = RefCell::new(false);

            let s = expand_template(line, |key| {
                if key == "wide_bar" {
                    *need_wide_bar.borrow_mut() = true;
                    "\x00".into()
                } else if key == "bar" {
                    // XXX: width?
                    self.format_bar(state, 20)
                } else if key == "msg" {
                    state.message().to_string()
                } else if key == "pos" {
                    pos.to_string()
                } else if key == "len" {
                    len.to_string()
                } else {
                    "".into()
                }
            });

            rv.push(if *need_wide_bar.borrow() {
                let total_width = state.width();
                let bar_width = total_width - s.len() - 1;
                s.replace("\x00", &self.format_bar(state, bar_width))
            } else {
                s.to_string()
            });
        }

        rv
    }
}

/// The state of a progress bar at a moment in time.
pub struct ProgressState {
    style: Style,
    draw_target: DrawTarget,
    width: Option<u16>,
    message: String,
    pos: u64,
    len: u64,
    tick: u64,
    status: Status,
}

impl ProgressState {
    /// Returns the character that should be drawn for the
    /// current spinner character.
    pub fn current_tick_char(&self) -> char {
        if self.is_finished() {
            self.style.get_final_tick_char()
        } else {
            self.style.get_tick_char(self.tick)
        }
    }

    /// Indicates that a spinner should be drawn.
    pub fn has_spinner(&self) -> bool {
        self.tick != !0
    }

    /// Indicates that a progress bar should be drawn.
    pub fn has_progress(&self) -> bool {
        self.len != !0
    }

    /// Indicates that the progress bar finished.
    pub fn is_finished(&self) -> bool {
        match self.status {
            Status::InProgress => false,
            Status::DoneVisible => true,
            Status::DoneHidden => true,
        }
    }

    /// Returns `false` if the progress bar should no longer be
    /// drawn.
    pub fn should_render(&self) -> bool {
        match self.status {
            Status::DoneHidden => false,
            _ => true,
        }
    }

    /// Returns the completion in percent
    pub fn percent(&self) -> f32 {
        if self.len == !0 {
            0.0
        } else {
            self.pos as f32 / self.len as f32
        }
    }

    /// Returns the position of the status bar as `(pos, len)` tuple.
    pub fn position(&self) -> (u64, u64) {
        (self.pos, self.len)
    }

    /// Returns the current message of the progress bar.
    pub fn message(&self) -> &str {
        &self.message
    }

    /// The entire draw width
    pub fn width(&self) -> usize {
        if let Some(width) = self.width {
            width as usize
        } else {
            let size = match self.draw_target {
                DrawTarget::Term(ref term, _) => term.size(),
                DrawTarget::Remote(..) => terminal_size(),
                DrawTarget::Hidden => None,
            };
            if let Some((_, width)) = size {
                width as usize
            } else {
                74
            }
        }
    }
}

/// A progress bar or spinner.
pub struct ProgressBar {
    state: RwLock<ProgressState>,
}

impl ProgressBar {
    /// Creates a new progress bar with a given length.
    ///
    /// This progress bar by default draws directly to stdout.
    pub fn new(len: u64) -> ProgressBar {
        ProgressBar {
            state: RwLock::new(ProgressState {
                style: Default::default(),
                draw_target: DrawTarget::stdout(),
                width: None,
                message: "".into(),
                pos: 0,
                len: len,
                tick: !0,
                status: Status::InProgress,
            }),
        }
    }

    /// Creates a new spinner.
    ///
    /// This spinner by default draws directly to stdout.
    pub fn new_spinner() -> ProgressBar {
        let rv = ProgressBar::new(!0);
        rv.enable_spinner();
        rv
    }

    /// Enables the spinner.
    ///
    /// This is obviously enabled by default if you create a progress
    /// bar with `new_spinner` but optionally a spinner can be added
    /// to a progress bar itself.
    pub fn enable_spinner(&self) {
        self.update_state(|mut state| {
            if state.tick == !0 {
                state.tick = 0;
            }
        });
    }

    /// Disables a spinner.
    ///
    /// This should not be called if the progress bar is a spinner itself.
    pub fn disable_spinner(&self) {
        self.update_state(|mut state| {
            if state.tick != !0 {
                state.tick = !0;
            }
        });
    }

    /// Manually ticks the spinner or progress bar.
    ///
    /// This automatically happens on any other change to a progress bar.
    pub fn tick(&self) {
        self.update_state(|mut state| {
            if state.tick == !0 {
                state.tick = 0;
            } else {
                state.tick += 1;
            }
        });
    }

    /// Advances the position of a progress bar by delta.
    pub fn inc(&self, delta: u64) {
        self.update_state(|mut state| {
            state.pos += delta;
            if state.tick != !0 {
                state.tick += 1;
            }
        })
    }

    /// Sets the length of the progress bar.
    pub fn set_length(&self, len: u64) {
        self.update_state(|mut state| {
            state.len = len;
        })
    }

    /// Sets the current message of the progress bar.
    pub fn set_message(&self, msg: &str) {
        let msg = msg.to_string();
        self.update_state(|mut state| {
            state.message = msg;
        })
    }

    /// Finishes the progress bar and sets a message.
    pub fn finish_with_message(&self, msg: &str) {
        let msg = msg.to_string();
        self.update_state(|mut state| {
            state.message = msg;
            state.pos = state.len;
            state.status = Status::DoneVisible;
        });
    }

    /// Finishes the progress bar and completely clears it.
    pub fn finish_and_clear(&self) {
        self.update_state(|mut state| {
            state.pos = state.len;
            state.status = Status::DoneHidden;
        });
    }

    /// Sets a different draw target for the progress bar.
    ///
    /// This can be used to draw the progress bar to stderr
    /// for instance:
    ///
    /// ```rust,no_run
    /// # use indicatif::{ProgressBar, DrawTarget};
    /// let pb = ProgressBar::new(100);
    /// pb.set_draw_target(DrawTarget::stderr());
    /// ```
    pub fn set_draw_target(&self, target: DrawTarget) {
        self.state.write().draw_target = target;
    }

    fn update_state<F: FnOnce(&mut ProgressState)>(&self, f: F) {
        {
            let mut state = self.state.write();
            f(&mut state);
        }
        self.draw().ok();
    }

    fn draw(&self) -> io::Result<()> {
        let mut state = self.state.write();
        let draw_state = state.draw_target.get_draw_state(&*state);
        state.draw_target.update(draw_state)
    }
}

/// Manages multiple progress bars from different threads.
pub struct MultiProgress {
    objects: usize,
    term: Term,
    refresh_rate: Option<usize>,
    tx: Sender<(usize, DrawState)>,
    rx: Receiver<(usize, DrawState)>,
}

impl MultiProgress {
    /// Creates a new multi progress object that draws to stdout.
    pub fn new() -> MultiProgress {
        let (tx, rx) = channel();
        MultiProgress {
            objects: 0,
            term: Term::buffered_stdout(),
            refresh_rate: Some(30),
            tx: tx,
            rx: rx,
        }
    }

    /// Adds a progress bar.
    ///
    /// The progress bar added will have the draw target changed to a
    /// remote draw target that is intercepted by the multi progress
    /// object.
    pub fn add(&mut self, bar: ProgressBar) -> ProgressBar {
        bar.set_draw_target(DrawTarget::Remote(self.objects,
                                               self.tx.clone()));
        self.objects += 1;
        bar
    }

    /// Waits for all progress bars to report that they are finished.
    ///
    /// You need to call this as this will request the draw instructions
    /// from the remote progress bars.  Not calling this will deadlock
    /// your program.
    pub fn join(self) -> io::Result<()> {
        self.join_impl(false)
    }

    /// Works like `join` but clears the progress bar in the end.
    pub fn join_and_clear(self) -> io::Result<()> {
        self.join_impl(true)
    }

    fn join_impl(self, clear: bool) -> io::Result<()> {
        if self.objects == 0 {
            return Ok(());
        }

        let mut outstanding = repeat(true).take(self.objects as usize).collect::<Vec<_>>();
        let mut clear_height = 0;
        let mut draw_states: Vec<Option<DrawState>> = outstanding.iter().map(|_| None).collect();
        let mut last_draw: Option<Instant> = None;
        let rate = self.refresh_rate.map(|n| Duration::from_millis(1000 / n as u64));

        while outstanding.iter().any(|&x| x) {
            let (idx, draw_state) = self.rx.recv().unwrap();

            if draw_state.finished {
                outstanding[idx] = false;
            }

            if draw_state.finished ||
               rate.is_none() ||
               last_draw.is_none() ||
               last_draw.unwrap().elapsed() > rate.unwrap() {
                self.term.clear_last_lines(clear_height)?;

                // persist the state for to-draw and drawn
                last_draw = Some(draw_state.ts);
                draw_states[idx] = Some(draw_state);

                // paint current state
                clear_height = 0;
                for draw_state_opt in draw_states.iter() {
                    if let Some(ref draw_state) = *draw_state_opt {
                        draw_state.draw_to_term(&self.term)?;
                        clear_height += draw_state.lines.len();
                    }
                }

                self.term.flush()?;
            } else {
                draw_states[idx] = Some(draw_state);
            }
        }

        if clear {
            self.term.clear_last_lines(clear_height)?;
            self.term.flush()?;
        }

        Ok(())
    }
}

impl Drop for ProgressBar {
    fn drop(&mut self) {
        if self.state.read().is_finished() {
            return;
        }
        self.update_state(|mut state| {
            state.status = Status::DoneHidden;
        });
    }
}
<commit_msg>Restructured refresh rate code<commit_after>use std::io;
use std::iter::repeat;
use std::borrow::Cow;
use std::cell::RefCell;
use std::time::{Duration, Instant};
use std::sync::mpsc::{channel, Sender, Receiver};

use parking_lot::RwLock;

use term::{Term, terminal_size};
use utils::expand_template;

/// Controls the rendering style of progress bars.
pub struct Style {
    pub tick_chars: Vec<char>,
    pub progress_chars: Vec<char>,
    pub bar_template: Cow<'static, str>,
    pub spinner_template: Cow<'static, str>,
}

/// The drawn state of an element.
#[derive(Clone)]
pub struct DrawState {
    /// The lines to print (can contain ANSI codes)
    pub lines: Vec<String>,
    /// True if the bar no longer needs drawing.
    pub finished: bool,
    /// Time when the draw state was created.
    pub ts: Instant,
}

enum Status {
    InProgress,
    DoneVisible,
    DoneHidden,
}

/// Target for draw operations
pub enum DrawTarget {
    /// Draws into a terminal
    Term(Term, Option<DrawState>, Option<Duration>),
    /// Draws to a remote receiver
    Remote(usize, Sender<(usize, DrawState)>),
    /// Do not draw at all
    Hidden,
}

impl DrawTarget {
    /// Draw to a buffered stdout terminal
    pub fn stdout() -> DrawTarget {
        let rate = Some(Duration::from_millis(1000 / 30));
        DrawTarget::Term(Term::buffered_stdout(), None, rate)
    }

    /// Draw to a buffered stderr terminal
    pub fn stderr() -> DrawTarget {
        let rate = Some(Duration::from_millis(1000 / 30));
        DrawTarget::Term(Term::buffered_stderr(), None, rate)
    }

    /// Apply the given draw state (draws it).
    pub fn apply_draw_state(&mut self, draw_state: DrawState) -> io::Result<()> {
        match *self {
            DrawTarget::Term(ref term, ref mut last_state, rate) => {
                let last_draw = last_state.as_ref().map(|x| x.ts);
                if draw_state.finished ||
                   rate.is_none() ||
                   last_draw.is_none() ||
                   last_draw.unwrap().elapsed() > rate.unwrap() {
                    if let Some(ref last_state) = *last_state {
                        last_state.clear_term(term)?;
                    }
                    draw_state.draw_to_term(term)?;
                    term.flush()?;
                    *last_state = Some(draw_state);
                }
            }
            DrawTarget::Remote(idx, ref chan) => {
                chan.send((idx, draw_state)).unwrap();
            }
            DrawTarget::Hidden => {}
        }
        Ok(())
    }
}

impl DrawState {
    pub fn clear_term(&self, term: &Term) -> io::Result<()> {
        term.clear_last_lines(self.lines.len())
    }

    pub fn draw_to_term(&self, term: &Term) -> io::Result<()> {
        for line in &self.lines {
            term.write_line(line)?;
        }
        Ok(())
    }
}

impl Default for Style {
    fn default() -> Style {
        Style {
            tick_chars: "⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈ ".chars().collect(),
            progress_chars: "██░".chars().collect(),
            bar_template: Cow::Borrowed("{msg}\n{wide_bar} {pos}/{len}"),
            spinner_template: Cow::Borrowed("{spinner} {msg}"),
        }
    }
}

impl Style {

    /// Returns the tick char for a given number.
    pub fn get_tick_char(&self, idx: u64) -> char {
        self.tick_chars[(idx as usize) % (self.tick_chars.len() - 1)]
    }

    /// Returns the tick char for the finished state.
    pub fn get_final_tick_char(&self) -> char {
        self.tick_chars[self.tick_chars.len() - 1]
    }

    pub fn format_bar(&self, state: &ProgressState, width: usize) -> String {
        let pct = state.percent();
        let fill = (pct * width as f32) as usize;
        let bar = repeat(state.style.progress_chars[0]).take(fill).collect::<String>();
        let rest = repeat(state.style.progress_chars[2]).take(width - fill).collect::<String>();
        format!("{}{}", bar, rest)
    }

    pub fn format_state(&self, state: &ProgressState) -> Vec<String> {
        let (pos, len) = state.position();
        let mut rv = vec![];

        for line in self.bar_template.lines() {
            let need_wide_bar = RefCell::new(false);

            let s = expand_template(line, |key| {
                if key == "wide_bar" {
                    *need_wide_bar.borrow_mut() = true;
                    "\x00".into()
                } else if key == "bar" {
                    // XXX: width?
                    self.format_bar(state, 20)
                } else if key == "msg" {
                    state.message().to_string()
                } else if key == "pos" {
                    pos.to_string()
                } else if key == "len" {
                    len.to_string()
                } else {
                    "".into()
                }
            });

            rv.push(if *need_wide_bar.borrow() {
                let total_width = state.width();
                let bar_width = total_width - s.len() - 1;
                s.replace("\x00", &self.format_bar(state, bar_width))
            } else {
                s.to_string()
            });
        }

        rv
    }
}

/// The state of a progress bar at a moment in time.
pub struct ProgressState {
    style: Style,
    draw_target: DrawTarget,
    width: Option<u16>,
    message: String,
    pos: u64,
    len: u64,
    tick: u64,
    status: Status,
}

impl ProgressState {
    /// Returns the character that should be drawn for the
    /// current spinner character.
    pub fn current_tick_char(&self) -> char {
        if self.is_finished() {
            self.style.get_final_tick_char()
        } else {
            self.style.get_tick_char(self.tick)
        }
    }

    /// Indicates that a spinner should be drawn.
    pub fn has_spinner(&self) -> bool {
        self.tick != !0
    }

    /// Indicates that a progress bar should be drawn.
    pub fn has_progress(&self) -> bool {
        self.len != !0
    }

    /// Indicates that the progress bar finished.
    pub fn is_finished(&self) -> bool {
        match self.status {
            Status::InProgress => false,
            Status::DoneVisible => true,
            Status::DoneHidden => true,
        }
    }

    /// Returns `false` if the progress bar should no longer be
    /// drawn.
    pub fn should_render(&self) -> bool {
        match self.status {
            Status::DoneHidden => false,
            _ => true,
        }
    }

    /// Returns the completion in percent
    pub fn percent(&self) -> f32 {
        if self.len == !0 {
            0.0
        } else {
            self.pos as f32 / self.len as f32
        }
    }

    /// Returns the position of the status bar as `(pos, len)` tuple.
    pub fn position(&self) -> (u64, u64) {
        (self.pos, self.len)
    }

    /// Returns the current message of the progress bar.
    pub fn message(&self) -> &str {
        &self.message
    }

    /// The entire draw width
    pub fn width(&self) -> usize {
        if let Some(width) = self.width {
            width as usize
        } else {
            terminal_size().map(|x| x.1).unwrap_or(74) as usize
        }
    }
}

/// A progress bar or spinner.
pub struct ProgressBar {
    state: RwLock<ProgressState>,
}

impl ProgressBar {
    /// Creates a new progress bar with a given length.
    ///
    /// This progress bar by default draws directly to stdout.
    pub fn new(len: u64) -> ProgressBar {
        ProgressBar {
            state: RwLock::new(ProgressState {
                style: Default::default(),
                draw_target: DrawTarget::stdout(),
                width: None,
                message: "".into(),
                pos: 0,
                len: len,
                tick: !0,
                status: Status::InProgress,
            }),
        }
    }

    /// Creates a new spinner.
    ///
    /// This spinner by default draws directly to stdout.
    pub fn new_spinner() -> ProgressBar {
        let rv = ProgressBar::new(!0);
        rv.enable_spinner();
        rv
    }

    /// Enables the spinner.
    ///
    /// This is obviously enabled by default if you create a progress
    /// bar with `new_spinner` but optionally a spinner can be added
    /// to a progress bar itself.
    pub fn enable_spinner(&self) {
        self.update_state(|mut state| {
            if state.tick == !0 {
                state.tick = 0;
            }
        });
    }

    /// Disables a spinner.
    ///
    /// This should not be called if the progress bar is a spinner itself.
    pub fn disable_spinner(&self) {
        self.update_state(|mut state| {
            if state.tick != !0 {
                state.tick = !0;
            }
        });
    }

    /// Manually ticks the spinner or progress bar.
    ///
    /// This automatically happens on any other change to a progress bar.
    pub fn tick(&self) {
        self.update_state(|mut state| {
            if state.tick == !0 {
                state.tick = 0;
            } else {
                state.tick += 1;
            }
        });
    }

    /// Advances the position of a progress bar by delta.
    pub fn inc(&self, delta: u64) {
        self.update_state(|mut state| {
            state.pos += delta;
            if state.tick != !0 {
                state.tick += 1;
            }
        })
    }

    /// Sets the length of the progress bar.
    pub fn set_length(&self, len: u64) {
        self.update_state(|mut state| {
            state.len = len;
        })
    }

    /// Sets the current message of the progress bar.
    pub fn set_message(&self, msg: &str) {
        let msg = msg.to_string();
        self.update_state(|mut state| {
            state.message = msg;
        })
    }

    /// Finishes the progress bar and sets a message.
    pub fn finish_with_message(&self, msg: &str) {
        let msg = msg.to_string();
        self.update_state(|mut state| {
            state.message = msg;
            state.pos = state.len;
            state.status = Status::DoneVisible;
        });
    }

    /// Finishes the progress bar and completely clears it.
    pub fn finish_and_clear(&self) {
        self.update_state(|mut state| {
            state.pos = state.len;
            state.status = Status::DoneHidden;
        });
    }

    /// Sets a different draw target for the progress bar.
    ///
    /// This can be used to draw the progress bar to stderr
    /// for instance:
    ///
    /// ```rust,no_run
    /// # use indicatif::{ProgressBar, DrawTarget};
    /// let pb = ProgressBar::new(100);
    /// pb.set_draw_target(DrawTarget::stderr());
    /// ```
    pub fn set_draw_target(&self, target: DrawTarget) {
        self.state.write().draw_target = target;
    }

    fn update_state<F: FnOnce(&mut ProgressState)>(&self, f: F) {
        {
            let mut state = self.state.write();
            f(&mut state);
        }
        self.draw().ok();
    }

    fn draw(&self) -> io::Result<()> {
        let mut state = self.state.write();
        let draw_state = DrawState {
            lines: if state.should_render() {
                state.style.format_state(&*state)
            } else {
                vec![]
            },
            finished: state.is_finished(),
            ts: Instant::now(),
        };
        state.draw_target.apply_draw_state(draw_state)
    }
}

/// Manages multiple progress bars from different threads.
pub struct MultiProgress {
    objects: usize,
    draw_target: DrawTarget,
    tx: Sender<(usize, DrawState)>,
    rx: Receiver<(usize, DrawState)>,
}

impl MultiProgress {
    /// Creates a new multi progress object that draws to stdout.
    pub fn new() -> MultiProgress {
        let (tx, rx) = channel();
        MultiProgress {
            objects: 0,
            draw_target: DrawTarget::stdout(),
            tx: tx,
            rx: rx,
        }
    }

    /// Adds a progress bar.
    ///
    /// The progress bar added will have the draw target changed to a
    /// remote draw target that is intercepted by the multi progress
    /// object.
    pub fn add(&mut self, bar: ProgressBar) -> ProgressBar {
        bar.set_draw_target(DrawTarget::Remote(self.objects,
                                               self.tx.clone()));
        self.objects += 1;
        bar
    }

    /// Waits for all progress bars to report that they are finished.
    ///
    /// You need to call this as this will request the draw instructions
    /// from the remote progress bars.  Not calling this will deadlock
    /// your program.
    pub fn join(self) -> io::Result<()> {
        self.join_impl(false)
    }

    /// Works like `join` but clears the progress bar in the end.
    pub fn join_and_clear(self) -> io::Result<()> {
        self.join_impl(true)
    }

    fn join_impl(mut self, clear: bool) -> io::Result<()> {
        if self.objects == 0 {
            return Ok(());
        }

        let mut outstanding = repeat(true).take(self.objects as usize).collect::<Vec<_>>();
        let mut draw_states: Vec<Option<DrawState>> = outstanding.iter().map(|_| None).collect();

        while outstanding.iter().any(|&x| x) {
            let (idx, draw_state) = self.rx.recv().unwrap();
            let ts = draw_state.ts;

            if draw_state.finished {
                outstanding[idx] = false;
            }
            draw_states[idx] = Some(draw_state);

            let mut lines = vec![];
            for draw_state_opt in draw_states.iter() {
                if let Some(ref draw_state) = *draw_state_opt {
                    lines.extend_from_slice(&draw_state.lines[..]);
                }
            }

            self.draw_target.apply_draw_state(DrawState {
                lines: lines,
                finished: false,
                ts: ts,
            })?;
        }

        if clear {
            self.draw_target.apply_draw_state(DrawState {
                lines: vec![],
                finished: true,
                ts: Instant::now(),
            })?;
        }

        Ok(())
    }
}

impl Drop for ProgressBar {
    fn drop(&mut self) {
        if self.state.read().is_finished() {
            return;
        }
        self.update_state(|mut state| {
            state.status = Status::DoneHidden;
        });
    }
}
<|endoftext|>


<commit_before>//! The rbattle game protocol.
//!
//! All hosts participating in an rbattle game have a complete copy of the
//! state, and the algorithm for evolving the state from one moment to the next
//! is strictly deterministic.
//!
//! Every effect the player can have on the game is represented as a
//! `state::Action` value. Given an initial state, and a record of which actions
//! were applied by which players on which turns, you can exactly recreate the
//! progression of the game. Since rbattle is such a simple game, there's only
//! one kind of action: toggling an outflow.
//!
//! In that light, the protocol focuses on gathering user actions, and
//! distributing them out in a way that helps apply them consistently. The
//! protocol has no means for communicating the game state. The only record of
//! the game state transmitted at all is a hash value to detect divergence,
//! which causes the game to end.
//!
//! For simplicity, we designate one host as the server; the protocol doesn't
//! provide for any resilience if the server goes down. All other hosts have TCP
//! connections to the server only.
//!
//! Game play is organized into 'turns', where turns are scheduled at fixed
//! intervals. (We'll aim for 33ms per turn, or 30 turns/second, and see how
//! that goes.) Clients send the server an action list every turn, even if it's
//! an empty action list; and the server broadcasts out the collected action
//! list for every turn, even if it's empty.
//!
//! The server is responsible for coordinating timing. For a given turn duration
//! T, the server broadcasts the list of gathered actions as soon as they are
//! available, but no sooner then T after the last broadcast.
//!
//! Clients should apply received action lists as soon as they are received,
//! advance their state, and send any collected actions immediately.

use map::MapParameters;
use jsonproto::JsonProto;
use scheduler::{CollectedActions, Notifier, PlayerActions, Scheduler};
use state::{Action, Player, SerializableState, State};

use futures::{Future};
use futures::future::ok;
use futures::sync::oneshot;
use tokio_proto::TcpServer;
use tokio_service::Service;

use std::io::{Error, ErrorKind};
use std::mem::replace;
use std::net::SocketAddr;
use std::sync::{Arc, mpsc, Mutex};
use std::thread;

#[derive(Clone)]
struct SchedulerService {
    scheduler: Arc<Mutex<Scheduler>>
}

/// Requests the server receives from clients.
#[derive(Debug, Serialize, Deserialize)]
enum Request {
    Join,
    Actions(PlayerActions),
}

/// The server's responses to those requests.
#[derive(Debug, Serialize, Deserialize)]
enum Response {
    Welcome { player: Player, state: SerializableState },
    GameFull,
    Turn(CollectedActions)
}

/// This impl allows `Scheduler` to resolve promises returned by
/// SchedulerService::call.
impl Notifier for oneshot::Sender<Response> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(Response::Turn(turn))
            .expect("oneshot notifier receiver died");
    }
}

/// This impl allows `Scheduler` to send the actions collected for a turn to the
/// local game.
impl Notifier for mpsc::Sender<CollectedActions> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(turn)
            .expect("mpsc notifier receiver died");
    }
}

impl Service for SchedulerService {
    type Request = Request;
    type Response = Response;
    type Error = Error;
    type Future = Box<Future<Item=Response, Error=Error>>;

    fn call(&self, req: Request) -> Self::Future {
        match req {
            Request::Join => {
                let mut guard = self.scheduler.lock().unwrap();
                match guard.player_join() {
                    Some((player, state)) =>
                        Box::new(ok(Response::Welcome { player, state })),
                    None =>
                        Box::new(ok(Response::GameFull))
                }
            },
            Request::Actions(actions) => {
                let (sender, receiver) = oneshot::channel();
                let mut guard = self.scheduler.lock().unwrap();
                guard.submit_actions(actions, Box::new(sender));

                // Turn oneshot errors into io::Error, as this service requires.
                let receiver = receiver.map_err(|e| Error::new(ErrorKind::Other, e));

                Box::new(receiver)
            }
        }
    }
}

/// A participant that has joined a game. This is the common trait that both client
/// and server sides implement.
pub trait Participant {
    /// Return a snapshot of the current state.
    fn snapshot(&self) -> State;

    /// Return the player number of this SynchronizedState.
    fn get_player(&self) -> Player;

    /// Submit `action` to be performed as soon as possible.
    fn request_action(&mut self, action: Action);
}

/// Information shared between the main thread and helper threads.
struct Shared {
    /// The player this state represents. Assigned by the server.
    player: Player,

    /// The current state of the game.
    state: State,

    /// The queue of actions to be sent to the scheduler on the next turn.
    pending: Vec<Action>
}

impl Shared {
    fn apply_collected_actions(&mut self,
                               collected_actions: CollectedActions)
                               -> PlayerActions
    {
        assert_eq!(self.state.turn + 1, collected_actions.turn);

        for action in collected_actions.actions {
            self.state.take_action(&action);
        }
        self.state.advance();

        // We should have applied the same actions to the same state,
        // and gotten the same checksum.
        assert_eq!(self.state.checksum(),
                   collected_actions.state_checksum);

        // Now that we've applied the actions from the prior turn, return
        // whatever actions have been queued up in the mean time as our next
        // turn.
        PlayerActions {
            player: self.player,
            turn: self.state.turn,
            actions: replace(&mut self.pending, vec![])
        }
    }
}

pub struct Server {
    /// The player on the local machine.
    player: Player,

    /// Information shared between the main thread, the server thread, and the
    /// scheduler thread.
    shared: Arc<Mutex<Shared>>,

    /// The scheduler we interact with to take turns.
    scheduler: Arc<Mutex<Scheduler>>
}

impl Server {
    pub fn new(addr: SocketAddr, params: MapParameters) -> Server {
        assert!(params.player_colors.len() >= 1);

        // Create a scheduler to coordinate turns amongst the players,
        // and add ourselves as the first player.
        let mut scheduler = Scheduler::new(State::new(params));
        let (player, current_state) = scheduler.player_join().unwrap();

        let scheduler = Arc::new(Mutex::new(scheduler));

        let shared = Arc::new(Mutex::new(Shared {
            player,
            state: State::from_serializable(current_state),
            pending: vec![]
        }));

        let (sender, receiver): (mpsc::Sender<CollectedActions>, _) = mpsc::channel();

        // Create a thread to apply actions received from the scheduler.
        // These variables get moved into the closure.
        let shared_handle = shared.clone();
        let scheduler_handle = scheduler.clone();
        let sender_handle = sender.clone();
        thread::spawn(move || {
            for collected_actions in receiver {
                let mut guard = shared_handle.lock().unwrap();
                let next_actions = guard.apply_collected_actions(collected_actions);

                // Drop the guard on the shared data first, to avoid having to
                // think about lock ordering.
                drop(guard);

                let mut guard = scheduler_handle.lock().unwrap();
                guard.submit_actions(next_actions, Box::new(sender_handle.clone()));
            }
        });

        // Spawn off a second thread to run the server.
        // This variable gets moved into the closure.
        let scheduler_handle = scheduler.clone();
        thread::spawn(move || {
            let server = TcpServer::new(JsonProto::<Request, Response>::new(), addr);
            server.serve(move || {
                Ok(SchedulerService { scheduler: scheduler_handle.clone() })
            });
        });

        // Get the ball rolling by submitting an empty first move.
        {
            let mut guard = scheduler.lock().unwrap();
            let actions = PlayerActions {
                player,
                turn: 0,
                actions: vec![]
            };
            guard.submit_actions(actions, Box::new(sender));
        }

        Server { player, shared, scheduler }
    }
}

impl Participant for Server {
    /// Return a snapshot of the current state.
    fn snapshot(&self) -> State {
        let guard = self.shared.lock().unwrap();
        guard.state.clone()
    }

    /// Return the player number of this SynchronizedState.
    fn get_player(&self) -> Player { self.player }

    /// Submit `action` to be performed as soon as possible.
    fn request_action(&mut self, action: Action) {
        let mut guard = self.shared.lock().unwrap();
        guard.pending.push(action);
    }
}
<commit_msg>Remove unnecessary Server field.<commit_after>//! The rbattle game protocol.
//!
//! All hosts participating in an rbattle game have a complete copy of the
//! state, and the algorithm for evolving the state from one moment to the next
//! is strictly deterministic.
//!
//! Every effect the player can have on the game is represented as a
//! `state::Action` value. Given an initial state, and a record of which actions
//! were applied by which players on which turns, you can exactly recreate the
//! progression of the game. Since rbattle is such a simple game, there's only
//! one kind of action: toggling an outflow.
//!
//! In that light, the protocol focuses on gathering user actions, and
//! distributing them out in a way that helps apply them consistently. The
//! protocol has no means for communicating the game state. The only record of
//! the game state transmitted at all is a hash value to detect divergence,
//! which causes the game to end.
//!
//! For simplicity, we designate one host as the server; the protocol doesn't
//! provide for any resilience if the server goes down. All other hosts have TCP
//! connections to the server only.
//!
//! Game play is organized into 'turns', where turns are scheduled at fixed
//! intervals. (We'll aim for 33ms per turn, or 30 turns/second, and see how
//! that goes.) Clients send the server an action list every turn, even if it's
//! an empty action list; and the server broadcasts out the collected action
//! list for every turn, even if it's empty.
//!
//! The server is responsible for coordinating timing. For a given turn duration
//! T, the server broadcasts the list of gathered actions as soon as they are
//! available, but no sooner then T after the last broadcast.
//!
//! Clients should apply received action lists as soon as they are received,
//! advance their state, and send any collected actions immediately.

use map::MapParameters;
use jsonproto::JsonProto;
use scheduler::{CollectedActions, Notifier, PlayerActions, Scheduler};
use state::{Action, Player, SerializableState, State};

use futures::{Future};
use futures::future::ok;
use futures::sync::oneshot;
use tokio_proto::TcpServer;
use tokio_service::Service;

use std::io::{Error, ErrorKind};
use std::mem::replace;
use std::net::SocketAddr;
use std::sync::{Arc, mpsc, Mutex};
use std::thread;

#[derive(Clone)]
struct SchedulerService {
    scheduler: Arc<Mutex<Scheduler>>
}

/// Requests the server receives from clients.
#[derive(Debug, Serialize, Deserialize)]
enum Request {
    Join,
    Actions(PlayerActions),
}

/// The server's responses to those requests.
#[derive(Debug, Serialize, Deserialize)]
enum Response {
    Welcome { player: Player, state: SerializableState },
    GameFull,
    Turn(CollectedActions)
}

/// This impl allows `Scheduler` to resolve promises returned by
/// SchedulerService::call.
impl Notifier for oneshot::Sender<Response> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(Response::Turn(turn))
            .expect("oneshot notifier receiver died");
    }
}

/// This impl allows `Scheduler` to send the actions collected for a turn to the
/// local game.
impl Notifier for mpsc::Sender<CollectedActions> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(turn)
            .expect("mpsc notifier receiver died");
    }
}

impl Service for SchedulerService {
    type Request = Request;
    type Response = Response;
    type Error = Error;
    type Future = Box<Future<Item=Response, Error=Error>>;

    fn call(&self, req: Request) -> Self::Future {
        match req {
            Request::Join => {
                let mut guard = self.scheduler.lock().unwrap();
                match guard.player_join() {
                    Some((player, state)) =>
                        Box::new(ok(Response::Welcome { player, state })),
                    None =>
                        Box::new(ok(Response::GameFull))
                }
            },
            Request::Actions(actions) => {
                let (sender, receiver) = oneshot::channel();
                let mut guard = self.scheduler.lock().unwrap();
                guard.submit_actions(actions, Box::new(sender));

                // Turn oneshot errors into io::Error, as this service requires.
                let receiver = receiver.map_err(|e| Error::new(ErrorKind::Other, e));

                Box::new(receiver)
            }
        }
    }
}

/// A participant that has joined a game. This is the common trait that both client
/// and server sides implement.
pub trait Participant {
    /// Return a snapshot of the current state.
    fn snapshot(&self) -> State;

    /// Return the player number of this SynchronizedState.
    fn get_player(&self) -> Player;

    /// Submit `action` to be performed as soon as possible.
    fn request_action(&mut self, action: Action);
}

/// Information shared between the main thread and helper threads.
struct Shared {
    /// The player this state represents. Assigned by the server.
    player: Player,

    /// The current state of the game.
    state: State,

    /// The queue of actions to be sent to the scheduler on the next turn.
    pending: Vec<Action>
}

impl Shared {
    fn apply_collected_actions(&mut self,
                               collected_actions: CollectedActions)
                               -> PlayerActions
    {
        assert_eq!(self.state.turn + 1, collected_actions.turn);

        for action in collected_actions.actions {
            self.state.take_action(&action);
        }
        self.state.advance();

        // We should have applied the same actions to the same state,
        // and gotten the same checksum.
        assert_eq!(self.state.checksum(),
                   collected_actions.state_checksum);

        // Now that we've applied the actions from the prior turn, return
        // whatever actions have been queued up in the mean time as our next
        // turn.
        PlayerActions {
            player: self.player,
            turn: self.state.turn,
            actions: replace(&mut self.pending, vec![])
        }
    }
}

pub struct Server {
    /// The player on the local machine.
    player: Player,

    /// Information shared between the main thread, the server thread, and the
    /// scheduler thread.
    shared: Arc<Mutex<Shared>>,
}

impl Server {
    pub fn new(addr: SocketAddr, params: MapParameters) -> Server {
        assert!(params.player_colors.len() >= 1);

        // Create a scheduler to coordinate turns amongst the players,
        // and add ourselves as the first player.
        let mut scheduler = Scheduler::new(State::new(params));
        let (player, current_state) = scheduler.player_join().unwrap();

        let scheduler = Arc::new(Mutex::new(scheduler));

        let shared = Arc::new(Mutex::new(Shared {
            player,
            state: State::from_serializable(current_state),
            pending: vec![]
        }));

        let (sender, receiver): (mpsc::Sender<CollectedActions>, _) = mpsc::channel();

        // Create a thread to apply actions received from the scheduler.
        // These variables get moved into the closure.
        let shared_handle = shared.clone();
        let scheduler_handle = scheduler.clone();
        let sender_handle = sender.clone();
        thread::spawn(move || {
            for collected_actions in receiver {
                let mut guard = shared_handle.lock().unwrap();
                let next_actions = guard.apply_collected_actions(collected_actions);

                // Drop the guard on the shared data first, to avoid having to
                // think about lock ordering.
                drop(guard);

                let mut guard = scheduler_handle.lock().unwrap();
                guard.submit_actions(next_actions, Box::new(sender_handle.clone()));
            }
        });

        // Spawn off a second thread to run the server.
        // This variable gets moved into the closure.
        let scheduler_handle = scheduler.clone();
        thread::spawn(move || {
            let server = TcpServer::new(JsonProto::<Request, Response>::new(), addr);
            server.serve(move || {
                Ok(SchedulerService { scheduler: scheduler_handle.clone() })
            });
        });

        // Get the ball rolling by submitting an empty first move.
        {
            let mut guard = scheduler.lock().unwrap();
            let actions = PlayerActions {
                player,
                turn: 0,
                actions: vec![]
            };
            guard.submit_actions(actions, Box::new(sender));
        }

        Server { player, shared }
    }
}

impl Participant for Server {
    /// Return a snapshot of the current state.
    fn snapshot(&self) -> State {
        let guard = self.shared.lock().unwrap();
        guard.state.clone()
    }

    /// Return the player number of this SynchronizedState.
    fn get_player(&self) -> Player { self.player }

    /// Submit `action` to be performed as soon as possible.
    fn request_action(&mut self, action: Action) {
        let mut guard = self.shared.lock().unwrap();
        guard.pending.push(action);
    }
}
<|endoftext|>


<commit_before>// extern crate bindgen;
#[cfg(target_env = "msvc")]
extern crate cc;
extern crate pkg_config;
extern crate num_cpus;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

// Automatically write bindings to libsass
//#[allow(dead_code)]
//fn write_bindings() {
//    let bindings = bindgen::Builder::default()
//        .header("libsass/include/sass.h")
//        .clang_arg("-Ilibsass/include")
//        // To avoid a test failing
//        .blacklist_type("max_align_t")
//        // we do static linking so it should be fine
//        // https://github.com/rust-lang/rust/issues/36927
//        .rustified_enum(".*")
//        .generate()
//        .expect("Unable to generate bindings");
//
//    // Write the bindings to the $OUT_DIR/bindings.rs file.
//    let out_path = PathBuf::from("src");
//    bindings
//        .write_to_file(out_path.join("bindings.rs"))
//        .expect("Couldn't write bindings!");
//}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => panic!("\n{} failed with {}\n", stringify!($e), e),
    })
}

fn cp_r(dir: &Path, dest: &Path) {
    for entry in t!(fs::read_dir(dir)) {
        let entry = t!(entry);
        let path = entry.path();
        let dst = dest.join(path.file_name().unwrap());
        if t!(fs::metadata(&path)).is_file() {
            t!(fs::copy(path, dst));
        } else {
            t!(fs::create_dir_all(&dst));
            cp_r(&path, &dst);
        }
    }
}

fn get_libsass_folder() -> PathBuf {
    env::current_dir().unwrap().join("libsass")
}

// linux/unix
#[cfg(not(target_env = "msvc"))]
fn compile() {
    let target = env::var("TARGET").expect("TARGET not found");
    let src = get_libsass_folder();
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);
    let is_bsd = target.contains("dragonfly")
        || target.contains("freebsd")
        || target.contains("netbsd")
        || target.contains("openbsd");
    let libprobe = | lib: &str | -> bool {
      Command::new("cc")
        .arg("-xc++")
        .arg("-o/dev/null")
        .arg(format!("-l{}",lib))
        .arg("-shared")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success()
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());
    let r = Command::new(if is_bsd { "gmake" } else { "make" })
        .current_dir(&build)
        .args(&["--jobs", &jobs])
        .output()
        .expect("error running make");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=sass");

    if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    }
    else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    }
    else if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    }
    else {
        panic!("no c++ library found");
    }

}

// windows
#[cfg(target_env = "msvc")]
fn compile() {
    let src = get_libsass_folder();
    let target = env::var("TARGET").expect("TARGET not found in environment");
    let msvc_platform = if target.contains("x86_64") {
        "Win64"
    } else {
        "Win32"
    };
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);

    // Find an instance of devenv.exe from Visual Studio IDE in order to upgrade
    // libsass.sln to the current available IDE. Do nothing if no devenv.exe found
    let d = cc::windows_registry::find(target.as_str(), "devenv.exe");
    if let Some(mut d) = d {
        let d = d
            .args(&["/upgrade", "win\\libsass.sln"])
            .current_dir(&build)
            .output()
            .expect("error running devenv");
        if !d.status.success() {
            let err = String::from_utf8_lossy(&d.stderr);
            let out = String::from_utf8_lossy(&d.stdout);
            println!("Upgrade error:\nSTDERR:{}\nSTDOUT:{}", err, out);
        }
    }

    let search = Command::new("which")
        .args(&["msbuild.exe"])
        .output()
        .expect("Could not search for msbuild.exe on path");
    let mut msbuild = if search.status.success() {
        Command::new("msbuild.exe")
    } else {
        cc::windows_registry::find(target.as_str(), "msbuild.exe")
            .expect("Could not find msbuild.exe on the registry")
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());

    let r = msbuild
        .args(&[
            "win\\libsass.sln",
            "/p:LIBSASS_STATIC_LIB=1",
            "/p:Configuration=Release",
            "/p:WholeProgramOptimization=false",
            format!("/m:{}", jobs).as_str(),
            format!("/p:Platform={}", msvc_platform).as_str(),
        ])
        .current_dir(&build)
        .output()
        .expect("error running msbuild");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("win").join("bin").display()
    );
    println!("cargo:rustc-link-lib=static=libsass");
}

fn main() {
    // Uncomment the line below to generate bindings. Doesn't work on CI as it
    // requires additional tooling
    // write_bindings();

    // Is it already built?
    if let Ok(_) = pkg_config::find_library("libsass") {
        return;
    }

    compile();
}
<commit_msg>Correct msbuild.exe search command for windows<commit_after>// extern crate bindgen;
#[cfg(target_env = "msvc")]
extern crate cc;
extern crate pkg_config;
extern crate num_cpus;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

// Automatically write bindings to libsass
//#[allow(dead_code)]
//fn write_bindings() {
//    let bindings = bindgen::Builder::default()
//        .header("libsass/include/sass.h")
//        .clang_arg("-Ilibsass/include")
//        // To avoid a test failing
//        .blacklist_type("max_align_t")
//        // we do static linking so it should be fine
//        // https://github.com/rust-lang/rust/issues/36927
//        .rustified_enum(".*")
//        .generate()
//        .expect("Unable to generate bindings");
//
//    // Write the bindings to the $OUT_DIR/bindings.rs file.
//    let out_path = PathBuf::from("src");
//    bindings
//        .write_to_file(out_path.join("bindings.rs"))
//        .expect("Couldn't write bindings!");
//}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => panic!("\n{} failed with {}\n", stringify!($e), e),
    })
}

fn cp_r(dir: &Path, dest: &Path) {
    for entry in t!(fs::read_dir(dir)) {
        let entry = t!(entry);
        let path = entry.path();
        let dst = dest.join(path.file_name().unwrap());
        if t!(fs::metadata(&path)).is_file() {
            t!(fs::copy(path, dst));
        } else {
            t!(fs::create_dir_all(&dst));
            cp_r(&path, &dst);
        }
    }
}

fn get_libsass_folder() -> PathBuf {
    env::current_dir().unwrap().join("libsass")
}

// linux/unix
#[cfg(not(target_env = "msvc"))]
fn compile() {
    let target = env::var("TARGET").expect("TARGET not found");
    let src = get_libsass_folder();
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);
    let is_bsd = target.contains("dragonfly")
        || target.contains("freebsd")
        || target.contains("netbsd")
        || target.contains("openbsd");
    let libprobe = | lib: &str | -> bool {
      Command::new("cc")
        .arg("-xc++")
        .arg("-o/dev/null")
        .arg(format!("-l{}",lib))
        .arg("-shared")
        .stderr(Stdio::null())
        .status()
        .expect("")
        .success()
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());
    let r = Command::new(if is_bsd { "gmake" } else { "make" })
        .current_dir(&build)
        .args(&["--jobs", &jobs])
        .output()
        .expect("error running make");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=sass");

    if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    }
    else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    }
    else if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    }
    else {
        panic!("no c++ library found");
    }

}

// windows
#[cfg(target_env = "msvc")]
fn compile() {
    let src = get_libsass_folder();
    let target = env::var("TARGET").expect("TARGET not found in environment");
    let msvc_platform = if target.contains("x86_64") {
        "Win64"
    } else {
        "Win32"
    };
    let dest = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);

    // Find an instance of devenv.exe from Visual Studio IDE in order to upgrade
    // libsass.sln to the current available IDE. Do nothing if no devenv.exe found
    let d = cc::windows_registry::find(target.as_str(), "devenv.exe");
    if let Some(mut d) = d {
        let d = d
            .args(&["/upgrade", "win\\libsass.sln"])
            .current_dir(&build)
            .output()
            .expect("error running devenv");
        if !d.status.success() {
            let err = String::from_utf8_lossy(&d.stderr);
            let out = String::from_utf8_lossy(&d.stdout);
            println!("Upgrade error:\nSTDERR:{}\nSTDOUT:{}", err, out);
        }
    }

    let search = Command::new("where")
        .args(&["msbuild.exe"])
        .output()
        .expect("Could not search for msbuild.exe on path");
    let mut msbuild = if search.status.success() {
        Command::new("msbuild.exe")
    } else {
        cc::windows_registry::find(target.as_str(), "msbuild.exe")
            .expect("Could not find msbuild.exe on the registry")
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or(num_cpus::get().to_string());

    let r = msbuild
        .args(&[
            "win\\libsass.sln",
            "/p:LIBSASS_STATIC_LIB=1",
            "/p:Configuration=Release",
            "/p:WholeProgramOptimization=false",
            format!("/m:{}", jobs).as_str(),
            format!("/p:Platform={}", msvc_platform).as_str(),
        ])
        .current_dir(&build)
        .output()
        .expect("error running msbuild");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("win").join("bin").display()
    );
    println!("cargo:rustc-link-lib=static=libsass");
}

fn main() {
    // Uncomment the line below to generate bindings. Doesn't work on CI as it
    // requires additional tooling
    // write_bindings();

    // Is it already built?
    if let Ok(_) = pkg_config::find_library("libsass") {
        return;
    }

    compile();
}
<|endoftext|>