1use super::prelude::*;
2use crate::{CalcableValue, LengthPercentage};
3
4#[node]
10#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
12#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
13#[derive(csskit_derives::NodeWithMetadata)]
14pub enum Position<'a> {
15 One(PositionOne<'a>),
16 Two(PositionTwo<'a>),
17 Four(PositionFour<'a>),
18}
19
20impl<'a> Peek<'a> for Position<'a> {
21 const PEEK_KINDSET: KindSet = PositionOne::PEEK_KINDSET;
22
23 #[inline(always)]
24 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
25 where
26 I: Iterator<Item = Cursor> + Clone,
27 {
28 PositionOne::peek(p, c)
29 }
30}
31
32impl<'a> Parse<'a> for Position<'a> {
33 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
34 where
35 I: Iterator<Item = Cursor> + Clone,
36 {
37 let first = p.parse::<PositionOne>()?;
38 if !p.peek::<PositionOne>() {
39 return Ok(Self::One(first));
40 }
41 let second_c = p.peek_n(1);
42 let second = p.parse::<PositionOne>()?;
43 if !p.peek::<PositionOne>() {
44 return Ok(Self::Two(PositionTwo::from_two(p, first, second, second_c)?));
45 }
46 Ok(Self::Four(PositionFour::from_four(p, first, second, second_c)?))
48 }
49}
50
51#[node]
53#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
55#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
56#[derive(csskit_derives::NodeWithMetadata)]
57pub struct AtPosition<'a> {
58 #[atom(CssAtomSet::At)]
59 pub keyword: T![Ident],
60 pub position: Position<'a>,
61}
62
63#[node]
75#[derive(Parse, Peek, ToSpan, SemanticEq, ToCursors, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
77#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
78#[derive(csskit_derives::NodeWithMetadata)]
79pub enum PositionOne<'a> {
80 #[atom(CssAtomSet::Left)]
81 Left(T![Ident]),
82 #[atom(CssAtomSet::Right)]
83 Right(T![Ident]),
84 #[atom(CssAtomSet::Center)]
85 Center(T![Ident]),
86 #[atom(CssAtomSet::Top)]
87 Top(T![Ident]),
88 #[atom(CssAtomSet::Bottom)]
89 Bottom(T![Ident]),
90 #[atom(CssAtomSet::XStart)]
91 XStart(T![Ident]),
92 #[atom(CssAtomSet::XEnd)]
93 XEnd(T![Ident]),
94 #[atom(CssAtomSet::YStart)]
95 YStart(T![Ident]),
96 #[atom(CssAtomSet::YEnd)]
97 YEnd(T![Ident]),
98 #[atom(CssAtomSet::BlockStart)]
99 BlockStart(T![Ident]),
100 #[atom(CssAtomSet::BlockEnd)]
101 BlockEnd(T![Ident]),
102 #[atom(CssAtomSet::InlineStart)]
103 InlineStart(T![Ident]),
104 #[atom(CssAtomSet::InlineEnd)]
105 InlineEnd(T![Ident]),
106 #[atom(CssAtomSet::Start)]
107 Start(T![Ident]),
108 #[atom(CssAtomSet::End)]
109 End(T![Ident]),
110 LengthPercentage(CalcableValue<'a, LengthPercentage>),
111}
112
113impl<'a> PositionOne<'a> {
114 pub(crate) fn to_horizontal(&self) -> Option<PositionHorizontal<'a>> {
115 match self {
116 Self::Left(t) => Some(PositionHorizontal::Left(*t)),
117 Self::Right(t) => Some(PositionHorizontal::Right(*t)),
118 Self::Center(t) => Some(PositionHorizontal::Center(*t)),
119 Self::XStart(t) => Some(PositionHorizontal::XStart(*t)),
120 Self::XEnd(t) => Some(PositionHorizontal::XEnd(*t)),
121 Self::LengthPercentage(l) => Some(PositionHorizontal::LengthPercentage(l.clone())),
122 _ => None,
123 }
124 }
125
126 pub(crate) fn to_vertical(&self) -> Option<PositionVertical<'a>> {
127 match self {
128 Self::Top(t) => Some(PositionVertical::Top(*t)),
129 Self::Bottom(t) => Some(PositionVertical::Bottom(*t)),
130 Self::Center(t) => Some(PositionVertical::Center(*t)),
131 Self::YStart(t) => Some(PositionVertical::YStart(*t)),
132 Self::YEnd(t) => Some(PositionVertical::YEnd(*t)),
133 Self::LengthPercentage(l) => Some(PositionVertical::LengthPercentage(l.clone())),
134 _ => None,
135 }
136 }
137
138 pub(crate) fn to_horizontal_keyword(&self) -> Option<PositionHorizontalKeyword> {
139 match self {
140 Self::Left(t) => Some(PositionHorizontalKeyword::Left(*t)),
141 Self::Right(t) => Some(PositionHorizontalKeyword::Right(*t)),
142 Self::XStart(t) => Some(PositionHorizontalKeyword::XStart(*t)),
143 Self::XEnd(t) => Some(PositionHorizontalKeyword::XEnd(*t)),
144 _ => None,
145 }
146 }
147
148 pub(crate) fn to_vertical_keyword(&self) -> Option<PositionVerticalKeyword> {
149 match self {
150 Self::Top(t) => Some(PositionVerticalKeyword::Top(*t)),
151 Self::Bottom(t) => Some(PositionVerticalKeyword::Bottom(*t)),
152 Self::YStart(t) => Some(PositionVerticalKeyword::YStart(*t)),
153 Self::YEnd(t) => Some(PositionVerticalKeyword::YEnd(*t)),
154 _ => None,
155 }
156 }
157
158 pub(crate) fn to_block_axis(&self) -> Option<PositionBlockAxis> {
159 match self {
160 Self::BlockStart(t) => Some(PositionBlockAxis::BlockStart(*t)),
161 Self::BlockEnd(t) => Some(PositionBlockAxis::BlockEnd(*t)),
162 Self::Center(t) => Some(PositionBlockAxis::Center(*t)),
163 _ => None,
164 }
165 }
166
167 pub(crate) fn to_inline_axis(&self) -> Option<PositionInlineAxis> {
168 match self {
169 Self::InlineStart(t) => Some(PositionInlineAxis::InlineStart(*t)),
170 Self::InlineEnd(t) => Some(PositionInlineAxis::InlineEnd(*t)),
171 Self::Center(t) => Some(PositionInlineAxis::Center(*t)),
172 _ => None,
173 }
174 }
175
176 pub(crate) fn to_block_axis_keyword(&self) -> Option<PositionBlockAxisKeyword> {
177 match self {
178 Self::BlockStart(t) => Some(PositionBlockAxisKeyword::BlockStart(*t)),
179 Self::BlockEnd(t) => Some(PositionBlockAxisKeyword::BlockEnd(*t)),
180 _ => None,
181 }
182 }
183
184 pub(crate) fn to_inline_axis_keyword(&self) -> Option<PositionInlineAxisKeyword> {
185 match self {
186 Self::InlineStart(t) => Some(PositionInlineAxisKeyword::InlineStart(*t)),
187 Self::InlineEnd(t) => Some(PositionInlineAxisKeyword::InlineEnd(*t)),
188 _ => None,
189 }
190 }
191
192 pub(crate) fn to_logical(&self) -> Option<StartEnd> {
193 match self {
194 Self::Start(t) => Some(StartEnd::Start(*t)),
195 Self::End(t) => Some(StartEnd::End(*t)),
196 _ => None,
197 }
198 }
199}
200
201#[node]
220#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
221#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
222#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
223#[derive(csskit_derives::NodeWithMetadata)]
224pub enum PositionTwo<'a> {
225 Physical(PositionHorizontal<'a>, PositionVertical<'a>),
228 FlowRelative(PositionBlockAxis, PositionInlineAxis),
231 Logical(StartEnd, StartEnd),
233}
234
235impl<'a> Peek<'a> for PositionTwo<'a> {
236 const PEEK_KINDSET: KindSet = PositionOne::PEEK_KINDSET;
237
238 #[inline(always)]
239 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
240 where
241 I: Iterator<Item = Cursor> + Clone,
242 {
243 PositionOne::peek(p, c)
244 }
245}
246
247impl<'a> PositionTwo<'a> {
248 pub(crate) fn from_two<I>(
249 _p: &mut Parser<'a, I>,
250 first: PositionOne<'a>,
251 second: PositionOne<'a>,
252 second_c: Cursor,
253 ) -> ParserResult<Self>
254 where
255 I: Iterator<Item = Cursor> + Clone,
256 {
257 let h = first.to_horizontal();
259 let v = second.to_vertical();
260 if let (Some(h), Some(v)) = (h, v) {
261 return Ok(Self::Physical(h, v));
262 }
263 let v = first.to_vertical();
265 let h = second.to_horizontal();
266 if let (Some(v), Some(h)) = (v, h) {
267 return Ok(Self::Physical(h, v));
268 }
269 let block = first.to_block_axis();
271 let inline = second.to_inline_axis();
272 if let (Some(block), Some(inline)) = (block, inline) {
273 return Ok(Self::FlowRelative(block, inline));
274 }
275 let inline = first.to_inline_axis();
276 let block = second.to_block_axis();
277 if let (Some(inline), Some(block)) = (inline, block) {
278 return Ok(Self::FlowRelative(block, inline));
279 }
280 let a = first.to_logical();
282 let b = second.to_logical();
283 if let (Some(a), Some(b)) = (a, b) {
284 return Ok(Self::Logical(a, b));
285 }
286 Err(Diagnostic::new(second_c, Diagnostic::unexpected))?
287 }
288}
289
290#[node]
306#[derive(ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
307#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
308#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
309#[derive(csskit_derives::NodeWithMetadata)]
310pub enum PositionFour<'a> {
311 Physical(
314 PositionHorizontalKeyword,
315 CalcableValue<'a, LengthPercentage>,
316 PositionVerticalKeyword,
317 CalcableValue<'a, LengthPercentage>,
318 ),
319 FlowRelative(
322 PositionBlockAxisKeyword,
323 CalcableValue<'a, LengthPercentage>,
324 PositionInlineAxisKeyword,
325 CalcableValue<'a, LengthPercentage>,
326 ),
327 Logical(StartEnd, CalcableValue<'a, LengthPercentage>, StartEnd, CalcableValue<'a, LengthPercentage>),
329}
330
331impl<'a> PositionFour<'a> {
332 pub(crate) fn from_four<I>(
333 p: &mut Parser<'a, I>,
334 first: PositionOne<'a>,
335 second: PositionOne<'a>,
336 second_c: Cursor,
337 ) -> ParserResult<Self>
338 where
339 I: Iterator<Item = Cursor> + Clone,
340 {
341 let PositionOne::LengthPercentage(lp1) = second else {
344 return Err(Diagnostic::new(second_c, Diagnostic::unexpected))?;
346 };
347 let third_c = p.peek_n(1);
348 let third = p.parse::<PositionOne>()?;
349 let fourth = p.parse::<CalcableValue<LengthPercentage>>()?;
350 if let Some(h_kw) = first.to_horizontal_keyword()
352 && let Some(v_kw) = third.to_vertical_keyword()
353 {
354 return Ok(Self::Physical(h_kw, lp1, v_kw, fourth));
355 }
356 if let Some(v_kw) = first.to_vertical_keyword()
358 && let Some(h_kw) = third.to_horizontal_keyword()
359 {
360 return Ok(Self::Physical(h_kw, fourth, v_kw, lp1));
361 }
362 if let Some(b_kw) = first.to_block_axis_keyword()
364 && let Some(i_kw) = third.to_inline_axis_keyword()
365 {
366 return Ok(Self::FlowRelative(b_kw, lp1, i_kw, fourth));
367 }
368 if let Some(i_kw) = first.to_inline_axis_keyword()
370 && let Some(b_kw) = third.to_block_axis_keyword()
371 {
372 return Ok(Self::FlowRelative(b_kw, fourth, i_kw, lp1));
373 }
374 if let Some(a) = first.to_logical()
376 && let Some(b) = third.to_logical()
377 {
378 return Ok(Self::Logical(a, lp1, b, fourth));
379 }
380 Err(Diagnostic::new(third_c, Diagnostic::unexpected))?
381 }
382}
383
384#[node]
388#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
389#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
390#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
391#[derive(csskit_derives::NodeWithMetadata)]
392pub enum PositionHorizontal<'a> {
393 #[atom(CssAtomSet::Left)]
394 Left(T![Ident]),
395 #[atom(CssAtomSet::Right)]
396 Right(T![Ident]),
397 #[atom(CssAtomSet::Center)]
398 Center(T![Ident]),
399 #[atom(CssAtomSet::XStart)]
400 XStart(T![Ident]),
401 #[atom(CssAtomSet::XEnd)]
402 XEnd(T![Ident]),
403 LengthPercentage(CalcableValue<'a, LengthPercentage>),
404}
405
406#[node]
410#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
411#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
412#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
413#[derive(csskit_derives::NodeWithMetadata)]
414pub enum PositionVertical<'a> {
415 #[atom(CssAtomSet::Top)]
416 Top(T![Ident]),
417 #[atom(CssAtomSet::Bottom)]
418 Bottom(T![Ident]),
419 #[atom(CssAtomSet::Center)]
420 Center(T![Ident]),
421 #[atom(CssAtomSet::YStart)]
422 YStart(T![Ident]),
423 #[atom(CssAtomSet::YEnd)]
424 YEnd(T![Ident]),
425 LengthPercentage(CalcableValue<'a, LengthPercentage>),
426}
427
428#[node]
432#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
433#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
434#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
435#[derive(csskit_derives::NodeWithMetadata)]
436pub enum PositionBlockAxis {
437 #[atom(CssAtomSet::BlockStart)]
438 BlockStart(T![Ident]),
439 #[atom(CssAtomSet::BlockEnd)]
440 BlockEnd(T![Ident]),
441 #[atom(CssAtomSet::Center)]
442 Center(T![Ident]),
443}
444
445#[node]
449#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
450#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
451#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
452#[derive(csskit_derives::NodeWithMetadata)]
453pub enum PositionInlineAxis {
454 #[atom(CssAtomSet::InlineStart)]
455 InlineStart(T![Ident]),
456 #[atom(CssAtomSet::InlineEnd)]
457 InlineEnd(T![Ident]),
458 #[atom(CssAtomSet::Center)]
459 Center(T![Ident]),
460}
461
462#[node]
469#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
470#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
471#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
472#[derive(csskit_derives::NodeWithMetadata)]
473pub enum StartEnd {
474 #[atom(CssAtomSet::Start)]
475 Start(T![Ident]),
476 #[atom(CssAtomSet::End)]
477 End(T![Ident]),
478}
479
480#[node]
484#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
485#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
486#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
487#[derive(csskit_derives::NodeWithMetadata)]
488pub enum PositionHorizontalKeyword {
489 #[atom(CssAtomSet::Left)]
490 Left(T![Ident]),
491 #[atom(CssAtomSet::Right)]
492 Right(T![Ident]),
493 #[atom(CssAtomSet::XStart)]
494 XStart(T![Ident]),
495 #[atom(CssAtomSet::XEnd)]
496 XEnd(T![Ident]),
497}
498
499#[node]
503#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
504#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
505#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
506#[derive(csskit_derives::NodeWithMetadata)]
507pub enum PositionVerticalKeyword {
508 #[atom(CssAtomSet::Top)]
509 Top(T![Ident]),
510 #[atom(CssAtomSet::Bottom)]
511 Bottom(T![Ident]),
512 #[atom(CssAtomSet::YStart)]
513 YStart(T![Ident]),
514 #[atom(CssAtomSet::YEnd)]
515 YEnd(T![Ident]),
516}
517
518#[node]
522#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
523#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
524#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
525#[derive(csskit_derives::NodeWithMetadata)]
526pub enum PositionBlockAxisKeyword {
527 #[atom(CssAtomSet::BlockStart)]
528 BlockStart(T![Ident]),
529 #[atom(CssAtomSet::BlockEnd)]
530 BlockEnd(T![Ident]),
531}
532
533#[node]
537#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
538#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
539#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
540#[derive(csskit_derives::NodeWithMetadata)]
541pub enum PositionInlineAxisKeyword {
542 #[atom(CssAtomSet::InlineStart)]
543 InlineStart(T![Ident]),
544 #[atom(CssAtomSet::InlineEnd)]
545 InlineEnd(T![Ident]),
546}
547
548#[cfg(test)]
549mod tests {
550 use super::*;
551 use crate::CssAtomSet;
552 use css_parse::{assert_parse, assert_parse_error, assert_parse_span};
553
554 #[test]
555 fn test_writes() {
556 assert_parse!(CssAtomSet::ATOMS, Position, "left", Position::One(PositionOne::Left(_)));
558 assert_parse!(CssAtomSet::ATOMS, Position, "right", Position::One(PositionOne::Right(_)));
559 assert_parse!(CssAtomSet::ATOMS, Position, "top", Position::One(PositionOne::Top(_)));
560 assert_parse!(CssAtomSet::ATOMS, Position, "bottom", Position::One(PositionOne::Bottom(_)));
561 assert_parse!(CssAtomSet::ATOMS, Position, "center", Position::One(PositionOne::Center(_)));
562 assert_parse!(CssAtomSet::ATOMS, Position, "x-start", Position::One(PositionOne::XStart(_)));
563 assert_parse!(CssAtomSet::ATOMS, Position, "x-end", Position::One(PositionOne::XEnd(_)));
564 assert_parse!(CssAtomSet::ATOMS, Position, "y-start", Position::One(PositionOne::YStart(_)));
565 assert_parse!(CssAtomSet::ATOMS, Position, "y-end", Position::One(PositionOne::YEnd(_)));
566 assert_parse!(CssAtomSet::ATOMS, Position, "block-start", Position::One(PositionOne::BlockStart(_)));
567 assert_parse!(CssAtomSet::ATOMS, Position, "block-end", Position::One(PositionOne::BlockEnd(_)));
568 assert_parse!(CssAtomSet::ATOMS, Position, "inline-start", Position::One(PositionOne::InlineStart(_)));
569 assert_parse!(CssAtomSet::ATOMS, Position, "inline-end", Position::One(PositionOne::InlineEnd(_)));
570 assert_parse!(CssAtomSet::ATOMS, Position, "start", Position::One(PositionOne::Start(_)));
571 assert_parse!(CssAtomSet::ATOMS, Position, "end", Position::One(PositionOne::End(_)));
572 assert_parse!(
573 CssAtomSet::ATOMS,
574 Position,
575 "50%",
576 Position::One(PositionOne::LengthPercentage(CalcableValue::Literal(LengthPercentage::Percent(_))))
577 );
578 assert_parse!(
580 CssAtomSet::ATOMS,
581 Position,
582 "center center",
583 Position::Two(PositionTwo::Physical(PositionHorizontal::Center(_), PositionVertical::Center(_)))
584 );
585 assert_parse!(
586 CssAtomSet::ATOMS,
587 Position,
588 "center top",
589 Position::Two(PositionTwo::Physical(PositionHorizontal::Center(_), PositionVertical::Top(_)))
590 );
591 assert_parse!(
592 CssAtomSet::ATOMS,
593 Position,
594 "50% 50%",
595 Position::Two(PositionTwo::Physical(
596 PositionHorizontal::LengthPercentage(CalcableValue::Literal(LengthPercentage::Percent(_))),
597 PositionVertical::LengthPercentage(CalcableValue::Literal(LengthPercentage::Percent(_)))
598 ))
599 );
600 assert_parse!(
601 CssAtomSet::ATOMS,
602 Position,
603 "20px 30px",
604 Position::Two(PositionTwo::Physical(
605 PositionHorizontal::LengthPercentage(CalcableValue::Literal(_)),
606 PositionVertical::LengthPercentage(CalcableValue::Literal(_))
607 ))
608 );
609 assert_parse!(
610 CssAtomSet::ATOMS,
611 Position,
612 "2% bottom",
613 Position::Two(PositionTwo::Physical(
614 PositionHorizontal::LengthPercentage(CalcableValue::Literal(LengthPercentage::Percent(_))),
615 PositionVertical::Bottom(_)
616 ))
617 );
618 assert_parse!(
619 CssAtomSet::ATOMS,
620 Position,
621 "-70% -180%",
622 Position::Two(PositionTwo::Physical(
623 PositionHorizontal::LengthPercentage(CalcableValue::Literal(LengthPercentage::Percent(_))),
624 PositionVertical::LengthPercentage(CalcableValue::Literal(LengthPercentage::Percent(_)))
625 ))
626 );
627 assert_parse!(
628 CssAtomSet::ATOMS,
629 Position,
630 "right 8.5%",
631 Position::Two(PositionTwo::Physical(
632 PositionHorizontal::Right(_),
633 PositionVertical::LengthPercentage(CalcableValue::Literal(LengthPercentage::Percent(_)))
634 ))
635 );
636 assert_parse!(
638 CssAtomSet::ATOMS,
639 Position,
640 "x-start y-end",
641 Position::Two(PositionTwo::Physical(PositionHorizontal::XStart(_), PositionVertical::YEnd(_)))
642 );
643 assert_parse!(
645 CssAtomSet::ATOMS,
646 Position,
647 "block-start inline-end",
648 Position::Two(PositionTwo::FlowRelative(
649 PositionBlockAxis::BlockStart(_),
650 PositionInlineAxis::InlineEnd(_)
651 ))
652 );
653 assert_parse!(
654 CssAtomSet::ATOMS,
655 Position,
656 "inline-end block-start",
657 Position::Two(PositionTwo::FlowRelative(
658 PositionBlockAxis::BlockStart(_),
659 PositionInlineAxis::InlineEnd(_)
660 ))
661 );
662 assert_parse!(
664 CssAtomSet::ATOMS,
665 Position,
666 "start end",
667 Position::Two(PositionTwo::Logical(StartEnd::Start(_), StartEnd::End(_)))
668 );
669 assert_parse!(
671 CssAtomSet::ATOMS,
672 Position,
673 "right -6px bottom 12vmin",
674 Position::Four(PositionFour::Physical(
675 PositionHorizontalKeyword::Right(_),
676 CalcableValue::Literal(LengthPercentage::Length(_)),
677 PositionVerticalKeyword::Bottom(_),
678 CalcableValue::Literal(LengthPercentage::Length(_))
679 ))
680 );
681 assert_parse!(
682 CssAtomSet::ATOMS,
683 Position,
684 "bottom 12vmin right -6px",
685 Position::Four(PositionFour::Physical(
686 PositionHorizontalKeyword::Right(_),
687 CalcableValue::Literal(LengthPercentage::Length(_)),
688 PositionVerticalKeyword::Bottom(_),
689 CalcableValue::Literal(LengthPercentage::Length(_))
690 ))
691 );
692 assert_parse!(
694 CssAtomSet::ATOMS,
695 Position,
696 "block-start 10px inline-end 20px",
697 Position::Four(PositionFour::FlowRelative(
698 PositionBlockAxisKeyword::BlockStart(_),
699 CalcableValue::Literal(LengthPercentage::Length(_)),
700 PositionInlineAxisKeyword::InlineEnd(_),
701 CalcableValue::Literal(LengthPercentage::Length(_))
702 ))
703 );
704 assert_parse!(
706 CssAtomSet::ATOMS,
707 Position,
708 "start 10px end 20px",
709 Position::Four(PositionFour::Logical(
710 StartEnd::Start(_),
711 CalcableValue::Literal(LengthPercentage::Length(_)),
712 StartEnd::End(_),
713 CalcableValue::Literal(LengthPercentage::Length(_))
714 ))
715 );
716 }
717
718 #[test]
719 fn test_substitution() {
720 assert_parse!(CssAtomSet::ATOMS, Position, "calc(50% + 10px)");
722 assert_parse!(CssAtomSet::ATOMS, Position, "var(--x)");
723 assert_parse!(CssAtomSet::ATOMS, Position, "calc(50% + 10px) calc(50% - 10px)");
724 assert_parse!(CssAtomSet::ATOMS, Position, "left calc(50% + 10px)");
725 assert_parse!(CssAtomSet::ATOMS, Position, "right var(--x) bottom calc(10px)");
726 }
727
728 #[test]
729 fn test_errors() {
730 assert_parse_error!(CssAtomSet::ATOMS, Position, "left left");
731 assert_parse_error!(CssAtomSet::ATOMS, Position, "bottom top");
732 assert_parse_error!(CssAtomSet::ATOMS, Position, "10px 15px 20px 15px");
733 assert_parse_error!(CssAtomSet::ATOMS, Position, "right -6px bottom");
735 }
736
737 #[test]
738 fn test_spans() {
739 assert_parse_span!(
742 CssAtomSet::ATOMS,
743 Position,
744 r#"
745 right var(--foo)
746 ^^^^^^^^^^^^^^^^
747 "#
748 );
749 assert_parse_span!(
751 CssAtomSet::ATOMS,
752 Position,
753 r#"
754 right -6px bottom 12rem 8px 20%
755 ^^^^^^^^^^^^^^^^^^^^^^^
756 "#
757 );
758 }
759}