<commit_before>use cairo;
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, PaintServerError, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve, ResolvedPaintSource};
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_else (|| $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 Resolved = NodePattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<Self::Resolved, PaintServerError> {
        *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(ref fallback) = result.fallback {
                if let Some(acquired) = draw_ctx
                    .acquired_nodes()
                    .get_node_of_type(fallback, NodeType::Pattern)
                {
                    let a_node = acquired.get();

                    if stack.contains(a_node) {
                        return Err(PaintServerError::CircularReference(fallback.clone()));
                    }

                    let node_data = a_node.borrow();

                    let fallback_pattern = node_data.get_impl::<NodePattern>();
                    *fallback_pattern.node.borrow_mut() = Some(a_node.downgrade());

                    result.resolve_from_fallback(fallback_pattern);

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

        Ok(result)
    }
}

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

        if self.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 = self.units.unwrap();
        let content_units = self.content_units.unwrap();
        let pattern_affine = self.affine.unwrap();
        let vbox = self.vbox.unwrap();
        let preserve_aspect_ratio = self.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 = self.x.unwrap().normalize(values, &params);
            let pattern_y = self.y.unwrap().normalize(values, &params);
            let pattern_width = self.width.unwrap().normalize(values, &params);
            let pattern_height = self.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 = self.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(&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());
    }
}
<commit_msg>pattern: set the weak ref on the result, not the long-lived node<commit_after>use cairo;
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, PaintServerError, RenderingError};
use crate::float_eq_cairo::ApproxEqCairo;
use crate::length::*;
use crate::node::*;
use crate::paint_server::{PaintSource, Resolve, ResolvedPaintSource};
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_else (|| $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 Resolved = NodePattern;

    fn resolve(
        &self,
        node: &RsvgNode,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<Self::Resolved, PaintServerError> {
        let mut result = node.borrow().get_impl::<NodePattern>().clone();
        *result.node.borrow_mut() = Some(node.downgrade());

        let mut stack = NodeStack::new();

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

                    if stack.contains(a_node) {
                        return Err(PaintServerError::CircularReference(fallback.clone()));
                    }

                    let node_data = a_node.borrow();

                    let fallback_pattern = node_data.get_impl::<NodePattern>();
                    *fallback_pattern.node.borrow_mut() = Some(a_node.downgrade());

                    result.resolve_from_fallback(fallback_pattern);

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

        Ok(result)
    }
}

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

        if self.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 = self.units.unwrap();
        let content_units = self.content_units.unwrap();
        let pattern_affine = self.affine.unwrap();
        let vbox = self.vbox.unwrap();
        let preserve_aspect_ratio = self.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 = self.x.unwrap().normalize(values, &params);
            let pattern_y = self.y.unwrap().normalize(values, &params);
            let pattern_width = self.width.unwrap().normalize(values, &params);
            let pattern_height = self.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 = self.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(&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>extern crate leaf;
extern crate collenchyma as co;

#[cfg(test)]
mod layer_spec {
    use std::rc::Rc;
    use co::prelude::*;

    // only used by cuda right now
    #[allow(dead_code)]
    fn new_layer_config() -> LayerConfig {
        LayerConfig::new("foo", LayerType::Sigmoid)
    }

    fn native_backend() -> Rc<Backend<Native>> {
        Rc::new(Backend::<Native>::default().unwrap())
    }

    #[cfg(feature="cuda")]
    fn cuda_backend() -> Rc<Backend<Cuda>> {
        Rc::new(Backend::<Cuda>::default().unwrap())
    }

    #[cfg(all(feature="native", feature="cuda"))]
    mod native_cuda {
        use leaf::layer::*;
        use leaf::layers::*;
        use super::{native_backend, cuda_backend};

        #[test]
        fn create_layer_with_either() {
            let cfg = super::new_layer_config();
            Layer::from_config(native_backend(), &cfg);

            let cfg = super::new_layer_config();
            Layer::from_config(cuda_backend(), &cfg);
        }
    }

    #[cfg(feature="native")]
    mod native {
        use co::prelude::*;
        use leaf::layer::*;
        use leaf::layers::*;
        use super::native_backend;

        fn simple_network() -> LayerConfig {
            let mut net_cfg = SequentialConfig::default();
            net_cfg.add_input("data", &vec![1, 1, 28, 28]);
            net_cfg.add_layer(LayerConfig::new("linear", LayerType::Linear(LinearConfig { output_size: 10 })));

            LayerConfig::new("network", net_cfg)
        }

	#[test]
	fn xor_forward() {
		// let _ = env_logger::init();
		let mut cfg = SequentialConfig::default();
		// Layer: data
		cfg.add_input("data", &[2]);
		// Layer: fc1
		cfg.add_layer(LayerConfig::new("fc1", LayerType::Linear(LinearConfig { output_size: 2 })));
		cfg.add_layer(LayerConfig::new("fc1_out/sigmoid", LayerType::Sigmoid));
		// Layer: fc2 equiv. output
		cfg.add_layer(LayerConfig::new("fc2", LayerType::Linear(LinearConfig { output_size: 1 })));
		cfg.add_layer(LayerConfig::new("fc2_out/sigmoid", LayerType::Sigmoid));

		let backend = native_backend();
		let _ = Layer::from_config(
		    backend.clone(), &LayerConfig::new("network", LayerType::Sequential(cfg)));
	}

        #[test]
        fn save_and_load_layer() {
            let cfg = simple_network();
            let mut original_layer = Layer::from_config(native_backend(), &cfg);

            original_layer.save("target/testnetwork").unwrap();
            let loaded_layer = Layer::<Backend<Native>>::load(native_backend(), "target/testnetwork").unwrap();

            assert_eq!(original_layer.input_blob_names(), loaded_layer.input_blob_names());

            let original_weights = original_layer.learnable_weights_data();
            let original_weight_lock = original_weights[0].read().unwrap();
            let loaded_weights = loaded_layer.learnable_weights_data();
            let loaded_weight_lock = loaded_weights[0].read().unwrap();

            let original_weight = original_weight_lock.read(native_backend().device())
                .unwrap().as_native().unwrap().as_slice::<f32>();
            let loaded_weight = loaded_weight_lock.read(native_backend().device())
                .unwrap().as_native().unwrap().as_slice::<f32>();

            assert_eq!(original_weight, loaded_weight);
        }
    }

    #[cfg(feature="cuda")]
    mod cuda {
        use std::sync::{Arc, RwLock};
        use co::prelude::*;
        use leaf::layer::*;
        use leaf::layers::*;
        use leaf::util::write_to_memory;
        use super::{native_backend, cuda_backend};

        #[test]
        fn new_layer() {
            let cfg = super::new_layer_config();
            Layer::from_config(cuda_backend(), &cfg);
        }

        #[test]
        fn can_create_empty_sequential_layer() {
            let model = SequentialConfig::default();
            Layer::from_config(cuda_backend(), &LayerConfig::new("model", LayerType::Sequential(model)));
        }

        #[test]
        fn can_create_single_layer_sequential_layer() {
            let mut model = SequentialConfig::default();
            model.add_input("data", &[28, 28]);
            model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));

            Layer::from_config(cuda_backend(), &LayerConfig::new("model", LayerType::Sequential(model)));
        }

        #[test]
        fn can_create_simple_network_sequential_layer() {
            let mut model = SequentialConfig::default();
            model.add_input("data", &[1, 784]);
            model.add_layer(LayerConfig::new("linear1", LinearConfig { output_size: 1568 }));
            model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));
            model.add_layer(LayerConfig::new("linear2", LinearConfig { output_size: 10 }));

            let _ = Layer::from_config(cuda_backend(), &LayerConfig::new("model", LayerType::Sequential(model)));
        }

        #[test]
        fn reshape_does_not_affect_output() {
            let native_backend = native_backend();
            let cuda_backend = cuda_backend();

            let mut normal_model = SequentialConfig::default();
            normal_model.add_input("data", &[3]);
            normal_model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));
            let mut normal_network = Layer::from_config(cuda_backend.clone(), &LayerConfig::new("normal_model", LayerType::Sequential(normal_model)));

            let mut reshape_model = SequentialConfig::default();
            reshape_model.add_input("data", &[3]);
            reshape_model.add_layer(LayerConfig::new("reshape", ReshapeConfig { shape: vec![1, 1, 3] }));
            reshape_model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));
            let mut reshape_network = Layer::from_config(cuda_backend.clone(), &LayerConfig::new("reshape_model", LayerType::Sequential(reshape_model)));

            let input = vec![1f32, 1f32, 2f32];
            let mut normal_tensor = SharedTensor::<f32>::new(&[3]);
            // let mut normal_tensor_output = SharedTensor::<f32>::new(native_backend.device(), &(3)).unwrap();
            let mut reshape_tensor = SharedTensor::<f32>::new(&[3]);
            // let mut reshape_tensor_output = SharedTensor::<f32>::new(native_backend.device(), &(3)).unwrap();
            write_to_memory(normal_tensor.write_only(native_backend.device()).unwrap(), &input);
            write_to_memory(reshape_tensor.write_only(native_backend.device()).unwrap(), &input);

            let normal_tensor_output = normal_network.forward(&[Arc::new(RwLock::new(normal_tensor))])[0].clone();
            let normal_tensor_output_native_ = normal_tensor_output.read().unwrap();
            let normal_tensor_output_native = normal_tensor_output_native_
                .read(native_backend.device()).unwrap().as_native().unwrap();
            assert_eq!(&[0.7310585786f32, 0.7310586f32, 0.880797f32],
                       normal_tensor_output_native.as_slice::<f32>());

            let reshape_tensor_output = reshape_network.forward(&[Arc::new(RwLock::new(reshape_tensor))])[0].clone();
            let reshape_tensor_output_native_ = reshape_tensor_output.read().unwrap();
            let reshape_tensor_output_native = reshape_tensor_output_native_
                .read(native_backend.device()).unwrap().as_native().unwrap();
            assert_eq!(&[0.7310585786f32, 0.7310586f32, 0.880797f32],
                       reshape_tensor_output_native.as_slice::<f32>());
            assert_eq!(normal_tensor_output_native.as_slice::<f32>(),
                       reshape_tensor_output_native.as_slice::<f32>());
        }
    }

    // #[test]
    // fn dim_check_strict() {
    //     let cfg = WeightConfig { share_mode: DimCheckMode::Strict, ..WeightConfig::default() };
    //     let blob_one = SharedTensor::<f32>::new(backend().device(), &vec![2, 3, 3]);
    //     let blob_two = SharedTensor::<f32>::new(backend().device(), &vec![3, 2, 3]);
    //     let param_name = "foo".to_owned();
    //     let owner_name = "owner".to_owned();
    //     let layer_name = "layer".to_owned();
    //
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_one,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_ok());
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_two,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_err());
    // }

    // #[test]
    // fn dim_check_permissive() {
    //     let cfg = WeightConfig { share_mode: DimCheckMode::Permissive, ..WeightConfig::default() };
    //     let blob_one = SharedTensor::<f32>::new(backend().device(), &vec![2, 3, 3]);
    //     let blob_two = SharedTensor::<f32>::new(backend().device(), &vec![3, 2, 3]);
    //     let blob_three = SharedTensor::<f32>::new(backend().device(), &vec![3, 10, 3]);
    //     let param_name = "foo".to_owned();
    //     let owner_name = "owner".to_owned();
    //     let layer_name = "layer".to_owned();
    //
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_one,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_ok());
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_two,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_ok());
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_three,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_err());
    //     assert!(cfg.check_dimensions(&blob_two,
    //                                  &blob_three,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_err());
    // }
}
<commit_msg>fix/test: fix cuda tests due to missing LayerConfig<commit_after>extern crate leaf;
extern crate collenchyma as co;

#[cfg(test)]
mod layer_spec {
    use std::rc::Rc;
    use co::prelude::*;
    use leaf::layer::*;
    // only used by cuda right now
    #[allow(dead_code)]
    fn new_layer_config() -> LayerConfig {
        LayerConfig::new("foo", LayerType::Sigmoid)
    }

    fn native_backend() -> Rc<Backend<Native>> {
        Rc::new(Backend::<Native>::default().unwrap())
    }

    #[cfg(feature="cuda")]
    fn cuda_backend() -> Rc<Backend<Cuda>> {
        Rc::new(Backend::<Cuda>::default().unwrap())
    }

    #[cfg(all(feature="native", feature="cuda"))]
    mod native_cuda {
        use leaf::layer::*;
        use leaf::layers::*;
        use super::{native_backend, cuda_backend};

        #[test]
        fn create_layer_with_either() {
            let cfg = super::new_layer_config();
            Layer::from_config(native_backend(), &cfg);

            let cfg = super::new_layer_config();
            Layer::from_config(cuda_backend(), &cfg);
        }
    }

    #[cfg(feature="native")]
    mod native {
        use co::prelude::*;
        use leaf::layer::*;
        use leaf::layers::*;
        use super::native_backend;

        fn simple_network() -> LayerConfig {
            let mut net_cfg = SequentialConfig::default();
            net_cfg.add_input("data", &vec![1, 1, 28, 28]);
            net_cfg.add_layer(LayerConfig::new("linear", LayerType::Linear(LinearConfig { output_size: 10 })));

            LayerConfig::new("network", net_cfg)
        }

	#[test]
	fn xor_forward() {
		// let _ = env_logger::init();
		let mut cfg = SequentialConfig::default();
		// Layer: data
		cfg.add_input("data", &[2]);
		// Layer: fc1
		cfg.add_layer(LayerConfig::new("fc1", LayerType::Linear(LinearConfig { output_size: 2 })));
		cfg.add_layer(LayerConfig::new("fc1_out/sigmoid", LayerType::Sigmoid));
		// Layer: fc2 equiv. output
		cfg.add_layer(LayerConfig::new("fc2", LayerType::Linear(LinearConfig { output_size: 1 })));
		cfg.add_layer(LayerConfig::new("fc2_out/sigmoid", LayerType::Sigmoid));

		let backend = native_backend();
		let _ = Layer::from_config(
		    backend.clone(), &LayerConfig::new("network", LayerType::Sequential(cfg)));
	}

        #[test]
        fn save_and_load_layer() {
            let cfg = simple_network();
            let mut original_layer = Layer::from_config(native_backend(), &cfg);

            original_layer.save("target/testnetwork").unwrap();
            let loaded_layer = Layer::<Backend<Native>>::load(native_backend(), "target/testnetwork").unwrap();

            assert_eq!(original_layer.input_blob_names(), loaded_layer.input_blob_names());

            let original_weights = original_layer.learnable_weights_data();
            let original_weight_lock = original_weights[0].read().unwrap();
            let loaded_weights = loaded_layer.learnable_weights_data();
            let loaded_weight_lock = loaded_weights[0].read().unwrap();

            let original_weight = original_weight_lock.read(native_backend().device())
                .unwrap().as_native().unwrap().as_slice::<f32>();
            let loaded_weight = loaded_weight_lock.read(native_backend().device())
                .unwrap().as_native().unwrap().as_slice::<f32>();

            assert_eq!(original_weight, loaded_weight);
        }
    }

    #[cfg(feature="cuda")]
    mod cuda {
        use std::sync::{Arc, RwLock};
        use co::prelude::*;
        use leaf::layer::*;
        use leaf::layers::*;
        use leaf::util::write_to_memory;
        use super::{native_backend, cuda_backend};

        #[test]
        fn new_layer() {
            let cfg = super::new_layer_config();
            Layer::from_config(cuda_backend(), &cfg);
        }

        #[test]
        fn can_create_empty_sequential_layer() {
            let model = SequentialConfig::default();
            Layer::from_config(cuda_backend(), &LayerConfig::new("model", LayerType::Sequential(model)));
        }

        #[test]
        fn can_create_single_layer_sequential_layer() {
            let mut model = SequentialConfig::default();
            model.add_input("data", &[28, 28]);
            model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));

            Layer::from_config(cuda_backend(), &LayerConfig::new("model", LayerType::Sequential(model)));
        }

        #[test]
        fn can_create_simple_network_sequential_layer() {
            let mut model = SequentialConfig::default();
            model.add_input("data", &[1, 784]);
            model.add_layer(LayerConfig::new("linear1", LinearConfig { output_size: 1568 }));
            model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));
            model.add_layer(LayerConfig::new("linear2", LinearConfig { output_size: 10 }));

            let _ = Layer::from_config(cuda_backend(), &LayerConfig::new("model", LayerType::Sequential(model)));
        }

        #[test]
        fn reshape_does_not_affect_output() {
            let native_backend = native_backend();
            let cuda_backend = cuda_backend();

            let mut normal_model = SequentialConfig::default();
            normal_model.add_input("data", &[3]);
            normal_model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));
            let mut normal_network = Layer::from_config(cuda_backend.clone(), &LayerConfig::new("normal_model", LayerType::Sequential(normal_model)));

            let mut reshape_model = SequentialConfig::default();
            reshape_model.add_input("data", &[3]);
            reshape_model.add_layer(LayerConfig::new("reshape", ReshapeConfig { shape: vec![1, 1, 3] }));
            reshape_model.add_layer(LayerConfig::new("sigmoid", LayerType::Sigmoid));
            let mut reshape_network = Layer::from_config(cuda_backend.clone(), &LayerConfig::new("reshape_model", LayerType::Sequential(reshape_model)));

            let input = vec![1f32, 1f32, 2f32];
            let mut normal_tensor = SharedTensor::<f32>::new(&[3]);
            // let mut normal_tensor_output = SharedTensor::<f32>::new(native_backend.device(), &(3)).unwrap();
            let mut reshape_tensor = SharedTensor::<f32>::new(&[3]);
            // let mut reshape_tensor_output = SharedTensor::<f32>::new(native_backend.device(), &(3)).unwrap();
            write_to_memory(normal_tensor.write_only(native_backend.device()).unwrap(), &input);
            write_to_memory(reshape_tensor.write_only(native_backend.device()).unwrap(), &input);

            let normal_tensor_output = normal_network.forward(&[Arc::new(RwLock::new(normal_tensor))])[0].clone();
            let normal_tensor_output_native_ = normal_tensor_output.read().unwrap();
            let normal_tensor_output_native = normal_tensor_output_native_
                .read(native_backend.device()).unwrap().as_native().unwrap();
            assert_eq!(&[0.7310585786f32, 0.7310586f32, 0.880797f32],
                       normal_tensor_output_native.as_slice::<f32>());

            let reshape_tensor_output = reshape_network.forward(&[Arc::new(RwLock::new(reshape_tensor))])[0].clone();
            let reshape_tensor_output_native_ = reshape_tensor_output.read().unwrap();
            let reshape_tensor_output_native = reshape_tensor_output_native_
                .read(native_backend.device()).unwrap().as_native().unwrap();
            assert_eq!(&[0.7310585786f32, 0.7310586f32, 0.880797f32],
                       reshape_tensor_output_native.as_slice::<f32>());
            assert_eq!(normal_tensor_output_native.as_slice::<f32>(),
                       reshape_tensor_output_native.as_slice::<f32>());
        }
    }

    // #[test]
    // fn dim_check_strict() {
    //     let cfg = WeightConfig { share_mode: DimCheckMode::Strict, ..WeightConfig::default() };
    //     let blob_one = SharedTensor::<f32>::new(backend().device(), &vec![2, 3, 3]);
    //     let blob_two = SharedTensor::<f32>::new(backend().device(), &vec![3, 2, 3]);
    //     let param_name = "foo".to_owned();
    //     let owner_name = "owner".to_owned();
    //     let layer_name = "layer".to_owned();
    //
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_one,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_ok());
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_two,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_err());
    // }

    // #[test]
    // fn dim_check_permissive() {
    //     let cfg = WeightConfig { share_mode: DimCheckMode::Permissive, ..WeightConfig::default() };
    //     let blob_one = SharedTensor::<f32>::new(backend().device(), &vec![2, 3, 3]);
    //     let blob_two = SharedTensor::<f32>::new(backend().device(), &vec![3, 2, 3]);
    //     let blob_three = SharedTensor::<f32>::new(backend().device(), &vec![3, 10, 3]);
    //     let param_name = "foo".to_owned();
    //     let owner_name = "owner".to_owned();
    //     let layer_name = "layer".to_owned();
    //
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_one,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_ok());
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_two,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_ok());
    //     assert!(cfg.check_dimensions(&blob_one,
    //                                  &blob_three,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_err());
    //     assert!(cfg.check_dimensions(&blob_two,
    //                                  &blob_three,
    //                                  param_name.clone(),
    //                                  owner_name.clone(),
    //                                  layer_name.clone())
    //                .is_err());
    // }
}
<|endoftext|>


<commit_before>use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;

fn read_file(filename:String) -> Result<String, io::Error>{
    let mut file = try!(File::open(filename));
    let mut content = String::new();
    try!(file.read_to_string(&mut content));
    Ok(content)
}

fn main() {
    let mut args = env::args();
    if let Some(file) = args.nth(1) {
        println!("{}", match read_file(file.clone()){
            Ok(content) => content,
            Err(reason) => panic!(reason)
        });
    }
}
<commit_msg>editorial update<commit_after>use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;

fn read_file(filename: &String) -> Result<String, io::Error>{
    let mut file = try!(File::open(filename));
    let mut content = String::new();
    try!(file.read_to_string(&mut content));
    Ok(content)
}

fn main() {
    let mut args = env::args();
    if let Some(file) = args.nth(1) {
        match read_file(file){
            Ok(content) => println!("{}", content),
            Err(reason) => panic!("{}", reason)
        }
    }
}
<|endoftext|>


<commit_before>//! The `Parse` trait for CSS properties, and utilities for parsers.

use cssparser::{Parser, ParserInput};
use markup5ever::QualName;

use std::str;

use crate::error::*;

/// Trait to parse values using `cssparser::Parser`.
pub trait Parse: Sized {
    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse(parser: &mut Parser<'_, '_>) -> Result<Self, ValueErrorKind>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.
    fn parse_str(s: &str) -> Result<Self, ValueErrorKind> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

/// Consumes a comma if it exists, or does nothing.
pub fn optional_comma<'i, 't>(parser: &mut Parser<'i, 't>) {
    let _ = parser.try_parse(|p| p.expect_comma());
}

pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValue<T: Parse> {
    /// Parses a `value` string into a type `T`.
    fn parse(&self, value: &str) -> Result<T, NodeError>;
}

impl<T: Parse> ParseValue<T> for QualName {
    fn parse(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser).attribute(self.clone())
    }
}

pub trait ParseValueToParseError<T: ParseToParseError> {
    /// Parses a `value` string into a type `T`.
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError>;
}

impl<T: ParseToParseError> ParseValueToParseError<T> for QualName {
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse_to_parse_error(&mut parser).attribute(self.clone())
    }

    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        let v = T::parse_to_parse_error(&mut parser).attribute(self.clone())?;

        validate(v).map_err(|e| parser.new_custom_error(e)).attribute(self.clone())
    }
}

pub trait ParseToParseError: Sized {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>>;
    fn parse_str_to_parse_error<'i>(s: &'i str) -> Result<Self, CssParseError<'i>> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse_to_parse_error(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

impl ParseToParseError for f64 {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        let loc = parser.current_source_location();
        parser
            .expect_number()
            .map_err(|e| e.into())
            .and_then(|n| {
                if n.is_finite() {
                    Ok(f64::from(n))
                } else {
                    Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
                }
            })
    }
}

/// CSS number-optional-number
///
/// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct NumberOptionalNumber<T: ParseToParseError>(pub T, pub T);

impl<T: ParseToParseError + Copy> ParseToParseError for NumberOptionalNumber<T> {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        let x = ParseToParseError::parse_to_parse_error(parser)?;

        if !parser.is_exhausted() {
            optional_comma(parser);
            let y = ParseToParseError::parse_to_parse_error(parser)?;
            parser.expect_exhausted()?;
            Ok(NumberOptionalNumber(x, y))
        } else {
            Ok(NumberOptionalNumber(x, x))
        }
    }
}

impl ParseToParseError for i32 {
    /// CSS integer
    ///
    /// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        Ok(parser.expect_integer()?)
    }
}

/// Parses a list of identifiers from a `cssparser::Parser`
///
/// # Example
///
/// ```ignore
/// let my_boolean = parse_identifiers!(
///     parser,
///     "true" => true,
///     "false" => false,
/// )?;
/// ```
macro_rules! parse_identifiers {
    ($parser:expr,
     $($str:expr => $val:expr,)+) => {
        {
            let loc = $parser.current_source_location();
            let token = $parser.next()?;

            match token {
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+

                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
            }
        }
    };
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1, 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1"),
            Ok(NumberOptionalNumber(1.0, 1.0))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1, -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1 -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1"),
            Ok(NumberOptionalNumber(-1.0, -1.0))
        );
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("x1").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 , x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(i32::parse_str_to_parse_error("1"), Ok(1));
        assert_eq!(i32::parse_str_to_parse_error("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(i32::parse_str_to_parse_error("").is_err());
        assert!(i32::parse_str_to_parse_error("1x").is_err());
        assert!(i32::parse_str_to_parse_error("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1, 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1"),
            Ok(NumberOptionalNumber(1, 1))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1, -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1 -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1"),
            Ok(NumberOptionalNumber(-1, -1))
        );
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("x1").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 , x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 2 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 2.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1, 2.5").is_err());
    }
}
<commit_msg>Remove the Parse and ParseValue traits<commit_after>//! The `Parse` trait for CSS properties, and utilities for parsers.

use cssparser::{Parser, ParserInput};
use markup5ever::QualName;

use std::str;

use crate::error::*;

/// Trait to parse values using `cssparser::Parser`.
pub trait ParseToParseError: Sized {
    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.
    fn parse_str_to_parse_error<'i>(s: &'i str) -> Result<Self, CssParseError<'i>> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse_to_parse_error(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

/// Consumes a comma if it exists, or does nothing.
pub fn optional_comma<'i, 't>(parser: &mut Parser<'i, 't>) {
    let _ = parser.try_parse(|p| p.expect_comma());
}

pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValueToParseError<T: ParseToParseError> {
    /// Parses a `value` string into a type `T`.
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError>;
}

impl<T: ParseToParseError> ParseValueToParseError<T> for QualName {
    fn parse_to_parse_error(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse_to_parse_error(&mut parser).attribute(self.clone())
    }

    fn parse_to_parse_error_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        let v = T::parse_to_parse_error(&mut parser).attribute(self.clone())?;

        validate(v).map_err(|e| parser.new_custom_error(e)).attribute(self.clone())
    }
}

impl ParseToParseError for f64 {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        let loc = parser.current_source_location();
        parser
            .expect_number()
            .map_err(|e| e.into())
            .and_then(|n| {
                if n.is_finite() {
                    Ok(f64::from(n))
                } else {
                    Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
                }
            })
    }
}

/// CSS number-optional-number
///
/// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct NumberOptionalNumber<T: ParseToParseError>(pub T, pub T);

impl<T: ParseToParseError + Copy> ParseToParseError for NumberOptionalNumber<T> {
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        let x = ParseToParseError::parse_to_parse_error(parser)?;

        if !parser.is_exhausted() {
            optional_comma(parser);
            let y = ParseToParseError::parse_to_parse_error(parser)?;
            parser.expect_exhausted()?;
            Ok(NumberOptionalNumber(x, y))
        } else {
            Ok(NumberOptionalNumber(x, x))
        }
    }
}

impl ParseToParseError for i32 {
    /// CSS integer
    ///
    /// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
    fn parse_to_parse_error<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, CssParseError<'i>> {
        Ok(parser.expect_integer()?)
    }
}

/// Parses a list of identifiers from a `cssparser::Parser`
///
/// # Example
///
/// ```ignore
/// let my_boolean = parse_identifiers!(
///     parser,
///     "true" => true,
///     "false" => false,
/// )?;
/// ```
macro_rules! parse_identifiers {
    ($parser:expr,
     $($str:expr => $val:expr,)+) => {
        {
            let loc = $parser.current_source_location();
            let token = $parser.next()?;

            match token {
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+

                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
            }
        }
    };
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1, 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1"),
            Ok(NumberOptionalNumber(1.0, 1.0))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1, -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1 -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1"),
            Ok(NumberOptionalNumber(-1.0, -1.0))
        );
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("x1").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 , x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str_to_parse_error("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(i32::parse_str_to_parse_error("1"), Ok(1));
        assert_eq!(i32::parse_str_to_parse_error("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(i32::parse_str_to_parse_error("").is_err());
        assert!(i32::parse_str_to_parse_error("1x").is_err());
        assert!(i32::parse_str_to_parse_error("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1, 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("1"),
            Ok(NumberOptionalNumber(1, 1))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1, -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1 -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str_to_parse_error("-1"),
            Ok(NumberOptionalNumber(-1, -1))
        );
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("x1").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 , x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 2 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1 2.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str_to_parse_error("1, 2.5").is_err());
    }
}
<|endoftext|>


<commit_before>//! The `Parse` trait for CSS properties, and utilities for parsers.

use cssparser::{Parser, ParserInput, Token};
use markup5ever::QualName;
use std::str;

use crate::error::*;

/// Trait to parse values using `cssparser::Parser`.
pub trait Parse: Sized {
    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.  Property types do not need to reimplement this.
    fn parse_str(s: &str) -> Result<Self, ParseError> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        let res = Self::parse(&mut parser)?;
        parser.expect_exhausted()?;

        Ok(res)
    }
}

/// Consumes a comma if it exists, or does nothing.
pub fn optional_comma<'i, 't>(parser: &mut Parser<'i, 't>) {
    let _ = parser.try_parse(|p| p.expect_comma());
}

/// Parses an `f32` and ensures that it is not an infinity or NaN.
pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValue<T: Parse> {
    /// Parses a `value` string into a type `T`.
    fn parse(&self, value: &str) -> Result<T, ElementError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, ElementError>;
}

impl<T: Parse> ParseValue<T> for QualName {
    fn parse(&self, value: &str) -> Result<T, ElementError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser).attribute(self.clone())
    }

    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, ElementError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        let v = T::parse(&mut parser).attribute(self.clone())?;

        validate(v)
            .map_err(|e| parser.new_custom_error(e))
            .attribute(self.clone())
    }
}

impl Parse for f64 {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        let loc = parser.current_source_location();
        let n = parser.expect_number()?;
        if n.is_finite() {
            Ok(f64::from(n))
        } else {
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
        }
    }
}

/// CSS number-optional-number
///
/// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct NumberOptionalNumber<T: Parse>(pub T, pub T);

impl<T: Parse + Copy> Parse for NumberOptionalNumber<T> {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        let x = Parse::parse(parser)?;

        if !parser.is_exhausted() {
            optional_comma(parser);
            let y = Parse::parse(parser)?;
            parser.expect_exhausted()?;
            Ok(NumberOptionalNumber(x, y))
        } else {
            Ok(NumberOptionalNumber(x, x))
        }
    }
}

impl Parse for i32 {
    /// CSS integer
    ///
    /// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parser.expect_integer()?)
    }
}

/// Parses a list of identifiers from a `cssparser::Parser`
///
/// # Example
///
/// ```ignore
/// let my_boolean = parse_identifiers!(
///     parser,
///     "true" => true,
///     "false" => false,
/// )?;
/// ```
macro_rules! parse_identifiers {
    ($parser:expr,
     $($str:expr => $val:expr,)+) => {
        {
            let loc = $parser.current_source_location();
            let token = $parser.next()?;

            match token {
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+

                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
            }
        }
    };
}

/// https://www.w3.org/TR/css-values-4/#custom-idents
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CustomIdent(String);

impl Parse for CustomIdent {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        let loc = parser.current_source_location();
        let token = parser.next()?;

        match token {
            // CSS-wide keywords and "default" are errors here
            // https://www.w3.org/TR/css-values-4/#css-wide-keywords
            Token::Ident(ref cow) => {
                for s in &["initial", "inherit", "unset", "default"] {
                    if cow.eq_ignore_ascii_case(s) {
                        return Err(loc.new_basic_unexpected_token_error(token.clone()).into());
                    }
                }

                Ok(CustomIdent(cow.as_ref().to_string()))
            }

            _ => Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
        }
    }
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(
            NumberOptionalNumber::parse_str("1, 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1"),
            Ok(NumberOptionalNumber(1.0, 1.0))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str("-1, -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1 -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1"),
            Ok(NumberOptionalNumber(-1.0, -1.0))
        );
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(NumberOptionalNumber::<f64>::parse_str("").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("x1").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(i32::parse_str("1"), Ok(1));
        assert_eq!(i32::parse_str("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(i32::parse_str("").is_err());
        assert!(i32::parse_str("1x").is_err());
        assert!(i32::parse_str("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(
            NumberOptionalNumber::parse_str("1, 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1"),
            Ok(NumberOptionalNumber(1, 1))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str("-1, -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1 -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1"),
            Ok(NumberOptionalNumber(-1, -1))
        );
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(NumberOptionalNumber::<i32>::parse_str("").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("x1").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1, 2.5").is_err());
    }

    #[test]
    fn parses_custom_ident() {
        assert_eq!(
            CustomIdent::parse_str("hello"),
            Ok(CustomIdent("hello".to_string()))
        );
    }

    #[test]
    fn invalid_custom_ident_yields_error() {
        assert!(CustomIdent::parse_str("initial").is_err());
        assert!(CustomIdent::parse_str("inherit").is_err());
        assert!(CustomIdent::parse_str("unset").is_err());
        assert!(CustomIdent::parse_str("default").is_err());
        assert!(CustomIdent::parse_str("").is_err());
    }
}
<commit_msg>NumberOptionalNumber.parse: don't expect_exhausted<commit_after>//! The `Parse` trait for CSS properties, and utilities for parsers.

use cssparser::{Parser, ParserInput, Token};
use markup5ever::QualName;
use std::str;

use crate::error::*;

/// Trait to parse values using `cssparser::Parser`.
pub trait Parse: Sized {
    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.  Property types do not need to reimplement this.
    fn parse_str(s: &str) -> Result<Self, ParseError> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        let res = Self::parse(&mut parser)?;
        parser.expect_exhausted()?;

        Ok(res)
    }
}

/// Consumes a comma if it exists, or does nothing.
pub fn optional_comma<'i, 't>(parser: &mut Parser<'i, 't>) {
    let _ = parser.try_parse(|p| p.expect_comma());
}

/// Parses an `f32` and ensures that it is not an infinity or NaN.
pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValue<T: Parse> {
    /// Parses a `value` string into a type `T`.
    fn parse(&self, value: &str) -> Result<T, ElementError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, ElementError>;
}

impl<T: Parse> ParseValue<T> for QualName {
    fn parse(&self, value: &str) -> Result<T, ElementError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser).attribute(self.clone())
    }

    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, ElementError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        let v = T::parse(&mut parser).attribute(self.clone())?;

        validate(v)
            .map_err(|e| parser.new_custom_error(e))
            .attribute(self.clone())
    }
}

impl Parse for f64 {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        let loc = parser.current_source_location();
        let n = parser.expect_number()?;
        if n.is_finite() {
            Ok(f64::from(n))
        } else {
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
        }
    }
}

/// CSS number-optional-number
///
/// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct NumberOptionalNumber<T: Parse>(pub T, pub T);

impl<T: Parse + Copy> Parse for NumberOptionalNumber<T> {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        let x = Parse::parse(parser)?;

        if !parser.is_exhausted() {
            optional_comma(parser);
            let y = Parse::parse(parser)?;
            Ok(NumberOptionalNumber(x, y))
        } else {
            Ok(NumberOptionalNumber(x, x))
        }
    }
}

impl Parse for i32 {
    /// CSS integer
    ///
    /// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        Ok(parser.expect_integer()?)
    }
}

/// Parses a list of identifiers from a `cssparser::Parser`
///
/// # Example
///
/// ```ignore
/// let my_boolean = parse_identifiers!(
///     parser,
///     "true" => true,
///     "false" => false,
/// )?;
/// ```
macro_rules! parse_identifiers {
    ($parser:expr,
     $($str:expr => $val:expr,)+) => {
        {
            let loc = $parser.current_source_location();
            let token = $parser.next()?;

            match token {
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+

                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
            }
        }
    };
}

/// https://www.w3.org/TR/css-values-4/#custom-idents
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CustomIdent(String);

impl Parse for CustomIdent {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
        let loc = parser.current_source_location();
        let token = parser.next()?;

        match token {
            // CSS-wide keywords and "default" are errors here
            // https://www.w3.org/TR/css-values-4/#css-wide-keywords
            Token::Ident(ref cow) => {
                for s in &["initial", "inherit", "unset", "default"] {
                    if cow.eq_ignore_ascii_case(s) {
                        return Err(loc.new_basic_unexpected_token_error(token.clone()).into());
                    }
                }

                Ok(CustomIdent(cow.as_ref().to_string()))
            }

            _ => Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
        }
    }
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(
            NumberOptionalNumber::parse_str("1, 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1 2"),
            Ok(NumberOptionalNumber(1.0, 2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1"),
            Ok(NumberOptionalNumber(1.0, 1.0))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str("-1, -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1 -2"),
            Ok(NumberOptionalNumber(-1.0, -2.0))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1"),
            Ok(NumberOptionalNumber(-1.0, -1.0))
        );
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(NumberOptionalNumber::<f64>::parse_str("").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("x1").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<f64>::parse_str("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(i32::parse_str("1"), Ok(1));
        assert_eq!(i32::parse_str("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(i32::parse_str("").is_err());
        assert!(i32::parse_str("1x").is_err());
        assert!(i32::parse_str("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(
            NumberOptionalNumber::parse_str("1, 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1 2"),
            Ok(NumberOptionalNumber(1, 2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("1"),
            Ok(NumberOptionalNumber(1, 1))
        );

        assert_eq!(
            NumberOptionalNumber::parse_str("-1, -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1 -2"),
            Ok(NumberOptionalNumber(-1, -2))
        );
        assert_eq!(
            NumberOptionalNumber::parse_str("-1"),
            Ok(NumberOptionalNumber(-1, -1))
        );
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(NumberOptionalNumber::<i32>::parse_str("").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("x1").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , 2x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2 x").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2.5").is_err());
        assert!(NumberOptionalNumber::<i32>::parse_str("1, 2.5").is_err());
    }

    #[test]
    fn parses_custom_ident() {
        assert_eq!(
            CustomIdent::parse_str("hello"),
            Ok(CustomIdent("hello".to_string()))
        );
    }

    #[test]
    fn invalid_custom_ident_yields_error() {
        assert!(CustomIdent::parse_str("initial").is_err());
        assert!(CustomIdent::parse_str("inherit").is_err());
        assert!(CustomIdent::parse_str("unset").is_err());
        assert!(CustomIdent::parse_str("default").is_err());
        assert!(CustomIdent::parse_str("").is_err());
    }
}
<|endoftext|>


<commit_before>use cssparser::Parser;

use crate::error::*;
use crate::number_list::{NumberList, NumberListLength};
use crate::parsers::{Parse, ParseError};

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ViewBox {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
}

impl ViewBox {
    pub fn new(x: f64, y: f64, w: f64, h: f64) -> ViewBox {
        assert!(
            w >= 0.0 && h >= 0.0,
            "width and height must not be negative"
        );

        ViewBox {
            x,
            y,
            width: w,
            height: h,
        }
    }
}

impl Parse for ViewBox {
    type Err = ValueErrorKind;

    // Parse a viewBox attribute
    // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
    //
    // viewBox: double [,] double [,] double [,] double [,]
    //
    // x, y, w, h
    //
    // Where w and h must be nonnegative.
    fn parse(parser: &mut Parser<'_, '_>) -> Result<ViewBox, ValueErrorKind> {
        let NumberList(v) = NumberList::parse(parser, NumberListLength::Exact(4))
            .map_err(|_| ParseError::new("string does not match 'x [,] y [,] w [,] h'"))?;

        let (x, y, width, height) = (v[0], v[1], v[2], v[3]);

        if width >= 0.0 && height >= 0.0 {
            Ok(ViewBox {
                x,
                y,
                width,
                height,
            })
        } else {
            Err(ValueErrorKind::Value(
                "width and height must not be negative".to_string(),
            ))
        }
    }
}

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

    #[test]
    fn parses_valid_viewboxes() {
        assert_eq!(
            ViewBox::parse_str("  1 2 3 4"),
            Ok(ViewBox::new(1.0, 2.0, 3.0, 4.0))
        );

        assert_eq!(
            ViewBox::parse_str(" -1.5 -2.5e1,34,56e2  "),
            Ok(ViewBox::new(-1.5, -25.0, 34.0, 5600.0))
        );
    }

    #[test]
    fn parsing_invalid_viewboxes_yields_error() {
        assert!(is_parse_error(&ViewBox::parse_str("")));

        assert!(is_value_error(&ViewBox::parse_str(" 1,2,-3,-4 ")));

        assert!(is_parse_error(&ViewBox::parse_str("qwerasdfzxcv")));

        assert!(is_parse_error(&ViewBox::parse_str(" 1 2 3 4   5")));

        assert!(is_parse_error(&ViewBox::parse_str(" 1 2 foo 3 4")));

        // https://gitlab.gnome.org/GNOME/librsvg/issues/344
        assert!(is_parse_error(&ViewBox::parse_str("0 0 9E80.7")));
    }
}
<commit_msg>viewbox: small cleanup<commit_after>use cssparser::Parser;

use crate::error::*;
use crate::number_list::{NumberList, NumberListLength};
use crate::parsers::{Parse, ParseError};

#[derive(Debug, Copy, Clone, PartialEq)]
pub struct ViewBox {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
}

impl ViewBox {
    pub fn new(x: f64, y: f64, w: f64, h: f64) -> ViewBox {
        assert!(
            w >= 0.0 && h >= 0.0,
            "width and height must not be negative"
        );

        ViewBox {
            x,
            y,
            width: w,
            height: h,
        }
    }
}

impl Parse for ViewBox {
    type Err = ValueErrorKind;

    // Parse a viewBox attribute
    // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
    //
    // viewBox: double [,] double [,] double [,] double [,]
    //
    // x, y, w, h
    //
    // Where w and h must be nonnegative.
    fn parse(parser: &mut Parser<'_, '_>) -> Result<ViewBox, ValueErrorKind> {
        let NumberList(v) = NumberList::parse(parser, NumberListLength::Exact(4))
            .map_err(|_| ParseError::new("string does not match 'x [,] y [,] w [,] h'"))?;

        let (x, y, width, height) = (v[0], v[1], v[2], v[3]);

        if width >= 0.0 && height >= 0.0 {
            Ok(ViewBox::new(x, y, width, height))
        } else {
            Err(ValueErrorKind::Value(
                "width and height must not be negative".to_string(),
            ))
        }
    }
}

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

    #[test]
    fn parses_valid_viewboxes() {
        assert_eq!(
            ViewBox::parse_str("  1 2 3 4"),
            Ok(ViewBox::new(1.0, 2.0, 3.0, 4.0))
        );

        assert_eq!(
            ViewBox::parse_str(" -1.5 -2.5e1,34,56e2  "),
            Ok(ViewBox::new(-1.5, -25.0, 34.0, 5600.0))
        );
    }

    #[test]
    fn parsing_invalid_viewboxes_yields_error() {
        assert!(is_parse_error(&ViewBox::parse_str("")));

        assert!(is_value_error(&ViewBox::parse_str(" 1,2,-3,-4 ")));

        assert!(is_parse_error(&ViewBox::parse_str("qwerasdfzxcv")));

        assert!(is_parse_error(&ViewBox::parse_str(" 1 2 3 4   5")));

        assert!(is_parse_error(&ViewBox::parse_str(" 1 2 foo 3 4")));

        // https://gitlab.gnome.org/GNOME/librsvg/issues/344
        assert!(is_parse_error(&ViewBox::parse_str("0 0 9E80.7")));
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

fn process_anagram_case(word: &str, inputs: &[&str], expected: &[&str]) {
    let result = anagram::anagrams_for(word, inputs);

    let expected: HashSet<&str> = expected.iter().cloned().collect();

    assert_eq!(result, expected);
}

#[test]
fn test_no_matches() {
    let word = "diaper";

    let inputs = ["hello", "world", "zombies", "pants"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_detect_simple_anagram() {
    let word = "ant";

    let inputs = ["tan", "stand", "at"];

    let outputs = vec!["tan"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_does_not_confuse_different_duplicates() {
    let word = "galea";

    let inputs = ["eagle"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_eliminate_anagram_subsets() {
    let word = "good";

    let inputs = ["dog", "goody"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_detect_anagram() {
    let word = "listen";

    let inputs = ["enlists", "google", "inlets", "banana"];

    let outputs = vec!["inlets"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_multiple_anagrams() {
    let word = "allergy";

    let inputs = [
        "gallery",
        "ballerina",
        "regally",
        "clergy",
        "largely",
        "leading",
    ];

    let outputs = vec!["gallery", "regally", "largely"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_case_insensitive_anagrams() {
    let word = "Orchestra";

    let inputs = ["cashregister", "Carthorse", "radishes"];

    let outputs = vec!["Carthorse"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_unicode_anagrams() {
    let word = "ΑΒΓ";

    // These words don't make sense, they're just greek letters cobbled together.
    let inputs = ["ΒΓΑ", "ΒΓΔ", "γβα"];

    let outputs = vec!["ΒΓΑ", "γβα"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_misleading_unicode_anagrams() {
    // Despite what a human might think these words contain different letters, the input uses Greek
    // A and B while the list of potential anagrams uses Latin A and B.
    let word = "ΑΒΓ";

    let inputs = ["ABΓ"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_does_not_detect_a_differently_cased_word_as_its_own_anagram() {
    let word = "banana";

    let inputs = ["bAnana"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
#[ignore]
fn test_does_not_detect_a_differently_cased_unicode_word_as_its_own_anagram() {
    let word = "ΑΒΓ";

    let inputs = ["ΑΒγ"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
#[ignore]
fn test_same_bytes_different_chars() {
    let word = "a⬂"; // 61 E2 AC 82

    let inputs = ["€a"]; // E2 82 AC 61

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
#[ignore]
fn test_different_words_but_same_ascii_sum() {
    let word = "bc";

    let inputs = ["ad"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}
<commit_msg>Solve anagram<commit_after>use std::collections::HashSet;

fn process_anagram_case(word: &str, inputs: &[&str], expected: &[&str]) {
    let result = anagram::anagrams_for(word, inputs);

    let expected: HashSet<&str> = expected.iter().cloned().collect();

    assert_eq!(result, expected);
}

#[test]
fn test_no_matches() {
    let word = "diaper";

    let inputs = ["hello", "world", "zombies", "pants"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_detect_simple_anagram() {
    let word = "ant";

    let inputs = ["tan", "stand", "at"];

    let outputs = vec!["tan"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_does_not_confuse_different_duplicates() {
    let word = "galea";

    let inputs = ["eagle"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_eliminate_anagram_subsets() {
    let word = "good";

    let inputs = ["dog", "goody"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_detect_anagram() {
    let word = "listen";

    let inputs = ["enlists", "google", "inlets", "banana"];

    let outputs = vec!["inlets"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_multiple_anagrams() {
    let word = "allergy";

    let inputs = [
        "gallery",
        "ballerina",
        "regally",
        "clergy",
        "largely",
        "leading",
    ];

    let outputs = vec!["gallery", "regally", "largely"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_case_insensitive_anagrams() {
    let word = "Orchestra";

    let inputs = ["cashregister", "Carthorse", "radishes"];

    let outputs = vec!["Carthorse"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_unicode_anagrams() {
    let word = "ΑΒΓ";

    // These words don't make sense, they're just greek letters cobbled together.
    let inputs = ["ΒΓΑ", "ΒΓΔ", "γβα"];

    let outputs = vec!["ΒΓΑ", "γβα"];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_misleading_unicode_anagrams() {
    // Despite what a human might think these words contain different letters, the input uses Greek
    // A and B while the list of potential anagrams uses Latin A and B.
    let word = "ΑΒΓ";

    let inputs = ["ABΓ"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_does_not_detect_a_differently_cased_word_as_its_own_anagram() {
    let word = "banana";

    let inputs = ["bAnana"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_does_not_detect_a_differently_cased_unicode_word_as_its_own_anagram() {
    let word = "ΑΒΓ";

    let inputs = ["ΑΒγ"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_same_bytes_different_chars() {
    let word = "a⬂"; // 61 E2 AC 82

    let inputs = ["€a"]; // E2 82 AC 61

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}

#[test]
fn test_different_words_but_same_ascii_sum() {
    let word = "bc";

    let inputs = ["ad"];

    let outputs = vec![];

    process_anagram_case(word, &inputs, &outputs);
}
<|endoftext|>