<commit_before>use cairo;
use cairo::{MatrixTrait, PatternTrait};
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

#[derive(Clone, Default)]
pub struct PatternData {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: RefCell<Option<RsvgWeakNode>>,
}

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Resolve for PatternData {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &PatternData) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.borrow().is_some() {
                *self.node.borrow_mut() = fallback.node.borrow().clone();
            } else {
                *self.node.borrow_mut() = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl PatternData {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref weak) = *self.node.borrow() {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

pub struct NodePattern {
    pattern: PatternData,
}

impl Default for NodePattern {
    fn default() -> NodePattern {
        NodePattern {
            pattern: PatternData::default(),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => self.pattern.units = Some(attr.parse(value)?),
                local_name!("patternContentUnits") => {
                    self.pattern.content_units = Some(attr.parse(value)?)
                }
                local_name!("viewBox") => self.pattern.vbox = Some(Some(attr.parse(value)?)),
                local_name!("preserveAspectRatio") => {
                    self.pattern.preserve_aspect_ratio = Some(attr.parse(value)?)
                }
                local_name!("patternTransform") => self.pattern.affine = Some(attr.parse(value)?),
                local_name!("xlink:href") => {
                    self.pattern.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }
                local_name!("x") => self.pattern.x = Some(attr.parse(value)?),
                local_name!("y") => self.pattern.y = Some(attr.parse(value)?),
                local_name!("width") => {
                    self.pattern.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }
                local_name!("height") => {
                    self.pattern.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }
                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl PaintSource for NodePattern {
    type Source = PatternData;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        _bbox: &BoundingBox,
    ) -> Result<Option<Self::Source>, RenderingError> {
        *self.pattern.node.borrow_mut() = Some(node.downgrade());

        let mut result = node.borrow().get_impl::<NodePattern>().pattern.clone();
        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(acquired) = draw_ctx
                .acquired_nodes()
                .get_node_of_type(result.fallback.as_ref(), NodeType::Pattern)
            {
                let a_node = acquired.get();

                if stack.contains(a_node) {
                    rsvg_log!("circular reference in pattern {}", node);
                    return Err(RenderingError::CircularReference);
                }

                let node_data = a_node.borrow();
                result.resolve_from_fallback(&node_data.get_impl::<NodePattern>().pattern);

                stack.push(a_node);
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(Some(result))
    }

    fn set_pattern_on_draw_context(
        &self,
        pattern: &Self::Source,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(pattern.is_resolved());

        if pattern.node.borrow().is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = pattern.units.unwrap();
        let content_units = pattern.content_units.unwrap();
        let pattern_affine = pattern.affine.unwrap();
        let vbox = pattern.vbox.unwrap();
        let preserve_aspect_ratio = pattern.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = pattern.x.unwrap().normalize(values, &params);
            let pattern_y = pattern.y.unwrap().normalize(values, &params);
            let pattern_width = pattern.width.unwrap().normalize(values, &params);
            let pattern_height = pattern.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(&1.0) || !schscale.approx_eq_cairo(&1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node = pattern.node.borrow().as_ref().unwrap().upgrade().unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(&pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&cairo::Pattern::SurfacePattern(surface_pattern));

        res.and_then(|_| Ok(true))
    }
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = PatternData::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<commit_msg>pattern: remove the intermediate PatternData struct<commit_after>use cairo;
use cairo::{MatrixTrait, PatternTrait};
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

#[derive(Clone, Default)]
pub struct NodePattern {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: RefCell<Option<RsvgWeakNode>>,
}

impl NodeTrait for NodePattern {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => self.units = Some(attr.parse(value)?),
                local_name!("patternContentUnits") => {
                    self.content_units = Some(attr.parse(value)?)
                }
                local_name!("viewBox") => self.vbox = Some(Some(attr.parse(value)?)),
                local_name!("preserveAspectRatio") => {
                    self.preserve_aspect_ratio = Some(attr.parse(value)?)
                }
                local_name!("patternTransform") => self.affine = Some(attr.parse(value)?),
                local_name!("xlink:href") => {
                    self.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }
                local_name!("x") => self.x = Some(attr.parse(value)?),
                local_name!("y") => self.y = Some(attr.parse(value)?),
                local_name!("width") => {
                    self.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }
                local_name!("height") => {
                    self.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }
                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl Resolve for NodePattern {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &NodePattern) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.borrow().is_some() {
                *self.node.borrow_mut() = fallback.node.borrow().clone();
            } else {
                *self.node.borrow_mut() = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl PaintSource for NodePattern {
    type Source = NodePattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        _bbox: &BoundingBox,
    ) -> Result<Option<Self::Source>, RenderingError> {
        *self.node.borrow_mut() = Some(node.downgrade());

        let mut result = node.borrow().get_impl::<NodePattern>().clone();
        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(acquired) = draw_ctx
                .acquired_nodes()
                .get_node_of_type(result.fallback.as_ref(), NodeType::Pattern)
            {
                let a_node = acquired.get();

                if stack.contains(a_node) {
                    rsvg_log!("circular reference in pattern {}", node);
                    return Err(RenderingError::CircularReference);
                }

                let node_data = a_node.borrow();
                result.resolve_from_fallback(&node_data.get_impl::<NodePattern>());

                stack.push(a_node);
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(Some(result))
    }

    fn set_pattern_on_draw_context(
        &self,
        pattern: &Self::Source,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(pattern.is_resolved());

        if pattern.node.borrow().is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = pattern.units.unwrap();
        let content_units = pattern.content_units.unwrap();
        let pattern_affine = pattern.affine.unwrap();
        let vbox = pattern.vbox.unwrap();
        let preserve_aspect_ratio = pattern.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = pattern.x.unwrap().normalize(values, &params);
            let pattern_y = pattern.y.unwrap().normalize(values, &params);
            let pattern_width = pattern.width.unwrap().normalize(values, &params);
            let pattern_height = pattern.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(&1.0) || !schscale.approx_eq_cairo(&1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node = pattern.node.borrow().as_ref().unwrap().upgrade().unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(&pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&cairo::Pattern::SurfacePattern(surface_pattern));

        res.and_then(|_| Ok(true))
    }
}

impl NodePattern {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref weak) = *self.node.borrow() {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = NodePattern::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<|endoftext|>


<commit_before>use cairo;
use cairo::{MatrixTrait, PatternTrait};
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

#[derive(Clone, Default)]
pub struct Pattern {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: Option<RsvgWeakNode>,
}

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Resolve for Pattern {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &Pattern) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.is_some() {
                self.node = fallback.node.clone();
            } else {
                self.node = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl Pattern {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref weak) = self.node {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

pub struct NodePattern {
    pattern: RefCell<Pattern>,
}

impl Default for NodePattern {
    fn default() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(Pattern::default()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        let mut p = self.pattern.borrow_mut();

        p.node = Some(node.downgrade());

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => p.units = Some(attr.parse(value)?),

                local_name!("patternContentUnits") => p.content_units = Some(attr.parse(value)?),

                local_name!("viewBox") => p.vbox = Some(Some(attr.parse(value)?)),

                local_name!("preserveAspectRatio") => {
                    p.preserve_aspect_ratio = Some(attr.parse(value)?)
                }

                local_name!("patternTransform") => p.affine = Some(attr.parse(value)?),

                local_name!("xlink:href") => {
                    p.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }

                local_name!("x") => p.x = Some(attr.parse(value)?),

                local_name!("y") => p.y = Some(attr.parse(value)?),

                local_name!("width") => {
                    p.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }

                local_name!("height") => {
                    p.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl PaintSource for NodePattern {
    type Source = Pattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        _bbox: &BoundingBox,
    ) -> Result<Option<Self::Source>, RenderingError> {
        let mut result = node
            .borrow()
            .get_impl::<NodePattern>()
            .pattern
            .borrow()
            .clone();
        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(acquired) = draw_ctx
                .acquired_nodes()
                .get_node_of_type(result.fallback.as_ref(), NodeType::Pattern)
            {
                let a_node = acquired.get();

                if stack.contains(a_node) {
                    rsvg_log!("circular reference in pattern {}", node);
                    return Err(RenderingError::CircularReference);
                }

                let fallback = a_node.borrow().get_impl::<NodePattern>().pattern.borrow();
                result.resolve_from_fallback(&fallback);

                stack.push(a_node);
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(Some(result))
    }

    fn set_pattern_on_draw_context(
        &self,
        pattern: &Self::Source,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(pattern.is_resolved());

        if pattern.node.is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = pattern.units.unwrap();
        let content_units = pattern.content_units.unwrap();
        let pattern_affine = pattern.affine.unwrap();
        let vbox = pattern.vbox.unwrap();
        let preserve_aspect_ratio = pattern.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = pattern.x.unwrap().normalize(values, &params);
            let pattern_y = pattern.y.unwrap().normalize(values, &params);
            let pattern_width = pattern.width.unwrap().normalize(values, &params);
            let pattern_height = pattern.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(&1.0) || !schscale.approx_eq_cairo(&1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node = RsvgNode::upgrade(pattern.node.as_ref().unwrap()).unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(&pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&cairo::Pattern::SurfacePattern(surface_pattern));

        res.and_then(|_| Ok(true))
    }
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = Pattern::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<commit_msg>pattern: store the weak ref to node in resolve()<commit_after>use cairo;
use cairo::{MatrixTrait, PatternTrait};
use markup5ever::local_name;
use std::cell::RefCell;
use std::f64;

use crate::allowed_url::Fragment;
use crate::aspect_ratio::*;
use crate::bbox::*;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{DrawingCtx, NodeStack};
use crate::error::{AttributeResultExt, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve};
use crate::parsers::ParseValue;
use crate::properties::ComputedValues;
use crate::property_bag::PropertyBag;
use crate::rect::RectangleExt;
use crate::unit_interval::UnitInterval;
use crate::viewbox::*;

coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);

#[derive(Clone, Default)]
pub struct Pattern {
    pub units: Option<PatternUnits>,
    pub content_units: Option<PatternContentUnits>,
    // This Option<Option<ViewBox>> is a bit strange.  We want a field
    // with value None to mean, "this field isn't resolved yet".  However,
    // the vbox can very well be *not* specified in the SVG file.
    // In that case, the fully resolved pattern will have a .vbox=Some(None) value.
    pub vbox: Option<Option<ViewBox>>,
    pub preserve_aspect_ratio: Option<AspectRatio>,
    pub affine: Option<cairo::Matrix>,
    pub fallback: Option<Fragment>,
    pub x: Option<LengthHorizontal>,
    pub y: Option<LengthVertical>,
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,

    // Point back to our corresponding node, or to the fallback node which has children.
    // If the value is None, it means we are fully resolved and didn't find any children
    // among the fallbacks.
    pub node: Option<RsvgWeakNode>,
}

macro_rules! fallback_to (
    ($dest:expr, $default:expr) => (
        $dest = $dest.take ().or ($default)
    );
);

impl Resolve for Pattern {
    fn is_resolved(&self) -> bool {
        self.units.is_some()
            && self.content_units.is_some()
            && self.vbox.is_some()
            && self.preserve_aspect_ratio.is_some()
            && self.affine.is_some()
            && self.x.is_some()
            && self.y.is_some()
            && self.width.is_some()
            && self.height.is_some()
            && self.children_are_resolved()
    }

    fn resolve_from_fallback(&mut self, fallback: &Pattern) {
        fallback_to!(self.units, fallback.units);
        fallback_to!(self.content_units, fallback.content_units);
        fallback_to!(self.vbox, fallback.vbox);
        fallback_to!(self.preserve_aspect_ratio, fallback.preserve_aspect_ratio);
        fallback_to!(self.affine, fallback.affine);
        fallback_to!(self.x, fallback.x);
        fallback_to!(self.y, fallback.y);
        fallback_to!(self.width, fallback.width);
        fallback_to!(self.height, fallback.height);

        self.fallback = fallback.fallback.clone();

        if !self.children_are_resolved() {
            if fallback.node.is_some() {
                self.node = fallback.node.clone();
            } else {
                self.node = None;
            }
        }
    }

    fn resolve_from_defaults(&mut self) {
        fallback_to!(self.units, Some(PatternUnits::default()));
        fallback_to!(self.content_units, Some(PatternContentUnits::default()));
        fallback_to!(self.vbox, Some(None));
        fallback_to!(self.preserve_aspect_ratio, Some(AspectRatio::default()));
        fallback_to!(self.affine, Some(cairo::Matrix::identity()));
        fallback_to!(self.x, Some(Default::default()));
        fallback_to!(self.y, Some(Default::default()));
        fallback_to!(self.width, Some(Default::default()));
        fallback_to!(self.height, Some(Default::default()));
    }
}

impl Pattern {
    fn children_are_resolved(&self) -> bool {
        if let Some(ref weak) = self.node {
            let strong_node = &weak.clone().upgrade().unwrap();
            strong_node.has_children()
        } else {
            // We are an empty pattern; there is nothing further that
            // can be resolved for children.
            true
        }
    }
}

pub struct NodePattern {
    pattern: RefCell<Pattern>,
}

impl Default for NodePattern {
    fn default() -> NodePattern {
        NodePattern {
            pattern: RefCell::new(Pattern::default()),
        }
    }
}

impl NodeTrait for NodePattern {
    fn set_atts(&self, _node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        let mut p = self.pattern.borrow_mut();

        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("patternUnits") => p.units = Some(attr.parse(value)?),

                local_name!("patternContentUnits") => p.content_units = Some(attr.parse(value)?),

                local_name!("viewBox") => p.vbox = Some(Some(attr.parse(value)?)),

                local_name!("preserveAspectRatio") => {
                    p.preserve_aspect_ratio = Some(attr.parse(value)?)
                }

                local_name!("patternTransform") => p.affine = Some(attr.parse(value)?),

                local_name!("xlink:href") => {
                    p.fallback = Some(Fragment::parse(value).attribute(attr)?);
                }

                local_name!("x") => p.x = Some(attr.parse(value)?),

                local_name!("y") => p.y = Some(attr.parse(value)?),

                local_name!("width") => {
                    p.width =
                        Some(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?)
                }

                local_name!("height") => {
                    p.height =
                        Some(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn overflow_hidden(&self) -> bool {
        true
    }
}

impl PaintSource for NodePattern {
    type Source = Pattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
        _bbox: &BoundingBox,
    ) -> Result<Option<Self::Source>, RenderingError> {
        {
            // Initialize pattern.node and release the mutable borrow
            let mut p = self.pattern.borrow_mut();
            p.node = Some(node.downgrade());
        }

        let mut result = node
            .borrow()
            .get_impl::<NodePattern>()
            .pattern
            .borrow()
            .clone();
        let mut stack = NodeStack::new();

        while !result.is_resolved() {
            if let Some(acquired) = draw_ctx
                .acquired_nodes()
                .get_node_of_type(result.fallback.as_ref(), NodeType::Pattern)
            {
                let a_node = acquired.get();

                if stack.contains(a_node) {
                    rsvg_log!("circular reference in pattern {}", node);
                    return Err(RenderingError::CircularReference);
                }

                let fallback = a_node.borrow().get_impl::<NodePattern>().pattern.borrow();
                result.resolve_from_fallback(&fallback);

                stack.push(a_node);
            } else {
                result.resolve_from_defaults();
            }
        }

        Ok(Some(result))
    }

    fn set_pattern_on_draw_context(
        &self,
        pattern: &Self::Source,
        values: &ComputedValues,
        draw_ctx: &mut DrawingCtx,
        _opacity: &UnitInterval,
        bbox: &BoundingBox,
    ) -> Result<bool, RenderingError> {
        assert!(pattern.is_resolved());

        if pattern.node.is_none() {
            // This means we didn't find any children among the fallbacks,
            // so there is nothing to render.
            return Ok(false);
        }

        let units = pattern.units.unwrap();
        let content_units = pattern.content_units.unwrap();
        let pattern_affine = pattern.affine.unwrap();
        let vbox = pattern.vbox.unwrap();
        let preserve_aspect_ratio = pattern.preserve_aspect_ratio.unwrap();

        let (pattern_x, pattern_y, pattern_width, pattern_height) = {
            let params = if units == PatternUnits(CoordUnits::ObjectBoundingBox) {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let pattern_x = pattern.x.unwrap().normalize(values, &params);
            let pattern_y = pattern.y.unwrap().normalize(values, &params);
            let pattern_width = pattern.width.unwrap().normalize(values, &params);
            let pattern_height = pattern.height.unwrap().normalize(values, &params);

            (pattern_x, pattern_y, pattern_width, pattern_height)
        };

        // Work out the size of the rectangle so it takes into account the object bounding box

        let bbwscale: f64;
        let bbhscale: f64;

        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                bbwscale = bbrect.width;
                bbhscale = bbrect.height;
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                bbwscale = 1.0;
                bbhscale = 1.0;
            }
        }

        let cr = draw_ctx.get_cairo_context();
        let affine = cr.get_matrix();
        let taffine = cairo::Matrix::multiply(&pattern_affine, &affine);

        let mut scwscale = (taffine.xx * taffine.xx + taffine.xy * taffine.xy).sqrt();
        let mut schscale = (taffine.yx * taffine.yx + taffine.yy * taffine.yy).sqrt();

        let pw: i32 = (pattern_width * bbwscale * scwscale) as i32;
        let ph: i32 = (pattern_height * bbhscale * schscale) as i32;

        let scaled_width = pattern_width * bbwscale;
        let scaled_height = pattern_height * bbhscale;

        if scaled_width.abs() < f64::EPSILON
            || scaled_height.abs() < f64::EPSILON
            || pw < 1
            || ph < 1
        {
            return Ok(false);
        }

        scwscale = f64::from(pw) / scaled_width;
        schscale = f64::from(ph) / scaled_height;

        let mut affine: cairo::Matrix = cairo::Matrix::identity();

        // Create the pattern coordinate system
        match units {
            PatternUnits(CoordUnits::ObjectBoundingBox) => {
                let bbrect = bbox.rect.unwrap();
                affine.translate(
                    bbrect.x + pattern_x * bbrect.width,
                    bbrect.y + pattern_y * bbrect.height,
                );
            }

            PatternUnits(CoordUnits::UserSpaceOnUse) => {
                affine.translate(pattern_x, pattern_y);
            }
        }

        // Apply the pattern transform
        affine = cairo::Matrix::multiply(&affine, &pattern_affine);

        let mut caffine: cairo::Matrix;

        // Create the pattern contents coordinate system
        let _params = if let Some(vbox) = vbox {
            // If there is a vbox, use that
            let (mut x, mut y, w, h) = preserve_aspect_ratio.compute(
                &vbox,
                &cairo::Rectangle::new(
                    0.0,
                    0.0,
                    pattern_width * bbwscale,
                    pattern_height * bbhscale,
                ),
            );

            x -= vbox.x * w / vbox.width;
            y -= vbox.y * h / vbox.height;

            caffine = cairo::Matrix::new(w / vbox.width, 0.0, 0.0, h / vbox.height, x, y);

            draw_ctx.push_view_box(vbox.width, vbox.height)
        } else if content_units == PatternContentUnits(CoordUnits::ObjectBoundingBox) {
            // If coords are in terms of the bounding box, use them
            let bbrect = bbox.rect.unwrap();

            caffine = cairo::Matrix::identity();
            caffine.scale(bbrect.width, bbrect.height);

            draw_ctx.push_view_box(1.0, 1.0)
        } else {
            caffine = cairo::Matrix::identity();
            draw_ctx.get_view_params()
        };

        if !scwscale.approx_eq_cairo(&1.0) || !schscale.approx_eq_cairo(&1.0) {
            let mut scalematrix = cairo::Matrix::identity();
            scalematrix.scale(scwscale, schscale);
            caffine = cairo::Matrix::multiply(&caffine, &scalematrix);

            scalematrix = cairo::Matrix::identity();
            scalematrix.scale(1.0 / scwscale, 1.0 / schscale);

            affine = cairo::Matrix::multiply(&scalematrix, &affine);
        }

        // Draw to another surface

        let cr_save = draw_ctx.get_cairo_context();

        let surface = cr_save
            .get_target()
            .create_similar(cairo::Content::ColorAlpha, pw, ph);

        let cr_pattern = cairo::Context::new(&surface);

        draw_ctx.set_cairo_context(&cr_pattern);

        // Set up transformations to be determined by the contents units

        // Draw everything
        let pattern_node = RsvgNode::upgrade(pattern.node.as_ref().unwrap()).unwrap();
        let pattern_cascaded = CascadedValues::new_from_node(&pattern_node);
        let pattern_values = pattern_cascaded.get();

        cr_pattern.set_matrix(caffine);

        let res = draw_ctx.with_discrete_layer(&pattern_node, pattern_values, false, &mut |dc| {
            pattern_node.draw_children(&pattern_cascaded, dc, false)
        });

        // Return to the original coordinate system and rendering context

        draw_ctx.set_cairo_context(&cr_save);

        // Set the final surface as a Cairo pattern into the Cairo context

        let surface_pattern = cairo::SurfacePattern::create(&surface);
        surface_pattern.set_extend(cairo::Extend::Repeat);

        let mut matrix = affine;
        matrix.invert();

        surface_pattern.set_matrix(matrix);
        surface_pattern.set_filter(cairo::Filter::Best);

        cr_save.set_source(&cairo::Pattern::SurfacePattern(surface_pattern));

        res.and_then(|_| Ok(true))
    }
}

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

    #[test]
    fn pattern_resolved_from_defaults_is_really_resolved() {
        let mut pat = Pattern::default();

        pat.resolve_from_defaults();
        assert!(pat.is_resolved());
    }
}
<|endoftext|>


<commit_before>extern crate rand;
use std::collections::HashSet;
use once_cell::sync::Lazy;

const COLORS: Lazy<HashSet<&'static str>> = sync_lazy! {
    let mut set = HashSet::new();
    set.insert("black");
    set.insert("red");
    set.insert("green");
    set.insert("yellow");
    set.insert("blue");
    set.insert("magenta");
    set.insert("cyan");
    set.insert("white");
    set.insert("default");
    set
};

pub fn replace_all(input: String, from: &str, to: &str) -> String {
    input.replace(from, to)
}

pub fn consolidate_whitespace( input: String ) -> String {
    let found = input.find( |c: char| !c.is_whitespace() );
    let mut result = String::new();

    if let Some(found) = found {
        let (leading,rest) = input.split_at(found);
        let lastchar = input.chars().rev().next().unwrap();

        result.push_str(leading);

        let iter = rest.split_whitespace();
        for elem in iter {
            result.push_str(elem);
            result.push(' ');
        }
        result.pop();
        if lastchar.is_whitespace() {
            result.push(' ');
        }
    }

    result
}

pub fn to_u(rs_str: String, default_value: u32) -> u32 {
    let mut result = rs_str.parse::<u32>();

    if result.is_err() {
        result = Ok(default_value);
    }

    result.unwrap()
}

pub fn is_http_url(url: &str) -> bool {
    url.starts_with("https://") || url.starts_with("http://")
}

pub fn is_query_url(url: &str) -> bool {
   url.starts_with("query:")
}

pub fn is_filter_url(url: &str) -> bool {
    url.starts_with("filter:")
}

pub fn is_exec_url(url: &str) -> bool {
    url.starts_with("exec:")
}

pub fn get_default_browser() -> String {
    use std::env;
    match env::var("BROWSER") {
        Ok(val) => return val,
        Err(_) => return String::from("lynx"),
    }
}

pub fn trim(rs_str: String) -> String {
    rs_str.trim().to_string()
}

pub fn trim_end(rs_str: String) -> String {
    let x: &[_] = &['\n','\r'];
    rs_str.trim_right_matches(x).to_string()
}

pub fn get_random_value(max: u32) -> u32 {
   rand::random::<u32>() % max
}

pub fn is_valid_color(color: &str) -> bool {
    if COLORS.contains(color) {
        return true;
    }
    if color.starts_with("color0") {
        return color == "color0";
    }
    if color.starts_with("color") {
        let num_part = &color[5..];
        match num_part.parse::<u8>() {
            Ok(_) => return true,
            _ => return false,
        }
    }
    false
}

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

    #[test]
    fn t_replace_all() {
	assert_eq!(
            replace_all(String::from("aaa"), "a", "b"),
            String::from("bbb"));
	assert_eq!(
            replace_all(String::from("aaa"), "aa", "ba"),
            String::from("baa"));
	assert_eq!(
            replace_all(String::from("aaaaaa"), "aa", "ba"),
            String::from("bababa"));
	assert_eq!(
            replace_all(String::new(), "a", "b"),
            String::new());

        let input = String::from("aaaa");
	assert_eq!(replace_all(input.clone(), "b", "c"), input);

	assert_eq!(
            replace_all(String::from("this is a normal test text"), " t", " T"),
            String::from("this is a normal Test Text"));

	assert_eq!(
            replace_all(String::from("o o o"), "o", "<o>"),
            String::from("<o> <o> <o>"));
    }

    #[test]
    fn t_consolidate_whitespace() {
        assert_eq!(
            consolidate_whitespace(String::from("LoremIpsum")),
            String::from("LoremIpsum"));
        assert_eq!(
            consolidate_whitespace(String::from("Lorem Ipsum")),
            String::from("Lorem Ipsum"));
        assert_eq!(
            consolidate_whitespace(String::from(" Lorem \t\tIpsum \t ")),
            String::from(" Lorem Ipsum "));
        assert_eq!(consolidate_whitespace(String::from(" Lorem \r\n\r\n\tIpsum")),
            String::from(" Lorem Ipsum"));
        assert_eq!(consolidate_whitespace(String::new()), String::new());
        assert_eq!(consolidate_whitespace(String::from("    Lorem \t\tIpsum \t ")),
            String::from("    Lorem Ipsum "));
        assert_eq!(consolidate_whitespace(String::from("   Lorem \r\n\r\n\tIpsum")),
            String::from("   Lorem Ipsum"));
    }

    #[test]
    fn t_to_u() {
        assert_eq!(to_u(String::from("0"), 10), 0);
        assert_eq!(to_u(String::from("23"), 1), 23);
        assert_eq!(to_u(String::from(""), 0), 0);
        assert_eq!(to_u(String::from("zero"), 1), 1);
    }

    #[test]
    fn t_is_http_url() {
	assert!(is_http_url("https://foo.bar"));
	assert!(is_http_url("http://"));
	assert!(is_http_url("https://"));

	assert!(!is_http_url("htt://foo.bar"));
	assert!(!is_http_url("http:/"));
	assert!(!is_http_url("foo://bar"));
    }

    #[test]
    fn t_trim() {
        assert_eq!(trim(String::from("  xxx\r\n")), "xxx");
        assert_eq!(trim(String::from("\n\n abc  foobar\n")), "abc  foobar");
        assert_eq!(trim(String::from("")), "");
        assert_eq!(trim(String::from("     \n")), "");
    }

    #[test]
    fn t_trim_end() {
        assert_eq!(trim_end(String::from("quux\n")), "quux");
    }

    #[test]
    fn t_is_valid_color() {
        let invalid = [
            "awesome",
            "list",
            "of",
            "things",
            "that",
            "aren't",
            "colors",
            "color0123",
            "color1024",
        ];
        for color in &invalid {
            assert!(!is_valid_color(color));
        }

        let valid = [
            "black",
            "red",
            "green",
            "yellow",
            "blue",
            "magenta",
            "cyan",
            "white",
            "default",
            "color0",
            "color163",
        ];

        for color in &valid {
            assert!(is_valid_color(color));
        }
    }
}

<commit_msg>Port is_xx_url tests to Rust<commit_after>extern crate rand;
use std::collections::HashSet;
use once_cell::sync::Lazy;

const COLORS: Lazy<HashSet<&'static str>> = sync_lazy! {
    let mut set = HashSet::new();
    set.insert("black");
    set.insert("red");
    set.insert("green");
    set.insert("yellow");
    set.insert("blue");
    set.insert("magenta");
    set.insert("cyan");
    set.insert("white");
    set.insert("default");
    set
};

pub fn replace_all(input: String, from: &str, to: &str) -> String {
    input.replace(from, to)
}

pub fn consolidate_whitespace( input: String ) -> String {
    let found = input.find( |c: char| !c.is_whitespace() );
    let mut result = String::new();

    if let Some(found) = found {
        let (leading,rest) = input.split_at(found);
        let lastchar = input.chars().rev().next().unwrap();

        result.push_str(leading);

        let iter = rest.split_whitespace();
        for elem in iter {
            result.push_str(elem);
            result.push(' ');
        }
        result.pop();
        if lastchar.is_whitespace() {
            result.push(' ');
        }
    }

    result
}

pub fn to_u(rs_str: String, default_value: u32) -> u32 {
    let mut result = rs_str.parse::<u32>();

    if result.is_err() {
        result = Ok(default_value);
    }

    result.unwrap()
}

pub fn is_http_url(url: &str) -> bool {
    url.starts_with("https://") || url.starts_with("http://")
}

pub fn is_query_url(url: &str) -> bool {
   url.starts_with("query:")
}

pub fn is_filter_url(url: &str) -> bool {
    url.starts_with("filter:")
}

pub fn is_exec_url(url: &str) -> bool {
    url.starts_with("exec:")
}

pub fn get_default_browser() -> String {
    use std::env;
    match env::var("BROWSER") {
        Ok(val) => return val,
        Err(_) => return String::from("lynx"),
    }
}

pub fn trim(rs_str: String) -> String {
    rs_str.trim().to_string()
}

pub fn trim_end(rs_str: String) -> String {
    let x: &[_] = &['\n','\r'];
    rs_str.trim_right_matches(x).to_string()
}

pub fn get_random_value(max: u32) -> u32 {
   rand::random::<u32>() % max
}

pub fn is_valid_color(color: &str) -> bool {
    if COLORS.contains(color) {
        return true;
    }
    if color.starts_with("color0") {
        return color == "color0";
    }
    if color.starts_with("color") {
        let num_part = &color[5..];
        match num_part.parse::<u8>() {
            Ok(_) => return true,
            _ => return false,
        }
    }
    false
}

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

    #[test]
    fn t_replace_all() {
	assert_eq!(
            replace_all(String::from("aaa"), "a", "b"),
            String::from("bbb"));
	assert_eq!(
            replace_all(String::from("aaa"), "aa", "ba"),
            String::from("baa"));
	assert_eq!(
            replace_all(String::from("aaaaaa"), "aa", "ba"),
            String::from("bababa"));
	assert_eq!(
            replace_all(String::new(), "a", "b"),
            String::new());

        let input = String::from("aaaa");
	assert_eq!(replace_all(input.clone(), "b", "c"), input);

	assert_eq!(
            replace_all(String::from("this is a normal test text"), " t", " T"),
            String::from("this is a normal Test Text"));

	assert_eq!(
            replace_all(String::from("o o o"), "o", "<o>"),
            String::from("<o> <o> <o>"));
    }

    #[test]
    fn t_consolidate_whitespace() {
        assert_eq!(
            consolidate_whitespace(String::from("LoremIpsum")),
            String::from("LoremIpsum"));
        assert_eq!(
            consolidate_whitespace(String::from("Lorem Ipsum")),
            String::from("Lorem Ipsum"));
        assert_eq!(
            consolidate_whitespace(String::from(" Lorem \t\tIpsum \t ")),
            String::from(" Lorem Ipsum "));
        assert_eq!(consolidate_whitespace(String::from(" Lorem \r\n\r\n\tIpsum")),
            String::from(" Lorem Ipsum"));
        assert_eq!(consolidate_whitespace(String::new()), String::new());
        assert_eq!(consolidate_whitespace(String::from("    Lorem \t\tIpsum \t ")),
            String::from("    Lorem Ipsum "));
        assert_eq!(consolidate_whitespace(String::from("   Lorem \r\n\r\n\tIpsum")),
            String::from("   Lorem Ipsum"));
    }

    #[test]
    fn t_to_u() {
        assert_eq!(to_u(String::from("0"), 10), 0);
        assert_eq!(to_u(String::from("23"), 1), 23);
        assert_eq!(to_u(String::from(""), 0), 0);
        assert_eq!(to_u(String::from("zero"), 1), 1);
    }

    #[test]
    fn t_is_http_url() {
	assert!(is_http_url("https://foo.bar"));
	assert!(is_http_url("http://"));
	assert!(is_http_url("https://"));

	assert!(!is_http_url("htt://foo.bar"));
	assert!(!is_http_url("http:/"));
	assert!(!is_http_url("foo://bar"));
    }

    #[test]
    fn t_is_query_url() {
	assert!(is_query_url("query:"));
	assert!(is_query_url("query: example"));

	assert!(!is_query_url("query"));
	assert!(!is_query_url("   query:"));
    }

    #[test]
    fn t_is_filter_url() {
	assert!(is_filter_url("filter:"));
	assert!(is_filter_url("filter: example"));

	assert!(!is_filter_url("filter"));
	assert!(!is_filter_url("   filter:"));
    }

    #[test]
    fn t_is_exec_url() {
	assert!(is_exec_url("exec:"));
	assert!(is_exec_url("exec: example"));

	assert!(!is_exec_url("exec"));
	assert!(!is_exec_url("   exec:"));
    }

    #[test]
    fn t_trim() {
        assert_eq!(trim(String::from("  xxx\r\n")), "xxx");
        assert_eq!(trim(String::from("\n\n abc  foobar\n")), "abc  foobar");
        assert_eq!(trim(String::from("")), "");
        assert_eq!(trim(String::from("     \n")), "");
    }

    #[test]
    fn t_trim_end() {
        assert_eq!(trim_end(String::from("quux\n")), "quux");
    }

    #[test]
    fn t_is_valid_color() {
        let invalid = [
            "awesome",
            "list",
            "of",
            "things",
            "that",
            "aren't",
            "colors",
            "color0123",
            "color1024",
        ];
        for color in &invalid {
            assert!(!is_valid_color(color));
        }

        let valid = [
            "black",
            "red",
            "green",
            "yellow",
            "blue",
            "magenta",
            "cyan",
            "white",
            "default",
            "color0",
            "color163",
        ];

        for color in &valid {
            assert!(is_valid_color(color));
        }
    }
}

<|endoftext|>


<commit_before>extern crate amq_protocol_types;

use amq_protocol_types::*;

#[test]
fn test_full_integration() {
    let mut table = FieldTable::new();
    let mut table2 = FieldTable::new();

    table2.insert("foo".to_string(), AMQPValue::DecimalValue(DecimalValue { scale: 55, value: 999, }));
    table2.insert("bar".to_string(), AMQPValue::ShortString("blah".to_string()));
    table2.insert("baz".to_string(), AMQPValue::LongString("blah42".to_string()));

    table.insert("foo".to_string(),  AMQPValue::FieldArray(vec![AMQPValue::Void]));
    table.insert("oof".to_string(),  AMQPValue::FieldArray(FieldArray::new()));
    table.insert("bar".to_string(),  AMQPValue::FieldTable(FieldTable::new()));
    table.insert("blah".to_string(), AMQPValue::Boolean(false));
    table.insert("aaaa".to_string(), AMQPValue::ShortShortInt(42));
    table.insert("bbbb".to_string(), AMQPValue::ShortShortUInt(64));
    table.insert("cccc".to_string(), AMQPValue::ShortInt(32));
    table.insert("dddd".to_string(), AMQPValue::ShortUInt(132));
    table.insert("eeee".to_string(), AMQPValue::LongInt(-53));
    table.insert("ffff".to_string(), AMQPValue::LongUInt(66666));
    table.insert("gggg".to_string(), AMQPValue::LongLongInt(-9999));
    table.insert("hhhh".to_string(), AMQPValue::LongLongUInt(0));
    table.insert("iiii".to_string(), AMQPValue::Float(42.3));
    table.insert("jjjj".to_string(), AMQPValue::Double(0.00987654321));
    table.insert("kkkk".to_string(), AMQPValue::Timestamp(1234567890));
    table.insert("llll".to_string(), AMQPValue::Void);

    let value              = AMQPValue::FieldTable(table);
    let mut buf: [u8; 512] = [0; 512];

    assert_eq!(parse_value(gen_value((&mut buf[..], 0), &value).unwrap().0).to_result().unwrap(), value);
}
<commit_msg>types: update integration test<commit_after>extern crate amq_protocol_types;

use amq_protocol_types::*;

#[test]
fn test_full_integration() {
    let mut table = FieldTable::new();
    let mut table2 = FieldTable::new();

    table2.insert("foo".to_string(), AMQPValue::DecimalValue(DecimalValue { scale: 55, value: 999, }));
    table2.insert("bar".to_string(), AMQPValue::ShortString("blah".to_string()));
    table2.insert("baz".to_string(), AMQPValue::LongString("blah42".to_string()));

    table.insert("foo".to_string(),  AMQPValue::FieldArray(vec![AMQPValue::Void]));
    table.insert("oof".to_string(),  AMQPValue::FieldArray(FieldArray::new()));
    table.insert("bar".to_string(),  AMQPValue::FieldTable(FieldTable::new()));
    table.insert("blah".to_string(), AMQPValue::Boolean(false));
    table.insert("aaaa".to_string(), AMQPValue::ShortShortInt(42));
    table.insert("bbbb".to_string(), AMQPValue::ShortShortUInt(64));
    table.insert("cccc".to_string(), AMQPValue::ShortInt(32));
    table.insert("dddd".to_string(), AMQPValue::ShortUInt(132));
    table.insert("eeee".to_string(), AMQPValue::LongInt(-53));
    table.insert("ffff".to_string(), AMQPValue::LongUInt(66666));
    table.insert("gggg".to_string(), AMQPValue::LongLongInt(-9999));
    table.insert("hhhh".to_string(), AMQPValue::LongLongUInt(0));
    table.insert("iiii".to_string(), AMQPValue::Float(42.3));
    table.insert("tabl".to_string(), AMQPValue::FieldTable(table2));
    table.insert("jjjj".to_string(), AMQPValue::Double(0.00987654321));
    table.insert("kkkk".to_string(), AMQPValue::Timestamp(1234567890));
    table.insert("llll".to_string(), AMQPValue::Void);

    let value              = AMQPValue::FieldTable(table);
    let mut buf: [u8; 512] = [0; 512];

    assert_eq!(parse_value(gen_value((&mut buf[..], 0), &value).unwrap().0).to_result().unwrap(), value);
}
<|endoftext|>


<commit_before>use *;

#[test]
fn test_standard() {
    let v = Varlink::from_string(
        "
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
vendor: string,
product: string,
version: string,
url: string,
interfaces: []string
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.varlink.service");
    assert_eq!(
        v.interface.doc,
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\
         "
    );
    assert_eq!(
        v.interface
            .methods
            .get("GetInterfaceDescription".into())
            .unwrap()
            .doc,
        "# Get the description of an interface that is implemented by this service."
    );
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(),
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\n\
         interface org.varlink.service\n\
         \n\
         # Get a list of all the interfaces a service provides and information\n\
         # about the implementation.\n\
         method GetInfo() -> (vendor: string, product: string, version: string, url: string, \
         interfaces: []string)\n\
         \n\
         # Get the description of an interface that is implemented by this service.\n\
         method GetInterfaceDescription(interface: string) -> (description: string)\n\
         \n\
         # The requested interface was not found.\n\
         error InterfaceNotFound (interface: string)\n\
         \n\
         # One of the passed parameters is invalid.\n\
         error InvalidParameter (parameter: string)\n\
         \n\
         # The requested method was not found\n\
         error MethodNotFound (method: string)\n\
         \n\
         # The interface defines the requested method, but the service does not\n\
         # implement it.\n\
         error MethodNotImplemented (method: string)\n\
         "
    );
}

#[test]
fn test_complex() {
    let v = Varlink::from_string(
        "interface org.example.complex
type TypeEnum ( a, b, c )

type TypeFoo (
bool: bool,
int: int,
float: float,
string: string,
enum: ( foo, bar, baz ),
type: TypeEnum,
anon: ( foo: bool, bar: int, baz: ( a: int, b: int) )
)

method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)

error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.example.complex");
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(), "\
        interface org.example.complex\n\n\
        type TypeEnum (a, b, c)\n\n\
        type TypeFoo (bool: bool, int: int, float: float, string: string, enum: (foo, bar, baz), type: \
        TypeEnum, anon: (foo: bool, bar: int, baz: (a: int, b: int)))\n\n\
        method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)\n\n\
        error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)\n"
    );
}

#[test]
fn test_one_method() {
    let v = Varlink::from_string("interface foo.bar\nmethod Foo()->()");
    assert!(v.is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(VInterface("interface foo.bar\nmethod Foo()->(b:)").is_err());
}

#[test]
fn test_domainnames() {
    assert!(Varlink::from_string("interface org.varlink.service\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.0example\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.example-dash\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.-example.leadinghyphen\nmethod F()->()").is_err());
    assert!(
        Varlink::from_string("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()").is_ok()
    );
    assert!(
        Varlink::from_string("interface com.example-.danglinghyphen-\nmethod F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface Com.example.uppercase-toplevel\nmethod F()->()").is_err()
    );
    assert!(Varlink::from_string("interface Co9.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 1om.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.Example\nmethod F()->()").is_err());
}

#[test]
fn test_type_no_args() {
    assert!(Varlink::from_string("interface foo.bar\n type I ()\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_enum() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: (foo, bar, baz))\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_string() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: string)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_stringmap() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]string)\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_stringmap_set() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]())\nmethod F()->()").is_ok()
    );
}

#[test]
fn test_type_object() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: object)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_int() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: int)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_float() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: float)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:[]bool)\nmethod  F()->()").is_ok());
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:bool[ ])\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[1]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 1 ]bool)\nmethod  F()->()")
            .is_err()
    );
}

#[test]
fn test_format() {
    let v = Varlink::from_string("interface foo.bar\ntype I(b:[]bool)\nmethod  F()->()").unwrap();
    assert_eq!(
        v.interface.to_string(),
        "interface foo.bar\n\ntype I (b: []bool)\n\nmethod F() -> ()\n"
    );
}

#[test]
fn test_duplicate() {
    let e = Varlink::from_string(
        "
interface foo.example
type Device()
type Device()
type T()
type T()
method F() -> ()
method F() -> ()
",
    ).err()
    .unwrap();
    assert_eq!(
        e.to_string(),
        "Interface definition error: '\
Interface `foo.example`: multiple definitions of type `Device`!
Interface `foo.example`: multiple definitions of type `F`!
Interface `foo.example`: multiple definitions of type `T`!'"
    );
}
<commit_msg>varlink_parser/src/test.rs: add tests from c-lib<commit_after>use *;

#[test]
fn test_standard() {
    let v = Varlink::from_string(
        "
# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service

# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
vendor: string,
product: string,
version: string,
url: string,
interfaces: []string
)

# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)

# The requested interface was not found.
error InterfaceNotFound (interface: string)

# The requested method was not found
error MethodNotFound (method: string)

# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)

# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.varlink.service");
    assert_eq!(
        v.interface.doc,
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\
         "
    );
    assert_eq!(
        v.interface
            .methods
            .get("GetInterfaceDescription".into())
            .unwrap()
            .doc,
        "# Get the description of an interface that is implemented by this service."
    );
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(),
        "\
         # The Varlink Service Interface is provided by every varlink service. It\n\
         # describes the service and the interfaces it implements.\n\
         interface org.varlink.service\n\
         \n\
         # Get a list of all the interfaces a service provides and information\n\
         # about the implementation.\n\
         method GetInfo() -> (vendor: string, product: string, version: string, url: string, \
         interfaces: []string)\n\
         \n\
         # Get the description of an interface that is implemented by this service.\n\
         method GetInterfaceDescription(interface: string) -> (description: string)\n\
         \n\
         # The requested interface was not found.\n\
         error InterfaceNotFound (interface: string)\n\
         \n\
         # One of the passed parameters is invalid.\n\
         error InvalidParameter (parameter: string)\n\
         \n\
         # The requested method was not found\n\
         error MethodNotFound (method: string)\n\
         \n\
         # The interface defines the requested method, but the service does not\n\
         # implement it.\n\
         error MethodNotImplemented (method: string)\n\
         "
    );
}

#[test]
fn test_complex() {
    let v = Varlink::from_string(
        "interface org.example.complex
type TypeEnum ( a, b, c )

type TypeFoo (
bool: bool,
int: int,
float: float,
string: string,
enum: ( foo, bar, baz ),
type: TypeEnum,
anon: ( foo: bool, bar: int, baz: ( a: int, b: int) )
)

method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)

error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)
",
    ).unwrap();
    assert_eq!(v.interface.name, "org.example.complex");
    //println!("{}", v.interface.to_string());
    assert_eq!(
        v.interface.to_string(), "\
        interface org.example.complex\n\n\
        type TypeEnum (a, b, c)\n\n\
        type TypeFoo (bool: bool, int: int, float: float, string: string, enum: (foo, bar, baz), type: \
        TypeEnum, anon: (foo: bool, bar: int, baz: (a: int, b: int)))\n\n\
        method Foo(a: (b: bool, c: int), foo: TypeFoo) -> (a: (b: bool, c: int), foo: TypeFoo)\n\n\
        error ErrorFoo (a: (b: bool, c: int), foo: TypeFoo)\n"
    );
}

#[test]
fn test_one_method() {
    let v = Varlink::from_string("interface foo.bar\nmethod Foo()->()");
    assert!(v.is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(VInterface("interface foo.bar\nmethod Foo()->(b:)").is_err());
}

#[test]
fn test_domainnames() {
    assert!(Varlink::from_string("interface org.varlink.service\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.0example\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.example.example-dash\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface com.-example.leadinghyphen\nmethod F()->()").is_err());
    assert!(
        Varlink::from_string("interface xn--lgbbat1ad8j.example.algeria\nmethod F()->()").is_ok()
    );
    assert!(
        Varlink::from_string("interface com.example-.danglinghyphen-\nmethod F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface Com.example.uppercase-toplevel\nmethod F()->()").is_err()
    );
    assert!(Varlink::from_string("interface Co9.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 1om.example.number-toplevel\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface com.Example\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.b\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface a.b.c\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface a1.b1.c1\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface a1.b--1.c--1\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface a--1.b--1.c--1\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface a.21.c\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface a.1\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface a.0.0\nmethod F()->()").is_ok());
    assert!(Varlink::from_string("interface ab\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface .a.b.c\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.b.c.\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a..b.c\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 1.b.c\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface 8a.0.0\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface -a.b.c\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.b.c-\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.b-.c-\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.-b.c-\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.-.c\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.*.c\nmethod F()->()").is_err());
    assert!(Varlink::from_string("interface a.?\nmethod F()->()").is_err());
}

#[test]
fn test_type_no_args() {
    assert!(Varlink::from_string("interface foo.bar\n type I ()\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:bool)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_enum() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: (foo, bar, baz))\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_string() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: string)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_stringmap() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]string)\nmethod F()->()")
            .is_ok()
    );
}

#[test]
fn test_type_stringmap_set() {
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b: [string]())\nmethod F()->()").is_ok()
    );
}

#[test]
fn test_type_object() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: object)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_int() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: int)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_float() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b: float)\nmethod F()->()").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(Varlink::from_string("interface foo.bar\n type I (b:[]bool)\nmethod  F()->()").is_ok());
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:bool[ ])\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[1]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 ]bool)\nmethod  F()->()").is_err()
    );
    assert!(
        Varlink::from_string("interface foo.bar\n type I (b:[ 1 1 ]bool)\nmethod  F()->()")
            .is_err()
    );
}

#[test]
fn test_format() {
    let v = Varlink::from_string("interface foo.bar\ntype I(b:[]bool)\nmethod  F()->()").unwrap();
    assert_eq!(
        v.interface.to_string(),
        "interface foo.bar\n\ntype I (b: []bool)\n\nmethod F() -> ()\n"
    );
}

#[test]
fn test_duplicate() {
    let e = Varlink::from_string(
        "
interface foo.example
type Device()
type Device()
type T()
type T()
method F() -> ()
method F() -> ()
",
    ).err()
    .unwrap();
    assert_eq!(
        e.to_string(),
        "Interface definition error: '\
Interface `foo.example`: multiple definitions of type `Device`!
Interface `foo.example`: multiple definitions of type `F`!
Interface `foo.example`: multiple definitions of type `T`!'"
    );
}
<|endoftext|>


<commit_before>extern crate criterion;
extern crate fraction;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use fraction::GenericDecimal;

pub fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("Decimal u128/u16 init", |b| {
        b.iter(|| {
            GenericDecimal::<u128, u16>::from(black_box(15978.649));
        })
    });

    c.bench_function("Decimal i64/u16 init", |b| {
        b.iter(|| {
            GenericDecimal::<i64, u16>::from(black_box(15978.649));
            GenericDecimal::<i64, u16>::from(black_box(-15978.649));
        })
    });
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
<commit_msg>Add benchmarking<commit_after>extern crate criterion;
extern crate fraction;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use fraction::{GenericDecimal, GenericFraction};
use std::str::FromStr;

pub fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("Decimal u128/u16 init", |b| {
        b.iter(|| {
            GenericDecimal::<u128, u16>::from(black_box(15978.649));
        })
    });

    c.bench_function("Decimal i64/u16 init", |b| {
        b.iter(|| {
            GenericDecimal::<i64, u16>::from(black_box(15978.649));
            GenericDecimal::<i64, u16>::from(black_box(-15978.649));
        })
    });

    c.bench_function("Convert int like from str", |b| {
        b.iter(|| {
            GenericFraction::<u8>::from_str(black_box("1"));
            GenericFraction::<u8>::from_str(black_box("100"));
        })
    });

    c.bench_function("Convert float like from str", |b| {
        b.iter(|| {
            GenericFraction::<u8>::from_str(black_box("1.0"));
            GenericFraction::<u8>::from_str(black_box("100.001"));
        })
    });

    c.bench_function("Convert fraction like from str", |b| {
        b.iter(|| {
            GenericFraction::<u8>::from_str(black_box("1/1"));
            GenericFraction::<u8>::from_str(black_box("255/255"));
        })
    });
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
<|endoftext|>