Skip to main content

css_ast/traits/
declaration_metadata.rs

1use bitmask_enum::bitmask;
2
3use crate::{CssAtomSet, UnitlessZeroResolves};
4
5/// The CSS specification/module that a property belongs to.
6#[bitmask(u128)]
7#[bitmask_config(vec_debug)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum PropertyGroup {
10	Align,
11	AnchorPosition,
12	Css2,
13	AnimationTriggers,
14	Animations,
15	Backgrounds,
16	Borders,
17	Box,
18	Break,
19	Cascade,
20	Color,
21	ColorAdjust,
22	ColorHdr,
23	Compositing,
24	Conditional,
25	Contain,
26	Content,
27	CounterStyle,
28	Display,
29	Exclusions,
30	FillStroke,
31	FilterEffects,
32	Flexbox,
33	Fonts,
34	Forms,
35	Gaps,
36	Gcpm,
37	Grid,
38	Images,
39	ImageAnimation,
40	Inline,
41	LineGrid,
42	LinkParams,
43	Lists,
44	Logical,
45	Masking,
46	Motion,
47	Multicol,
48	Nav,
49	Overflow,
50	Overscroll,
51	Page,
52	PageFloats,
53	PointerAnimations,
54	PointerEvents,
55	Position,
56	Regions,
57	Rhythm,
58	RoundDisplay,
59	Ruby,
60	ScrollAnchoring,
61	ScrollAnimations,
62	ScrollSnap,
63	Scrollbars,
64	Shaders,
65	Shapes,
66	SvgPainting,
67	SizeAdjust,
68	Sizing,
69	Speech,
70	Tables,
71	Text,
72	TextDecor,
73	Transforms,
74	Transitions,
75	Ui,
76	Values,
77	Variables,
78	ViewTransitions,
79	Viewport,
80	WillChange,
81	WritingModes,
82}
83
84pub enum Inherits {
85	False,
86	True,
87	Unknown,
88}
89
90impl Inherits {
91	pub fn to_bool(self, unknown: bool) -> bool {
92		match self {
93			Self::False => false,
94			Self::True => true,
95			Self::Unknown => unknown,
96		}
97	}
98}
99
100pub enum Percentages {
101	/// This style value has no way of expressing values as a percentage.
102	None,
103	/// Any percentage expressed in this value pertains to the size of the containing block.
104	ContainingBlock,
105	/// Any percentage expressed in this value pertains to the size of the border box.
106	BorderBox,
107	/// Any percentage expressed in this value is a syntax affordance; a Number token would be the equivalent value.
108	Number,
109	/// Relative to the 1em Font-Size
110	FontSize,
111	/// Relative to the Font-Size of the parent element
112	ParentFontSize,
113	/// Relative to the scroll container's scrollport
114	Scrollport,
115	/// Relative to the content area dimension
116	ContentArea,
117	/// Relative to the border-edge side length
118	BorderEdge,
119	/// Relative to the background positioning area
120	BackgroundPositioningArea,
121	/// Relative to the reference box size
122	ReferenceBox,
123	/// Relative to the element's own dimensions
124	SelfSize,
125	/// Relative to the line box
126	LineBox,
127	/// Relative to the flex container
128	FlexContainer,
129	/// Relative to the border image area
130	BorderImageArea,
131	/// Map to a normalized range (e.g., `[0,1]`)
132	NormalizedRange,
133	/// Unknown or complex percentage resolution
134	Unknown,
135}
136
137/// The type of element or container this style value applies to.
138#[bitmask(u16)]
139#[bitmask_config(vec_debug)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141pub enum AppliesTo {
142	/// Any element which is `display: block` or equivalent.
143	Block,
144	/// Any element which is `display: grid` or equivalent.
145	Grid,
146	/// Any element which is `display: flex` or equivalent.
147	Flex,
148	/// Any inline-level box.
149	Inline,
150	/// Any floated element.
151	Float,
152	/// Any Ruby container
153	Ruby,
154	/// Any absolutely positioned element.
155	AbsPos,
156	/// Any text node.
157	Text,
158	/// Any Pseudo Elements
159	PseudoElements,
160	/// Any Element
161	Elements,
162	/// What this applies to still needs to be established.
163	Unknown,
164}
165
166pub enum AnimationType {
167	/// This property is not animatable.
168	None,
169	/// This property animates between discrete values.
170	Discrete,
171	/// Animates by interpolating computed values
172	ByComputedValue,
173	/// Each item in a list animates independently
174	RepeatableList,
175	/// Animates as a transform list
176	TransformList,
177	/// Animates as a shadow list
178	ShadowList,
179	/// Animates as a length value
180	Length,
181	/// Animates as a number value
182	Number,
183	/// Unknown or complex animation behavior
184	Unknown,
185}
186
187/// How the computed value is calculated from the specified value
188pub enum ComputedValueType {
189	/// The computed value is the same as the specified value
190	AsSpecified,
191	/// Computed to an absolute length
192	AbsoluteLength,
193	/// Computed to an absolute length or percentage
194	AbsoluteLengthOrPercentage,
195	/// Computed to an absolute length or 'none'
196	AbsoluteLengthOrNone,
197	/// A specified keyword plus an absolute length
198	SpecifiedKeywordPlusAbsoluteLength,
199	/// Two absolute lengths (e.g., for background-position)
200	TwoAbsoluteLengths,
201	/// A list of absolute lengths
202	ListOfAbsoluteLengths,
203	/// Computed as specified, but with relative lengths converted to absolute
204	SpecifiedWithAbsoluteLengths,
205	/// Computed as specified, but with relative URLs converted to absolute
206	SpecifiedWithAbsoluteUrls,
207	/// Special computation rules - see spec
208	SeeIndividualProperties,
209	/// Computed value calculation is complex or spec-specific
210	Complex,
211	/// Not yet categorized
212	Unknown,
213}
214
215/// Which side(s) of the box a property applies to.
216/// This is a bitmask so properties can apply to multiple sides.
217#[bitmask(u8)]
218#[bitmask_config(vec_debug)]
219#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
220pub enum BoxSide {
221	/// Applies to the physical top side
222	Top = 0b00000001,
223	/// Applies to the physical bottom side
224	Bottom = 0b00000010,
225	/// Applies to the physical left side
226	Left = 0b00000100,
227	/// Applies to the physical right side
228	Right = 0b00001000,
229	/// Applies to the logical block-start side
230	BlockStart = 0b00010000,
231	/// Applies to the logical block-end side
232	BlockEnd = 0b00100000,
233	/// Applies to the logical inline-start side
234	InlineStart = 0b01000000,
235	/// Applies to the logical inline-end side
236	InlineEnd = 0b10000000,
237}
238
239impl BoxSide {
240	#[inline]
241	pub fn num_sides(&self, logical: bool) -> u32 {
242		if logical { (self.bits() & 0b11110000).count_ones() } else { (self.bits() & 0b00001111).count_ones() }
243	}
244}
245
246/// Which portion(s) of the box model a property affects.
247/// This is a bitmask so properties can affect multiple portions.
248#[bitmask(u8)]
249#[bitmask_config(vec_debug)]
250#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251pub enum BoxPortion {
252	/// Affects the content size (width/height)
253	Size,
254	/// Affects the margin area
255	Margin,
256	/// Affects the padding area
257	Padding,
258	/// Affects the border area
259	Border,
260	/// Affects the position/placement of the box
261	Position,
262}
263/// Reset coverage recorded for a shorthand property.
264#[derive(Clone, Copy, Debug, Eq, PartialEq)]
265pub enum ShorthandReset {
266	/// Properties reset in addition to the expressible longhands.
267	Properties(&'static [CssAtomSet]),
268	/// All properties are reset except the exclusions defined for `all`.
269	All,
270}
271
272/// The part of a shorthand's value that represents the longhand property that part sets.
273///
274/// The slots of `font`, for example, report that it writes an optional style and weight, then a size, then an optional
275/// line height after a `/`, then a family.
276#[derive(Clone, Copy, Debug, Eq, PartialEq)]
277pub struct Slot {
278	pub property: CssAtomSet,
279	/// The token the grammar writes before this slot to tell it apart from the slot before it, such as the `/` of
280	/// `font`'s line height. Empty when the grammar writes nothing.
281	pub before: &'static str,
282	/// The token the grammar writes after this slot. Empty when the grammar writes nothing.
283	pub after: &'static str,
284	/// True when the grammar lets the slot be left out.
285	pub optional: bool,
286	/// True when leaving the slot out gives its property the value of the slot before it, rather than the initial value.
287	pub copies: bool,
288}
289
290/// How a shorthand's value writes the longhands it sets.
291#[derive(Clone, Copy, Debug, Eq, PartialEq)]
292pub enum Writes {
293	/// The value writes one value per slot, in this order.
294	Slots(&'static [Slot]),
295	/// The value takes one to as many values as the shorthand sets longhands, and the position of each value picks the
296	/// longhand in [Shorthand::longhands] it sets, as `margin`'s `<'margin-top'>{1,4}` does. A left-out position takes
297	/// the value of an earlier one.
298	Repeat,
299	/// The value is written once and every longhand takes it, as `marker`'s `none | <url>` does.
300	Same,
301}
302
303/// What a shorthand property does to the properties it covers.
304#[derive(Clone, Copy, Debug, Eq, PartialEq)]
305pub struct Shorthand {
306	/// Every property it sets.
307	///
308	/// Nested shorthands are included with the properties they set, so `border` states `border-width` as well as
309	/// `border-top-width`.
310	pub longhands: &'static [CssAtomSet],
311	/// How its value writes them, when that is stated. Every longhand it sets is written, so a grammar which states only
312	/// some of them states none.
313	pub writes: Option<Writes>,
314	/// What it resets besides them. In some cases a shorthand will reset properties it cannot express itself, such as
315	/// `border` resetting `border-image`.
316	pub resets: ShorthandReset,
317}
318
319/// What the shorthands which cover a property do to it.
320///
321/// A shorthand may have one of these as well, since a shorthand can be nested inside another: `border-width` is both a
322/// shorthand of `border-top-width` and a longhand of `border`.
323#[derive(Clone, Copy, Debug, Eq, PartialEq)]
324pub struct Longhand {
325	/// Every shorthand which expresses this property, narrowest first.
326	pub shorthands: &'static [CssAtomSet],
327	/// Every shorthand which resets this property without expressing it.
328	pub reset_by: &'static [CssAtomSet],
329}
330
331pub trait DeclarationMetadata: Sized {
332	/// Returns the initial value of this property, as a string
333	fn initial() -> &'static str;
334
335	/// Determines if this style value inherits from parent rules
336	fn inherits() -> Inherits {
337		// Most properties do not inherit, so this is a sensible default
338		Inherits::False
339	}
340
341	/// Determines what types of frames this rule applies to
342	fn applies_to() -> AppliesTo {
343		AppliesTo::none()
344	}
345
346	/// Determines how this style value resolves percentages, if they are allowed as values
347	fn percentages() -> Percentages {
348		Percentages::None
349	}
350
351	/// Returns how this style value animates
352	fn animation_type() -> AnimationType {
353		// Most properties do not animate, so this is a sensible default
354		AnimationType::None
355	}
356
357	/// Returns what this property does to the properties it covers, or [None] if it is not a
358	/// shorthand.
359	fn shorthand() -> Option<&'static Shorthand> {
360		None
361	}
362
363	/// Returns what the shorthands which cover this property do to it, or [None] if no shorthand
364	/// covers it.
365	fn longhand() -> Option<&'static Longhand> {
366		None
367	}
368
369	/// Returns which CSS specification(s) this property belongs to.
370	/// This allows tracking which CSS modules are used in a stylesheet.
371	fn property_group() -> PropertyGroup {
372		PropertyGroup::none()
373	}
374
375	/// Returns how the computed value is calculated from the specified value.
376	fn computed_value_type() -> ComputedValueType {
377		ComputedValueType::Unknown
378	}
379
380	/// Returns the canonical order for serialization (e.g., "per grammar", "unique").
381	/// Returns None if not specified or not applicable.
382	fn canonical_order() -> Option<&'static str> {
383		None
384	}
385
386	/// Returns the logical property group this property belongs to (e.g., "Margin", "Border").
387	/// This groups related logical/physical properties together.
388	/// Returns None if this is not part of a logical property group.
389	fn logical_property_group() -> Option<CssAtomSet> {
390		None
391	}
392
393	/// Returns which side(s) of the box this property applies to.
394	/// For example, `margin-top` returns BoxSide::Top, while `margin` returns all sides.
395	/// Returns BoxSide::none() if the property doesn't apply to a specific side.
396	fn box_side() -> BoxSide {
397		BoxSide::none()
398	}
399
400	/// Returns which portion(s) of the box model this property affects.
401	/// For example, `margin-top` returns BoxPortion::Margin, `border-width` returns BoxPortion::Border.
402	/// Returns BoxPortion::none() if the property doesn't affect the box model.
403	fn box_portion() -> BoxPortion {
404		BoxPortion::none()
405	}
406
407	/// Returns how unitless zero resolves for this property.
408	///
409	/// For properties that accept both `<number>` and `<length>`, unitless zero
410	/// may resolve to a number rather than a length. This affects whether the
411	/// minifier can safely reduce `0px` to `0`.
412	///
413	/// Examples where unitless zero resolves to Number (NOT safe to reduce):
414	/// - `line-height: 0` means 0x font-size multiplier
415	/// - `tab-size: 0` means 0 tab characters
416	/// - `border-image-outset: 0` means 0x border-width
417	fn unitless_zero_resolves() -> UnitlessZeroResolves {
418		// Default: most properties accept unitless zero as length
419		UnitlessZeroResolves::Length
420	}
421}
422
423#[cfg(test)]
424mod test {
425	use crate::*;
426
427	#[test]
428	fn test_box_side_count() {
429		assert_eq!(BoxSide::Top.num_sides(false), 1);
430		assert_eq!((BoxSide::Top | BoxSide::Right).num_sides(false), 2);
431		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom).num_sides(false), 3);
432		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(false), 4);
433		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(true), 0);
434
435		assert_eq!(BoxSide::all_bits().num_sides(false), 4);
436		assert_eq!(BoxSide::all_bits().num_sides(true), 4);
437
438		assert_eq!(BoxSide::BlockStart.num_sides(true), 1);
439		assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd).num_sides(true), 2);
440		assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart).num_sides(true), 3);
441		assert_eq!(
442			(BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(true),
443			4
444		);
445		assert_eq!(
446			(BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(false),
447			0
448		);
449	}
450}