css_ast/traits/
declaration_metadata.rs

1use bitmask_enum::bitmask;
2
3use crate::CssAtomSet;
4
5/// The CSS specification/module that a property belongs to.
6#[bitmask(u64)]
7#[bitmask_config(vec_debug)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum PropertyGroup {
10	Align,
11	AnchorPosition,
12	Animations,
13	Backgrounds,
14	Borders,
15	Box,
16	Break,
17	Cascade,
18	Color,
19	ColorAdjust,
20	ColorHdr,
21	Conditional,
22	Contain,
23	Content,
24	Display,
25	Exclusions,
26	Flexbox,
27	Fonts,
28	Forms,
29	Gaps,
30	Gcpm,
31	Grid,
32	Images,
33	Inline,
34	LineGrid,
35	LinkParams,
36	Lists,
37	Logical,
38	Multicol,
39	Nav,
40	Overflow,
41	Overscroll,
42	Page,
43	PageFloats,
44	Position,
45	Regions,
46	Rhythm,
47	RoundDisplay,
48	Ruby,
49	ScrollAnchoring,
50	ScrollSnap,
51	Scrollbars,
52	Shapes,
53	SizeAdjust,
54	Sizing,
55	Speech,
56	Tables,
57	Text,
58	TextDecor,
59	Transforms,
60	Transitions,
61	Ui,
62	Values,
63	Variables,
64	ViewTransitions,
65	Viewport,
66	WillChange,
67	WritingModes,
68}
69
70pub enum Inherits {
71	False,
72	True,
73	Unknown,
74}
75
76impl Inherits {
77	pub fn to_bool(self, unknown: bool) -> bool {
78		match self {
79			Self::False => false,
80			Self::True => true,
81			Self::Unknown => unknown,
82		}
83	}
84}
85
86pub enum Percentages {
87	/// This style value has no way of expressing values as a percentage.
88	None,
89	/// Any percentage expressed in this value pertains to the size of the containing block.
90	ContainingBlock,
91	/// Any percentage expressed in this value pertains to the size of the border box.
92	BorderBox,
93	/// Any percentage expressed in this value is a syntax affordance; a Number token would be the equivalent value.
94	Number,
95	/// Relative to the 1em Font-Size
96	FontSize,
97	/// Relative to the Font-Size of the parent element
98	ParentFontSize,
99	/// Relative to the scroll container's scrollport
100	Scrollport,
101	/// Relative to the content area dimension
102	ContentArea,
103	/// Relative to the border-edge side length
104	BorderEdge,
105	/// Relative to the background positioning area
106	BackgroundPositioningArea,
107	/// Relative to the reference box size
108	ReferenceBox,
109	/// Relative to the element's own dimensions
110	SelfSize,
111	/// Relative to the line box
112	LineBox,
113	/// Relative to the flex container
114	FlexContainer,
115	/// Relative to the border image area
116	BorderImageArea,
117	/// Map to a normalized range (e.g., `[0,1]`)
118	NormalizedRange,
119	/// Unknown or complex percentage resolution
120	Unknown,
121}
122
123/// The type of element or container this style value applies to.
124#[bitmask(u16)]
125#[bitmask_config(vec_debug)]
126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
127pub enum AppliesTo {
128	/// Any element which is `display: block` or equivalent.
129	Block,
130	/// Any element which is `display: grid` or equivalent.
131	Grid,
132	/// Any element which is `display: flex` or equivalent.
133	Flex,
134	/// Any inline-level box.
135	Inline,
136	/// Any floated element.
137	Float,
138	/// Any Ruby container
139	Ruby,
140	/// Any absolutely positioned element.
141	AbsPos,
142	/// Any text node.
143	Text,
144	/// Any Pseudo Elements
145	PseudoElements,
146	/// Any Element
147	Elements,
148	/// What this applies to still needs to be established.
149	Unknown,
150}
151
152pub enum AnimationType {
153	/// This property is not animatable.
154	None,
155	/// This property animates between discrete values.
156	Discrete,
157	/// Animates by interpolating computed values
158	ByComputedValue,
159	/// Each item in a list animates independently
160	RepeatableList,
161	/// Animates as a transform list
162	TransformList,
163	/// Animates as a shadow list
164	ShadowList,
165	/// Animates as a length value
166	Length,
167	/// Animates as a number value
168	Number,
169	/// Unknown or complex animation behavior
170	Unknown,
171}
172
173/// How the computed value is calculated from the specified value
174pub enum ComputedValueType {
175	/// The computed value is the same as the specified value
176	AsSpecified,
177	/// Computed to an absolute length
178	AbsoluteLength,
179	/// Computed to an absolute length or percentage
180	AbsoluteLengthOrPercentage,
181	/// Computed to an absolute length or 'none'
182	AbsoluteLengthOrNone,
183	/// A specified keyword plus an absolute length
184	SpecifiedKeywordPlusAbsoluteLength,
185	/// Two absolute lengths (e.g., for background-position)
186	TwoAbsoluteLengths,
187	/// A list of absolute lengths
188	ListOfAbsoluteLengths,
189	/// Computed as specified, but with relative lengths converted to absolute
190	SpecifiedWithAbsoluteLengths,
191	/// Computed as specified, but with relative URLs converted to absolute
192	SpecifiedWithAbsoluteUrls,
193	/// Special computation rules - see spec
194	SeeIndividualProperties,
195	/// Computed value calculation is complex or spec-specific
196	Complex,
197	/// Not yet categorized
198	Unknown,
199}
200
201/// Which side(s) of the box a property applies to.
202/// This is a bitmask so properties can apply to multiple sides.
203#[bitmask(u8)]
204#[bitmask_config(vec_debug)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
206pub enum BoxSide {
207	/// Applies to the physical top side
208	Top = 0b00000001,
209	/// Applies to the physical bottom side
210	Bottom = 0b00000010,
211	/// Applies to the physical left side
212	Left = 0b00000100,
213	/// Applies to the physical right side
214	Right = 0b00001000,
215	/// Applies to the logical block-start side
216	BlockStart = 0b00010000,
217	/// Applies to the logical block-end side
218	BlockEnd = 0b00100000,
219	/// Applies to the logical inline-start side
220	InlineStart = 0b01000000,
221	/// Applies to the logical inline-end side
222	InlineEnd = 0b10000000,
223}
224
225impl BoxSide {
226	#[inline]
227	pub fn num_sides(&self, logical: bool) -> u32 {
228		if logical { (self.bits() & 0b11110000).count_ones() } else { (self.bits() & 0b00001111).count_ones() }
229	}
230}
231
232/// Which portion(s) of the box model a property affects.
233/// This is a bitmask so properties can affect multiple portions.
234#[bitmask(u8)]
235#[bitmask_config(vec_debug)]
236#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
237pub enum BoxPortion {
238	/// Affects the content size (width/height)
239	Size,
240	/// Affects the margin area
241	Margin,
242	/// Affects the padding area
243	Padding,
244	/// Affects the border area
245	Border,
246	/// Affects the position/placement of the box
247	Position,
248}
249
250pub trait DeclarationMetadata: Sized {
251	/// Returns the initial value of this property, as a string
252	fn initial() -> &'static str;
253
254	/// Determines if this style value inherits from parent rules
255	fn inherits() -> Inherits {
256		// Most properties do not inherit, so this is a sensible default
257		Inherits::False
258	}
259
260	/// Determines what types of frames this rule applies to
261	fn applies_to() -> AppliesTo {
262		AppliesTo::none()
263	}
264
265	/// Determines how this style value resolves percentages, if they are allowed as values
266	fn percentages() -> Percentages {
267		Percentages::None
268	}
269
270	/// Returns how this style value animates
271	fn animation_type() -> AnimationType {
272		// Most properties do not animate, so this is a sensible default
273		AnimationType::None
274	}
275
276	/// Determines if this style value is a "shorthand" value, meaning it is comprised of other "longhand" style values.
277	fn is_shorthand() -> bool {
278		false
279	}
280
281	/// Determines if this style value is a "longhand" value, meaning a "shorthand" style value exists that could also
282	/// express this.
283	fn is_longhand() -> bool {
284		Self::shorthand_group() == CssAtomSet::_None
285	}
286
287	/// Returns a list of direct "longhand" declaration IDs, or None if this is not a shorthand.
288	/// For nested shorthands, this only returns the immediate children.
289	/// Use `StyleValue::expanded_longhands_by_name()` to get all transitive longhands.
290	fn longhands() -> Option<&'static [CssAtomSet]> {
291		None
292	}
293
294	/// Returns the declaration ID of the shorthand that this property is part of.
295	/// If this is not a longhand then it will be `CssAtomSet::_None`.
296	fn shorthand_group() -> CssAtomSet {
297		CssAtomSet::_None
298	}
299
300	/// Returns which CSS specification(s) this property belongs to.
301	/// This allows tracking which CSS modules are used in a stylesheet.
302	fn property_group() -> PropertyGroup {
303		PropertyGroup::none()
304	}
305
306	/// Returns how the computed value is calculated from the specified value.
307	fn computed_value_type() -> ComputedValueType {
308		ComputedValueType::Unknown
309	}
310
311	/// Returns the canonical order for serialization (e.g., "per grammar", "unique").
312	/// Returns None if not specified or not applicable.
313	fn canonical_order() -> Option<&'static str> {
314		None
315	}
316
317	/// Returns the logical property group this property belongs to (e.g., "Margin", "Border").
318	/// This groups related logical/physical properties together.
319	/// Returns None if this is not part of a logical property group.
320	fn logical_property_group() -> Option<CssAtomSet> {
321		None
322	}
323
324	/// Returns which side(s) of the box this property applies to.
325	/// For example, `margin-top` returns BoxSide::Top, while `margin` returns all sides.
326	/// Returns BoxSide::none() if the property doesn't apply to a specific side.
327	fn box_side() -> BoxSide {
328		BoxSide::none()
329	}
330
331	/// Returns which portion(s) of the box model this property affects.
332	/// For example, `margin-top` returns BoxPortion::Margin, `border-width` returns BoxPortion::Border.
333	/// Returns BoxPortion::none() if the property doesn't affect the box model.
334	fn box_portion() -> BoxPortion {
335		BoxPortion::none()
336	}
337}
338
339#[cfg(test)]
340mod test {
341	use crate::*;
342
343	#[test]
344	fn test_box_side_count() {
345		assert_eq!(BoxSide::Top.num_sides(false), 1);
346		assert_eq!((BoxSide::Top | BoxSide::Right).num_sides(false), 2);
347		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom).num_sides(false), 3);
348		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(false), 4);
349		assert_eq!((BoxSide::Top | BoxSide::Right | BoxSide::Bottom | BoxSide::Left).num_sides(true), 0);
350
351		assert_eq!(BoxSide::all_bits().num_sides(false), 4);
352		assert_eq!(BoxSide::all_bits().num_sides(true), 4);
353
354		assert_eq!(BoxSide::BlockStart.num_sides(true), 1);
355		assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd).num_sides(true), 2);
356		assert_eq!((BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart).num_sides(true), 3);
357		assert_eq!(
358			(BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(true),
359			4
360		);
361		assert_eq!(
362			(BoxSide::BlockStart | BoxSide::BlockEnd | BoxSide::InlineStart | BoxSide::InlineEnd).num_sides(false),
363			0
364		);
365	}
366}