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)]
#[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 m11: T, pub m12: T, pub m13: T, pub m14: 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
pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, LengthOrPercentage, LoPoNumber> {
/// Represents a 2D 2x3 matrix.
@ -200,31 +201,37 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
/// For `moz-transform`.
PrefixedMatrix(Matrix<Number, LoPoNumber>),
/// Represents a 3D 4x4 matrix.
#[allow(missing_docs)]
Matrix3D(Matrix3D<Number>),
/// Represents a 3D 4x4 matrix with percentage and length values.
/// For `moz-transform`.
#[allow(missing_docs)]
PrefixedMatrix3D(Matrix3D<Number, LoPoNumber, LengthOrNumber>),
/// A 2D skew.
///
/// If the second angle is not provided it is assumed zero.
///
/// Syntax can be skew(angle) or skew(angle, angle)
#[css(comma, function)]
Skew(Angle, Option<Angle>),
/// skewX(angle)
#[css(function = "skewX")]
SkewX(Angle),
/// skewY(angle)
#[css(function = "skewY")]
SkewY(Angle),
/// translate(x, y) or translate(x)
#[css(comma, function)]
Translate(LengthOrPercentage, Option<LengthOrPercentage>),
/// translateX(x)
#[css(function = "translateX")]
TranslateX(LengthOrPercentage),
/// translateY(y)
#[css(function = "translateY")]
TranslateY(LengthOrPercentage),
/// translateZ(z)
#[css(function = "translateZ")]
TranslateZ(Length),
/// translate3d(x, y, z)
#[css(comma, function = "translate3d")]
Translate3D(LengthOrPercentage, LengthOrPercentage, Length),
/// 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.
///
/// Syntax can be scale(factor) or scale(factor, factor)
#[css(comma, function)]
Scale(Number, Option<Number>),
/// scaleX(factor)
#[css(function = "scaleX")]
ScaleX(Number),
/// scaleY(factor)
#[css(function = "scaleY")]
ScaleY(Number),
/// scaleZ(factor)
#[css(function = "scaleZ")]
ScaleZ(Number),
/// scale3D(factorX, factorY, factorZ)
#[css(comma, function = "scale3d")]
Scale3D(Number, Number, Number),
/// Describes a 2D Rotation.
///
/// In a 3D scene `rotate(angle)` is equivalent to `rotateZ(angle)`.
#[css(function)]
Rotate(Angle),
/// Rotation in 3D space around the x-axis.
#[css(function = "rotateX")]
RotateX(Angle),
/// Rotation in 3D space around the y-axis.
#[css(function = "rotateY")]
RotateY(Angle),
/// Rotation in 3D space around the z-axis.
#[css(function = "rotateZ")]
RotateZ(Angle),
/// Rotation in 3D space.
///
/// Generalization of rotateX, rotateY and rotateZ.
#[css(comma, function = "rotate3d")]
Rotate3D(Number, Number, Number, Angle),
/// 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).
///
/// The value must be greater than or equal to zero.
#[css(function)]
Perspective(Length),
/// A intermediate type for interpolation of mismatched transform lists.
#[allow(missing_docs)]
#[css(comma, function = "interpolatematrix")]
InterpolateMatrix {
#[compute(ignore_bound)]
#[css(ignore_bound)]
from_list: Transform<
TransformOperation<
Angle,
@ -280,6 +300,7 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
>,
>,
#[compute(ignore_bound)]
#[css(ignore_bound)]
to_list: Transform<
TransformOperation<
Angle,
@ -296,8 +317,10 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
},
/// A intermediate type for accumulation of mismatched transform lists.
#[allow(missing_docs)]
#[css(comma, function = "accumulatematrix")]
AccumulateMatrix {
#[compute(ignore_bound)]
#[css(ignore_bound)]
from_list: Transform<
TransformOperation<
Angle,
@ -310,6 +333,7 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthOrNumber, Leng
>,
>,
#[compute(ignore_bound)]
#[css(ignore_bound)]
to_list: Transform<
TransformOperation<
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> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where

View file

@ -3,8 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cg;
use darling::{Error, FromMetaItem};
use quote::Tokens;
use syn::DeriveInput;
use syn::{self, DeriveInput, Ident};
use synstructure;
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 style = synstructure::BindStyle::Ref.into();
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 separator = if variant_attrs.comma { ", " } else { " " };
if variant_attrs.dimension {
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() {
@ -39,7 +40,10 @@ pub fn derive(input: DeriveInput) -> Tokens {
};
} else {
for binding in bindings {
where_clause.add_trait_bound(&binding.field.ty);
let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.field);
if !attrs.ignore_bound {
where_clause.add_trait_bound(&binding.field.ty);
}
expr = quote! {
#expr
writer.item(#binding)?;
@ -63,7 +67,8 @@ pub fn derive(input: DeriveInput) -> Tokens {
#expr?;
::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("(");
expr = quote! {
::std::fmt::Write::write_str(dest, #identifier)?;
@ -112,15 +117,36 @@ pub fn derive(input: DeriveInput) -> Tokens {
#[derive(Default, FromDeriveInput)]
struct CssInputAttrs {
derive_debug: bool,
function: bool,
function: Option<Function>,
comma: bool,
}
#[darling(attributes(css), default)]
#[derive(Default, FromVariant)]
struct CssVariantAttrs {
function: bool,
function: Option<Function>,
iterable: bool,
comma: 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) })
}
}