Derive ToCss for TransformOperation

Now that SequenceWriter<W> does not monomorphise excessively, we can actually
type check a derived ToCss without too much type recursion.
This commit is contained in:
Anthony Ramine 2018-01-22 23:37:15 +01:00
parent cd8f96cc9e
commit 42c8dc983f
2 changed files with 61 additions and 125 deletions

View file

@ -30,7 +30,8 @@ pub struct Matrix<T, U = T> {
#[allow(missing_docs)] #[allow(missing_docs)]
#[cfg_attr(rustfmt, rustfmt_skip)] #[cfg_attr(rustfmt, rustfmt_skip)]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue)] #[css(comma, function = "matrix3d")]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct Matrix3D<T, U = T, V = T> { pub struct Matrix3D<T, U = T, V = T> {
pub m11: T, pub m12: T, pub m13: T, pub m14: T, pub m11: T, pub m12: T, pub m13: T, pub m14: T,
pub m21: T, pub m22: T, pub m23: T, pub m24: T, pub m21: T, pub m22: T, pub m23: T, pub m24: T,
@ -191,7 +192,7 @@ impl TimingKeyword {
} }
} }
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
/// A single operation in the list of a `transform` value /// A single operation in the list of a `transform` value
pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> { pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> {
/// Represents a 2D 2x3 matrix. /// Represents a 2D 2x3 matrix.
@ -200,31 +201,37 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
/// For `moz-transform`. /// For `moz-transform`.
PrefixedMatrix(Matrix<Number, LoPoNumber>), PrefixedMatrix(Matrix<Number, LoPoNumber>),
/// Represents a 3D 4x4 matrix. /// Represents a 3D 4x4 matrix.
#[allow(missing_docs)]
Matrix3D(Matrix3D<Number>), Matrix3D(Matrix3D<Number>),
/// Represents a 3D 4x4 matrix with percentage and length values. /// Represents a 3D 4x4 matrix with percentage and length values.
/// For `moz-transform`. /// For `moz-transform`.
#[allow(missing_docs)]
PrefixedMatrix3D(Matrix3D<Number, LoPoNumber, LengthOrNumber>), PrefixedMatrix3D(Matrix3D<Number, LoPoNumber, LengthOrNumber>),
/// A 2D skew. /// A 2D skew.
/// ///
/// If the second angle is not provided it is assumed zero. /// If the second angle is not provided it is assumed zero.
/// ///
/// Syntax can be skew(angle) or skew(angle, angle) /// Syntax can be skew(angle) or skew(angle, angle)
#[css(comma, function)]
Skew(Angle, Option<Angle>), Skew(Angle, Option<Angle>),
/// skewX(angle) /// skewX(angle)
#[css(function = "skewX")]
SkewX(Angle), SkewX(Angle),
/// skewY(angle) /// skewY(angle)
#[css(function = "skewY")]
SkewY(Angle), SkewY(Angle),
/// translate(x, y) or translate(x) /// translate(x, y) or translate(x)
#[css(comma, function)]
Translate(LengthOrPercentage, Option<LengthOrPercentage>), Translate(LengthOrPercentage, Option<LengthOrPercentage>),
/// translateX(x) /// translateX(x)
#[css(function = "translateX")]
TranslateX(LengthOrPercentage), TranslateX(LengthOrPercentage),
/// translateY(y) /// translateY(y)
#[css(function = "translateY")]
TranslateY(LengthOrPercentage), TranslateY(LengthOrPercentage),
/// translateZ(z) /// translateZ(z)
#[css(function = "translateZ")]
TranslateZ(Length), TranslateZ(Length),
/// translate3d(x, y, z) /// translate3d(x, y, z)
#[css(comma, function = "translate3d")]
Translate3D(LengthOrPercentage, LengthOrPercentage, Length), Translate3D(LengthOrPercentage, LengthOrPercentage, Length),
/// A 2D scaling factor. /// A 2D scaling factor.
/// ///
@ -234,28 +241,38 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
/// Negative values are allowed and flip the element. /// Negative values are allowed and flip the element.
/// ///
/// Syntax can be scale(factor) or scale(factor, factor) /// Syntax can be scale(factor) or scale(factor, factor)
#[css(comma, function)]
Scale(Number, Option<Number>), Scale(Number, Option<Number>),
/// scaleX(factor) /// scaleX(factor)
#[css(function = "scaleX")]
ScaleX(Number), ScaleX(Number),
/// scaleY(factor) /// scaleY(factor)
#[css(function = "scaleY")]
ScaleY(Number), ScaleY(Number),
/// scaleZ(factor) /// scaleZ(factor)
#[css(function = "scaleZ")]
ScaleZ(Number), ScaleZ(Number),
/// scale3D(factorX, factorY, factorZ) /// scale3D(factorX, factorY, factorZ)
#[css(comma, function = "scale3d")]
Scale3D(Number, Number, Number), Scale3D(Number, Number, Number),
/// Describes a 2D Rotation. /// Describes a 2D Rotation.
/// ///
/// In a 3D scene `rotate(angle)` is equivalent to `rotateZ(angle)`. /// In a 3D scene `rotate(angle)` is equivalent to `rotateZ(angle)`.
#[css(function)]
Rotate(Angle), Rotate(Angle),
/// Rotation in 3D space around the x-axis. /// Rotation in 3D space around the x-axis.
#[css(function = "rotateX")]
RotateX(Angle), RotateX(Angle),
/// Rotation in 3D space around the y-axis. /// Rotation in 3D space around the y-axis.
#[css(function = "rotateY")]
RotateY(Angle), RotateY(Angle),
/// Rotation in 3D space around the z-axis. /// Rotation in 3D space around the z-axis.
#[css(function = "rotateZ")]
RotateZ(Angle), RotateZ(Angle),
/// Rotation in 3D space. /// Rotation in 3D space.
/// ///
/// Generalization of rotateX, rotateY and rotateZ. /// Generalization of rotateX, rotateY and rotateZ.
#[css(comma, function = "rotate3d")]
Rotate3D(Number, Number, Number, Angle), Rotate3D(Number, Number, Number, Angle),
/// Specifies a perspective projection matrix. /// Specifies a perspective projection matrix.
/// ///
@ -263,11 +280,14 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
/// [§ 13.1. 3D Transform Function](https://drafts.csswg.org/css-transforms-2/#funcdef-perspective). /// [§ 13.1. 3D Transform Function](https://drafts.csswg.org/css-transforms-2/#funcdef-perspective).
/// ///
/// The value must be greater than or equal to zero. /// The value must be greater than or equal to zero.
#[css(function)]
Perspective(Length), Perspective(Length),
/// A intermediate type for interpolation of mismatched transform lists. /// A intermediate type for interpolation of mismatched transform lists.
#[allow(missing_docs)] #[allow(missing_docs)]
#[css(comma, function = "interpolatematrix")]
InterpolateMatrix { InterpolateMatrix {
#[compute(ignore_bound)] #[compute(ignore_bound)]
#[css(ignore_bound)]
from_list: Transform< from_list: Transform<
TransformOperation< TransformOperation<
Angle, Angle,
@ -280,6 +300,7 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
>, >,
>, >,
#[compute(ignore_bound)] #[compute(ignore_bound)]
#[css(ignore_bound)]
to_list: Transform< to_list: Transform<
TransformOperation< TransformOperation<
Angle, Angle,
@ -296,8 +317,10 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
}, },
/// A intermediate type for accumulation of mismatched transform lists. /// A intermediate type for accumulation of mismatched transform lists.
#[allow(missing_docs)] #[allow(missing_docs)]
#[css(comma, function = "accumulatematrix")]
AccumulateMatrix { AccumulateMatrix {
#[compute(ignore_bound)] #[compute(ignore_bound)]
#[css(ignore_bound)]
from_list: Transform< from_list: Transform<
TransformOperation< TransformOperation<
Angle, Angle,
@ -310,6 +333,7 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
>, >,
>, >,
#[compute(ignore_bound)] #[compute(ignore_bound)]
#[css(ignore_bound)]
to_list: Transform< to_list: Transform<
TransformOperation< TransformOperation<
Angle, Angle,
@ -541,120 +565,6 @@ where
} }
} }
#[cfg_attr(rustfmt, rustfmt_skip)]
impl<Angle: ToCss + Copy, Number: ToCss + Copy, Length: ToCss,
Integer: ToCss + Copy, LengthOrNumber: ToCss, LengthOrPercentage: ToCss, LoPoNumber: ToCss>
ToCss for
TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
TransformOperation::Matrix(ref m) => m.to_css(dest),
TransformOperation::PrefixedMatrix(ref m) => m.to_css(dest),
TransformOperation::Matrix3D(Matrix3D {
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44,
}) => {
serialize_function!(dest, matrix3d(
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44,
))
}
TransformOperation::PrefixedMatrix3D(Matrix3D {
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
ref m41, ref m42, ref m43, m44,
}) => {
serialize_function!(dest, matrix3d(
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44,
))
}
TransformOperation::Skew(ax, None) => {
serialize_function!(dest, skew(ax))
}
TransformOperation::Skew(ax, Some(ay)) => {
serialize_function!(dest, skew(ax, ay))
}
TransformOperation::SkewX(angle) => {
serialize_function!(dest, skewX(angle))
}
TransformOperation::SkewY(angle) => {
serialize_function!(dest, skewY(angle))
}
TransformOperation::Translate(ref tx, None) => {
serialize_function!(dest, translate(tx))
}
TransformOperation::Translate(ref tx, Some(ref ty)) => {
serialize_function!(dest, translate(tx, ty))
}
TransformOperation::TranslateX(ref tx) => {
serialize_function!(dest, translateX(tx))
}
TransformOperation::TranslateY(ref ty) => {
serialize_function!(dest, translateY(ty))
}
TransformOperation::TranslateZ(ref tz) => {
serialize_function!(dest, translateZ(tz))
}
TransformOperation::Translate3D(ref tx, ref ty, ref tz) => {
serialize_function!(dest, translate3d(tx, ty, tz))
}
TransformOperation::Scale(factor, None) => {
serialize_function!(dest, scale(factor))
}
TransformOperation::Scale(sx, Some(sy)) => {
serialize_function!(dest, scale(sx, sy))
}
TransformOperation::ScaleX(sx) => {
serialize_function!(dest, scaleX(sx))
}
TransformOperation::ScaleY(sy) => {
serialize_function!(dest, scaleY(sy))
}
TransformOperation::ScaleZ(sz) => {
serialize_function!(dest, scaleZ(sz))
}
TransformOperation::Scale3D(sx, sy, sz) => {
serialize_function!(dest, scale3d(sx, sy, sz))
}
TransformOperation::Rotate(theta) => {
serialize_function!(dest, rotate(theta))
}
TransformOperation::RotateX(theta) => {
serialize_function!(dest, rotateX(theta))
}
TransformOperation::RotateY(theta) => {
serialize_function!(dest, rotateY(theta))
}
TransformOperation::RotateZ(theta) => {
serialize_function!(dest, rotateZ(theta))
}
TransformOperation::Rotate3D(x, y, z, theta) => {
serialize_function!(dest, rotate3d(x, y, z, theta))
}
TransformOperation::Perspective(ref length) => {
serialize_function!(dest, perspective(length))
}
TransformOperation::InterpolateMatrix { ref from_list, ref to_list, progress } => {
serialize_function!(dest, interpolatematrix(from_list, to_list, progress))
}
TransformOperation::AccumulateMatrix { ref from_list, ref to_list, count } => {
serialize_function!(dest, accumulatematrix(from_list, to_list, count))
}
}
}
}
impl<T: ToCss> ToCss for Transform<T> { impl<T: ToCss> ToCss for Transform<T> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where

View file

@ -3,8 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cg; use cg;
use darling::{Error, FromMetaItem};
use quote::Tokens; use quote::Tokens;
use syn::DeriveInput; use syn::{self, DeriveInput, Ident};
use synstructure; use synstructure;
pub fn derive(input: DeriveInput) -> Tokens { pub fn derive(input: DeriveInput) -> Tokens {
@ -16,13 +17,13 @@ pub fn derive(input: DeriveInput) -> Tokens {
let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input); let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);
let style = synstructure::BindStyle::Ref.into(); let style = synstructure::BindStyle::Ref.into();
let match_body = synstructure::each_variant(&input, &style, |bindings, variant| { let match_body = synstructure::each_variant(&input, &style, |bindings, variant| {
let mut identifier = cg::to_css_identifier(variant.ident.as_ref()); let identifier = cg::to_css_identifier(variant.ident.as_ref());
let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(variant); let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(variant);
let separator = if variant_attrs.comma { ", " } else { " " }; let separator = if variant_attrs.comma { ", " } else { " " };
if variant_attrs.dimension { if variant_attrs.dimension {
assert_eq!(bindings.len(), 1); assert_eq!(bindings.len(), 1);
assert!(!variant_attrs.function, "That makes no sense"); assert!(variant_attrs.function.is_none(), "That makes no sense");
} }
let mut expr = if !bindings.is_empty() { let mut expr = if !bindings.is_empty() {
@ -39,7 +40,10 @@ pub fn derive(input: DeriveInput) -> Tokens {
}; };
} else { } else {
for binding in bindings { for binding in bindings {
let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.field);
if !attrs.ignore_bound {
where_clause.add_trait_bound(&binding.field.ty); where_clause.add_trait_bound(&binding.field.ty);
}
expr = quote! { expr = quote! {
#expr #expr
writer.item(#binding)?; writer.item(#binding)?;
@ -63,7 +67,8 @@ pub fn derive(input: DeriveInput) -> Tokens {
#expr?; #expr?;
::std::fmt::Write::write_str(dest, #identifier) ::std::fmt::Write::write_str(dest, #identifier)
} }
} else if variant_attrs.function { } else if let Some(function) = variant_attrs.function {
let mut identifier = function.name.map_or(identifier, |name| name.to_string());
identifier.push_str("("); identifier.push_str("(");
expr = quote! { expr = quote! {
::std::fmt::Write::write_str(dest, #identifier)?; ::std::fmt::Write::write_str(dest, #identifier)?;
@ -112,15 +117,36 @@ pub fn derive(input: DeriveInput) -> Tokens {
#[derive(Default, FromDeriveInput)] #[derive(Default, FromDeriveInput)]
struct CssInputAttrs { struct CssInputAttrs {
derive_debug: bool, derive_debug: bool,
function: bool, function: Option<Function>,
comma: bool, comma: bool,
} }
#[darling(attributes(css), default)] #[darling(attributes(css), default)]
#[derive(Default, FromVariant)] #[derive(Default, FromVariant)]
struct CssVariantAttrs { struct CssVariantAttrs {
function: bool, function: Option<Function>,
iterable: bool, iterable: bool,
comma: bool, comma: bool,
dimension: bool, dimension: bool,
} }
#[darling(attributes(css), default)]
#[derive(Default, FromField)]
struct CssFieldAttrs {
ignore_bound: bool,
}
struct Function {
name: Option<Ident>,
}
impl FromMetaItem for Function {
fn from_word() -> Result<Self, Error> {
Ok(Self { name: None })
}
fn from_string(name: &str) -> Result<Self, Error> {
let name = syn::parse_ident(name).map_err(Error::custom)?;
Ok(Self { name: Some(name) })
}
}