From 76e31cf963cc0ef6ace0415664cc987c6977b07f Mon Sep 17 00:00:00 2001
From: Devon Govett
Date: Sun, 2 Jun 2024 21:17:43 -0700
Subject: [PATCH 001/139] Use macros to derive implementations of Parse and
ToCss for many enums
---
Cargo.lock | 1 +
Cargo.toml | 4 +-
derive/Cargo.toml | 1 +
derive/src/lib.rs | 295 ++-------------------------------
derive/src/parse.rs | 213 ++++++++++++++++++++++++
derive/src/to_css.rs | 156 +++++++++++++++++
derive/src/visit.rs | 292 ++++++++++++++++++++++++++++++++
src/macros.rs | 57 +------
src/properties/align.rs | 203 +++--------------------
src/properties/animation.rs | 182 ++++++--------------
src/properties/background.rs | 24 +--
src/properties/border.rs | 33 +---
src/properties/border_image.rs | 34 +---
src/properties/contain.rs | 6 +-
src/properties/custom.rs | 20 +--
src/properties/display.rs | 59 ++-----
src/properties/flex.rs | 16 +-
src/properties/font.rs | 182 +++-----------------
src/properties/grid.rs | 58 +------
src/properties/list.rs | 171 +++++++------------
src/properties/masking.rs | 77 +++------
src/properties/outline.rs | 25 +--
src/properties/position.rs | 25 +--
src/properties/size.rs | 4 +-
src/properties/svg.rs | 111 ++-----------
src/properties/text.rs | 161 +++++-------------
src/properties/transform.rs | 38 +----
src/properties/ui.rs | 97 +++++------
src/rules/keyframes.rs | 20 +--
src/rules/page.rs | 32 ++--
src/traits.rs | 37 +++++
src/values/calc.rs | 8 +-
src/values/color.rs | 1 +
src/values/easing.rs | 16 +-
src/values/gradient.rs | 42 +----
src/values/image.rs | 38 +----
src/values/length.rs | 62 +------
src/values/percentage.rs | 32 +---
src/values/shape.rs | 30 +---
39 files changed, 1100 insertions(+), 1763 deletions(-)
create mode 100644 derive/src/parse.rs
create mode 100644 derive/src/to_css.rs
create mode 100644 derive/src/visit.rs
diff --git a/Cargo.lock b/Cargo.lock
index a5d8b853..07d5f7e1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -804,6 +804,7 @@ dependencies = [
name = "lightningcss-derive"
version = "1.0.0-alpha.42"
dependencies = [
+ "convert_case",
"proc-macro2",
"quote",
"syn 1.0.109",
diff --git a/Cargo.toml b/Cargo.toml
index 7036f761..75bc397c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -43,7 +43,7 @@ jsonschema = ["schemars", "serde", "parcel_selectors/jsonschema"]
nodejs = ["dep:serde"]
serde = ["dep:serde", "smallvec/serde", "cssparser/serde", "parcel_selectors/serde", "into_owned"]
sourcemap = ["parcel_sourcemap"]
-visitor = ["lightningcss-derive"]
+visitor = []
into_owned = ["static-self", "static-self/smallvec", "parcel_selectors/into_owned"]
substitute_variables = ["visitor", "into_owned"]
@@ -69,7 +69,7 @@ browserslist-rs = { version = "0.15.0", optional = true }
rayon = { version = "1.5.1", optional = true }
dashmap = { version = "5.0.0", optional = true }
serde_json = { version = "1.0.78", optional = true }
-lightningcss-derive = { version = "=1.0.0-alpha.42", path = "./derive", optional = true }
+lightningcss-derive = { version = "=1.0.0-alpha.42", path = "./derive" }
schemars = { version = "0.8.19", features = ["smallvec"], optional = true }
static-self = { version = "0.1.0", path = "static-self", optional = true }
diff --git a/derive/Cargo.toml b/derive/Cargo.toml
index 1864748e..e1f82c70 100644
--- a/derive/Cargo.toml
+++ b/derive/Cargo.toml
@@ -14,3 +14,4 @@ proc-macro = true
syn = { version = "1.0", features = ["extra-traits"] }
quote = "1.0"
proc-macro2 = "1.0"
+convert_case = "0.6.0"
diff --git a/derive/src/lib.rs b/derive/src/lib.rs
index 00b77a26..12241491 100644
--- a/derive/src/lib.rs
+++ b/derive/src/lib.rs
@@ -1,293 +1,20 @@
-use std::collections::HashSet;
+use proc_macro::TokenStream;
-use proc_macro::{self, TokenStream};
-use proc_macro2::{Span, TokenStream as TokenStream2};
-use quote::quote;
-use syn::{
- parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields,
- GenericParam, Generics, Ident, Member, Token, Type, Visibility,
-};
+mod parse;
+mod to_css;
+mod visit;
#[proc_macro_derive(Visit, attributes(visit, skip_visit, skip_type, visit_types))]
pub fn derive_visit_children(input: TokenStream) -> TokenStream {
- let DeriveInput {
- ident,
- data,
- generics,
- attrs,
- ..
- } = parse_macro_input!(input);
-
- let options: Vec = attrs
- .iter()
- .filter_map(|attr| {
- if attr.path.is_ident("visit") {
- let opts: VisitOptions = attr.parse_args().unwrap();
- Some(opts)
- } else {
- None
- }
- })
- .collect();
-
- let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) {
- let types: VisitTypes = attr.parse_args().unwrap();
- let types = types.types;
- Some(quote! { crate::visit_types!(#(#types)|*) })
- } else {
- None
- };
-
- if options.is_empty() {
- derive(&ident, &data, &generics, None, visit_types)
- } else {
- options
- .into_iter()
- .map(|options| derive(&ident, &data, &generics, Some(options), visit_types.clone()))
- .collect()
- }
-}
-
-fn derive(
- ident: &Ident,
- data: &Data,
- generics: &Generics,
- options: Option,
- visit_types: Option,
-) -> TokenStream {
- let mut impl_generics = generics.clone();
- let mut type_defs = quote! {};
- let generics = if let Some(VisitOptions {
- generic: Some(generic), ..
- }) = &options
- {
- let mappings = generics
- .type_params()
- .zip(generic.type_params())
- .map(|(a, b)| quote! { type #a = #b; });
- type_defs = quote! { #(#mappings)* };
- impl_generics.params.clear();
- generic
- } else {
- &generics
- };
-
- if impl_generics.lifetimes().next().is_none() {
- impl_generics.params.insert(0, parse_quote! { 'i })
- }
-
- let lifetime = impl_generics.lifetimes().next().unwrap().clone();
- let t = impl_generics.type_params().find(|g| &g.ident.to_string() == "R");
- let v = quote! { __V };
- let t = if let Some(t) = t {
- GenericParam::Type(t.ident.clone().into())
- } else {
- let t: GenericParam = parse_quote! { __T };
- impl_generics
- .params
- .push(parse_quote! { #t: crate::visitor::Visit<#lifetime, __T, #v> });
- t
- };
-
- impl_generics
- .params
- .push(parse_quote! { #v: ?Sized + crate::visitor::Visitor<#lifetime, #t> });
-
- for ty in generics.type_params() {
- let name = &ty.ident;
- impl_generics.make_where_clause().predicates.push(parse_quote! {
- #name: Visit<#lifetime, #t, #v>
- })
- }
-
- let mut seen_types = HashSet::new();
- let mut child_types = Vec::new();
- let mut visit = Vec::new();
- match data {
- Data::Struct(s) => {
- for (
- index,
- Field {
- vis, ty, ident, attrs, ..
- },
- ) in s.fields.iter().enumerate()
- {
- if attrs.iter().any(|attr| attr.path.is_ident("skip_visit")) {
- continue;
- }
-
- if matches!(ty, Type::Reference(_)) || !matches!(vis, Visibility::Public(..)) {
- continue;
- }
-
- if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) {
- seen_types.insert(ty.clone());
- child_types.push(quote! {
- <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
- });
- }
-
- let name = ident
- .as_ref()
- .map_or_else(|| Member::Unnamed(index.into()), |ident| Member::Named(ident.clone()));
- visit.push(quote! { self.#name.visit(visitor)?; })
- }
- }
- Data::Enum(DataEnum { variants, .. }) => {
- let variants = variants
- .iter()
- .map(|variant| {
- let name = &variant.ident;
- let mut field_names = Vec::new();
- let mut visit_fields = Vec::new();
- for (index, Field { ty, ident, attrs, .. }) in variant.fields.iter().enumerate() {
- let name = ident.as_ref().map_or_else(
- || Ident::new(&format!("_{}", index), Span::call_site()),
- |ident| ident.clone(),
- );
- field_names.push(name.clone());
-
- if matches!(ty, Type::Reference(_)) {
- continue;
- }
-
- if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) && !skip_type(&variant.attrs)
- {
- seen_types.insert(ty.clone());
- child_types.push(quote! {
- <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
- });
- }
-
- visit_fields.push(quote! { #name.visit(visitor)?; })
- }
-
- match variant.fields {
- Fields::Unnamed(_) => {
- quote! {
- Self::#name(#(#field_names),*) => {
- #(#visit_fields)*
- }
- }
- }
- Fields::Named(_) => {
- quote! {
- Self::#name { #(#field_names),* } => {
- #(#visit_fields)*
- }
- }
- }
- Fields::Unit => quote! {},
- }
- })
- .collect::();
-
- visit.push(quote! {
- match self {
- #variants
- _ => {}
- }
- })
- }
- _ => {}
- }
-
- if visit_types.is_none() && child_types.is_empty() {
- child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() });
- }
-
- let (_, ty_generics, _) = generics.split_for_impl();
- let (impl_generics, _, where_clause) = impl_generics.split_for_impl();
-
- let self_visit = if let Some(VisitOptions {
- visit: Some(visit),
- kind: Some(kind),
- ..
- }) = &options
- {
- child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() });
-
- quote! {
- fn visit(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
- if visitor.visit_types().contains(crate::visitor::VisitTypes::#kind) {
- visitor.#visit(self)
- } else {
- self.visit_children(visitor)
- }
- }
- }
- } else {
- quote! {}
- };
-
- let child_types = visit_types.unwrap_or_else(|| {
- quote! {
- {
- #type_defs
- crate::visitor::VisitTypes::from_bits_retain(#(#child_types)|*)
- }
- }
- });
-
- let output = quote! {
- impl #impl_generics Visit<#lifetime, #t, #v> for #ident #ty_generics #where_clause {
- const CHILD_TYPES: crate::visitor::VisitTypes = #child_types;
-
- #self_visit
-
- fn visit_children(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
- if !<#lifetime, #t, #v>>::CHILD_TYPES.intersects(visitor.visit_types()) {
- return Ok(())
- }
-
- #(#visit)*
-
- Ok(())
- }
- }
- };
-
- output.into()
-}
-
-fn skip_type(attrs: &Vec) -> bool {
- attrs.iter().any(|attr| attr.path.is_ident("skip_type"))
-}
-
-struct VisitOptions {
- visit: Option,
- kind: Option,
- generic: Option,
-}
-
-impl Parse for VisitOptions {
- fn parse(input: syn::parse::ParseStream) -> syn::Result {
- let (visit, kind, comma) = if input.peek(Ident) {
- let visit: Ident = input.parse()?;
- let _: Token![,] = input.parse()?;
- let kind: Ident = input.parse()?;
- let comma: Result = input.parse();
- (Some(visit), Some(kind), comma.is_ok())
- } else {
- (None, None, true)
- };
- let generic: Option = if comma { Some(input.parse()?) } else { None };
- Ok(Self { visit, kind, generic })
- }
+ visit::derive_visit_children(input)
}
-struct VisitTypes {
- types: Vec,
+#[proc_macro_derive(Parse, attributes(css))]
+pub fn derive_parse(input: TokenStream) -> TokenStream {
+ parse::derive_parse(input)
}
-impl Parse for VisitTypes {
- fn parse(input: syn::parse::ParseStream) -> syn::Result {
- let first: Ident = input.parse()?;
- let mut types = vec![first];
- while input.parse::().is_ok() {
- let id: Ident = input.parse()?;
- types.push(id);
- }
- Ok(Self { types })
- }
+#[proc_macro_derive(ToCss, attributes(css))]
+pub fn derive_to_css(input: TokenStream) -> TokenStream {
+ to_css::derive_to_css(input)
}
diff --git a/derive/src/parse.rs b/derive/src/parse.rs
new file mode 100644
index 00000000..995b344e
--- /dev/null
+++ b/derive/src/parse.rs
@@ -0,0 +1,213 @@
+use convert_case::Casing;
+use proc_macro::{self, TokenStream};
+use proc_macro2::{Literal, Span, TokenStream as TokenStream2};
+use quote::quote;
+use syn::{
+ parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Fields, Ident, Token,
+};
+
+pub fn derive_parse(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ data,
+ mut generics,
+ attrs,
+ ..
+ } = parse_macro_input!(input);
+ let opts = CssOptions::parse_attributes(&attrs).unwrap();
+ let cloned_generics = generics.clone();
+ let (_, ty_generics, _) = cloned_generics.split_for_impl();
+
+ if generics.lifetimes().next().is_none() {
+ generics.params.insert(0, parse_quote! { 'i })
+ }
+
+ let lifetime = generics.lifetimes().next().unwrap().clone();
+ let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+ let imp = match &data {
+ Data::Enum(data) => derive_enum(&data, &ident, &opts),
+ _ => todo!(),
+ };
+
+ let output = quote! {
+ impl #impl_generics Parse<#lifetime> for #ident #ty_generics #where_clause {
+ fn parse<'t>(input: &mut Parser<#lifetime, 't>) -> Result<#lifetime, ParserError<#lifetime>>> {
+ #imp
+ }
+ }
+ };
+
+ output.into()
+}
+
+fn derive_enum(data: &DataEnum, ident: &Ident, opts: &CssOptions) -> TokenStream2 {
+ let mut idents = Vec::new();
+ let mut non_idents = Vec::new();
+ for (index, variant) in data.variants.iter().enumerate() {
+ let name = &variant.ident;
+ let fields = variant
+ .fields
+ .iter()
+ .enumerate()
+ .map(|(index, field)| {
+ field.ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ )
+ })
+ .collect::<_>>();
+
+ let mut expr = match &variant.fields {
+ Fields::Unit => {
+ idents.push((
+ Literal::string(&variant.ident.to_string().to_case(opts.case)),
+ name.clone(),
+ ));
+ continue;
+ }
+ Fields::Named(_) => {
+ quote! {
+ return Ok(#ident::#name { #(#fields),* })
+ }
+ }
+ Fields::Unnamed(_) => {
+ quote! {
+ return Ok(#ident::#name(#(#fields),*))
+ }
+ }
+ };
+
+ // Group multiple ident branches together.
+ if !idents.is_empty() {
+ if idents.len() == 1 {
+ let (s, name) = idents.remove(0);
+ non_idents.push(quote! {
+ if input.try_parse(|input| input.expect_ident_matching(#s)).is_ok() {
+ return Ok(#ident::#name)
+ }
+ });
+ } else {
+ let matches = idents
+ .iter()
+ .map(|(s, name)| {
+ quote! {
+ #s => return Ok(#ident::#name),
+ }
+ })
+ .collect::<_>>();
+ non_idents.push(quote! {
+ {
+ let state = input.state();
+ if let Ok(ident) = input.try_parse(|input| input.expect_ident_cloned()) {
+ cssparser::match_ignore_ascii_case! { &*ident,
+ #(#matches)*
+ _ => {}
+ }
+ input.reset(&state);
+ }
+ }
+ });
+ idents.clear();
+ }
+ }
+
+ let is_last = index == data.variants.len() - 1;
+
+ for (index, field) in variant.fields.iter().enumerate().rev() {
+ let ty = &field.ty;
+ let field_name = field.ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ );
+ if is_last {
+ expr = quote! {
+ let #field_name = <#ty>::parse(input)?;
+ #expr
+ };
+ } else {
+ expr = quote! {
+ if let Ok(#field_name) = input.try_parse(<#ty>::parse) {
+ #expr
+ }
+ };
+ }
+ }
+
+ non_idents.push(expr);
+ }
+
+ let idents = if idents.is_empty() {
+ quote! {}
+ } else if idents.len() == 1 {
+ let (s, name) = idents.remove(0);
+ quote! {
+ input.expect_ident_matching(#s)?;
+ Ok(#ident::#name)
+ }
+ } else {
+ let idents = idents
+ .into_iter()
+ .map(|(s, name)| {
+ quote! {
+ #s => Ok(#ident::#name),
+ }
+ })
+ .collect::<_>>();
+ quote! {
+ let location = input.current_source_location();
+ let ident = input.expect_ident()?;
+ cssparser::match_ignore_ascii_case! { &*ident,
+ #(#idents)*
+ _ => Err(location.new_unexpected_token_error(
+ cssparser::Token::Ident(ident.clone())
+ ))
+ }
+ }
+ };
+
+ let output = quote! {
+ #(#non_idents)*
+ #idents
+ };
+
+ output.into()
+}
+
+pub struct CssOptions {
+ pub case: convert_case::Case,
+}
+
+impl CssOptions {
+ pub fn parse_attributes(attrs: &Vec) -> syn::Result {
+ for attr in attrs {
+ if attr.path.is_ident("css") {
+ let opts: CssOptions = attr.parse_args()?;
+ return Ok(opts);
+ }
+ }
+
+ Ok(CssOptions {
+ case: convert_case::Case::Kebab,
+ })
+ }
+}
+
+impl Parse for CssOptions {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ let mut case = convert_case::Case::Kebab;
+ while !input.is_empty() {
+ let k: Ident = input.parse()?;
+ let _: Token![=] = input.parse()?;
+ let v: Ident = input.parse()?;
+
+ if k == "case" {
+ if v == "lower" {
+ case = convert_case::Case::Flat;
+ }
+ }
+ }
+
+ Ok(Self { case })
+ }
+}
diff --git a/derive/src/to_css.rs b/derive/src/to_css.rs
new file mode 100644
index 00000000..739a16d4
--- /dev/null
+++ b/derive/src/to_css.rs
@@ -0,0 +1,156 @@
+use convert_case::Casing;
+use proc_macro::{self, TokenStream};
+use proc_macro2::{Literal, Span, TokenStream as TokenStream2};
+use quote::quote;
+use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, Ident, Type};
+
+use crate::parse::CssOptions;
+
+pub fn derive_to_css(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ data,
+ generics,
+ attrs,
+ ..
+ } = parse_macro_input!(input);
+
+ let opts = CssOptions::parse_attributes(&attrs).unwrap();
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let imp = match &data {
+ Data::Enum(data) => derive_enum(&data, &opts),
+ _ => todo!(),
+ };
+
+ let output = quote! {
+ impl #impl_generics ToCss for #ident #ty_generics #where_clause {
+ fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ #imp
+ }
+ }
+ };
+
+ output.into()
+}
+
+fn derive_enum(data: &DataEnum, opts: &CssOptions) -> TokenStream2 {
+ let variants = data
+ .variants
+ .iter()
+ .map(|variant| {
+ let name = &variant.ident;
+ let fields = variant
+ .fields
+ .iter()
+ .enumerate()
+ .map(|(index, field)| {
+ field.ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ )
+ })
+ .collect::<_>>();
+
+ #[derive(PartialEq)]
+ enum NeedsSpace {
+ Yes,
+ No,
+ Maybe,
+ }
+
+ let mut needs_space = NeedsSpace::No;
+ let mut fields_iter = variant.fields.iter().zip(fields.iter()).peekable();
+ let mut writes = Vec::new();
+ let mut has_needs_space = false;
+ while let Some((field, name)) = fields_iter.next() {
+ writes.push(if fields.len() > 1 {
+ let space = match needs_space {
+ NeedsSpace::Yes => quote! { dest.write_char(' ')?; },
+ NeedsSpace::No => quote! {},
+ NeedsSpace::Maybe => {
+ has_needs_space = true;
+ quote! {
+ if needs_space {
+ dest.write_char(' ')?;
+ }
+ }
+ }
+ };
+
+ if is_option(&field.ty) {
+ needs_space = NeedsSpace::Maybe;
+ let after_space = if matches!(fields_iter.peek(), Some((field, _)) if !is_option(&field.ty)) {
+ // If the next field is non-optional, just insert the space here.
+ needs_space = NeedsSpace::No;
+ quote! { dest.write_char(' ')?; }
+ } else {
+ quote! {}
+ };
+ quote! {
+ if let Some(v) = #name {
+ #space
+ v.to_css(dest)?;
+ #after_space
+ }
+ }
+ } else {
+ needs_space = NeedsSpace::Yes;
+ quote! {
+ #space
+ #name.to_css(dest)?;
+ }
+ }
+ } else {
+ quote! { #name.to_css(dest) }
+ });
+ }
+
+ if writes.len() > 1 {
+ writes.push(quote! { Ok(()) });
+ }
+
+ if has_needs_space {
+ writes.insert(0, quote! { let mut needs_space = false });
+ }
+
+ match variant.fields {
+ Fields::Unit => {
+ let s = Literal::string(&variant.ident.to_string().to_case(opts.case));
+ quote! {
+ Self::#name => dest.write_str(#s)
+ }
+ }
+ Fields::Named(_) => {
+ quote! {
+ Self::#name { #(#fields),* } => {
+ #(#writes)*
+ }
+ }
+ }
+ Fields::Unnamed(_) => {
+ quote! {
+ Self::#name(#(#fields),*) => {
+ #(#writes)*
+ }
+ }
+ }
+ }
+ })
+ .collect::<_>>();
+
+ let output = quote! {
+ match self {
+ #(#variants),*
+ }
+ };
+
+ output.into()
+}
+
+fn is_option(ty: &Type) -> bool {
+ matches!(&ty, Type::Path(p) if p.qself.is_none() && p.path.segments.iter().next().unwrap().ident == "Option")
+}
diff --git a/derive/src/visit.rs b/derive/src/visit.rs
new file mode 100644
index 00000000..02093364
--- /dev/null
+++ b/derive/src/visit.rs
@@ -0,0 +1,292 @@
+use std::collections::HashSet;
+
+use proc_macro::{self, TokenStream};
+use proc_macro2::{Span, TokenStream as TokenStream2};
+use quote::quote;
+use syn::{
+ parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields,
+ GenericParam, Generics, Ident, Member, Token, Type, Visibility,
+};
+
+pub fn derive_visit_children(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ data,
+ generics,
+ attrs,
+ ..
+ } = parse_macro_input!(input);
+
+ let options: Vec = attrs
+ .iter()
+ .filter_map(|attr| {
+ if attr.path.is_ident("visit") {
+ let opts: VisitOptions = attr.parse_args().unwrap();
+ Some(opts)
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) {
+ let types: VisitTypes = attr.parse_args().unwrap();
+ let types = types.types;
+ Some(quote! { crate::visit_types!(#(#types)|*) })
+ } else {
+ None
+ };
+
+ if options.is_empty() {
+ derive(&ident, &data, &generics, None, visit_types)
+ } else {
+ options
+ .into_iter()
+ .map(|options| derive(&ident, &data, &generics, Some(options), visit_types.clone()))
+ .collect()
+ }
+}
+
+fn derive(
+ ident: &Ident,
+ data: &Data,
+ generics: &Generics,
+ options: Option,
+ visit_types: Option,
+) -> TokenStream {
+ let mut impl_generics = generics.clone();
+ let mut type_defs = quote! {};
+ let generics = if let Some(VisitOptions {
+ generic: Some(generic), ..
+ }) = &options
+ {
+ let mappings = generics
+ .type_params()
+ .zip(generic.type_params())
+ .map(|(a, b)| quote! { type #a = #b; });
+ type_defs = quote! { #(#mappings)* };
+ impl_generics.params.clear();
+ generic
+ } else {
+ &generics
+ };
+
+ if impl_generics.lifetimes().next().is_none() {
+ impl_generics.params.insert(0, parse_quote! { 'i })
+ }
+
+ let lifetime = impl_generics.lifetimes().next().unwrap().clone();
+ let t = impl_generics.type_params().find(|g| &g.ident.to_string() == "R");
+ let v = quote! { __V };
+ let t = if let Some(t) = t {
+ GenericParam::Type(t.ident.clone().into())
+ } else {
+ let t: GenericParam = parse_quote! { __T };
+ impl_generics
+ .params
+ .push(parse_quote! { #t: crate::visitor::Visit<#lifetime, __T, #v> });
+ t
+ };
+
+ impl_generics
+ .params
+ .push(parse_quote! { #v: ?Sized + crate::visitor::Visitor<#lifetime, #t> });
+
+ for ty in generics.type_params() {
+ let name = &ty.ident;
+ impl_generics.make_where_clause().predicates.push(parse_quote! {
+ #name: Visit<#lifetime, #t, #v>
+ })
+ }
+
+ let mut seen_types = HashSet::new();
+ let mut child_types = Vec::new();
+ let mut visit = Vec::new();
+ match data {
+ Data::Struct(s) => {
+ for (
+ index,
+ Field {
+ vis, ty, ident, attrs, ..
+ },
+ ) in s.fields.iter().enumerate()
+ {
+ if attrs.iter().any(|attr| attr.path.is_ident("skip_visit")) {
+ continue;
+ }
+
+ if matches!(ty, Type::Reference(_)) || !matches!(vis, Visibility::Public(..)) {
+ continue;
+ }
+
+ if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) {
+ seen_types.insert(ty.clone());
+ child_types.push(quote! {
+ <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
+ });
+ }
+
+ let name = ident
+ .as_ref()
+ .map_or_else(|| Member::Unnamed(index.into()), |ident| Member::Named(ident.clone()));
+ visit.push(quote! { self.#name.visit(visitor)?; })
+ }
+ }
+ Data::Enum(DataEnum { variants, .. }) => {
+ let variants = variants
+ .iter()
+ .map(|variant| {
+ let name = &variant.ident;
+ let mut field_names = Vec::new();
+ let mut visit_fields = Vec::new();
+ for (index, Field { ty, ident, attrs, .. }) in variant.fields.iter().enumerate() {
+ let name = ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ );
+ field_names.push(name.clone());
+
+ if matches!(ty, Type::Reference(_)) {
+ continue;
+ }
+
+ if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) && !skip_type(&variant.attrs)
+ {
+ seen_types.insert(ty.clone());
+ child_types.push(quote! {
+ <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
+ });
+ }
+
+ visit_fields.push(quote! { #name.visit(visitor)?; })
+ }
+
+ match variant.fields {
+ Fields::Unnamed(_) => {
+ quote! {
+ Self::#name(#(#field_names),*) => {
+ #(#visit_fields)*
+ }
+ }
+ }
+ Fields::Named(_) => {
+ quote! {
+ Self::#name { #(#field_names),* } => {
+ #(#visit_fields)*
+ }
+ }
+ }
+ Fields::Unit => quote! {},
+ }
+ })
+ .collect::();
+
+ visit.push(quote! {
+ match self {
+ #variants
+ _ => {}
+ }
+ })
+ }
+ _ => {}
+ }
+
+ if visit_types.is_none() && child_types.is_empty() {
+ child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() });
+ }
+
+ let (_, ty_generics, _) = generics.split_for_impl();
+ let (impl_generics, _, where_clause) = impl_generics.split_for_impl();
+
+ let self_visit = if let Some(VisitOptions {
+ visit: Some(visit),
+ kind: Some(kind),
+ ..
+ }) = &options
+ {
+ child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() });
+
+ quote! {
+ fn visit(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
+ if visitor.visit_types().contains(crate::visitor::VisitTypes::#kind) {
+ visitor.#visit(self)
+ } else {
+ self.visit_children(visitor)
+ }
+ }
+ }
+ } else {
+ quote! {}
+ };
+
+ let child_types = visit_types.unwrap_or_else(|| {
+ quote! {
+ {
+ #type_defs
+ crate::visitor::VisitTypes::from_bits_retain(#(#child_types)|*)
+ }
+ }
+ });
+
+ let output = quote! {
+ impl #impl_generics Visit<#lifetime, #t, #v> for #ident #ty_generics #where_clause {
+ const CHILD_TYPES: crate::visitor::VisitTypes = #child_types;
+
+ #self_visit
+
+ fn visit_children(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
+ if !<#lifetime, #t, #v>>::CHILD_TYPES.intersects(visitor.visit_types()) {
+ return Ok(())
+ }
+
+ #(#visit)*
+
+ Ok(())
+ }
+ }
+ };
+
+ output.into()
+}
+
+fn skip_type(attrs: &Vec) -> bool {
+ attrs.iter().any(|attr| attr.path.is_ident("skip_type"))
+}
+
+struct VisitOptions {
+ visit: Option,
+ kind: Option,
+ generic: Option,
+}
+
+impl Parse for VisitOptions {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ let (visit, kind, comma) = if input.peek(Ident) {
+ let visit: Ident = input.parse()?;
+ let _: Token![,] = input.parse()?;
+ let kind: Ident = input.parse()?;
+ let comma: Result = input.parse();
+ (Some(visit), Some(kind), comma.is_ok())
+ } else {
+ (None, None, true)
+ };
+ let generic: Option = if comma { Some(input.parse()?) } else { None };
+ Ok(Self { visit, kind, generic })
+ }
+}
+
+struct VisitTypes {
+ types: Vec,
+}
+
+impl Parse for VisitTypes {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ let first: Ident = input.parse()?;
+ let mut types = vec![first];
+ while input.parse::().is_ok() {
+ let id: Ident = input.parse()?;
+ types.push(id);
+ }
+ Ok(Self { types })
+ }
+}
diff --git a/src/macros.rs b/src/macros.rs
index e102676f..7e573816 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -8,12 +8,12 @@ macro_rules! enum_property {
)+
}
) => {
- $(#[$outer])*
- #[derive(Debug, Clone, Copy, PartialEq)]
+ #[derive(Debug, Clone, Copy, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "lowercase"))]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
+ $(#[$outer])*
$vis enum $name {
$(
$(#[$meta])*
@@ -21,50 +21,17 @@ macro_rules! enum_property {
)+
}
- impl<'i> Parse<'i> for $name {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match &ident[..] {
- $(
- s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x),
- )+
- _ => Err(location.new_unexpected_token_error(
- cssparser::Token::Ident(ident.clone())
- ))
- }
- }
-
- fn parse_string(input: &'i str) -> Result<'i, ParserError<'i>>> {
- match input {
- $(
- s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x),
- )+
- _ => return Err(ParseError {
- kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(cssparser::Token::Ident(input.into()))),
- location: cssparser::SourceLocation { line: 0, column: 1 }
- })
- }
- }
- }
-
impl $name {
/// Returns a string representation of the value.
pub fn as_str(&self) -> &str {
use $name::*;
match self {
$(
- $x => const_str::convert_ascii_case!(lower, stringify!($x)),
+ $x => const_str::convert_ascii_case!(kebab, stringify!($x)),
)+
}
}
}
-
- impl ToCss for $name {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError> where W: std::fmt::Write {
- dest.write_str(self.as_str())
- }
- }
};
(
$(#[$outer:meta])*
@@ -92,25 +59,13 @@ macro_rules! enum_property {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
let location = input.current_source_location();
let ident = input.expect_ident()?;
- match &ident[..] {
+ cssparser::match_ignore_ascii_case! { &*ident,
$(
- s if s.eq_ignore_ascii_case($str) => Ok($name::$id),
+ $str => Ok($name::$id),
)+
_ => Err(location.new_unexpected_token_error(
cssparser::Token::Ident(ident.clone())
- ))
- }
- }
-
- fn parse_string(input: &'i str) -> Result<'i, ParserError<'i>>> {
- match input {
- $(
- s if s.eq_ignore_ascii_case($str) => Ok($name::$id),
- )+
- _ => return Err(ParseError {
- kind: ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(cssparser::Token::Ident(input.into()))),
- location: cssparser::SourceLocation { line: 0, column: 1 }
- })
+ )),
}
}
}
diff --git a/src/properties/align.rs b/src/properties/align.rs
index 12917c18..819e9ac4 100644
--- a/src/properties/align.rs
+++ b/src/properties/align.rs
@@ -73,13 +73,13 @@ enum_property! {
/// A [``](https://www.w3.org/TR/css-align-3/#typedef-content-distribution) value.
pub enum ContentDistribution {
/// Items are spaced evenly, with the first and last items against the edge of the container.
- "space-between": SpaceBetween,
+ SpaceBetween,
/// Items are spaced evenly, with half-size spaces at the start and end.
- "space-around": SpaceAround,
+ SpaceAround,
/// Items are spaced evenly, with full-size spaces at the start and end.
- "space-evenly": SpaceEvenly,
+ SpaceEvenly,
/// Items are stretched evenly to fill free space.
- "stretch": Stretch,
+ Stretch,
}
}
@@ -99,20 +99,20 @@ enum_property! {
/// A [``](https://www.w3.org/TR/css-align-3/#typedef-content-position) value.
pub enum ContentPosition {
/// Content is centered within the container.
- "center": Center,
+ Center,
/// Content is aligned to the start of the container.
- "start": Start,
+ Start,
/// Content is aligned to the end of the container.
- "end": End,
+ End,
/// Same as `start` when within a flexbox container.
- "flex-start": FlexStart,
+ FlexStart,
/// Same as `end` when within a flexbox container.
- "flex-end": FlexEnd,
+ FlexEnd,
}
}
/// A value for the [align-content](https://www.w3.org/TR/css-align-3/#propdef-align-content) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -132,54 +132,13 @@ pub enum AlignContent {
ContentDistribution(ContentDistribution),
/// A content position keyword.
ContentPosition {
- /// A content position keyword.
- value: ContentPosition,
/// An overflow alignment mode.
overflow: Option,
+ /// A content position keyword.
+ value: ContentPosition,
},
}
-impl<'i> Parse<'i> for AlignContent {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- return Ok(AlignContent::Normal);
- }
-
- if let Ok(val) = input.try_parse(BaselinePosition::parse) {
- return Ok(AlignContent::BaselinePosition(val));
- }
-
- if let Ok(val) = input.try_parse(ContentDistribution::parse) {
- return Ok(AlignContent::ContentDistribution(val));
- }
-
- let overflow = input.try_parse(OverflowPosition::parse).ok();
- let value = ContentPosition::parse(input)?;
- Ok(AlignContent::ContentPosition { overflow, value })
- }
-}
-
-impl ToCss for AlignContent {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- AlignContent::Normal => dest.write_str("normal"),
- AlignContent::BaselinePosition(val) => val.to_css(dest),
- AlignContent::ContentDistribution(val) => val.to_css(dest),
- AlignContent::ContentPosition { overflow, value } => {
- if let Some(overflow) = overflow {
- overflow.to_css(dest)?;
- dest.write_str(" ")?;
- }
-
- value.to_css(dest)
- }
- }
- }
-}
-
/// A value for the [justify-content](https://www.w3.org/TR/css-align-3/#propdef-justify-content) property.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
@@ -349,24 +308,24 @@ enum_property! {
/// A [``](https://www.w3.org/TR/css-align-3/#typedef-self-position) value.
pub enum SelfPosition {
/// Item is centered within the container.
- "center": Center,
+ Center,
/// Item is aligned to the start of the container.
- "start": Start,
+ Start,
/// Item is aligned to the end of the container.
- "end": End,
+ End,
/// Item is aligned to the edge of the container corresponding to the start side of the item.
- "self-start": SelfStart,
+ SelfStart,
/// Item is aligned to the edge of the container corresponding to the end side of the item.
- "self-end": SelfEnd,
+ SelfEnd,
/// Item is aligned to the start of the container, within flexbox layouts.
- "flex-start": FlexStart,
+ FlexStart,
/// Item is aligned to the end of the container, within flexbox layouts.
- "flex-end": FlexEnd,
+ FlexEnd,
}
}
/// A value for the [align-self](https://www.w3.org/TR/css-align-3/#align-self-property) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -387,59 +346,13 @@ pub enum AlignSelf {
BaselinePosition(BaselinePosition),
/// A self position keyword.
SelfPosition {
- /// A self position keyword.
- value: SelfPosition,
/// An overflow alignment mode.
overflow: Option,
+ /// A self position keyword.
+ value: SelfPosition,
},
}
-impl<'i> Parse<'i> for AlignSelf {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() {
- return Ok(AlignSelf::Auto);
- }
-
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- return Ok(AlignSelf::Normal);
- }
-
- if input.try_parse(|input| input.expect_ident_matching("stretch")).is_ok() {
- return Ok(AlignSelf::Stretch);
- }
-
- if let Ok(val) = input.try_parse(BaselinePosition::parse) {
- return Ok(AlignSelf::BaselinePosition(val));
- }
-
- let overflow = input.try_parse(OverflowPosition::parse).ok();
- let value = SelfPosition::parse(input)?;
- Ok(AlignSelf::SelfPosition { overflow, value })
- }
-}
-
-impl ToCss for AlignSelf {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- AlignSelf::Auto => dest.write_str("auto"),
- AlignSelf::Normal => dest.write_str("normal"),
- AlignSelf::Stretch => dest.write_str("stretch"),
- AlignSelf::BaselinePosition(val) => val.to_css(dest),
- AlignSelf::SelfPosition { overflow, value } => {
- if let Some(overflow) = overflow {
- overflow.to_css(dest)?;
- dest.write_str(" ")?;
- }
-
- value.to_css(dest)
- }
- }
- }
-}
-
/// A value for the [justify-self](https://www.w3.org/TR/css-align-3/#justify-self-property) property.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
@@ -615,7 +528,7 @@ impl ToCss for PlaceSelf {
}
/// A value for the [align-items](https://www.w3.org/TR/css-align-3/#align-items-property) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -634,54 +547,13 @@ pub enum AlignItems {
BaselinePosition(BaselinePosition),
/// A self position keyword.
SelfPosition {
- /// A self position keyword.
- value: SelfPosition,
/// An overflow alignment mode.
overflow: Option,
+ /// A self position keyword.
+ value: SelfPosition,
},
}
-impl<'i> Parse<'i> for AlignItems {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- return Ok(AlignItems::Normal);
- }
-
- if input.try_parse(|input| input.expect_ident_matching("stretch")).is_ok() {
- return Ok(AlignItems::Stretch);
- }
-
- if let Ok(val) = input.try_parse(BaselinePosition::parse) {
- return Ok(AlignItems::BaselinePosition(val));
- }
-
- let overflow = input.try_parse(OverflowPosition::parse).ok();
- let value = SelfPosition::parse(input)?;
- Ok(AlignItems::SelfPosition { overflow, value })
- }
-}
-
-impl ToCss for AlignItems {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- AlignItems::Normal => dest.write_str("normal"),
- AlignItems::Stretch => dest.write_str("stretch"),
- AlignItems::BaselinePosition(val) => val.to_css(dest),
- AlignItems::SelfPosition { overflow, value } => {
- if let Some(overflow) = overflow {
- overflow.to_css(dest)?;
- dest.write_str(" ")?;
- }
-
- value.to_css(dest)
- }
- }
- }
-}
-
/// A legacy justification keyword, as used in the `justify-items` property.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
@@ -925,7 +797,7 @@ impl ToCss for PlaceItems {
/// A [gap](https://www.w3.org/TR/css-align-3/#column-row-gap) value, as used in the
/// `column-gap` and `row-gap` properties.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -941,29 +813,6 @@ pub enum GapValue {
LengthPercentage(LengthPercentage),
}
-impl<'i> Parse<'i> for GapValue {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- return Ok(GapValue::Normal);
- }
-
- let val = LengthPercentage::parse(input)?;
- Ok(GapValue::LengthPercentage(val))
- }
-}
-
-impl ToCss for GapValue {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- GapValue::Normal => dest.write_str("normal"),
- GapValue::LengthPercentage(lp) => lp.to_css(dest),
- }
- }
-}
-
define_shorthand! {
/// A value for the [gap](https://www.w3.org/TR/css-align-3/#gap-shorthand) shorthand property.
pub struct Gap {
diff --git a/src/properties/animation.rs b/src/properties/animation.rs
index 03b76166..de83b9fa 100644
--- a/src/properties/animation.rs
+++ b/src/properties/animation.rs
@@ -13,7 +13,7 @@ use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero};
use crate::values::ident::DashedIdent;
use crate::values::number::CSSNumber;
use crate::values::size::Size2D;
-use crate::values::string::CowArcStr;
+use crate::values::string::CSSString;
use crate::values::{easing::EasingFunction, ident::CustomIdent, time::Time};
#[cfg(feature = "visitor")]
use crate::visitor::Visit;
@@ -24,7 +24,7 @@ use smallvec::SmallVec;
use super::LengthPercentageOrAuto;
/// A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
@@ -41,22 +41,7 @@ pub enum AnimationName<'i> {
Ident(CustomIdent<'i>),
/// A `` name of a `@keyframes` rule.
#[cfg_attr(feature = "serde", serde(borrow))]
- String(CowArcStr<'i>),
-}
-
-impl<'i> Parse<'i> for AnimationName<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Ok(AnimationName::None);
- }
-
- if let Ok(s) = input.try_parse(|input| input.expect_string_cloned()) {
- return Ok(AnimationName::String(s.into()));
- }
-
- let ident = CustomIdent::parse(input)?;
- Ok(AnimationName::Ident(ident))
- }
+ String(CSSString<'i>),
}
impl<'i> ToCss for AnimationName<'i> {
@@ -103,7 +88,7 @@ impl<'i> ToCss for AnimationName<'i> {
pub type AnimationNameList<'i> = SmallVec<[AnimationName<'i>; 1]>;
/// A value for the [animation-iteration-count](https://drafts.csswg.org/css-animations/#animation-iteration-count) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -125,40 +110,17 @@ impl Default for AnimationIterationCount {
}
}
-impl<'i> Parse<'i> for AnimationIterationCount {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("infinite")).is_ok() {
- return Ok(AnimationIterationCount::Infinite);
- }
-
- let number = CSSNumber::parse(input)?;
- return Ok(AnimationIterationCount::Number(number));
- }
-}
-
-impl ToCss for AnimationIterationCount {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- AnimationIterationCount::Number(val) => val.to_css(dest),
- AnimationIterationCount::Infinite => dest.write_str("infinite"),
- }
- }
-}
-
enum_property! {
/// A value for the [animation-direction](https://drafts.csswg.org/css-animations/#animation-direction) property.
pub enum AnimationDirection {
/// The animation is played as specified
- "normal": Normal,
+ Normal,
/// The animation is played in reverse.
- "reverse": Reverse,
+ Reverse,
/// The animation iterations alternate between forward and reverse.
- "alternate": Alternate,
+ Alternate,
/// The animation iterations alternate between forward and reverse, with reverse occurring first.
- "alternate-reverse": AlternateReverse,
+ AlternateReverse,
}
}
@@ -217,7 +179,7 @@ enum_property! {
}
/// A value for the [animation-timeline](https://drafts.csswg.org/css-animations-2/#animation-timeline) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -261,25 +223,28 @@ pub struct ScrollTimeline {
impl<'i> Parse<'i> for ScrollTimeline {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- let mut scroller = None;
- let mut axis = None;
- loop {
- if scroller.is_none() {
- scroller = input.try_parse(Scroller::parse).ok();
- }
+ input.expect_function_matching("scroll")?;
+ input.parse_nested_block(|input| {
+ let mut scroller = None;
+ let mut axis = None;
+ loop {
+ if scroller.is_none() {
+ scroller = input.try_parse(Scroller::parse).ok();
+ }
- if axis.is_none() {
- axis = input.try_parse(ScrollAxis::parse).ok();
- if axis.is_some() {
- continue;
+ if axis.is_none() {
+ axis = input.try_parse(ScrollAxis::parse).ok();
+ if axis.is_some() {
+ continue;
+ }
}
+ break;
}
- break;
- }
- Ok(ScrollTimeline {
- scroller: scroller.unwrap_or_default(),
- axis: axis.unwrap_or_default(),
+ Ok(ScrollTimeline {
+ scroller: scroller.unwrap_or_default(),
+ axis: axis.unwrap_or_default(),
+ })
})
}
}
@@ -289,6 +254,8 @@ impl ToCss for ScrollTimeline {
where
W: std::fmt::Write,
{
+ dest.write_str("scroll(")?;
+
let mut needs_space = false;
if self.scroller != Scroller::default() {
self.scroller.to_css(dest)?;
@@ -302,7 +269,7 @@ impl ToCss for ScrollTimeline {
self.axis.to_css(dest)?;
}
- Ok(())
+ dest.write_char(')')
}
}
@@ -359,25 +326,28 @@ pub struct ViewTimeline {
impl<'i> Parse<'i> for ViewTimeline {
fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- let mut axis = None;
- let mut inset = None;
- loop {
- if axis.is_none() {
- axis = input.try_parse(ScrollAxis::parse).ok();
- }
+ input.expect_function_matching("view")?;
+ input.parse_nested_block(|input| {
+ let mut axis = None;
+ let mut inset = None;
+ loop {
+ if axis.is_none() {
+ axis = input.try_parse(ScrollAxis::parse).ok();
+ }
- if inset.is_none() {
- inset = input.try_parse(Size2D::parse).ok();
- if inset.is_some() {
- continue;
+ if inset.is_none() {
+ inset = input.try_parse(Size2D::parse).ok();
+ if inset.is_some() {
+ continue;
+ }
}
+ break;
}
- break;
- }
- Ok(ViewTimeline {
- axis: axis.unwrap_or_default(),
- inset: inset.unwrap_or(Size2D(LengthPercentageOrAuto::Auto, LengthPercentageOrAuto::Auto)),
+ Ok(ViewTimeline {
+ axis: axis.unwrap_or_default(),
+ inset: inset.unwrap_or(Size2D(LengthPercentageOrAuto::Auto, LengthPercentageOrAuto::Auto)),
+ })
})
}
}
@@ -387,6 +357,7 @@ impl ToCss for ViewTimeline {
where
W: std::fmt::Write,
{
+ dest.write_str("view(")?;
let mut needs_space = false;
if self.axis != ScrollAxis::default() {
self.axis.to_css(dest)?;
@@ -400,56 +371,7 @@ impl ToCss for ViewTimeline {
self.inset.to_css(dest)?;
}
- Ok(())
- }
-}
-
-impl<'i> Parse<'i> for AnimationTimeline<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- return Ok(AnimationTimeline::Auto);
- }
-
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(AnimationTimeline::None);
- }
-
- if let Ok(name) = input.try_parse(DashedIdent::parse) {
- return Ok(AnimationTimeline::DashedIdent(name));
- }
-
- let location = input.current_source_location();
- let f = input.expect_function()?.clone();
- input.parse_nested_block(move |input| {
- match_ignore_ascii_case! { &f,
- "scroll" => ScrollTimeline::parse(input).map(AnimationTimeline::Scroll),
- "view" => ViewTimeline::parse(input).map(AnimationTimeline::View),
- _ => Err(location.new_custom_error(ParserError::UnexpectedToken(crate::properties::custom::Token::Function(f.into()))))
- }
- })
- }
-}
-
-impl<'i> ToCss for AnimationTimeline<'i> {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- AnimationTimeline::Auto => dest.write_str("auto"),
- AnimationTimeline::None => dest.write_str("none"),
- AnimationTimeline::DashedIdent(name) => name.to_css(dest),
- AnimationTimeline::Scroll(scroll) => {
- dest.write_str("scroll(")?;
- scroll.to_css(dest)?;
- dest.write_char(')')
- }
- AnimationTimeline::View(view) => {
- dest.write_str("view(")?;
- view.to_css(dest)?;
- dest.write_char(')')
- }
- }
+ dest.write_char(')')
}
}
@@ -535,7 +457,7 @@ impl<'i> ToCss for Animation<'i> {
{
match &self.name {
AnimationName::None => {}
- AnimationName::Ident(CustomIdent(name)) | AnimationName::String(name) => {
+ AnimationName::Ident(CustomIdent(name)) | AnimationName::String(CSSString(name)) => {
if !self.duration.is_zero() || !self.delay.is_zero() {
self.duration.to_css(dest)?;
dest.write_char(' ')?;
@@ -709,7 +631,7 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> {
}
}
TokenOrValue::Token(Token::String(s)) => {
- *token = TokenOrValue::AnimationName(AnimationName::String(s.clone()));
+ *token = TokenOrValue::AnimationName(AnimationName::String(CSSString(s.clone())));
}
_ => {}
}
diff --git a/src/properties/background.rs b/src/properties/background.rs
index 1dc1aeb3..f47821db 100644
--- a/src/properties/background.rs
+++ b/src/properties/background.rs
@@ -113,13 +113,13 @@ enum_property! {
/// See [BackgroundRepeat](BackgroundRepeat).
pub enum BackgroundRepeatKeyword {
/// The image is repeated in this direction.
- "repeat": Repeat,
+ Repeat,
/// The image is repeated so that it fits, and then spaced apart evenly.
- "space": Space,
+ Space,
/// The image is scaled so that it repeats an even number of times.
- "round": Round,
+ Round,
/// The image is placed once and not repeated in this direction.
- "no-repeat": NoRepeat,
+ NoRepeat,
}
}
@@ -214,11 +214,11 @@ enum_property! {
/// A value for the [background-origin](https://www.w3.org/TR/css-backgrounds-3/#background-origin) property.
pub enum BackgroundOrigin {
/// The position is relative to the border box.
- "border-box": BorderBox,
+ BorderBox,
/// The position is relative to the padding box.
- "padding-box": PaddingBox,
+ PaddingBox,
/// The position is relative to the content box.
- "content-box": ContentBox,
+ ContentBox,
}
}
@@ -226,15 +226,15 @@ enum_property! {
/// A value for the [background-clip](https://drafts.csswg.org/css-backgrounds-4/#background-clip) property.
pub enum BackgroundClip {
/// The background is clipped to the border box.
- "border-box": BorderBox,
+ BorderBox,
/// The background is clipped to the padding box.
- "padding-box": PaddingBox,
+ PaddingBox,
/// The background is clipped to the content box.
- "content-box": ContentBox,
+ ContentBox,
/// The background is clipped to the area painted by the border.
- "border": Border,
+ Border,
/// The background is clipped to the text content of the element.
- "text": Text,
+ Text,
}
}
diff --git a/src/properties/border.rs b/src/properties/border.rs
index ee2c4102..9308eebb 100644
--- a/src/properties/border.rs
+++ b/src/properties/border.rs
@@ -23,7 +23,7 @@ use crate::visitor::Visit;
use cssparser::*;
/// A value for the [border-width](https://www.w3.org/TR/css-backgrounds-3/#border-width) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -58,37 +58,6 @@ impl IsCompatible for BorderSideWidth {
}
}
-impl<'i> Parse<'i> for BorderSideWidth {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(length) = input.try_parse(|i| Length::parse(i)) {
- return Ok(BorderSideWidth::Length(length));
- }
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match_ignore_ascii_case! { &ident,
- "thin" => Ok(BorderSideWidth::Thin),
- "medium" => Ok(BorderSideWidth::Medium),
- "thick" => Ok(BorderSideWidth::Thick),
- _ => return Err(location.new_unexpected_token_error(Token::Ident(ident.clone())))
- }
- }
-}
-
-impl ToCss for BorderSideWidth {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- use BorderSideWidth::*;
- match self {
- Thin => dest.write_str("thin"),
- Medium => dest.write_str("medium"),
- Thick => dest.write_str("thick"),
- Length(length) => length.to_css(dest),
- }
- }
-}
-
enum_property! {
/// A [``](https://drafts.csswg.org/css-backgrounds/#typedef-line-style) value, used in the `border-style` property.
pub enum LineStyle {
diff --git a/src/properties/border_image.rs b/src/properties/border_image.rs
index 8f444efe..300d458e 100644
--- a/src/properties/border_image.rs
+++ b/src/properties/border_image.rs
@@ -102,7 +102,7 @@ impl IsCompatible for BorderImageRepeat {
}
/// A value for the [border-image-width](https://www.w3.org/TR/css-backgrounds-3/#border-image-width) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -126,38 +126,6 @@ impl Default for BorderImageSideWidth {
}
}
-impl<'i> Parse<'i> for BorderImageSideWidth {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- return Ok(BorderImageSideWidth::Auto);
- }
-
- if let Ok(number) = input.try_parse(CSSNumber::parse) {
- return Ok(BorderImageSideWidth::Number(number));
- }
-
- if let Ok(percent) = input.try_parse(|input| LengthPercentage::parse(input)) {
- return Ok(BorderImageSideWidth::LengthPercentage(percent));
- }
-
- Err(input.new_error_for_next_token())
- }
-}
-
-impl ToCss for BorderImageSideWidth {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- use BorderImageSideWidth::*;
- match self {
- Auto => dest.write_str("auto"),
- LengthPercentage(l) => l.to_css(dest),
- Number(n) => n.to_css(dest),
- }
- }
-}
-
impl IsCompatible for BorderImageSideWidth {
fn is_compatible(&self, browsers: Browsers) -> bool {
match self {
diff --git a/src/properties/contain.rs b/src/properties/contain.rs
index 761e2d92..1c57bab4 100644
--- a/src/properties/contain.rs
+++ b/src/properties/contain.rs
@@ -25,11 +25,11 @@ enum_property! {
pub enum ContainerType {
/// The element is not a query container for any container size queries,
/// but remains a query container for container style queries.
- "normal": Normal,
+ Normal,
/// Establishes a query container for container size queries on the container’s own inline axis.
- "inline-size": InlineSize,
+ InlineSize,
/// Establishes a query container for container size queries on both the inline and block axis.
- "size": Size,
+ Size,
}
}
diff --git a/src/properties/custom.rs b/src/properties/custom.rs
index 3198422b..40b522e8 100644
--- a/src/properties/custom.rs
+++ b/src/properties/custom.rs
@@ -1329,25 +1329,25 @@ enum_property! {
/// A UA-defined environment variable name.
pub enum UAEnvironmentVariable {
/// The safe area inset from the top of the viewport.
- "safe-area-inset-top": SafeAreaInsetTop,
+ SafeAreaInsetTop,
/// The safe area inset from the right of the viewport.
- "safe-area-inset-right": SafeAreaInsetRight,
+ SafeAreaInsetRight,
/// The safe area inset from the bottom of the viewport.
- "safe-area-inset-bottom": SafeAreaInsetBottom,
+ SafeAreaInsetBottom,
/// The safe area inset from the left of the viewport.
- "safe-area-inset-left": SafeAreaInsetLeft,
+ SafeAreaInsetLeft,
/// The viewport segment width.
- "viewport-segment-width": ViewportSegmentWidth,
+ ViewportSegmentWidth,
/// The viewport segment height.
- "viewport-segment-height": ViewportSegmentHeight,
+ ViewportSegmentHeight,
/// The viewport segment top position.
- "viewport-segment-top": ViewportSegmentTop,
+ ViewportSegmentTop,
/// The viewport segment left position.
- "viewport-segment-left": ViewportSegmentLeft,
+ ViewportSegmentLeft,
/// The viewport segment bottom position.
- "viewport-segment-bottom": ViewportSegmentBottom,
+ ViewportSegmentBottom,
/// The viewport segment right position.
- "viewport-segment-right": ViewportSegmentRight,
+ ViewportSegmentRight,
}
}
diff --git a/src/properties/display.rs b/src/properties/display.rs
index d5a53d13..3df7c819 100644
--- a/src/properties/display.rs
+++ b/src/properties/display.rs
@@ -18,9 +18,9 @@ enum_property! {
/// A [``](https://drafts.csswg.org/css-display-3/#typedef-display-outside) value.
#[allow(missing_docs)]
pub enum DisplayOutside {
- "block": Block,
- "inline": Inline,
- "run-in": RunIn,
+ Block,
+ Inline,
+ RunIn,
}
}
@@ -309,25 +309,25 @@ enum_property! {
/// See [Display](Display).
#[allow(missing_docs)]
pub enum DisplayKeyword {
- "none": None,
- "contents": Contents,
- "table-row-group": TableRowGroup,
- "table-header-group": TableHeaderGroup,
- "table-footer-group": TableFooterGroup,
- "table-row": TableRow,
- "table-cell": TableCell,
- "table-column-group": TableColumnGroup,
- "table-column": TableColumn,
- "table-caption": TableCaption,
- "ruby-base": RubyBase,
- "ruby-text": RubyText,
- "ruby-base-container": RubyBaseContainer,
- "ruby-text-container": RubyTextContainer,
+ None,
+ Contents,
+ TableRowGroup,
+ TableHeaderGroup,
+ TableFooterGroup,
+ TableRow,
+ TableCell,
+ TableColumnGroup,
+ TableColumn,
+ TableCaption,
+ RubyBase,
+ RubyText,
+ RubyBaseContainer,
+ RubyTextContainer,
}
}
/// A value for the [display](https://drafts.csswg.org/css-display-3/#the-display-properties) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -347,29 +347,6 @@ pub enum Display {
Pair(DisplayPair),
}
-impl<'i> Parse<'i> for Display {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(pair) = input.try_parse(DisplayPair::parse) {
- return Ok(Display::Pair(pair));
- }
-
- let keyword = DisplayKeyword::parse(input)?;
- Ok(Display::Keyword(keyword))
- }
-}
-
-impl ToCss for Display {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- Display::Keyword(keyword) => keyword.to_css(dest),
- Display::Pair(pair) => pair.to_css(dest),
- }
- }
-}
-
enum_property! {
/// A value for the [visibility](https://drafts.csswg.org/css-display-3/#visibility) property.
pub enum Visibility {
diff --git a/src/properties/flex.rs b/src/properties/flex.rs
index cf631993..e885c597 100644
--- a/src/properties/flex.rs
+++ b/src/properties/flex.rs
@@ -25,13 +25,13 @@ enum_property! {
/// A value for the [flex-direction](https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#propdef-flex-direction) property.
pub enum FlexDirection {
/// Flex items are laid out in a row.
- "row": Row,
+ Row,
/// Flex items are laid out in a row, and reversed.
- "row-reverse": RowReverse,
+ RowReverse,
/// Flex items are laid out in a column.
- "column": Column,
+ Column,
/// Flex items are laid out in a column, and reversed.
- "column-reverse": ColumnReverse,
+ ColumnReverse,
}
}
@@ -233,13 +233,13 @@ enum_property! {
/// Partially equivalent to `flex-direction` in the standard syntax.
pub enum BoxOrient {
/// Items are laid out horizontally.
- "horizontal": Horizontal,
+ Horizontal,
/// Items are laid out vertically.
- "vertical": Vertical,
+ Vertical,
/// Items are laid out along the inline axis, according to the writing direction.
- "inline-axis": InlineAxis,
+ InlineAxis,
/// Items are laid out along the block axis, according to the writing direction.
- "block-axis": BlockAxis,
+ BlockAxis,
}
}
diff --git a/src/properties/font.rs b/src/properties/font.rs
index 84396cdb..377ea77c 100644
--- a/src/properties/font.rs
+++ b/src/properties/font.rs
@@ -20,7 +20,7 @@ use crate::visitor::Visit;
use cssparser::*;
/// A value for the [font-weight](https://www.w3.org/TR/css-fonts-4/#font-weight-prop) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -44,38 +44,6 @@ impl Default for FontWeight {
}
}
-impl<'i> Parse<'i> for FontWeight {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(val) = input.try_parse(AbsoluteFontWeight::parse) {
- return Ok(FontWeight::Absolute(val));
- }
-
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match_ignore_ascii_case! { &*ident,
- "bolder" => Ok(FontWeight::Bolder),
- "lighter" => Ok(FontWeight::Lighter),
- _ => Err(location.new_unexpected_token_error(
- cssparser::Token::Ident(ident.clone())
- ))
- }
- }
-}
-
-impl ToCss for FontWeight {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- use FontWeight::*;
- match self {
- Absolute(val) => val.to_css(dest),
- Bolder => dest.write_str("bolder"),
- Lighter => dest.write_str("lighter"),
- }
- }
-}
-
impl IsCompatible for FontWeight {
fn is_compatible(&self, browsers: crate::targets::Browsers) -> bool {
match self {
@@ -89,7 +57,7 @@ impl IsCompatible for FontWeight {
/// as used in the `font-weight` property.
///
/// See [FontWeight](FontWeight).
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -112,24 +80,6 @@ impl Default for AbsoluteFontWeight {
}
}
-impl<'i> Parse<'i> for AbsoluteFontWeight {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(val) = input.try_parse(CSSNumber::parse) {
- return Ok(AbsoluteFontWeight::Weight(val));
- }
-
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match_ignore_ascii_case! { &*ident,
- "normal" => Ok(AbsoluteFontWeight::Normal),
- "bold" => Ok(AbsoluteFontWeight::Bold),
- _ => Err(location.new_unexpected_token_error(
- cssparser::Token::Ident(ident.clone())
- ))
- }
- }
-}
-
impl ToCss for AbsoluteFontWeight {
fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
where
@@ -197,7 +147,7 @@ enum_property! {
}
/// A value for the [font-size](https://www.w3.org/TR/css-fonts-4/#font-size-prop) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -215,35 +165,6 @@ pub enum FontSize {
Relative(RelativeFontSize),
}
-impl<'i> Parse<'i> for FontSize {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(val) = input.try_parse(LengthPercentage::parse) {
- return Ok(FontSize::Length(val));
- }
-
- if let Ok(val) = input.try_parse(AbsoluteFontSize::parse) {
- return Ok(FontSize::Absolute(val));
- }
-
- let val = RelativeFontSize::parse(input)?;
- Ok(FontSize::Relative(val))
- }
-}
-
-impl ToCss for FontSize {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- use FontSize::*;
- match self {
- Absolute(val) => val.to_css(dest),
- Length(val) => val.to_css(dest),
- Relative(val) => val.to_css(dest),
- }
- }
-}
-
impl IsCompatible for FontSize {
fn is_compatible(&self, browsers: crate::targets::Browsers) -> bool {
match self {
@@ -309,7 +230,7 @@ impl Into for &FontStretchKeyword {
}
/// A value for the [font-stretch](https://www.w3.org/TR/css-fonts-4/#font-stretch-prop) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -331,17 +252,6 @@ impl Default for FontStretch {
}
}
-impl<'i> Parse<'i> for FontStretch {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(val) = input.try_parse(Percentage::parse) {
- return Ok(FontStretch::Percentage(val));
- }
-
- let keyword = FontStretchKeyword::parse(input)?;
- Ok(FontStretch::Keyword(keyword))
- }
-}
-
impl Into for &FontStretch {
fn into(self) -> Percentage {
match self {
@@ -601,19 +511,19 @@ enum_property! {
/// A value for the [font-variant-caps](https://www.w3.org/TR/css-fonts-4/#font-variant-caps-prop) property.
pub enum FontVariantCaps {
/// No special capitalization features are applied.
- "normal": Normal,
+ Normal,
/// The small capitals feature is used for lower case letters.
- "small-caps": SmallCaps,
+ SmallCaps,
/// Small capitals are used for both upper and lower case letters.
- "all-small-caps": AllSmallCaps,
+ AllSmallCaps,
/// Petite capitals are used.
- "petite-caps": PetiteCaps,
+ PetiteCaps,
/// Petite capitals are used for both upper and lower case letters.
- "all-petite-caps": AllPetiteCaps,
+ AllPetiteCaps,
/// Enables display of mixture of small capitals for uppercase letters with normal lowercase letters.
- "unicase": Unicase,
+ Unicase,
/// Uses titling capitals.
- "titling-caps": TitlingCaps,
+ TitlingCaps,
}
}
@@ -644,7 +554,7 @@ impl IsCompatible for FontVariantCaps {
}
/// A value for the [line-height](https://www.w3.org/TR/2020/WD-css-inline-3-20200827/#propdef-line-height) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -668,33 +578,6 @@ impl Default for LineHeight {
}
}
-impl<'i> Parse<'i> for LineHeight {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- return Ok(LineHeight::Normal);
- }
-
- if let Ok(val) = input.try_parse(CSSNumber::parse) {
- return Ok(LineHeight::Number(val));
- }
-
- Ok(LineHeight::Length(LengthPercentage::parse(input)?))
- }
-}
-
-impl ToCss for LineHeight {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- LineHeight::Normal => dest.write_str("normal"),
- LineHeight::Number(val) => val.to_css(dest),
- LineHeight::Length(val) => val.to_css(dest),
- }
- }
-}
-
impl IsCompatible for LineHeight {
fn is_compatible(&self, browsers: crate::targets::Browsers) -> bool {
match self {
@@ -708,27 +591,27 @@ enum_property! {
/// A keyword for the [vertical align](https://drafts.csswg.org/css2/#propdef-vertical-align) property.
pub enum VerticalAlignKeyword {
/// Align the baseline of the box with the baseline of the parent box.
- "baseline": Baseline,
+ Baseline,
/// Lower the baseline of the box to the proper position for subscripts of the parent’s box.
- "sub": Sub,
+ Sub,
/// Raise the baseline of the box to the proper position for superscripts of the parent’s box.
- "super": Super,
+ Super,
/// Align the top of the aligned subtree with the top of the line box.
- "top": Top,
+ Top,
/// Align the top of the box with the top of the parent’s content area.
- "text-top": TextTop,
+ TextTop,
/// Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.
- "middle": Middle,
+ Middle,
/// Align the bottom of the aligned subtree with the bottom of the line box.
- "bottom": Bottom,
+ Bottom,
/// Align the bottom of the box with the bottom of the parent’s content area.
- "text-bottom": TextBottom,
+ TextBottom,
}
}
/// A value for the [vertical align](https://drafts.csswg.org/css2/#propdef-vertical-align) property.
// TODO: there is a more extensive spec in CSS3 but it doesn't seem any browser implements it? https://www.w3.org/TR/css-inline-3/#transverse-alignment
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -744,29 +627,6 @@ pub enum VerticalAlign {
Length(LengthPercentage),
}
-impl<'i> Parse<'i> for VerticalAlign {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(len) = input.try_parse(LengthPercentage::parse) {
- return Ok(VerticalAlign::Length(len));
- }
-
- let kw = VerticalAlignKeyword::parse(input)?;
- Ok(VerticalAlign::Keyword(kw))
- }
-}
-
-impl ToCss for VerticalAlign {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- VerticalAlign::Keyword(kw) => kw.to_css(dest),
- VerticalAlign::Length(len) => len.to_css(dest),
- }
- }
-}
-
define_shorthand! {
/// A value for the [font](https://www.w3.org/TR/css-fonts-4/#font-prop) shorthand property.
pub struct Font<'i> {
diff --git a/src/properties/grid.rs b/src/properties/grid.rs
index 912f563d..6e706319 100644
--- a/src/properties/grid.rs
+++ b/src/properties/grid.rs
@@ -24,7 +24,7 @@ use crate::serialization::ValueWrapper;
/// A [track sizing](https://drafts.csswg.org/css-grid-2/#track-sizing) value
/// for the `grid-template-rows` and `grid-template-columns` properties.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
@@ -178,7 +178,7 @@ pub struct TrackRepeat<'i> {
/// used in the `repeat()` function.
///
/// See [TrackRepeat](TrackRepeat).
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -365,37 +365,6 @@ impl<'i> ToCss for TrackRepeat<'i> {
}
}
-impl<'i> Parse<'i> for RepeatCount {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(num) = input.try_parse(CSSInteger::parse) {
- return Ok(RepeatCount::Number(num));
- }
-
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match_ignore_ascii_case! { &*ident,
- "auto-fill" => Ok(RepeatCount::AutoFill),
- "auto-fit" => Ok(RepeatCount::AutoFit),
- _ => Err(location.new_unexpected_token_error(
- cssparser::Token::Ident(ident.clone())
- ))
- }
- }
-}
-
-impl ToCss for RepeatCount {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- RepeatCount::AutoFill => dest.write_str("auto-fill"),
- RepeatCount::AutoFit => dest.write_str("auto-fit"),
- RepeatCount::Number(num) => num.to_css(dest),
- }
- }
-}
-
fn parse_line_names<'i, 't>(
input: &mut Parser<'i, 't>,
) -> Result<'i>, ParseError<'i, ParserError<'i>>> {
@@ -519,29 +488,6 @@ impl<'i> TrackList<'i> {
}
}
-impl<'i> Parse<'i> for TrackSizing<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Ok(TrackSizing::None);
- }
-
- let track_list = TrackList::parse(input)?;
- Ok(TrackSizing::TrackList(track_list))
- }
-}
-
-impl<'i> ToCss for TrackSizing<'i> {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- TrackSizing::None => dest.write_str("none"),
- TrackSizing::TrackList(list) => list.to_css(dest),
- }
- }
-}
-
impl<'i> TrackSizing<'i> {
fn is_explicit(&self) -> bool {
match self {
diff --git a/src/properties/list.rs b/src/properties/list.rs
index 4112ddfe..54a0c052 100644
--- a/src/properties/list.rs
+++ b/src/properties/list.rs
@@ -15,7 +15,7 @@ use crate::visitor::Visit;
use cssparser::*;
/// A value for the [list-style-type](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#text-markers) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
@@ -40,34 +40,6 @@ impl Default for ListStyleType<'_> {
}
}
-impl<'i> Parse<'i> for ListStyleType<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Ok(ListStyleType::None);
- }
-
- if let Ok(val) = input.try_parse(CounterStyle::parse) {
- return Ok(ListStyleType::CounterStyle(val));
- }
-
- let s = CSSString::parse(input)?;
- Ok(ListStyleType::String(s))
- }
-}
-
-impl ToCss for ListStyleType<'_> {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- ListStyleType::None => dest.write_str("none"),
- ListStyleType::CounterStyle(style) => style.to_css(dest),
- ListStyleType::String(s) => s.to_css(dest),
- }
- }
-}
-
impl IsCompatible for ListStyleType<'_> {
fn is_compatible(&self, browsers: Browsers) -> bool {
match self {
@@ -117,7 +89,7 @@ macro_rules! counter_styles {
$vis:vis enum $name:ident {
$(
$(#[$meta: meta])*
- $str: literal: $id: ident,
+ $id: ident,
)+
}
) => {
@@ -127,7 +99,7 @@ macro_rules! counter_styles {
pub enum PredefinedCounterStyle {
$(
$(#[$meta])*
- $str: $id,
+ $id,
)+
}
}
@@ -151,68 +123,68 @@ counter_styles! {
#[allow(missing_docs)]
pub enum PredefinedCounterStyle {
// https://www.w3.org/TR/css-counter-styles-3/#simple-numeric
- "decimal": Decimal,
- "decimal-leading-zero": DecimalLeadingZero,
- "arabic-indic": ArabicIndic,
- "armenian": Armenian,
- "upper-armenian": UpperArmenian,
- "lower-armenian": LowerArmenian,
- "bengali": Bengali,
- "cambodian": Cambodian,
- "khmer": Khmer,
- "cjk-decimal": CjkDecimal,
- "devanagari": Devanagari,
- "georgian": Georgian,
- "gujarati": Gujarati,
- "gurmukhi": Gurmukhi,
- "hebrew": Hebrew,
- "kannada": Kannada,
- "lao": Lao,
- "malayalam": Malayalam,
- "mongolian": Mongolian,
- "myanmar": Myanmar,
- "oriya": Oriya,
- "persian": Persian,
- "lower-roman": LowerRoman,
- "upper-roman": UpperRoman,
- "tamil": Tamil,
- "telugu": Telugu,
- "thai": Thai,
- "tibetan": Tibetan,
+ Decimal,
+ DecimalLeadingZero,
+ ArabicIndic,
+ Armenian,
+ UpperArmenian,
+ LowerArmenian,
+ Bengali,
+ Cambodian,
+ Khmer,
+ CjkDecimal,
+ Devanagari,
+ Georgian,
+ Gujarati,
+ Gurmukhi,
+ Hebrew,
+ Kannada,
+ Lao,
+ Malayalam,
+ Mongolian,
+ Myanmar,
+ Oriya,
+ Persian,
+ LowerRoman,
+ UpperRoman,
+ Tamil,
+ Telugu,
+ Thai,
+ Tibetan,
// https://www.w3.org/TR/css-counter-styles-3/#simple-alphabetic
- "lower-alpha": LowerAlpha,
- "lower-latin": LowerLatin,
- "upper-alpha": UpperAlpha,
- "upper-latin": UpperLatin,
- "lower-greek": LowerGreek,
- "hiragana": Hiragana,
- "hiragana-iroha": HiraganaIroha,
- "katakana": Katakana,
- "katakana-iroha": KatakanaIroha,
+ LowerAlpha,
+ LowerLatin,
+ UpperAlpha,
+ UpperLatin,
+ LowerGreek,
+ Hiragana,
+ HiraganaIroha,
+ Katakana,
+ KatakanaIroha,
// https://www.w3.org/TR/css-counter-styles-3/#simple-symbolic
- "disc": Disc,
- "circle": Circle,
- "square": Square,
- "disclosure-open": DisclosureOpen,
- "disclosure-closed": DisclosureClosed,
+ Disc,
+ Circle,
+ Square,
+ DisclosureOpen,
+ DisclosureClosed,
// https://www.w3.org/TR/css-counter-styles-3/#simple-fixed
- "cjk-earthly-branch": CjkEarthlyBranch,
- "cjk-heavenly-stem": CjkHeavenlyStem,
+ CjkEarthlyBranch,
+ CjkHeavenlyStem,
// https://www.w3.org/TR/css-counter-styles-3/#complex-cjk
- "japanese-informal": JapaneseInformal,
- "japanese-formal": JapaneseFormal,
- "korean-hangul-formal": KoreanHangulFormal,
- "korean-hanja-informal": KoreanHanjaInformal,
- "korean-hanja-formal": KoreanHanjaFormal,
- "simp-chinese-informal": SimpChineseInformal,
- "simp-chinese-formal": SimpChineseFormal,
- "trad-chinese-informal": TradChineseInformal,
- "trad-chinese-formal": TradChineseFormal,
- "ethiopic-numeric": EthiopicNumeric,
+ JapaneseInformal,
+ JapaneseFormal,
+ KoreanHangulFormal,
+ KoreanHanjaInformal,
+ KoreanHanjaFormal,
+ SimpChineseInformal,
+ SimpChineseFormal,
+ TradChineseInformal,
+ TradChineseFormal,
+ EthiopicNumeric,
}
}
@@ -309,7 +281,7 @@ impl Default for SymbolsType {
/// `symbols()` function.
///
/// See [CounterStyle](CounterStyle).
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
@@ -326,29 +298,6 @@ pub enum Symbol<'i> {
Image(Image<'i>),
}
-impl<'i> Parse<'i> for Symbol<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(img) = input.try_parse(Image::parse) {
- return Ok(Symbol::Image(img));
- }
-
- let s = CSSString::parse(input)?;
- Ok(Symbol::String(s.into()))
- }
-}
-
-impl<'i> ToCss for Symbol<'i> {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- Symbol::String(s) => s.to_css(dest),
- Symbol::Image(img) => img.to_css(dest),
- }
- }
-}
-
enum_property! {
/// A value for the [list-style-position](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#list-style-position-property) property.
pub enum ListStylePosition {
@@ -375,8 +324,8 @@ enum_property! {
/// A value for the [marker-side](https://www.w3.org/TR/2020/WD-css-lists-3-20201117/#marker-side) property.
#[allow(missing_docs)]
pub enum MarkerSide {
- "match-self": MatchSelf,
- "match-parent": MatchParent,
+ MatchSelf,
+ MatchParent,
}
}
diff --git a/src/properties/masking.rs b/src/properties/masking.rs
index 35560c6e..ff05e1f6 100644
--- a/src/properties/masking.rs
+++ b/src/properties/masking.rs
@@ -37,11 +37,11 @@ enum_property! {
/// A value for the [mask-mode](https://www.w3.org/TR/css-masking-1/#the-mask-mode) property.
pub enum MaskMode {
/// The luminance values of the mask image is used.
- "luminance": Luminance,
+ Luminance,
/// The alpha values of the mask image is used.
- "alpha": Alpha,
+ Alpha,
/// If an SVG source is used, the value matches the `mask-type` property. Otherwise, the alpha values are used.
- "match-source": MatchSource,
+ MatchSource,
}
}
@@ -58,11 +58,11 @@ enum_property! {
/// See also [MaskMode](MaskMode).
pub enum WebKitMaskSourceType {
/// Equivalent to `match-source` in the standard `mask-mode` syntax.
- "auto": Auto,
+ Auto,
/// The luminance values of the mask image is used.
- "luminance": Luminance,
+ Luminance,
/// The alpha values of the mask image is used.
- "alpha": Alpha,
+ Alpha,
}
}
@@ -81,19 +81,19 @@ enum_property! {
/// as used in the `mask-clip` and `clip-path` properties.
pub enum GeometryBox {
/// The painted content is clipped to the content box.
- "border-box": BorderBox,
+ BorderBox,
/// The painted content is clipped to the padding box.
- "padding-box": PaddingBox,
+ PaddingBox,
/// The painted content is clipped to the border box.
- "content-box": ContentBox,
+ ContentBox,
/// The painted content is clipped to the margin box.
- "margin-box": MarginBox,
+ MarginBox,
/// The painted content is clipped to the object bounding box.
- "fill-box": FillBox,
+ FillBox,
/// The painted content is clipped to the stroke bounding box.
- "stroke-box": StrokeBox,
+ StrokeBox,
/// Uses the nearest SVG viewport as reference box.
- "view-box": ViewBox,
+ ViewBox,
}
}
@@ -104,7 +104,7 @@ impl Default for GeometryBox {
}
/// A value for the [mask-clip](https://www.w3.org/TR/css-masking-1/#the-mask-clip) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -120,29 +120,6 @@ pub enum MaskClip {
NoClip,
}
-impl<'i> Parse<'i> for MaskClip {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(b) = input.try_parse(GeometryBox::parse) {
- return Ok(MaskClip::GeometryBox(b));
- }
-
- input.expect_ident_matching("no-clip")?;
- Ok(MaskClip::NoClip)
- }
-}
-
-impl ToCss for MaskClip {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- MaskClip::GeometryBox(b) => b.to_css(dest),
- MaskClip::NoClip => dest.write_str("no-clip"),
- }
- }
-}
-
impl IsCompatible for MaskClip {
fn is_compatible(&self, browsers: Browsers) -> bool {
match self {
@@ -191,21 +168,21 @@ enum_property! {
/// See also [MaskComposite](MaskComposite).
#[allow(missing_docs)]
pub enum WebKitMaskComposite {
- "clear": Clear,
- "copy": Copy,
+ Clear,
+ Copy,
/// Equivalent to `add` in the standard `mask-composite` syntax.
- "source-over": SourceOver,
+ SourceOver,
/// Equivalent to `intersect` in the standard `mask-composite` syntax.
- "source-in": SourceIn,
+ SourceIn,
/// Equivalent to `subtract` in the standard `mask-composite` syntax.
- "source-out": SourceOut,
- "source-atop": SourceAtop,
- "destination-over": DestinationOver,
- "destination-in": DestinationIn,
- "destination-out": DestinationOut,
- "destination-atop": DestinationAtop,
+ SourceOut,
+ SourceAtop,
+ DestinationOver,
+ DestinationIn,
+ DestinationOut,
+ DestinationAtop,
/// Equivalent to `exclude` in the standard `mask-composite` syntax.
- "xor": Xor,
+ Xor,
}
}
@@ -477,9 +454,9 @@ enum_property! {
/// A value for the [mask-border-mode](https://www.w3.org/TR/css-masking-1/#the-mask-border-mode) property.
pub enum MaskBorderMode {
/// The luminance values of the mask image is used.
- "luminance": Luminance,
+ Luminance,
/// The alpha values of the mask image is used.
- "alpha": Alpha,
+ Alpha,
}
}
diff --git a/src/properties/outline.rs b/src/properties/outline.rs
index e0a710e2..2ecdb202 100644
--- a/src/properties/outline.rs
+++ b/src/properties/outline.rs
@@ -15,7 +15,7 @@ use crate::visitor::Visit;
use cssparser::*;
/// A value for the [outline-style](https://drafts.csswg.org/css-ui/#outline-style) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -31,29 +31,6 @@ pub enum OutlineStyle {
LineStyle(LineStyle),
}
-impl<'i> Parse<'i> for OutlineStyle {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(border_style) = input.try_parse(LineStyle::parse) {
- return Ok(OutlineStyle::LineStyle(border_style));
- }
-
- input.expect_ident_matching("auto")?;
- Ok(OutlineStyle::Auto)
- }
-}
-
-impl ToCss for OutlineStyle {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- OutlineStyle::Auto => dest.write_str("auto"),
- OutlineStyle::LineStyle(border_style) => border_style.to_css(dest),
- }
- }
-}
-
impl Default for OutlineStyle {
fn default() -> OutlineStyle {
OutlineStyle::LineStyle(LineStyle::None)
diff --git a/src/properties/position.rs b/src/properties/position.rs
index ace9a40d..34a0cf3b 100644
--- a/src/properties/position.rs
+++ b/src/properties/position.rs
@@ -73,7 +73,7 @@ impl ToCss for Position {
}
/// A value for the [z-index](https://drafts.csswg.org/css2/#z-index) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -89,29 +89,6 @@ pub enum ZIndex {
Integer(CSSInteger),
}
-impl<'i> Parse<'i> for ZIndex {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(value) = input.expect_integer() {
- return Ok(ZIndex::Integer(value));
- }
-
- input.expect_ident_matching("auto")?;
- Ok(ZIndex::Auto)
- }
-}
-
-impl ToCss for ZIndex {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- ZIndex::Auto => dest.write_str("auto"),
- ZIndex::Integer(value) => value.to_css(dest),
- }
- }
-}
-
#[derive(Default)]
pub(crate) struct PositionHandler {
position: Option,
diff --git a/src/properties/size.rs b/src/properties/size.rs
index 54cc134d..2475773b 100644
--- a/src/properties/size.rs
+++ b/src/properties/size.rs
@@ -300,9 +300,9 @@ enum_property! {
/// A value for the [box-sizing](https://drafts.csswg.org/css-sizing-3/#box-sizing) property.
pub enum BoxSizing {
/// Exclude the margin/border/padding from the width and height.
- "content-box": ContentBox,
+ ContentBox,
/// Include the padding and border (but not the margin) in the width and height.
- "border-box": BorderBox,
+ BorderBox,
}
}
diff --git a/src/properties/svg.rs b/src/properties/svg.rs
index 26109941..150e5a25 100644
--- a/src/properties/svg.rs
+++ b/src/properties/svg.rs
@@ -13,7 +13,7 @@ use cssparser::*;
/// An SVG [``](https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint) value
/// used in the `fill` and `stroke` properties.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
@@ -23,8 +23,6 @@ use cssparser::*;
)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum SVGPaint<'i> {
- /// No paint.
- None,
/// A URL reference to a paint server element, e.g. `linearGradient`, `radialGradient`, and `pattern`.
Url {
#[cfg_attr(feature = "serde", serde(borrow))]
@@ -40,12 +38,14 @@ pub enum SVGPaint<'i> {
ContextFill,
/// Use the paint value of stroke from a context element.
ContextStroke,
+ /// No paint.
+ None,
}
/// A fallback for an SVG paint in case a paint server `url()` cannot be resolved.
///
/// See [SVGPaint](SVGPaint).
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -61,74 +61,6 @@ pub enum SVGPaintFallback {
Color(CssColor),
}
-impl<'i> Parse<'i> for SVGPaint<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(url) = input.try_parse(Url::parse) {
- let fallback = input.try_parse(SVGPaintFallback::parse).ok();
- return Ok(SVGPaint::Url { url, fallback });
- }
-
- if let Ok(color) = input.try_parse(CssColor::parse) {
- return Ok(SVGPaint::Color(color));
- }
-
- let location = input.current_source_location();
- let keyword = input.expect_ident()?;
- match_ignore_ascii_case! { &keyword,
- "none" => Ok(SVGPaint::None),
- "context-fill" => Ok(SVGPaint::ContextFill),
- "context-stroke" => Ok(SVGPaint::ContextStroke),
- _ => Err(location.new_unexpected_token_error(
- cssparser::Token::Ident(keyword.clone())
- ))
- }
- }
-}
-
-impl<'i> ToCss for SVGPaint<'i> {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- SVGPaint::None => dest.write_str("none"),
- SVGPaint::Url { url, fallback } => {
- url.to_css(dest)?;
- if let Some(fallback) = fallback {
- dest.write_char(' ')?;
- fallback.to_css(dest)?;
- }
- Ok(())
- }
- SVGPaint::Color(color) => color.to_css(dest),
- SVGPaint::ContextFill => dest.write_str("context-fill"),
- SVGPaint::ContextStroke => dest.write_str("context-stroke"),
- }
- }
-}
-
-impl<'i> Parse<'i> for SVGPaintFallback {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Ok(SVGPaintFallback::None);
- }
-
- Ok(SVGPaintFallback::Color(CssColor::parse(input)?))
- }
-}
-
-impl ToCss for SVGPaintFallback {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- SVGPaintFallback::None => dest.write_str("none"),
- SVGPaintFallback::Color(color) => color.to_css(dest),
- }
- }
-}
-
impl<'i> FallbackValues for SVGPaint<'i> {
fn get_fallbacks(&mut self, targets: Targets) -> Vec {
match self {
@@ -182,15 +114,15 @@ enum_property! {
/// A value for the [stroke-linejoin](https://www.w3.org/TR/SVG2/painting.html#LineJoin) property.
pub enum StrokeLinejoin {
/// A sharp corner is to be used to join path segments.
- "miter": Miter,
+ Miter,
/// Same as `miter` but clipped beyond `stroke-miterlimit`.
- "miter-clip": MiterClip,
+ MiterClip,
/// A round corner is to be used to join path segments.
- "round": Round,
+ Round,
/// A bevelled corner is to be used to join path segments.
- "bevel": Bevel,
+ Bevel,
/// An arcs corner is to be used to join path segments.
- "arcs": Arcs,
+ Arcs,
}
}
@@ -260,7 +192,7 @@ impl ToCss for StrokeDasharray {
}
/// A value for the [marker](https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties) properties.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(
@@ -277,29 +209,6 @@ pub enum Marker<'i> {
Url(Url<'i>),
}
-impl<'i> Parse<'i> for Marker<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(url) = input.try_parse(Url::parse) {
- return Ok(Marker::Url(url));
- }
-
- input.expect_ident_matching("none")?;
- Ok(Marker::None)
- }
-}
-
-impl<'i> ToCss for Marker<'i> {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- Marker::None => dest.write_str("none"),
- Marker::Url(url) => url.to_css(dest),
- }
- }
-}
-
enum_property! {
/// A value for the [color-interpolation](https://www.w3.org/TR/SVG2/painting.html#ColorInterpolation) property.
pub enum ColorInterpolation {
diff --git a/src/properties/text.rs b/src/properties/text.rs
index e22f6fab..622b870f 100644
--- a/src/properties/text.rs
+++ b/src/properties/text.rs
@@ -238,13 +238,13 @@ enum_property! {
/// A value for the [word-break](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#word-break-property) property.
pub enum WordBreak {
/// Words break according to their customary rules.
- "normal": Normal,
+ Normal,
/// Breaking is forbidden within “words”.
- "keep-all": KeepAll,
+ KeepAll,
/// Breaking is allowed within “words”.
- "break-all": BreakAll,
+ BreakAll,
/// Breaking is allowed if there is no otherwise acceptable break points in a line.
- "break-word": BreakWord,
+ BreakWord,
}
}
@@ -279,12 +279,12 @@ enum_property! {
/// A value for the [overflow-wrap](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#overflow-wrap-property) property.
pub enum OverflowWrap {
/// Lines may break only at allowed break points.
- "normal": Normal,
+ Normal,
/// Breaking is allowed if there is no otherwise acceptable break points in a line.
- "anywhere": Anywhere,
+ Anywhere,
/// As for anywhere except that soft wrap opportunities introduced by break-word are
/// not considered when calculating min-content intrinsic sizes.
- "break-word": BreakWord,
+ BreakWord,
}
}
@@ -292,21 +292,21 @@ enum_property! {
/// A value for the [text-align](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-align-property) property.
pub enum TextAlign {
/// Inline-level content is aligned to the start edge of the line box.
- "start": Start,
+ Start,
/// Inline-level content is aligned to the end edge of the line box.
- "end": End,
+ End,
/// Inline-level content is aligned to the line-left edge of the line box.
- "left": Left,
+ Left,
/// Inline-level content is aligned to the line-right edge of the line box.
- "right": Right,
+ Right,
/// Inline-level content is centered within the line box.
- "center": Center,
+ Center,
/// Text is justified according to the method specified by the text-justify property.
- "justify": Justify,
+ Justify,
/// Matches the parent element.
- "match-parent": MatchParent,
+ MatchParent,
/// Same as justify, but also justifies the last line.
- "justify-all": JustifyAll,
+ JustifyAll,
}
}
@@ -314,21 +314,21 @@ enum_property! {
/// A value for the [text-align-last](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-align-last-property) property.
pub enum TextAlignLast {
/// Content on the affected line is aligned per `text-align-all` unless set to `justify`, in which case it is start-aligned.
- "auto": Auto,
+ Auto,
/// Inline-level content is aligned to the start edge of the line box.
- "start": Start,
+ Start,
/// Inline-level content is aligned to the end edge of the line box.
- "end": End,
+ End,
/// Inline-level content is aligned to the line-left edge of the line box.
- "left": Left,
+ Left,
/// Inline-level content is aligned to the line-right edge of the line box.
- "right": Right,
+ Right,
/// Inline-level content is centered within the line box.
- "center": Center,
+ Center,
/// Text is justified according to the method specified by the text-justify property.
- "justify": Justify,
+ Justify,
/// Matches the parent element.
- "match-parent": MatchParent,
+ MatchParent,
}
}
@@ -336,19 +336,19 @@ enum_property! {
/// A value for the [text-justify](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-justify-property) property.
pub enum TextJustify {
/// The UA determines the justification algorithm to follow.
- "auto": Auto,
+ Auto,
/// Justification is disabled.
- "none": None,
+ None,
/// Justification adjusts spacing at word separators only.
- "inter-word": InterWord,
+ InterWord,
/// Justification adjusts spacing between each character.
- "inter-character": InterCharacter,
+ InterCharacter,
}
}
/// A value for the [word-spacing](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#word-spacing-property)
/// and [letter-spacing](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#letter-spacing-property) properties.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -364,29 +364,6 @@ pub enum Spacing {
Length(Length),
}
-impl<'i> Parse<'i> for Spacing {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
- return Ok(Spacing::Normal);
- }
-
- let length = Length::parse(input)?;
- Ok(Spacing::Length(length))
- }
-}
-
-impl ToCss for Spacing {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- Spacing::Normal => dest.write_str("normal"),
- Spacing::Length(len) => len.to_css(dest),
- }
- }
-}
-
/// A value for the [text-indent](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-indent-property) property.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
@@ -466,7 +443,7 @@ impl ToCss for TextIndent {
}
/// A value for the [text-size-adjust](https://w3c.github.io/csswg-drafts/css-size-adjust/#adjustment-control) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -484,34 +461,6 @@ pub enum TextSizeAdjust {
Percentage(Percentage),
}
-impl<'i> Parse<'i> for TextSizeAdjust {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(p) = input.try_parse(Percentage::parse) {
- return Ok(TextSizeAdjust::Percentage(p));
- }
-
- let ident = input.expect_ident_cloned()?;
- match_ignore_ascii_case! {&*ident,
- "auto" => Ok(TextSizeAdjust::Auto),
- "none" => Ok(TextSizeAdjust::None),
- _ => Err(input.new_unexpected_token_error(Token::Ident(ident.clone())))
- }
- }
-}
-
-impl ToCss for TextSizeAdjust {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- TextSizeAdjust::Auto => dest.write_str("auto"),
- TextSizeAdjust::None => dest.write_str("none"),
- TextSizeAdjust::Percentage(p) => p.to_css(dest),
- }
- }
-}
-
bitflags! {
/// A value for the [text-decoration-line](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-line-property) property.
///
@@ -749,7 +698,7 @@ impl Default for TextDecorationStyle {
}
/// A value for the [text-decoration-thickness](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-width-property) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -773,34 +722,6 @@ impl Default for TextDecorationThickness {
}
}
-impl<'i> Parse<'i> for TextDecorationThickness {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() {
- return Ok(TextDecorationThickness::Auto);
- }
-
- if input.try_parse(|input| input.expect_ident_matching("from-font")).is_ok() {
- return Ok(TextDecorationThickness::FromFont);
- }
-
- let lp = LengthPercentage::parse(input)?;
- Ok(TextDecorationThickness::LengthPercentage(lp))
- }
-}
-
-impl ToCss for TextDecorationThickness {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- TextDecorationThickness::Auto => dest.write_str("auto"),
- TextDecorationThickness::FromFont => dest.write_str("from-font"),
- TextDecorationThickness::LengthPercentage(lp) => lp.to_css(dest),
- }
- }
-}
-
define_shorthand! {
/// A value for the [text-decoration](https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-property) shorthand property.
pub struct TextDecoration(VendorPrefix) {
@@ -927,15 +848,15 @@ enum_property! {
/// See [TextEmphasisStyle](TextEmphasisStyle).
pub enum TextEmphasisShape {
/// Display small circles as marks.
- "dot": Dot,
+ Dot,
/// Display large circles as marks.
- "circle": Circle,
+ Circle,
/// Display double circles as marks.
- "double-circle": DoubleCircle,
+ DoubleCircle,
/// Display triangles as marks.
- "triangle": Triangle,
+ Triangle,
/// Display sesames as marks.
- "sesame": Sesame,
+ Sesame,
}
}
@@ -1614,16 +1535,16 @@ enum_property! {
/// A value for the [unicode-bidi](https://drafts.csswg.org/css-writing-modes-3/#unicode-bidi) property.
pub enum UnicodeBidi {
/// The box does not open an additional level of embedding.
- "normal": Normal,
+ Normal,
/// If the box is inline, this value creates a directional embedding by opening an additional level of embedding.
- "embed": Embed,
+ Embed,
/// On an inline box, this bidi-isolates its contents.
- "isolate": Isolate,
+ Isolate,
/// This value puts the box’s immediate inline content in a directional override.
- "bidi-override": BidiOverride,
+ BidiOverride,
/// This combines the isolation behavior of isolate with the directional override behavior of bidi-override.
- "isolate-override": IsolateOverride,
+ IsolateOverride,
/// This value behaves as isolate except that the base directionality is determined using a heuristic rather than the direction property.
- "plaintext": Plaintext,
+ Plaintext,
}
}
diff --git a/src/properties/transform.rs b/src/properties/transform.rs
index d58a08fe..c8f5a7f7 100644
--- a/src/properties/transform.rs
+++ b/src/properties/transform.rs
@@ -1382,8 +1382,8 @@ enum_property! {
/// A value for the [transform-style](https://drafts.csswg.org/css-transforms-2/#transform-style-property) property.
#[allow(missing_docs)]
pub enum TransformStyle {
- "flat": Flat,
- "preserve-3d": Preserve3d,
+ Flat,
+ Preserve3d,
}
}
@@ -1391,15 +1391,15 @@ enum_property! {
/// A value for the [transform-box](https://drafts.csswg.org/css-transforms-1/#transform-box) property.
pub enum TransformBox {
/// Uses the content box as reference box.
- "content-box": ContentBox,
+ ContentBox,
/// Uses the border box as reference box.
- "border-box": BorderBox,
+ BorderBox,
/// Uses the object bounding box as reference box.
- "fill-box": FillBox,
+ FillBox,
/// Uses the stroke bounding box as reference box.
- "stroke-box": StrokeBox,
+ StrokeBox,
/// Uses the nearest SVG viewport as reference box.
- "view-box": ViewBox,
+ ViewBox,
}
}
@@ -1413,7 +1413,7 @@ enum_property! {
}
/// A value for the [perspective](https://drafts.csswg.org/css-transforms-2/#perspective-property) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -1429,28 +1429,6 @@ pub enum Perspective {
Length(Length),
}
-impl<'i> Parse<'i> for Perspective {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
- return Ok(Perspective::None);
- }
-
- Ok(Perspective::Length(Length::parse(input)?))
- }
-}
-
-impl ToCss for Perspective {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- Perspective::None => dest.write_str("none"),
- Perspective::Length(len) => len.to_css(dest),
- }
- }
-}
-
/// A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "visitor", derive(Visit))]
diff --git a/src/properties/ui.rs b/src/properties/ui.rs
index 4952082e..11e1eade 100644
--- a/src/properties/ui.rs
+++ b/src/properties/ui.rs
@@ -94,42 +94,42 @@ enum_property! {
/// See [Cursor](Cursor).
#[allow(missing_docs)]
pub enum CursorKeyword {
- "auto": Auto,
- "default": Default,
- "none": None,
- "context-menu": ContextMenu,
- "help": Help,
- "pointer": Pointer,
- "progress": Progress,
- "wait": Wait,
- "cell": Cell,
- "crosshair": Crosshair,
- "text": Text,
- "vertical-text": VerticalText,
- "alias": Alias,
- "copy": Copy,
- "move": Move,
- "no-drop": NoDrop,
- "not-allowed": NotAllowed,
- "grab": Grab,
- "grabbing": Grabbing,
- "e-resize": EResize,
- "n-resize": NResize,
- "ne-resize": NeResize,
- "nw-resize": NwResize,
- "s-resize": SResize,
- "se-resize": SeResize,
- "sw-resize": SwResize,
- "w-resize": WResize,
- "ew-resize": EwResize,
- "ns-resize": NsResize,
- "nesw-resize": NeswResize,
- "nwse-resize": NwseResize,
- "col-resize": ColResize,
- "row-resize": RowResize,
- "all-scroll": AllScroll,
- "zoom-in": ZoomIn,
- "zoom-out": ZoomOut,
+ Auto,
+ Default,
+ None,
+ ContextMenu,
+ Help,
+ Pointer,
+ Progress,
+ Wait,
+ Cell,
+ Crosshair,
+ Text,
+ VerticalText,
+ Alias,
+ Copy,
+ Move,
+ NoDrop,
+ NotAllowed,
+ Grab,
+ Grabbing,
+ EResize,
+ NResize,
+ NeResize,
+ NwResize,
+ SResize,
+ SeResize,
+ SwResize,
+ WResize,
+ EwResize,
+ NsResize,
+ NeswResize,
+ NwseResize,
+ ColResize,
+ RowResize,
+ AllScroll,
+ ZoomIn,
+ ZoomOut,
}
}
@@ -179,7 +179,7 @@ impl<'i> ToCss for Cursor<'i> {
}
/// A value for the [caret-color](https://www.w3.org/TR/2021/WD-css-ui-4-20210316/#caret-color) property.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -201,29 +201,6 @@ impl Default for ColorOrAuto {
}
}
-impl<'i> Parse<'i> for ColorOrAuto {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|input| input.expect_ident_matching("auto")).is_ok() {
- return Ok(ColorOrAuto::Auto);
- }
-
- let color = CssColor::parse(input)?;
- Ok(ColorOrAuto::Color(color))
- }
-}
-
-impl ToCss for ColorOrAuto {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- ColorOrAuto::Auto => dest.write_str("auto"),
- ColorOrAuto::Color(color) => color.to_css(dest),
- }
- }
-}
-
impl FallbackValues for ColorOrAuto {
fn get_fallbacks(&mut self, targets: Targets) -> Vec {
match self {
diff --git a/src/rules/keyframes.rs b/src/rules/keyframes.rs
index 511a3172..adf2fb5b 100644
--- a/src/rules/keyframes.rs
+++ b/src/rules/keyframes.rs
@@ -267,7 +267,7 @@ impl<'i> ToCss for KeyframesRule<'i> {
/// A [keyframe selector](https://drafts.csswg.org/css-animations/#typedef-keyframe-selector)
/// within an `@keyframes` rule.
-#[derive(Debug, PartialEq, Clone)]
+#[derive(Debug, PartialEq, Clone, Parse)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -285,24 +285,6 @@ pub enum KeyframeSelector {
To,
}
-impl<'i> Parse<'i> for KeyframeSelector {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(val) = input.try_parse(Percentage::parse) {
- return Ok(KeyframeSelector::Percentage(val));
- }
-
- let location = input.current_source_location();
- let ident = input.expect_ident()?;
- match_ignore_ascii_case! { &*ident,
- "from" => Ok(KeyframeSelector::From),
- "to" => Ok(KeyframeSelector::To),
- _ => Err(location.new_unexpected_token_error(
- cssparser::Token::Ident(ident.clone())
- ))
- }
- }
-}
-
impl ToCss for KeyframeSelector {
fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
where
diff --git a/src/rules/page.rs b/src/rules/page.rs
index e4897cd3..d95c94c2 100644
--- a/src/rules/page.rs
+++ b/src/rules/page.rs
@@ -81,41 +81,41 @@ enum_property! {
/// A [page margin box](https://www.w3.org/TR/css-page-3/#margin-boxes).
pub enum PageMarginBox {
/// A fixed-size box defined by the intersection of the top and left margins of the page box.
- "top-left-corner": TopLeftCorner,
+ TopLeftCorner,
/// A variable-width box filling the top page margin between the top-left-corner and top-center page-margin boxes.
- "top-left": TopLeft,
+ TopLeft,
/// A variable-width box centered horizontally between the page’s left and right border edges and filling the
/// page top margin between the top-left and top-right page-margin boxes.
- "top-center": TopCenter,
+ TopCenter,
/// A variable-width box filling the top page margin between the top-center and top-right-corner page-margin boxes.
- "top-right": TopRight,
+ TopRight,
/// A fixed-size box defined by the intersection of the top and right margins of the page box.
- "top-right-corner": TopRightCorner,
+ TopRightCorner,
/// A variable-height box filling the left page margin between the top-left-corner and left-middle page-margin boxes.
- "left-top": LeftTop,
+ LeftTop,
/// A variable-height box centered vertically between the page’s top and bottom border edges and filling the
/// left page margin between the left-top and left-bottom page-margin boxes.
- "left-middle": LeftMiddle,
+ LeftMiddle,
/// A variable-height box filling the left page margin between the left-middle and bottom-left-corner page-margin boxes.
- "left-bottom": LeftBottom,
+ LeftBottom,
/// A variable-height box filling the right page margin between the top-right-corner and right-middle page-margin boxes.
- "right-top": RightTop,
+ RightTop,
/// A variable-height box centered vertically between the page’s top and bottom border edges and filling the right
/// page margin between the right-top and right-bottom page-margin boxes.
- "right-middle": RightMiddle,
+ RightMiddle,
/// A variable-height box filling the right page margin between the right-middle and bottom-right-corner page-margin boxes.
- "right-bottom": RightBottom,
+ RightBottom,
/// A fixed-size box defined by the intersection of the bottom and left margins of the page box.
- "bottom-left-corner": BottomLeftCorner,
+ BottomLeftCorner,
/// A variable-width box filling the bottom page margin between the bottom-left-corner and bottom-center page-margin boxes.
- "bottom-left": BottomLeft,
+ BottomLeft,
/// A variable-width box centered horizontally between the page’s left and right border edges and filling the bottom
/// page margin between the bottom-left and bottom-right page-margin boxes.
- "bottom-center": BottomCenter,
+ BottomCenter,
/// A variable-width box filling the bottom page margin between the bottom-center and bottom-right-corner page-margin boxes.
- "bottom-right": BottomRight,
+ BottomRight,
/// A fixed-size box defined by the intersection of the bottom and right margins of the page box.
- "bottom-right-corner": BottomRightCorner,
+ BottomRightCorner,
}
}
diff --git a/src/traits.rs b/src/traits.rs
index 1d520421..4aecaf7c 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -30,6 +30,20 @@ pub trait Parse<'i>: Sized {
}
}
+pub(crate) use lightningcss_derive::Parse;
+
+impl<'i, T: Parse<'i>> Parse<'i> for Option {
+ fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
+ Ok(input.try_parse(T::parse).ok())
+ }
+}
+
+impl<'i, T: Parse<'i>> Parse<'i> for Box {
+ fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
+ Ok(Box::new(T::parse(input)?))
+ }
+}
+
/// Trait for things that can be parsed from CSS syntax and require ParserOptions.
pub trait ParseWithOptions<'i>: Sized {
/// Parse a value of this type with the given options.
@@ -78,6 +92,8 @@ pub trait ToCss {
}
}
+pub(crate) use lightningcss_derive::ToCss;
+
impl<'a, T> ToCss for &'a T
where
T: ToCss + ?Sized,
@@ -90,6 +106,27 @@ where
}
}
+impl ToCss for Box {
+ fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ (**self).to_css(dest)
+ }
+}
+
+impl ToCss for Option {
+ fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ if let Some(v) = self {
+ v.to_css(dest)?;
+ }
+ Ok(())
+ }
+}
+
pub(crate) trait PropertyHandler<'i>: Sized {
fn handle_property(
&mut self,
diff --git a/src/values/calc.rs b/src/values/calc.rs
index 95a0bb59..742c5184 100644
--- a/src/values/calc.rs
+++ b/src/values/calc.rs
@@ -92,13 +92,13 @@ enum_property! {
/// as used in the `round()` function.
pub enum RoundingStrategy {
/// Round to the nearest integer.
- "nearest": Nearest,
+ Nearest,
/// Round up (ceil).
- "up": Up,
+ Up,
/// Round down (floor).
- "down": Down,
+ Down,
/// Round toward zero (truncate).
- "to-zero": ToZero,
+ ToZero,
}
}
diff --git a/src/values/color.rs b/src/values/color.rs
index efa2bd26..b91c1426 100644
--- a/src/values/color.rs
+++ b/src/values/color.rs
@@ -3581,6 +3581,7 @@ impl<'i, V: ?Sized + Visitor<'i, T>, T: Visit<'i, T, V>> Visit<'i, T, V> for RGB
}
enum_property! {
+ #[css(case = lower)]
/// A CSS [system color](https://drafts.csswg.org/css-color/#css-system-colors) keyword.
pub enum SystemColor {
/// Background of accented user interface controls.
diff --git a/src/values/easing.rs b/src/values/easing.rs
index c1e834a0..4db5c063 100644
--- a/src/values/easing.rs
+++ b/src/values/easing.rs
@@ -192,7 +192,7 @@ impl EasingFunction {
}
/// A [step position](https://www.w3.org/TR/css-easing-1/#step-position), used within the `steps()` function.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -233,17 +233,3 @@ impl<'i> Parse<'i> for StepPosition {
Ok(keyword)
}
}
-
-impl ToCss for StepPosition {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- StepPosition::Start => dest.write_str("start"),
- StepPosition::End => dest.write_str("end"),
- StepPosition::JumpNone => dest.write_str("jump-none"),
- StepPosition::JumpBoth => dest.write_str("jump-both"),
- }
- }
-}
diff --git a/src/values/gradient.rs b/src/values/gradient.rs
index 01054b44..fec51938 100644
--- a/src/values/gradient.rs
+++ b/src/values/gradient.rs
@@ -533,7 +533,7 @@ impl LineDirection {
/// A `radial-gradient()` [ending shape](https://www.w3.org/TR/css-images-3/#valdef-radial-gradient-ending-shape).
///
/// See [RadialGradient](RadialGradient).
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -542,10 +542,11 @@ impl LineDirection {
)]
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
pub enum EndingShape {
- /// A circle.
- Circle(Circle),
+ // Note: Ellipse::parse MUST run before Circle::parse for this to be correct.
/// An ellipse.
Ellipse(Ellipse),
+ /// A circle.
+ Circle(Circle),
}
impl Default for EndingShape {
@@ -554,33 +555,6 @@ impl Default for EndingShape {
}
}
-impl<'i> Parse<'i> for EndingShape {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- // Note: Ellipse::parse MUST run before Circle::parse for this to be correct.
- if let Ok(ellipse) = input.try_parse(Ellipse::parse) {
- return Ok(EndingShape::Ellipse(ellipse));
- }
-
- if let Ok(circle) = input.try_parse(Circle::parse) {
- return Ok(EndingShape::Circle(circle));
- }
-
- return Err(input.new_error_for_next_token());
- }
-}
-
-impl ToCss for EndingShape {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- EndingShape::Circle(circle) => circle.to_css(dest),
- EndingShape::Ellipse(ellipse) => ellipse.to_css(dest),
- }
- }
-}
-
/// A circle ending shape for a `radial-gradient()`.
///
/// See [RadialGradient](RadialGradient).
@@ -734,13 +708,13 @@ enum_property! {
/// See [RadialGradient](RadialGradient).
pub enum ShapeExtent {
/// The closest side of the box to the gradient's center.
- "closest-side": ClosestSide,
+ ClosestSide,
/// The farthest side of the box from the gradient's center.
- "farthest-side": FarthestSide,
+ FarthestSide,
/// The closest cornder of the box to the gradient's center.
- "closest-corner": ClosestCorner,
+ ClosestCorner,
/// The farthest corner of the box from the gradient's center.
- "farthest-corner": FarthestCorner,
+ FarthestCorner,
}
}
diff --git a/src/values/image.rs b/src/values/image.rs
index fa778cc2..e6ca26c5 100644
--- a/src/values/image.rs
+++ b/src/values/image.rs
@@ -19,7 +19,7 @@ use cssparser::*;
use smallvec::SmallVec;
/// A CSS [``](https://www.w3.org/TR/css-images-3/#image-values) value.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
#[cfg_attr(feature = "visitor", visit(visit_image, IMAGES))]
@@ -309,42 +309,6 @@ impl<'i, T: ImageFallback<'i>> FallbackValues for SmallVec<[T; 1]> {
}
}
-impl<'i> Parse<'i> for Image<'i> {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
- return Ok(Image::None);
- }
-
- if let Ok(url) = input.try_parse(Url::parse) {
- return Ok(Image::Url(url));
- }
-
- if let Ok(grad) = input.try_parse(Gradient::parse) {
- return Ok(Image::Gradient(Box::new(grad)));
- }
-
- if let Ok(image_set) = input.try_parse(ImageSet::parse) {
- return Ok(Image::ImageSet(image_set));
- }
-
- Err(input.new_error_for_next_token())
- }
-}
-
-impl<'i> ToCss for Image<'i> {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- Image::None => dest.write_str("none"),
- Image::Url(url) => url.to_css(dest),
- Image::Gradient(grad) => grad.to_css(dest),
- Image::ImageSet(image_set) => image_set.to_css(dest),
- }
- }
-}
-
/// A CSS [`image-set()`](https://drafts.csswg.org/css-images-4/#image-set-notation) value.
///
/// `image-set()` allows the user agent to choose between multiple versions of an image to
diff --git a/src/values/length.rs b/src/values/length.rs
index 58967f08..149f1dd1 100644
--- a/src/values/length.rs
+++ b/src/values/length.rs
@@ -49,7 +49,7 @@ impl IsCompatible for LengthPercentage {
}
/// Either a [``](https://www.w3.org/TR/css-values-4/#typedef-length-percentage), or the `auto` keyword.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -65,33 +65,6 @@ pub enum LengthPercentageOrAuto {
LengthPercentage(LengthPercentage),
}
-impl<'i> Parse<'i> for LengthPercentageOrAuto {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if input.try_parse(|i| i.expect_ident_matching("auto")).is_ok() {
- return Ok(LengthPercentageOrAuto::Auto);
- }
-
- if let Ok(percent) = input.try_parse(|input| LengthPercentage::parse(input)) {
- return Ok(LengthPercentageOrAuto::LengthPercentage(percent));
- }
-
- Err(input.new_error_for_next_token())
- }
-}
-
-impl ToCss for LengthPercentageOrAuto {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- use LengthPercentageOrAuto::*;
- match self {
- Auto => dest.write_str("auto"),
- LengthPercentage(l) => l.to_css(dest),
- }
- }
-}
-
impl IsCompatible for LengthPercentageOrAuto {
fn is_compatible(&self, browsers: Browsers) -> bool {
match self {
@@ -830,7 +803,7 @@ impl TrySign for Length {
impl_try_from_angle!(Length);
/// Either a [``](https://www.w3.org/TR/css-values-4/#lengths) or a [``](https://www.w3.org/TR/css-values-4/#numbers).
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -840,10 +813,10 @@ impl_try_from_angle!(Length);
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
pub enum LengthOrNumber {
- /// A length.
- Length(Length),
/// A number.
Number(CSSNumber),
+ /// A length.
+ Length(Length),
}
impl Default for LengthOrNumber {
@@ -865,33 +838,6 @@ impl Zero for LengthOrNumber {
}
}
-impl<'i> Parse<'i> for LengthOrNumber {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- // Parse number first so unitless numbers are not parsed as lengths.
- if let Ok(number) = input.try_parse(CSSNumber::parse) {
- return Ok(LengthOrNumber::Number(number));
- }
-
- if let Ok(length) = Length::parse(input) {
- return Ok(LengthOrNumber::Length(length));
- }
-
- Err(input.new_error_for_next_token())
- }
-}
-
-impl ToCss for LengthOrNumber {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- LengthOrNumber::Length(length) => length.to_css(dest),
- LengthOrNumber::Number(number) => number.to_css(dest),
- }
- }
-}
-
impl IsCompatible for LengthOrNumber {
fn is_compatible(&self, browsers: Browsers) -> bool {
match self {
diff --git a/src/values/percentage.rs b/src/values/percentage.rs
index c51be75e..54352750 100644
--- a/src/values/percentage.rs
+++ b/src/values/percentage.rs
@@ -144,7 +144,7 @@ impl_op!(Percentage, std::ops::Add, add);
impl_try_from_angle!(Percentage);
/// Either a `` or ``.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -154,36 +154,10 @@ impl_try_from_angle!(Percentage);
#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
pub enum NumberOrPercentage {
- /// A percentage.
- Percentage(Percentage),
/// A number.
Number(CSSNumber),
-}
-
-impl<'i> Parse<'i> for NumberOrPercentage {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(number) = input.try_parse(CSSNumber::parse) {
- return Ok(NumberOrPercentage::Number(number));
- }
-
- if let Ok(percent) = input.try_parse(|input| Percentage::parse(input)) {
- return Ok(NumberOrPercentage::Percentage(percent));
- }
-
- Err(input.new_error_for_next_token())
- }
-}
-
-impl ToCss for NumberOrPercentage {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- NumberOrPercentage::Percentage(percent) => percent.to_css(dest),
- NumberOrPercentage::Number(number) => number.to_css(dest),
- }
- }
+ /// A percentage.
+ Percentage(Percentage),
}
impl std::convert::Into for &NumberOrPercentage {
diff --git a/src/values/shape.rs b/src/values/shape.rs
index c5f4cc31..9cb5f585 100644
--- a/src/values/shape.rs
+++ b/src/values/shape.rs
@@ -59,7 +59,7 @@ pub struct Circle {
/// A [``](https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius) value
/// that defines the radius of a `circle()` or `ellipse()` shape.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
#[cfg_attr(feature = "visitor", derive(Visit))]
#[cfg_attr(
feature = "serde",
@@ -182,21 +182,6 @@ impl<'i> Parse<'i> for Circle {
}
}
-impl<'i> Parse<'i> for ShapeRadius {
- fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
- if let Ok(len) = input.try_parse(LengthPercentage::parse) {
- return Ok(ShapeRadius::LengthPercentage(len));
- }
-
- if input.try_parse(|input| input.expect_ident_matching("closest-side")).is_ok() {
- return Ok(ShapeRadius::ClosestSide);
- }
-
- input.expect_ident_matching("farthest-side")?;
- Ok(ShapeRadius::FarthestSide)
- }
-}
-
impl Default for ShapeRadius {
fn default() -> ShapeRadius {
ShapeRadius::ClosestSide
@@ -315,19 +300,6 @@ impl ToCss for Circle {
}
}
-impl ToCss for ShapeRadius {
- fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
- where
- W: std::fmt::Write,
- {
- match self {
- ShapeRadius::LengthPercentage(len) => len.to_css(dest),
- ShapeRadius::ClosestSide => dest.write_str("closest-side"),
- ShapeRadius::FarthestSide => dest.write_str("farthest-side"),
- }
- }
-}
-
impl ToCss for Ellipse {
fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
where
From 39964f1d78d237bb90006d1517e05b15c4b10514 Mon Sep 17 00:00:00 2001
From: Devon Govett
Date: Sun, 2 Jun 2024 23:36:22 -0700
Subject: [PATCH 002/139] Implement animation-range properties
#572
---
node/ast.d.ts | 66 ++++++++++
src/lib.rs | 243 ++++++++++++++++++++++++++++++++++
src/properties/animation.rs | 252 +++++++++++++++++++++++++++++++++++-
src/properties/mod.rs | 3 +
4 files changed, 563 insertions(+), 1 deletion(-)
diff --git a/node/ast.d.ts b/node/ast.d.ts
index 8bc02772..fba3f69e 100644
--- a/node/ast.d.ts
+++ b/node/ast.d.ts
@@ -1960,6 +1960,15 @@ export type PropertyId =
| {
property: "animation-timeline";
}
+ | {
+ property: "animation-range-start";
+ }
+ | {
+ property: "animation-range-end";
+ }
+ | {
+ property: "animation-range";
+ }
| {
property: "animation";
vendorPrefix: VendorPrefix;
@@ -3316,6 +3325,18 @@ export type Declaration =
property: "animation-timeline";
value: AnimationTimeline[];
}
+ | {
+ property: "animation-range-start";
+ value: AnimationRangeStart[];
+ }
+ | {
+ property: "animation-range-end";
+ value: AnimationRangeEnd[];
+ }
+ | {
+ property: "animation-range";
+ value: AnimationRange[];
+ }
| {
property: "animation";
value: Animation[];
@@ -5541,6 +5562,38 @@ export type Scroller = "root" | "nearest" | "self";
* @maxItems 2
*/
export type Size2DFor_LengthPercentageOrAuto = [LengthPercentageOrAuto, LengthPercentageOrAuto];
+/**
+ * A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) property.
+ */
+export type AnimationRangeStart = AnimationAttachmentRange;
+/**
+ * A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) or [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property.
+ */
+export type AnimationAttachmentRange =
+ | "Normal"
+ | {
+ LengthPercentage: DimensionPercentageFor_LengthValue;
+ }
+ | {
+ TimelineRange: {
+ /**
+ * The name of the timeline range.
+ */
+ name: TimelineRangeName;
+ /**
+ * The offset from the start of the named timeline range.
+ */
+ offset: DimensionPercentageFor_LengthValue;
+ };
+ };
+/**
+ * A [view progress timeline range](https://drafts.csswg.org/scroll-animations/#view-timelines-ranges)
+ */
+export type TimelineRangeName = "cover" | "contain" | "entry" | "exit" | "entry-crossing" | "exit-crossing";
+/**
+ * A value for the [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property.
+ */
+export type AnimationRangeEnd = AnimationAttachmentRange;
/**
* An individual [transform function](https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#two-d-transform-functions).
*/
@@ -8575,6 +8628,19 @@ export interface ViewTimeline {
*/
inset: Size2DFor_LengthPercentageOrAuto;
}
+/**
+ * A value for the [animation-range](https://drafts.csswg.org/scroll-animations/#animation-range) shorthand property.
+ */
+export interface AnimationRange {
+ /**
+ * The end of the animation's attachment range.
+ */
+ end: AnimationRangeEnd;
+ /**
+ * The start of the animation's attachment range.
+ */
+ start: AnimationRangeStart;
+}
/**
* A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property.
*/
diff --git a/src/lib.rs b/src/lib.rs
index 27a7df16..8b9535f0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11585,6 +11585,249 @@ mod tests {
..Browsers::default()
},
);
+
+ minify_test(
+ ".foo { animation-range-start: entry 10% }",
+ ".foo{animation-range-start:entry 10%}",
+ );
+ minify_test(
+ ".foo { animation-range-start: entry 0% }",
+ ".foo{animation-range-start:entry}",
+ );
+ minify_test(
+ ".foo { animation-range-start: entry }",
+ ".foo{animation-range-start:entry}",
+ );
+ minify_test(".foo { animation-range-start: 50% }", ".foo{animation-range-start:50%}");
+ minify_test(
+ ".foo { animation-range-end: exit 10% }",
+ ".foo{animation-range-end:exit 10%}",
+ );
+ minify_test(
+ ".foo { animation-range-end: exit 100% }",
+ ".foo{animation-range-end:exit}",
+ );
+ minify_test(".foo { animation-range-end: exit }", ".foo{animation-range-end:exit}");
+ minify_test(".foo { animation-range-end: 50% }", ".foo{animation-range-end:50%}");
+ minify_test(
+ ".foo { animation-range: entry 10% exit 90% }",
+ ".foo{animation-range:entry 10% exit 90%}",
+ );
+ minify_test(
+ ".foo { animation-range: entry 0% exit 100% }",
+ ".foo{animation-range:entry exit}",
+ );
+ minify_test(".foo { animation-range: entry }", ".foo{animation-range:entry}");
+ minify_test(
+ ".foo { animation-range: entry 0% entry 100% }",
+ ".foo{animation-range:entry}",
+ );
+ minify_test(".foo { animation-range: 50% normal }", ".foo{animation-range:50%}");
+ minify_test(
+ ".foo { animation-range: normal normal }",
+ ".foo{animation-range:normal}",
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: entry 10%;
+ animation-range-end: exit 90%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry 10% exit 90%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: entry 0%;
+ animation-range-end: entry 100%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: entry 0%;
+ animation-range-end: exit 100%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry exit;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: 10%;
+ animation-range-end: normal;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: 10%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: 10%;
+ animation-range-end: 90%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: 10% 90%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: entry 10%;
+ animation-range-end: exit 100%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry 10% exit;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: 10%;
+ animation-range-end: exit 90%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: 10% exit 90%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: entry 10%;
+ animation-range-end: 90%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry 10% 90%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range: entry;
+ animation-range-end: 90%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry 90%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range: entry;
+ animation-range-end: var(--end);
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry;
+ animation-range-end: var(--end);
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: entry 10%, entry 50%;
+ animation-range-end: exit 90%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range-start: entry 10%, entry 50%;
+ animation-range-end: exit 90%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range-start: entry 10%, entry 50%;
+ animation-range-end: exit 90%, exit 100%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation-range: entry 10% exit 90%, entry 50% exit;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range: entry;
+ animation-range-end: 90%;
+ animation: spin 100ms;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation: .1s spin;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation: spin 100ms;
+ animation-range: entry;
+ animation-range-end: 90%;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation: .1s spin;
+ animation-range: entry 90%;
+ }
+ "#},
+ );
+ test(
+ r#"
+ .foo {
+ animation-range: entry;
+ animation-range-end: 90%;
+ animation: var(--animation) 100ms;
+ }
+ "#,
+ indoc! {r#"
+ .foo {
+ animation: var(--animation) .1s;
+ }
+ "#},
+ );
}
#[test]
diff --git a/src/properties/animation.rs b/src/properties/animation.rs
index de83b9fa..9855127c 100644
--- a/src/properties/animation.rs
+++ b/src/properties/animation.rs
@@ -12,6 +12,7 @@ use crate::properties::{Property, PropertyId, TokenOrValue, VendorPrefix};
use crate::traits::{Parse, PropertyHandler, Shorthand, ToCss, Zero};
use crate::values::ident::DashedIdent;
use crate::values::number::CSSNumber;
+use crate::values::percentage::Percentage;
use crate::values::size::Size2D;
use crate::values::string::CSSString;
use crate::values::{easing::EasingFunction, ident::CustomIdent, time::Time};
@@ -21,7 +22,7 @@ use cssparser::*;
use itertools::izip;
use smallvec::SmallVec;
-use super::LengthPercentageOrAuto;
+use super::{LengthPercentage, LengthPercentageOrAuto};
/// A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property.
#[derive(Debug, Clone, PartialEq, Parse)]
@@ -375,6 +376,201 @@ impl ToCss for ViewTimeline {
}
}
+/// A [view progress timeline range](https://drafts.csswg.org/scroll-animations/#view-timelines-ranges)
+#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
+#[cfg_attr(feature = "visitor", derive(Visit))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
+#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
+pub enum TimelineRangeName {
+ /// Represents the full range of the view progress timeline.
+ Cover,
+ /// Represents the range during which the principal box is either fully contained by,
+ /// or fully covers, its view progress visibility range within the scrollport.
+ Contain,
+ /// Represents the range during which the principal box is entering the view progress visibility range.
+ Entry,
+ /// Represents the range during which the principal box is exiting the view progress visibility range.
+ Exit,
+ /// Represents the range during which the principal box crosses the end border edge.
+ EntryCrossing,
+ /// Represents the range during which the principal box crosses the start border edge.
+ ExitCrossing,
+}
+
+/// A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start)
+/// or [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "visitor", derive(Visit))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
+#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
+pub enum AnimationAttachmentRange {
+ /// The start of the animation’s attachment range is the start of its associated timeline.
+ Normal,
+ /// The animation attachment range starts at the specified point on the timeline measuring from the start of the timeline.
+ LengthPercentage(LengthPercentage),
+ /// The animation attachment range starts at the specified point on the timeline measuring from the start of the specified named timeline range.
+ TimelineRange {
+ /// The name of the timeline range.
+ name: TimelineRangeName,
+ /// The offset from the start of the named timeline range.
+ offset: LengthPercentage,
+ },
+}
+
+impl<'i> AnimationAttachmentRange {
+ fn parse<'t>(input: &mut Parser<'i, 't>, default: f32) -> Result<'i, ParserError<'i>>> {
+ if input.try_parse(|input| input.expect_ident_matching("normal")).is_ok() {
+ return Ok(AnimationAttachmentRange::Normal);
+ }
+
+ if let Ok(val) = input.try_parse(LengthPercentage::parse) {
+ return Ok(AnimationAttachmentRange::LengthPercentage(val));
+ }
+
+ let name = TimelineRangeName::parse(input)?;
+ let offset = input
+ .try_parse(LengthPercentage::parse)
+ .unwrap_or(LengthPercentage::Percentage(Percentage(default)));
+ Ok(AnimationAttachmentRange::TimelineRange { name, offset })
+ }
+
+ fn to_css(&self, dest: &mut Printer, default: f32) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ match self {
+ Self::Normal => dest.write_str("normal"),
+ Self::LengthPercentage(val) => val.to_css(dest),
+ Self::TimelineRange { name, offset } => {
+ name.to_css(dest)?;
+ if *offset != LengthPercentage::Percentage(Percentage(default)) {
+ dest.write_char(' ')?;
+ offset.to_css(dest)?;
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+impl Default for AnimationAttachmentRange {
+ fn default() -> Self {
+ AnimationAttachmentRange::Normal
+ }
+}
+
+/// A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) property.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "visitor", derive(Visit))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
+#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
+pub struct AnimationRangeStart(pub AnimationAttachmentRange);
+
+impl<'i> Parse<'i> for AnimationRangeStart {
+ fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
+ let range = AnimationAttachmentRange::parse(input, 0.0)?;
+ Ok(Self(range))
+ }
+}
+
+impl ToCss for AnimationRangeStart {
+ fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ self.0.to_css(dest, 0.0)
+ }
+}
+
+/// A value for the [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "visitor", derive(Visit))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
+#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
+pub struct AnimationRangeEnd(pub AnimationAttachmentRange);
+
+impl<'i> Parse<'i> for AnimationRangeEnd {
+ fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
+ let range = AnimationAttachmentRange::parse(input, 1.0)?;
+ Ok(Self(range))
+ }
+}
+
+impl ToCss for AnimationRangeEnd {
+ fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ self.0.to_css(dest, 1.0)
+ }
+}
+
+/// A value for the [animation-range](https://drafts.csswg.org/scroll-animations/#animation-range) shorthand property.
+#[derive(Debug, Clone, PartialEq)]
+#[cfg_attr(feature = "visitor", derive(Visit))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
+#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
+pub struct AnimationRange {
+ /// The start of the animation's attachment range.
+ pub start: AnimationRangeStart,
+ /// The end of the animation's attachment range.
+ pub end: AnimationRangeEnd,
+}
+
+impl<'i> Parse<'i> for AnimationRange {
+ fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<'i, ParserError<'i>>> {
+ let start = AnimationRangeStart::parse(input)?;
+ let end = input
+ .try_parse(AnimationRangeStart::parse)
+ .map(|r| AnimationRangeEnd(r.0))
+ .unwrap_or_else(|_| {
+ // If <'animation-range-end'> is omitted and <'animation-range-start'> includes a component, then
+ // animation-range-end is set to that same and 100%. Otherwise, any omitted longhand is set to its initial value.
+ match &start.0 {
+ AnimationAttachmentRange::TimelineRange { name, .. } => {
+ AnimationRangeEnd(AnimationAttachmentRange::TimelineRange {
+ name: name.clone(),
+ offset: LengthPercentage::Percentage(Percentage(1.0)),
+ })
+ }
+ _ => AnimationRangeEnd(AnimationAttachmentRange::default()),
+ }
+ });
+ Ok(AnimationRange { start, end })
+ }
+}
+
+impl ToCss for AnimationRange {
+ fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ self.start.to_css(dest)?;
+
+ let omit_end = match (&self.start.0, &self.end.0) {
+ (
+ AnimationAttachmentRange::TimelineRange { name: start_name, .. },
+ AnimationAttachmentRange::TimelineRange {
+ name: end_name,
+ offset: end_offset,
+ },
+ ) => start_name == end_name && *end_offset == LengthPercentage::Percentage(Percentage(1.0)),
+ (_, end) => *end == AnimationAttachmentRange::default(),
+ };
+
+ if !omit_end {
+ dest.write_char(' ')?;
+ self.end.to_css(dest)?;
+ }
+ Ok(())
+ }
+}
+
define_list_shorthand! {
/// A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property.
pub struct Animation<'i>(VendorPrefix) {
@@ -524,6 +720,8 @@ pub(crate) struct AnimationHandler<'i> {
delays: Option<(SmallVec<[Time; 1]>, VendorPrefix)>,
fill_modes: Option<(SmallVec<[AnimationFillMode; 1]>, VendorPrefix)>,
timelines: Option<[AnimationTimeline<'i>; 1]>>,
+ range_starts: Option<[AnimationRangeStart; 1]>>,
+ range_ends: Option<[AnimationRangeEnd; 1]>>,
has_any: bool,
}
@@ -574,6 +772,19 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> {
self.timelines = Some(val.clone());
self.has_any = true;
}
+ Property::AnimationRangeStart(val) => {
+ self.range_starts = Some(val.clone());
+ self.has_any = true;
+ }
+ Property::AnimationRangeEnd(val) => {
+ self.range_ends = Some(val.clone());
+ self.has_any = true;
+ }
+ Property::AnimationRange(val) => {
+ self.range_starts = Some(val.iter().map(|v| v.start.clone()).collect());
+ self.range_ends = Some(val.iter().map(|v| v.end.clone()).collect());
+ self.has_any = true;
+ }
Property::Animation(val, vp) => {
let names = val.iter().map(|b| b.name.clone()).collect();
maybe_flush!(names, &names, vp);
@@ -609,6 +820,11 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> {
property!(play_states, &play_states, vp);
property!(delays, &delays, vp);
property!(fill_modes, &fill_modes, vp);
+
+ // The animation shorthand resets animation-range
+ // https://drafts.csswg.org/scroll-animations/#named-range-animation-declaration
+ self.range_starts = None;
+ self.range_ends = None;
}
Property::Unparsed(val) if is_animation_property(&val.property_id) => {
let mut val = Cow::Borrowed(val);
@@ -636,6 +852,9 @@ impl<'i> PropertyHandler<'i> for AnimationHandler<'i> {
_ => {}
}
}
+
+ self.range_starts = None;
+ self.range_ends = None;
}
self.flush(dest, context);
@@ -671,6 +890,8 @@ impl<'i> AnimationHandler<'i> {
let mut delays = std::mem::take(&mut self.delays);
let mut fill_modes = std::mem::take(&mut self.fill_modes);
let mut timelines_value = std::mem::take(&mut self.timelines);
+ let range_starts = std::mem::take(&mut self.range_starts);
+ let range_ends = std::mem::take(&mut self.range_ends);
if let (
Some((names, names_vp)),
@@ -813,6 +1034,32 @@ impl<'i> AnimationHandler<'i> {
if let Some(val) = timelines_value {
dest.push(Property::AnimationTimeline(val));
}
+
+ match (range_starts, range_ends) {
+ (Some(range_starts), Some(range_ends)) => {
+ if range_starts.len() == range_ends.len() {
+ dest.push(Property::AnimationRange(
+ range_starts
+ .into_iter()
+ .zip(range_ends.into_iter())
+ .map(|(start, end)| AnimationRange { start, end })
+ .collect(),
+ ));
+ } else {
+ dest.push(Property::AnimationRangeStart(range_starts));
+ dest.push(Property::AnimationRangeEnd(range_ends));
+ }
+ }
+ (range_starts, range_ends) => {
+ if let Some(range_starts) = range_starts {
+ dest.push(Property::AnimationRangeStart(range_starts));
+ }
+
+ if let Some(range_ends) = range_ends {
+ dest.push(Property::AnimationRangeEnd(range_ends));
+ }
+ }
+ }
}
}
@@ -829,6 +1076,9 @@ fn is_animation_property(property_id: &PropertyId) -> bool {
| PropertyId::AnimationFillMode(_)
| PropertyId::AnimationComposition
| PropertyId::AnimationTimeline
+ | PropertyId::AnimationRange
+ | PropertyId::AnimationRangeStart
+ | PropertyId::AnimationRangeEnd
| PropertyId::Animation(_) => true,
_ => false,
}
diff --git a/src/properties/mod.rs b/src/properties/mod.rs
index 38ff8622..7c5f4071 100644
--- a/src/properties/mod.rs
+++ b/src/properties/mod.rs
@@ -1493,6 +1493,9 @@ define_properties! {
"animation-fill-mode": AnimationFillMode(SmallVec<[AnimationFillMode; 1]>, VendorPrefix) / WebKit / Moz / O,
"animation-composition": AnimationComposition(SmallVec<[AnimationComposition; 1]>),
"animation-timeline": AnimationTimeline(SmallVec<[AnimationTimeline<'i>; 1]>),
+ "animation-range-start": AnimationRangeStart(SmallVec<[AnimationRangeStart; 1]>),
+ "animation-range-end": AnimationRangeEnd(SmallVec<[AnimationRangeEnd; 1]>),
+ "animation-range": AnimationRange(SmallVec<[AnimationRange; 1]>),
"animation": Animation(AnimationList<'i>, VendorPrefix) / WebKit / Moz / O shorthand: true,
// https://drafts.csswg.org/css-transforms-2/
From f7c883131c3463756ac935496cfe5da710eec601 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?=
Date: Fri, 2 Aug 2024 00:30:56 +0900
Subject: [PATCH 003/139] fix: Fix codegen of `:is(input:checked)` (#783)
---
src/selector.rs | 7 ++++++-
tests/cli_integration_tests.rs | 20 ++++++++++++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/src/selector.rs b/src/selector.rs
index 62550198..56df04c8 100644
--- a/src/selector.rs
+++ b/src/selector.rs
@@ -1624,8 +1624,13 @@ where
#[inline]
fn has_type_selector(selector: &Selector) -> bool {
- let mut iter = selector.iter_raw_parse_order_from(0);
+ // For input:checked the component vector is
+ // [input, :checked] so we have to check it using matching order.
+ //
+ // This both happens for input:checked and is(input:checked)
+ let mut iter = selector.iter_raw_match_order();
let first = iter.next();
+
if is_namespace(first) {
is_type_selector(iter.next())
} else {
diff --git a/tests/cli_integration_tests.rs b/tests/cli_integration_tests.rs
index 744ac7ca..c57d26e8 100644
--- a/tests/cli_integration_tests.rs
+++ b/tests/cli_integration_tests.rs
@@ -759,3 +759,23 @@ fn browserslist_environment_from_browserslist_env() -> Result<(), Box Result<(), Box> {
+ let infile = assert_fs::NamedTempFile::new("test.css")?;
+ infile.write_str(
+ r#"
+ .cb:is(input:checked) {
+ margin: 3rem;
+ }
+ "#,
+ )?;
+ let outfile = assert_fs::NamedTempFile::new("test.out")?;
+ let mut cmd = Command::cargo_bin("lightningcss")?;
+ cmd.arg(infile.path());
+ cmd.arg("--output-file").arg(outfile.path());
+ cmd.assert().success();
+ outfile.assert(predicate::str::contains(indoc! {r#".cb:is(input:checked)"#}));
+
+ Ok(())
+}
From 3e623473839c7fc76b277f97f88c649c6c94e44e Mon Sep 17 00:00:00 2001
From: neverland
Date: Thu, 1 Aug 2024 23:31:42 +0800
Subject: [PATCH 004/139] Bump browserslist-rs 0.16.0 (#772)
---
Cargo.lock | 156 ++++++++++++++++++++++++++-----------------------
Cargo.toml | 2 +-
c/Cargo.toml | 2 +-
src/targets.rs | 4 +-
4 files changed, 88 insertions(+), 76 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index 07d5f7e1..29c70813 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -57,12 +57,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
-[[package]]
-name = "anyhow"
-version = "1.0.75"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
-
[[package]]
name = "assert_cmd"
version = "2.0.12"
@@ -145,23 +139,19 @@ dependencies = [
[[package]]
name = "browserslist-rs"
-version = "0.15.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "405bbd46590a441abe5db3e5c8af005aa42e640803fecb51912703e93e4ce8d3"
+checksum = "fdf0ca73de70c3da94e4194e4a01fe732378f55d47cf4c0588caab22a0dbfa14"
dependencies = [
"ahash 0.8.7",
- "anyhow",
"chrono",
"either",
"indexmap 2.2.6",
- "itertools 0.12.1",
+ "itertools 0.13.0",
"nom",
"once_cell",
- "quote",
"serde",
"serde_json",
- "string_cache",
- "string_cache_codegen",
"thiserror",
]
@@ -246,14 +236,14 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.31"
+version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -485,9 +475,9 @@ checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
[[package]]
name = "either"
-version = "1.9.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "equivalent"
@@ -699,9 +689,9 @@ dependencies = [
[[package]]
name = "itertools"
-version = "0.12.1"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
@@ -955,12 +945,6 @@ dependencies = [
"libloading",
]
-[[package]]
-name = "new_debug_unreachable"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
-
[[package]]
name = "nom"
version = "7.1.3"
@@ -1035,16 +1019,6 @@ dependencies = [
"vlq",
]
-[[package]]
-name = "parking_lot"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
[[package]]
name = "parking_lot_core"
version = "0.9.9"
@@ -1055,7 +1029,7 @@ dependencies = [
"libc",
"redox_syscall",
"smallvec",
- "windows-targets",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -1586,32 +1560,6 @@ dependencies = [
"syn 1.0.109",
]
-[[package]]
-name = "string_cache"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
-dependencies = [
- "new_debug_unreachable",
- "once_cell",
- "parking_lot",
- "phf_shared 0.10.0",
- "precomputed-hash",
- "serde",
-]
-
-[[package]]
-name = "string_cache_codegen"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
- "proc-macro2",
- "quote",
-]
-
[[package]]
name = "strsim"
version = "0.10.0"
@@ -1880,7 +1828,7 @@ version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -1889,7 +1837,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -1898,13 +1846,29 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
]
[[package]]
@@ -1913,42 +1877,90 @@ version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
[[package]]
name = "wyz"
version = "0.2.0"
diff --git a/Cargo.toml b/Cargo.toml
index 75bc397c..80e64af3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,7 +65,7 @@ paste = "1.0.12"
# CLI deps
atty = { version = "0.2", optional = true }
clap = { version = "3.0.6", features = ["derive"], optional = true }
-browserslist-rs = { version = "0.15.0", optional = true }
+browserslist-rs = { version = "0.16.0", optional = true }
rayon = { version = "1.5.1", optional = true }
dashmap = { version = "5.0.0", optional = true }
serde_json = { version = "1.0.78", optional = true }
diff --git a/c/Cargo.toml b/c/Cargo.toml
index 443d34ba..96641dc7 100644
--- a/c/Cargo.toml
+++ b/c/Cargo.toml
@@ -11,7 +11,7 @@ crate-type = ["cdylib"]
[dependencies]
lightningcss = { path = "../", features = ["browserslist"] }
parcel_sourcemap = { version = "2.1.1", features = ["json"] }
-browserslist-rs = { version = "0.15.0" }
+browserslist-rs = { version = "0.16.0" }
[build-dependencies]
cbindgen = "0.24.3"
diff --git a/src/targets.rs b/src/targets.rs
index 3dd40061..4d44ddae 100644
--- a/src/targets.rs
+++ b/src/targets.rs
@@ -48,7 +48,7 @@ impl Browsers {
) -> Result