Auto merge of #19903 - servo:derive-all-the-things, r=emilio

Derive more Parse implementations (fixes #19827)

Fixes #19827.

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19903)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-02-01 05:52:08 -06:00 committed by GitHub
commit b4339ab5c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 339 additions and 352 deletions

View file

@ -67,13 +67,17 @@ impl ToCss for UrlSource {
/// A font-display value for a @font-face rule. /// A font-display value for a @font-face rule.
/// The font-display descriptor determines how a font face is displayed based /// The font-display descriptor determines how a font face is displayed based
/// on whether and when it is downloaded and ready to use. /// on whether and when it is downloaded and ready to use.
define_css_keyword_enum!(FontDisplay: #[allow(missing_docs)]
"auto" => Auto, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"block" => Block, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"swap" => Swap, #[derive(ToComputedValue, ToCss)]
"fallback" => Fallback, pub enum FontDisplay {
"optional" => Optional); Auto,
add_impls_for_keyword_enum!(FontDisplay); Block,
Swap,
Fallback,
Optional,
}
/// A font-weight value for a @font-face rule. /// A font-weight value for a @font-face rule.
/// The font-weight CSS property specifies the weight or boldness of the font. /// The font-weight CSS property specifies the weight or boldness of the font.

View file

@ -707,9 +707,9 @@ pub mod basic_shape {
} }
StyleBasicShapeType::Polygon => { StyleBasicShapeType::Polygon => {
let fill_rule = if other.mFillRule == StyleFillRule::Evenodd { let fill_rule = if other.mFillRule == StyleFillRule::Evenodd {
FillRule::EvenOdd FillRule::Evenodd
} else { } else {
FillRule::NonZero FillRule::Nonzero
}; };
let mut coords = Vec::with_capacity(other.mCoordinates.len() / 2); let mut coords = Vec::with_capacity(other.mCoordinates.len() / 2);
for i in 0..(other.mCoordinates.len() / 2) { for i in 0..(other.mCoordinates.len() / 2) {

View file

@ -348,10 +348,10 @@ impl GeckoStyleCoordConvertible for ExtremumLength {
use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT}; use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT};
coord.set_value(CoordDataValue::Enumerated( coord.set_value(CoordDataValue::Enumerated(
match *self { match *self {
ExtremumLength::MaxContent => NS_STYLE_WIDTH_MAX_CONTENT, ExtremumLength::MozMaxContent => NS_STYLE_WIDTH_MAX_CONTENT,
ExtremumLength::MinContent => NS_STYLE_WIDTH_MIN_CONTENT, ExtremumLength::MozMinContent => NS_STYLE_WIDTH_MIN_CONTENT,
ExtremumLength::FitContent => NS_STYLE_WIDTH_FIT_CONTENT, ExtremumLength::MozFitContent => NS_STYLE_WIDTH_FIT_CONTENT,
ExtremumLength::FillAvailable => NS_STYLE_WIDTH_AVAILABLE, ExtremumLength::MozAvailable => NS_STYLE_WIDTH_AVAILABLE,
} }
)) ))
} }
@ -361,12 +361,12 @@ impl GeckoStyleCoordConvertible for ExtremumLength {
use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT}; use gecko_bindings::structs::{NS_STYLE_WIDTH_MAX_CONTENT, NS_STYLE_WIDTH_MIN_CONTENT};
match coord.as_value() { match coord.as_value() {
CoordDataValue::Enumerated(NS_STYLE_WIDTH_MAX_CONTENT) => CoordDataValue::Enumerated(NS_STYLE_WIDTH_MAX_CONTENT) =>
Some(ExtremumLength::MaxContent), Some(ExtremumLength::MozMaxContent),
CoordDataValue::Enumerated(NS_STYLE_WIDTH_MIN_CONTENT) => CoordDataValue::Enumerated(NS_STYLE_WIDTH_MIN_CONTENT) =>
Some(ExtremumLength::MinContent), Some(ExtremumLength::MozMinContent),
CoordDataValue::Enumerated(NS_STYLE_WIDTH_FIT_CONTENT) => CoordDataValue::Enumerated(NS_STYLE_WIDTH_FIT_CONTENT) =>
Some(ExtremumLength::FitContent), Some(ExtremumLength::MozFitContent),
CoordDataValue::Enumerated(NS_STYLE_WIDTH_AVAILABLE) => Some(ExtremumLength::FillAvailable), CoordDataValue::Enumerated(NS_STYLE_WIDTH_AVAILABLE) => Some(ExtremumLength::MozAvailable),
_ => None, _ => None,
} }
} }

View file

@ -25,8 +25,6 @@
#![deny(missing_docs)] #![deny(missing_docs)]
#![recursion_limit = "500"] // For define_css_keyword_enum! in -moz-appearance
extern crate app_units; extern crate app_units;
extern crate arrayvec; extern crate arrayvec;
extern crate atomic_refcell; extern crate atomic_refcell;
@ -72,7 +70,6 @@ extern crate smallbitvec;
extern crate smallvec; extern crate smallvec;
#[macro_use] #[macro_use]
extern crate style_derive; extern crate style_derive;
#[macro_use]
extern crate style_traits; extern crate style_traits;
extern crate time; extern crate time;
extern crate uluru; extern crate uluru;

View file

@ -65,67 +65,6 @@ macro_rules! try_match_ident_ignore_ascii_case {
}} }}
} }
macro_rules! define_numbered_css_keyword_enum {
($name: ident: $( $css: expr => $variant: ident = $value: expr ),+,) => {
define_numbered_css_keyword_enum!($name: $( $css => $variant = $value ),+);
};
($name: ident: $( $css: expr => $variant: ident = $value: expr ),+) => {
#[allow(non_camel_case_types, missing_docs)]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub enum $name {
$( $variant = $value ),+
}
impl $crate::parser::Parse for $name {
fn parse<'i, 't>(
_context: &$crate::parser::ParserContext,
input: &mut ::cssparser::Parser<'i, 't>,
) -> Result<$name, ::style_traits::ParseError<'i>> {
try_match_ident_ignore_ascii_case! { input,
$( $css => Ok($name::$variant), )+
}
}
}
impl ::style_traits::ToCss for $name {
fn to_css<W>(
&self,
dest: &mut ::style_traits::CssWriter<W>,
) -> ::std::fmt::Result
where
W: ::std::fmt::Write,
{
match *self {
$( $name::$variant => dest.write_str($css) ),+
}
}
}
}
}
/// A macro for implementing `ToComputedValue`, and `Parse` traits for
/// the enums defined using `define_css_keyword_enum` macro.
///
/// NOTE: We should either move `Parse` trait to `style_traits`
/// or `define_css_keyword_enum` macro to this crate, but that
/// may involve significant cleanup in both the crates.
macro_rules! add_impls_for_keyword_enum {
($name:ident) => {
impl $crate::parser::Parse for $name {
#[inline]
fn parse<'i, 't>(
_context: &$crate::parser::ParserContext,
input: &mut ::cssparser::Parser<'i, 't>,
) -> Result<Self, ::style_traits::ParseError<'i>> {
$name::parse(input)
}
}
trivial_to_computed_value!($name);
};
}
macro_rules! define_keyword_type { macro_rules! define_keyword_type {
($name: ident, $css: expr) => { ($name: ident, $css: expr) => {
#[allow(missing_docs)] #[allow(missing_docs)]

View file

@ -62,6 +62,7 @@ use values::computed::{NonNegativeLength, ToComputedValue, Percentage};
use values::computed::font::{FontSize, SingleFontFamily}; use values::computed::font::{FontSize, SingleFontFamily};
use values::computed::effects::{BoxShadow, Filter, SimpleShadow}; use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
use values::computed::outline::OutlineStyle; use values::computed::outline::OutlineStyle;
use values::generics::transform::TransformStyle;
use computed_values::border_style; use computed_values::border_style;
pub mod style_structs { pub mod style_structs {
@ -3345,6 +3346,25 @@ fn static_assert() {
self.copy_transition_property_from(other) self.copy_transition_property_from(other)
} }
// Hand-written because the Mako helpers transform `Preserve3d` into `PRESERVE3D`.
pub fn set_transform_style(&mut self, v: TransformStyle) {
self.gecko.mTransformStyle = match v {
TransformStyle::Flat => structs::NS_STYLE_TRANSFORM_STYLE_FLAT as u8,
TransformStyle::Preserve3d => structs::NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D as u8,
};
}
// Hand-written because the Mako helpers transform `Preserve3d` into `PRESERVE3D`.
pub fn clone_transform_style(&self) -> TransformStyle {
match self.gecko.mTransformStyle as u32 {
structs::NS_STYLE_TRANSFORM_STYLE_FLAT => TransformStyle::Flat,
structs::NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D => TransformStyle::Preserve3d,
_ => panic!("illegal transform style"),
}
}
${impl_simple_copy('transform_style', 'mTransformStyle')}
${impl_transition_count('property', 'Property')} ${impl_transition_count('property', 'Property')}
pub fn animations_equals(&self, other: &Self) -> bool { pub fn animations_equals(&self, other: &Self) -> bool {
@ -5081,7 +5101,7 @@ fn static_assert() {
coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]); coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]);
coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]); coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]);
} }
shape.mFillRule = if poly.fill == FillRule::EvenOdd { shape.mFillRule = if poly.fill == FillRule::Evenodd {
StyleFillRule::Evenodd StyleFillRule::Evenodd
} else { } else {
StyleFillRule::Nonzero StyleFillRule::Nonzero

View file

@ -424,22 +424,15 @@
use properties::longhands::system_font::SystemFont; use properties::longhands::system_font::SystemFont;
pub mod computed_value { pub mod computed_value {
use cssparser::Parser; #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
use parser::{Parse, ParserContext}; #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse)]
#[derive(PartialEq, ToCss)]
use style_traits::ParseError; pub enum T {
define_css_keyword_enum! { T:
% for value in keyword.values_for(product): % for value in keyword.values_for(product):
"${value}" => ${to_camel_case(value)}, ${to_camel_case(value)},
% endfor % endfor
} }
impl Parse for T {
fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
T::parse(input)
}
}
${gecko_keyword_conversion(keyword, keyword.values_for(product), type="T", cast_to="i32")} ${gecko_keyword_conversion(keyword, keyword.values_for(product), type="T", cast_to="i32")}
} }
@ -604,25 +597,31 @@
<%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)"> <%def name="inner_body(keyword, extra_specified=None, needs_conversion=False)">
% if extra_specified or keyword.aliases_for(product): % if extra_specified or keyword.aliases_for(product):
define_css_keyword_enum! { SpecifiedValue: #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
values { #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
pub enum SpecifiedValue {
% for value in keyword.values_for(product) + (extra_specified or "").split(): % for value in keyword.values_for(product) + (extra_specified or "").split():
"${value}" => ${to_camel_case(value)}, <%
aliases = []
for alias, v in keyword.aliases_for(product).iteritems():
if value == v:
aliases.append(alias)
%>
% if aliases:
#[css(aliases = "${','.join(aliases)}")]
% endif
${to_camel_case(value)},
% endfor % endfor
} }
aliases {
% for alias, value in keyword.aliases_for(product).iteritems():
"${alias}" => ${to_camel_case(value)},
% endfor
}
}
% else: % else:
pub use self::computed_value::T as SpecifiedValue; pub use self::computed_value::T as SpecifiedValue;
% endif % endif
pub mod computed_value { pub mod computed_value {
define_css_keyword_enum! { T: #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
pub enum T {
% for value in data.longhands_by_name[name].keyword.values_for(product): % for value in data.longhands_by_name[name].keyword.values_for(product):
"${value}" => ${to_camel_case(value)}, ${to_camel_case(value)},
% endfor % endfor
} }
} }
@ -639,13 +638,6 @@
-> Result<SpecifiedValue, ParseError<'i>> { -> Result<SpecifiedValue, ParseError<'i>> {
SpecifiedValue::parse(input) SpecifiedValue::parse(input)
} }
impl Parse for SpecifiedValue {
#[inline]
fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
-> Result<SpecifiedValue, ParseError<'i>> {
SpecifiedValue::parse(input)
}
}
% if needs_conversion: % if needs_conversion:
<% <%

View file

@ -32,13 +32,16 @@
ignored_when_colors_disabled=True, ignored_when_colors_disabled=True,
)} )}
${helpers.predefined_type("border-%s-style" % side_name, "BorderStyle", ${helpers.predefined_type(
"border-%s-style" % side_name, "BorderStyle",
"specified::BorderStyle::None", "specified::BorderStyle::None",
alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"), alias=maybe_moz_logical_alias(product, side, "-moz-border-%s-style"),
spec=maybe_logical_spec(side, "style"), spec=maybe_logical_spec(side, "style"),
flags="APPLIES_TO_FIRST_LETTER", flags="APPLIES_TO_FIRST_LETTER",
animation_value_type="discrete" if not is_logical else "none", animation_value_type="discrete" if not is_logical else "none",
logical=is_logical)} logical=is_logical,
needs_context=False,
)}
${helpers.predefined_type("border-%s-width" % side_name, ${helpers.predefined_type("border-%s-width" % side_name,
"BorderSideWidth", "BorderSideWidth",
@ -112,11 +115,14 @@ ${helpers.predefined_type("border-image-outset", "LengthOrNumberRect",
pub struct SpecifiedValue(pub RepeatKeyword, pub struct SpecifiedValue(pub RepeatKeyword,
pub Option<RepeatKeyword>); pub Option<RepeatKeyword>);
define_css_keyword_enum!(RepeatKeyword: #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"stretch" => Stretch, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
"repeat" => Repeat, pub enum RepeatKeyword {
"round" => Round, Stretch,
"space" => Space); Repeat,
Round,
Space,
}
#[inline] #[inline]
pub fn get_initial_value() -> computed_value::T { pub fn get_initial_value() -> computed_value::T {

View file

@ -526,14 +526,16 @@ ${helpers.single_keyword("transform-box",
gecko_inexhaustive="True", gecko_inexhaustive="True",
animation_value_type="discrete")} animation_value_type="discrete")}
// `auto` keyword is not supported in gecko yet. ${helpers.predefined_type(
${helpers.single_keyword("transform-style", "transform-style",
"auto flat preserve-3d" if product == "servo" else "TransformStyle",
"flat preserve-3d", "computed::TransformStyle::" + ("Auto" if product == "servo" else "Flat"),
spec="https://drafts.csswg.org/css-transforms/#transform-style-property", spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
needs_context=False,
extra_prefixes="moz webkit", extra_prefixes="moz webkit",
flags="CREATES_STACKING_CONTEXT FIXPOS_CB", flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
animation_value_type="discrete")} animation_value_type="discrete",
)}
${helpers.predefined_type("transform-origin", ${helpers.predefined_type("transform-origin",
"TransformOrigin", "TransformOrigin",

View file

@ -279,12 +279,14 @@ ${helpers.predefined_type(
} }
} }
define_css_keyword_enum!(ShapeKeyword: #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
"dot" => Dot, pub enum ShapeKeyword {
"circle" => Circle, Dot,
"double-circle" => DoubleCircle, Circle,
"triangle" => Triangle, DoubleCircle,
"sesame" => Sesame); Triangle,
Sesame,
}
impl ShapeKeyword { impl ShapeKeyword {
pub fn char(&self, fill: bool) -> &str { pub fn char(&self, fill: bool) -> &str {
@ -381,14 +383,19 @@ ${helpers.predefined_type(
<%helpers:longhand name="text-emphasis-position" animation_value_type="discrete" products="gecko" <%helpers:longhand name="text-emphasis-position" animation_value_type="discrete" products="gecko"
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position"> spec="https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position">
define_css_keyword_enum!(HorizontalWritingModeValue: #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"over" => Over, #[derive(ToComputedValue, ToCss)]
"under" => Under); pub enum HorizontalWritingModeValue {
add_impls_for_keyword_enum!(VerticalWritingModeValue); Over,
define_css_keyword_enum!(VerticalWritingModeValue: Under,
"right" => Right, }
"left" => Left);
add_impls_for_keyword_enum!(HorizontalWritingModeValue); #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
#[derive(ToComputedValue, ToCss)]
pub enum VerticalWritingModeValue {
Right,
Left,
}
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct SpecifiedValue(pub HorizontalWritingModeValue, pub VerticalWritingModeValue); pub struct SpecifiedValue(pub HorizontalWritingModeValue, pub VerticalWritingModeValue);

View file

@ -9,9 +9,13 @@ ${helpers.four_sides_shorthand("border-color", "border-%s-color", "specified::Co
spec="https://drafts.csswg.org/css-backgrounds/#border-color", spec="https://drafts.csswg.org/css-backgrounds/#border-color",
allow_quirks=True)} allow_quirks=True)}
${helpers.four_sides_shorthand("border-style", "border-%s-style", ${helpers.four_sides_shorthand(
"border-style",
"border-%s-style",
"specified::BorderStyle::parse", "specified::BorderStyle::parse",
spec="https://drafts.csswg.org/css-backgrounds/#border-style")} needs_context=False,
spec="https://drafts.csswg.org/css-backgrounds/#border-style",
)}
<%helpers:shorthand name="border-width" sub_properties="${ <%helpers:shorthand name="border-width" sub_properties="${
' '.join('border-%s-width' % side ' '.join('border-%s-width' % side
@ -63,7 +67,7 @@ pub fn parse_border<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
} }
} }
if style.is_none() { if style.is_none() {
if let Ok(value) = input.try(|i| BorderStyle::parse(context, i)) { if let Ok(value) = input.try(BorderStyle::parse) {
style = Some(value); style = Some(value);
any = true; any = true;
continue continue

View file

@ -73,7 +73,8 @@ pub use self::svg::MozContextProperties;
pub use self::table::XSpan; pub use self::table::XSpan;
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign, TextOverflow, WordSpacing}; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign, TextOverflow, WordSpacing};
pub use self::time::Time; pub use self::time::Time;
pub use self::transform::{TimingFunction, Transform, TransformOperation, TransformOrigin, Rotate, Translate, Scale}; pub use self::transform::{Rotate, Scale, TimingFunction, Transform, TransformOperation};
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
pub use self::ui::MozForceBrokenImageIcon; pub use self::ui::MozForceBrokenImageIcon;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]

View file

@ -18,6 +18,8 @@ use values::generics::transform::TimingFunction as GenericTimingFunction;
use values::generics::transform::TransformOrigin as GenericTransformOrigin; use values::generics::transform::TransformOrigin as GenericTransformOrigin;
use values::generics::transform::Translate as GenericTranslate; use values::generics::transform::Translate as GenericTranslate;
pub use values::generics::transform::TransformStyle;
/// A single operation in a computed CSS `transform` /// A single operation in a computed CSS `transform`
pub type TransformOperation = GenericTransformOperation< pub type TransformOperation = GenericTransformOperation<
Angle, Angle,

View file

@ -30,13 +30,16 @@ pub enum GeometryBox {
pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>; pub type FloatAreaShape<BasicShape, Image> = ShapeSource<BasicShape, ShapeBox, Image>;
// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box // https://drafts.csswg.org/css-shapes-1/#typedef-shape-box
define_css_keyword_enum!(ShapeBox: #[allow(missing_docs)]
"margin-box" => MarginBox, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"border-box" => BorderBox, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"padding-box" => PaddingBox, #[derive(ToComputedValue, ToCss)]
"content-box" => ContentBox pub enum ShapeBox {
); MarginBox,
add_impls_for_keyword_enum!(ShapeBox); BorderBox,
PaddingBox,
ContentBox,
}
/// A shape source, for some reference box. /// A shape source, for some reference box.
#[allow(missing_docs)] #[allow(missing_docs)]
@ -117,11 +120,14 @@ pub struct Polygon<LengthOrPercentage> {
// NOTE: Basic shapes spec says that these are the only two values, however // NOTE: Basic shapes spec says that these are the only two values, however
// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
// says that it can also be `inherit` // says that it can also be `inherit`
define_css_keyword_enum!(FillRule: #[allow(missing_docs)]
"nonzero" => NonZero, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"evenodd" => EvenOdd #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
); #[derive(ToComputedValue, ToCss)]
add_impls_for_keyword_enum!(FillRule); pub enum FillRule {
Nonzero,
Evenodd,
}
// FIXME(nox): Implement ComputeSquaredDistance for T types and stop // FIXME(nox): Implement ComputeSquaredDistance for T types and stop
// using PartialEq here, this will let us derive this impl. // using PartialEq here, this will let us derive this impl.
@ -239,5 +245,5 @@ impl<L: ToCss> ToCss for Polygon<L> {
impl Default for FillRule { impl Default for FillRule {
#[inline] #[inline]
fn default() -> Self { FillRule::NonZero } fn default() -> Self { FillRule::Nonzero }
} }

View file

@ -140,12 +140,15 @@ impl Parse for GridLine<specified::Integer> {
} }
} }
define_css_keyword_enum!{ TrackKeyword: #[allow(missing_docs)]
"auto" => Auto, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"max-content" => MaxContent, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"min-content" => MinContent #[derive(ToComputedValue, ToCss)]
pub enum TrackKeyword {
Auto,
MaxContent,
MinContent,
} }
add_impls_for_keyword_enum!(TrackKeyword);
/// A track breadth for explicit grid track sizing. It's generic solely to /// A track breadth for explicit grid track sizing. It's generic solely to
/// avoid re-implementing it for the computed type. /// avoid re-implementing it for the computed type.

View file

@ -97,15 +97,18 @@ pub enum Ellipse<LengthOrPercentage> {
} }
/// <https://drafts.csswg.org/css-images/#typedef-extent-keyword> /// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>
define_css_keyword_enum!(ShapeExtent: #[allow(missing_docs)]
"closest-side" => ClosestSide, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"farthest-side" => FarthestSide, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"closest-corner" => ClosestCorner, #[derive(ToComputedValue, ToCss)]
"farthest-corner" => FarthestCorner, pub enum ShapeExtent {
"contain" => Contain, ClosestSide,
"cover" => Cover FarthestSide,
); ClosestCorner,
add_impls_for_keyword_enum!(ShapeExtent); FarthestCorner,
Contain,
Cover,
}
/// A gradient item. /// A gradient item.
/// <https://drafts.csswg.org/css-images-4/#color-stop-syntax> /// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>

View file

@ -32,14 +32,17 @@ pub mod text;
pub mod transform; pub mod transform;
// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type // https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
define_css_keyword_enum! { SymbolsType: #[allow(missing_docs)]
"cyclic" => Cyclic, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"numeric" => Numeric, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"alphabetic" => Alphabetic, #[derive(ToComputedValue, ToCss)]
"symbolic" => Symbolic, pub enum SymbolsType {
"fixed" => Fixed, Cyclic,
Numeric,
Alphabetic,
Symbolic,
Fixed,
} }
add_impls_for_keyword_enum!(SymbolsType);
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl SymbolsType { impl SymbolsType {

View file

@ -98,20 +98,26 @@ pub enum TimingFunction<Integer, Number> {
Frames(Integer), Frames(Integer),
} }
define_css_keyword_enum! { TimingKeyword: #[allow(missing_docs)]
"linear" => Linear, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"ease" => Ease, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"ease-in" => EaseIn, #[derive(ToComputedValue, ToCss)]
"ease-out" => EaseOut, pub enum TimingKeyword {
"ease-in-out" => EaseInOut, Linear,
Ease,
EaseIn,
EaseOut,
EaseInOut,
} }
add_impls_for_keyword_enum!(TimingKeyword);
define_css_keyword_enum! { StepPosition: #[allow(missing_docs)]
"start" => Start, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"end" => End, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
#[derive(ToComputedValue, ToCss)]
pub enum StepPosition {
Start,
End,
} }
add_impls_for_keyword_enum!(StepPosition);
impl<H, V, D> TransformOrigin<H, V, D> { impl<H, V, D> TransformOrigin<H, V, D> {
/// Returns a new transform origin. /// Returns a new transform origin.
@ -714,3 +720,13 @@ pub enum Translate<LengthOrPercentage, Length> {
/// '<length-percentage> <length-percentage> <length>' /// '<length-percentage> <length-percentage> <length>'
Translate3D(LengthOrPercentage, LengthOrPercentage, Length), Translate3D(LengthOrPercentage, LengthOrPercentage, Length),
} }
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss)]
pub enum TransformStyle {
#[cfg(feature = "servo")]
Auto,
Flat,
#[css(keyword = "preserve-3d")]
Preserve3d,
}

View file

@ -195,10 +195,14 @@ impl ToCss for KeyframesName {
} }
} }
// A type for possible values for min- and max- flavors of width, /// A type for possible values for min- and max- flavors of width,
// height, block-size, and inline-size. /// height, block-size, and inline-size.
define_css_keyword_enum!(ExtremumLength: #[allow(missing_docs)]
"-moz-max-content" => MaxContent, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"-moz-min-content" => MinContent, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss)]
"-moz-fit-content" => FitContent, pub enum ExtremumLength {
"-moz-available" => FillAvailable); MozMaxContent,
MozMinContent,
MozFitContent,
MozAvailable,
}

View file

@ -28,9 +28,9 @@ pub enum Display {
Table, InlineTable, TableRowGroup, TableHeaderGroup, Table, InlineTable, TableRowGroup, TableHeaderGroup,
TableFooterGroup, TableRow, TableColumnGroup, TableFooterGroup, TableRow, TableColumnGroup,
TableColumn, TableCell, TableCaption, ListItem, None, TableColumn, TableCell, TableCaption, ListItem, None,
#[parse(aliases = "-webkit-flex")] #[css(aliases = "-webkit-flex")]
Flex, Flex,
#[parse(aliases = "-webkit-inline-flex")] #[css(aliases = "-webkit-inline-flex")]
InlineFlex, InlineFlex,
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
Grid, Grid,
@ -320,25 +320,34 @@ impl Parse for AnimationName {
} }
} }
define_css_keyword_enum! { ScrollSnapType: #[allow(missing_docs)]
"none" => None, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"mandatory" => Mandatory, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"proximity" => Proximity, #[derive(ToComputedValue, ToCss)]
pub enum ScrollSnapType {
None,
Mandatory,
Proximity,
} }
add_impls_for_keyword_enum!(ScrollSnapType);
define_css_keyword_enum! { OverscrollBehavior: #[allow(missing_docs)]
"auto" => Auto, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"contain" => Contain, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
"none" => None, #[derive(ToComputedValue, ToCss)]
pub enum OverscrollBehavior {
Auto,
Contain,
None,
} }
add_impls_for_keyword_enum!(OverscrollBehavior);
define_css_keyword_enum! { OverflowClipBox: #[allow(missing_docs)]
"padding-box" => PaddingBox, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"content-box" => ContentBox, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
#[derive(ToComputedValue, ToCss)]
pub enum OverflowClipBox {
PaddingBox,
ContentBox,
} }
add_impls_for_keyword_enum!(OverflowClipBox);
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
/// Provides a rendering hint to the user agent, /// Provides a rendering hint to the user agent,

View file

@ -47,12 +47,13 @@ pub enum Color {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
mod gecko { mod gecko {
define_css_keyword_enum! { SpecialColorKeyword: #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss)]
"-moz-default-color" => MozDefaultColor, pub enum SpecialColorKeyword {
"-moz-default-background-color" => MozDefaultBackgroundColor, MozDefaultColor,
"-moz-hyperlinktext" => MozHyperlinktext, MozDefaultBackgroundColor,
"-moz-activehyperlinktext" => MozActiveHyperlinktext, MozHyperlinktext,
"-moz-visitedhyperlinktext" => MozVisitedHyperlinktext, MozActiveHyperlinktext,
MozVisitedHyperlinktext,
} }
} }

View file

@ -69,7 +69,8 @@ pub use self::table::XSpan;
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextDecorationLine}; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextDecorationLine};
pub use self::text::{TextAlign, TextAlignKeyword, TextOverflow, WordSpacing}; pub use self::text::{TextAlign, TextAlignKeyword, TextOverflow, WordSpacing};
pub use self::time::Time; pub use self::time::Time;
pub use self::transform::{TimingFunction, Transform, TransformOrigin, Rotate, Translate, Scale}; pub use self::transform::{Rotate, Scale, TimingFunction, Transform};
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
pub use self::ui::MozForceBrokenImageIcon; pub use self::ui::MozForceBrokenImageIcon;
pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
@ -160,20 +161,23 @@ fn parse_number_with_clamping_mode<'i, 't>(
// 17.6.2.1. Higher values override lower values. // 17.6.2.1. Higher values override lower values.
// //
// FIXME(emilio): Should move to border.rs // FIXME(emilio): Should move to border.rs
define_numbered_css_keyword_enum! { BorderStyle: #[allow(missing_docs)]
"none" => None = -1, #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
"solid" => Solid = 6, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, Parse, PartialEq)]
"double" => Double = 7, #[derive(PartialOrd, ToCss)]
"dotted" => Dotted = 4, pub enum BorderStyle {
"dashed" => Dashed = 5, None = -1,
"hidden" => Hidden = -2, Solid = 6,
"groove" => Groove = 1, Double = 7,
"ridge" => Ridge = 3, Dotted = 4,
"inset" => Inset = 0, Dashed = 5,
"outset" => Outset = 2, Hidden = -2,
Groove = 1,
Ridge = 3,
Inset = 0,
Outset = 2,
} }
impl BorderStyle { impl BorderStyle {
/// Whether this border style is either none or hidden. /// Whether this border style is either none or hidden.
pub fn none_or_hidden(&self) -> bool { pub fn none_or_hidden(&self) -> bool {

View file

@ -39,10 +39,10 @@ impl OutlineStyle {
impl Parse for OutlineStyle { impl Parse for OutlineStyle {
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, _context: &ParserContext,
input: &mut Parser<'i, 't> input: &mut Parser<'i, 't>
) -> Result<OutlineStyle, ParseError<'i>> { ) -> Result<OutlineStyle, ParseError<'i>> {
if let Ok(border_style) = input.try(|i| BorderStyle::parse(context, i)) { if let Ok(border_style) = input.try(BorderStyle::parse) {
if let BorderStyle::Hidden = border_style { if let BorderStyle::Hidden = border_style {
return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("hidden".into()))); return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("hidden".into())));
} }

View file

@ -22,6 +22,8 @@ use values::specified::{self, Angle, Number, Length, Integer};
use values::specified::{LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber}; use values::specified::{LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrNumber};
use values::specified::position::{Side, X, Y}; use values::specified::position::{Side, X, Y};
pub use values::generics::transform::TransformStyle;
/// A single operation in a specified CSS `transform` /// A single operation in a specified CSS `transform`
pub type TransformOperation = GenericTransformOperation< pub type TransformOperation = GenericTransformOperation<
Angle, Angle,

View file

@ -39,7 +39,7 @@ pub fn derive_to_animated_value(stream: TokenStream) -> TokenStream {
to_animated_value::derive(input).to_string().parse().unwrap() to_animated_value::derive(input).to_string().parse().unwrap()
} }
#[proc_macro_derive(Parse, attributes(parse))] #[proc_macro_derive(Parse, attributes(css))]
pub fn derive_parse(stream: TokenStream) -> TokenStream { pub fn derive_parse(stream: TokenStream) -> TokenStream {
let input = syn::parse_derive_input(&stream.to_string()).unwrap(); let input = syn::parse_derive_input(&stream.to_string()).unwrap();
parse::derive(input).to_string().parse().unwrap() parse::derive(input).to_string().parse().unwrap()

View file

@ -6,6 +6,7 @@ use cg;
use quote::Tokens; use quote::Tokens;
use syn::DeriveInput; use syn::DeriveInput;
use synstructure; use synstructure;
use to_css::CssVariantAttrs;
pub fn derive(input: DeriveInput) -> Tokens { pub fn derive(input: DeriveInput) -> Tokens {
let name = &input.ident; let name = &input.ident;
@ -19,8 +20,10 @@ pub fn derive(input: DeriveInput) -> Tokens {
"Parse is only supported for single-variant enums for now" "Parse is only supported for single-variant enums for now"
); );
let variant_attrs = cg::parse_variant_attrs::<ParseVariantAttrs>(variant); let variant_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(variant);
let identifier = cg::to_css_identifier(variant.ident.as_ref()); let identifier = cg::to_css_identifier(
&variant_attrs.keyword.as_ref().unwrap_or(&variant.ident).as_ref(),
);
let ident = &variant.ident; let ident = &variant.ident;
match_body = quote! { match_body = quote! {
@ -87,11 +90,3 @@ pub fn derive(input: DeriveInput) -> Tokens {
#methods_impl #methods_impl
} }
} }
#[darling(attributes(parse), default)]
#[derive(Default, FromVariant)]
struct ParseVariantAttrs {
/// The comma-separated list of aliases this variant should be aliased to at
/// parse time.
aliases: Option<String>,
}

View file

@ -23,10 +23,19 @@ pub fn derive(input: DeriveInput) -> Tokens {
if variant_attrs.dimension { if variant_attrs.dimension {
assert_eq!(bindings.len(), 1); assert_eq!(bindings.len(), 1);
assert!(variant_attrs.function.is_none(), "That makes no sense"); assert!(
variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),
"That makes no sense"
);
} }
let mut expr = if !bindings.is_empty() { let mut expr = if let Some(keyword) = variant_attrs.keyword {
assert!(bindings.is_empty());
let keyword = keyword.to_string();
quote! {
::std::fmt::Write::write_str(dest, #keyword)
}
} else if !bindings.is_empty() {
let mut expr = quote! {}; let mut expr = quote! {};
if variant_attrs.iterable { if variant_attrs.iterable {
assert_eq!(bindings.len(), 1); assert_eq!(bindings.len(), 1);
@ -123,11 +132,13 @@ struct CssInputAttrs {
#[darling(attributes(css), default)] #[darling(attributes(css), default)]
#[derive(Default, FromVariant)] #[derive(Default, FromVariant)]
struct CssVariantAttrs { pub struct CssVariantAttrs {
function: Option<Function>, pub function: Option<Function>,
iterable: bool, pub iterable: bool,
comma: bool, pub comma: bool,
dimension: bool, pub dimension: bool,
pub keyword: Option<Ident>,
pub aliases: Option<String>,
} }
#[darling(attributes(css), default)] #[darling(attributes(css), default)]
@ -136,7 +147,7 @@ struct CssFieldAttrs {
ignore_bound: bool, ignore_bound: bool,
} }
struct Function { pub struct Function {
name: Option<Ident>, name: Option<Ident>,
} }

View file

@ -384,69 +384,12 @@ impl_to_css_for_predefined_type!(::cssparser::UnicodeRange);
#[macro_export] #[macro_export]
macro_rules! define_css_keyword_enum { macro_rules! define_css_keyword_enum {
($name: ident: values { $( $css: expr => $variant: ident),+, } (pub enum $name:ident { $($variant:ident = $css:expr,)+ }) => {
aliases { $( $alias: expr => $alias_variant: ident ),+, }) => { #[allow(missing_docs)]
__define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
[ $( $alias => $alias_variant ),+ ]); #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
};
($name: ident: values { $( $css: expr => $variant: ident),+, }
aliases { $( $alias: expr => $alias_variant: ident ),* }) => {
__define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]
[ $( $alias => $alias_variant ),* ]);
};
($name: ident: values { $( $css: expr => $variant: ident),+ }
aliases { $( $alias: expr => $alias_variant: ident ),+, }) => {
__define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]
[ $( $alias => $alias_variant ),+ ]);
};
($name: ident: values { $( $css: expr => $variant: ident),+ }
aliases { $( $alias: expr => $alias_variant: ident ),* }) => {
__define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ]
[ $( $alias => $alias_variant ),* ]);
};
($name: ident: $( $css: expr => $variant: ident ),+,) => {
__define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ] []);
};
($name: ident: $( $css: expr => $variant: ident ),+) => {
__define_css_keyword_enum__add_optional_traits!($name [ $( $css => $variant ),+ ] []);
};
}
#[cfg(feature = "servo")]
#[macro_export]
macro_rules! __define_css_keyword_enum__add_optional_traits {
($name: ident [ $( $css: expr => $variant: ident ),+ ]
[ $( $alias: expr => $alias_variant: ident),* ]) => {
__define_css_keyword_enum__actual! {
$name [ Deserialize, Serialize, MallocSizeOf ]
[ $( $css => $variant ),+ ]
[ $( $alias => $alias_variant ),* ]
}
};
}
#[cfg(not(feature = "servo"))]
#[macro_export]
macro_rules! __define_css_keyword_enum__add_optional_traits {
($name: ident [ $( $css: expr => $variant: ident ),+ ]
[ $( $alias: expr => $alias_variant: ident),* ]) => {
__define_css_keyword_enum__actual! {
$name [ MallocSizeOf ]
[ $( $css => $variant ),+ ]
[ $( $alias => $alias_variant ),* ]
}
};
}
#[macro_export]
macro_rules! __define_css_keyword_enum__actual {
($name: ident [ $( $derived_trait: ident),* ]
[ $( $css: expr => $variant: ident ),+ ]
[ $( $alias: expr => $alias_variant: ident ),* ]) => {
#[allow(non_camel_case_types, missing_docs)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq$(, $derived_trait )* )]
pub enum $name { pub enum $name {
$( $variant ),+ $($variant),+
} }
impl $name { impl $name {
@ -458,33 +401,40 @@ macro_rules! __define_css_keyword_enum__actual {
match *input.next()? { match *input.next()? {
Token::Ident(ref ident) => { Token::Ident(ref ident) => {
Self::from_ident(ident).map_err(|()| { Self::from_ident(ident).map_err(|()| {
location.new_unexpected_token_error(Token::Ident(ident.clone())) location.new_unexpected_token_error(
Token::Ident(ident.clone()),
)
}) })
} }
ref token => Err(location.new_unexpected_token_error(token.clone())) ref token => {
Err(location.new_unexpected_token_error(token.clone()))
}
} }
} }
/// Parse this property from an already-tokenized identifier. /// Parse this property from an already-tokenized identifier.
pub fn from_ident(ident: &str) -> Result<$name, ()> { pub fn from_ident(ident: &str) -> Result<$name, ()> {
match_ignore_ascii_case! { ident, match_ignore_ascii_case! { ident,
$( $css => Ok($name::$variant), )+ $($css => Ok($name::$variant),)+
$( $alias => Ok($name::$alias_variant), )*
_ => Err(()) _ => Err(())
} }
} }
} }
impl $crate::ToCss for $name { impl $crate::ToCss for $name {
fn to_css<W>(&self, dest: &mut $crate::CssWriter<W>) -> ::std::fmt::Result fn to_css<W>(
where W: ::std::fmt::Write &self,
dest: &mut $crate::CssWriter<W>,
) -> ::std::fmt::Result
where
W: ::std::fmt::Write,
{ {
match *self { match *self {
$( $name::$variant => ::std::fmt::Write::write_str(dest, $css) ),+ $( $name::$variant => ::std::fmt::Write::write_str(dest, $css) ),+
} }
} }
} }
} };
} }
/// Helper types for the handling of specified values. /// Helper types for the handling of specified values.

View file

@ -10,14 +10,20 @@ use euclid::TypedSize2D;
#[allow(unused_imports)] use std::ascii::AsciiExt; #[allow(unused_imports)] use std::ascii::AsciiExt;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
define_css_keyword_enum!(UserZoom: define_css_keyword_enum! {
"zoom" => Zoom, pub enum UserZoom {
"fixed" => Fixed); Zoom = "zoom",
Fixed = "fixed",
}
}
define_css_keyword_enum!(Orientation: define_css_keyword_enum! {
"auto" => Auto, pub enum Orientation {
"portrait" => Portrait, Auto = "auto",
"landscape" => Landscape); Portrait = "portrait",
Landscape = "landscape",
}
}
/// A set of viewport descriptors: /// A set of viewport descriptors:
/// ///

View file

@ -168,16 +168,16 @@ fn border_image_outset_should_return_length_on_length_zero() {
fn test_border_style() { fn test_border_style() {
use style::values::specified::BorderStyle; use style::values::specified::BorderStyle;
assert_roundtrip_with_context!(BorderStyle::parse, r#"none"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"none"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"hidden"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"hidden"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"solid"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"solid"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"double"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"double"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"dotted"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"dotted"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"dashed"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"dashed"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"groove"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"groove"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"ridge"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"ridge"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"inset"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"inset"#);
assert_roundtrip_with_context!(BorderStyle::parse, r#"outset"#); assert_roundtrip_with_context!(<BorderStyle as Parse>::parse, r#"outset"#);
} }
#[test] #[test]