1use super::prelude::*;
2use crate::LengthPercentage;
3
4#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
11#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
12#[derive(csskit_derives::NodeWithMetadata)]
13pub enum Position {
14 One(PositionOne),
15 Two(PositionTwo),
16 Four(PositionFour),
17}
18
19impl<'a> Peek<'a> for Position {
20 const PEEK_KINDSET: KindSet = PositionOne::PEEK_KINDSET;
21
22 #[inline(always)]
23 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
24 where
25 I: Iterator<Item = Cursor> + Clone,
26 {
27 PositionOne::peek(p, c)
28 }
29}
30
31impl<'a> Parse<'a> for Position {
32 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
33 where
34 I: Iterator<Item = Cursor> + Clone,
35 {
36 let first = p.parse::<PositionOne>()?;
37 if !p.peek::<PositionOne>() {
38 return Ok(Self::One(first));
39 }
40 let second = p.parse::<PositionOne>()?;
41 if !p.peek::<PositionOne>() {
42 return Ok(Self::Two(PositionTwo::from_two(p, first, second)?));
43 }
44 Ok(Self::Four(PositionFour::from_four(p, first, second)?))
46 }
47}
48
49#[derive(
61 Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
62)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
64#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
65#[derive(csskit_derives::NodeWithMetadata)]
66pub enum PositionOne {
67 #[atom(CssAtomSet::Left)]
68 Left(T![Ident]),
69 #[atom(CssAtomSet::Right)]
70 Right(T![Ident]),
71 #[atom(CssAtomSet::Center)]
72 Center(T![Ident]),
73 #[atom(CssAtomSet::Top)]
74 Top(T![Ident]),
75 #[atom(CssAtomSet::Bottom)]
76 Bottom(T![Ident]),
77 #[atom(CssAtomSet::XStart)]
78 XStart(T![Ident]),
79 #[atom(CssAtomSet::XEnd)]
80 XEnd(T![Ident]),
81 #[atom(CssAtomSet::YStart)]
82 YStart(T![Ident]),
83 #[atom(CssAtomSet::YEnd)]
84 YEnd(T![Ident]),
85 #[atom(CssAtomSet::BlockStart)]
86 BlockStart(T![Ident]),
87 #[atom(CssAtomSet::BlockEnd)]
88 BlockEnd(T![Ident]),
89 #[atom(CssAtomSet::InlineStart)]
90 InlineStart(T![Ident]),
91 #[atom(CssAtomSet::InlineEnd)]
92 InlineEnd(T![Ident]),
93 #[atom(CssAtomSet::Start)]
94 Start(T![Ident]),
95 #[atom(CssAtomSet::End)]
96 End(T![Ident]),
97 LengthPercentage(LengthPercentage),
98}
99
100impl PositionOne {
101 pub(crate) fn to_horizontal(self) -> Option<PositionHorizontal> {
102 match self {
103 Self::Left(t) => Some(PositionHorizontal::Left(t)),
104 Self::Right(t) => Some(PositionHorizontal::Right(t)),
105 Self::Center(t) => Some(PositionHorizontal::Center(t)),
106 Self::XStart(t) => Some(PositionHorizontal::XStart(t)),
107 Self::XEnd(t) => Some(PositionHorizontal::XEnd(t)),
108 Self::LengthPercentage(l) => Some(PositionHorizontal::LengthPercentage(l)),
109 _ => None,
110 }
111 }
112
113 pub(crate) fn to_vertical(self) -> Option<PositionVertical> {
114 match self {
115 Self::Top(t) => Some(PositionVertical::Top(t)),
116 Self::Bottom(t) => Some(PositionVertical::Bottom(t)),
117 Self::Center(t) => Some(PositionVertical::Center(t)),
118 Self::YStart(t) => Some(PositionVertical::YStart(t)),
119 Self::YEnd(t) => Some(PositionVertical::YEnd(t)),
120 Self::LengthPercentage(l) => Some(PositionVertical::LengthPercentage(l)),
121 _ => None,
122 }
123 }
124
125 pub(crate) fn to_horizontal_keyword(self) -> Option<PositionHorizontalKeyword> {
126 match self {
127 Self::Left(t) => Some(PositionHorizontalKeyword::Left(t)),
128 Self::Right(t) => Some(PositionHorizontalKeyword::Right(t)),
129 Self::XStart(t) => Some(PositionHorizontalKeyword::XStart(t)),
130 Self::XEnd(t) => Some(PositionHorizontalKeyword::XEnd(t)),
131 _ => None,
132 }
133 }
134
135 pub(crate) fn to_vertical_keyword(self) -> Option<PositionVerticalKeyword> {
136 match self {
137 Self::Top(t) => Some(PositionVerticalKeyword::Top(t)),
138 Self::Bottom(t) => Some(PositionVerticalKeyword::Bottom(t)),
139 Self::YStart(t) => Some(PositionVerticalKeyword::YStart(t)),
140 Self::YEnd(t) => Some(PositionVerticalKeyword::YEnd(t)),
141 _ => None,
142 }
143 }
144
145 pub(crate) fn to_block_axis(self) -> Option<PositionBlockAxis> {
146 match self {
147 Self::BlockStart(t) => Some(PositionBlockAxis::BlockStart(t)),
148 Self::BlockEnd(t) => Some(PositionBlockAxis::BlockEnd(t)),
149 Self::Center(t) => Some(PositionBlockAxis::Center(t)),
150 _ => None,
151 }
152 }
153
154 pub(crate) fn to_inline_axis(self) -> Option<PositionInlineAxis> {
155 match self {
156 Self::InlineStart(t) => Some(PositionInlineAxis::InlineStart(t)),
157 Self::InlineEnd(t) => Some(PositionInlineAxis::InlineEnd(t)),
158 Self::Center(t) => Some(PositionInlineAxis::Center(t)),
159 _ => None,
160 }
161 }
162
163 pub(crate) fn to_block_axis_keyword(self) -> Option<PositionBlockAxisKeyword> {
164 match self {
165 Self::BlockStart(t) => Some(PositionBlockAxisKeyword::BlockStart(t)),
166 Self::BlockEnd(t) => Some(PositionBlockAxisKeyword::BlockEnd(t)),
167 _ => None,
168 }
169 }
170
171 pub(crate) fn to_inline_axis_keyword(self) -> Option<PositionInlineAxisKeyword> {
172 match self {
173 Self::InlineStart(t) => Some(PositionInlineAxisKeyword::InlineStart(t)),
174 Self::InlineEnd(t) => Some(PositionInlineAxisKeyword::InlineEnd(t)),
175 _ => None,
176 }
177 }
178
179 pub(crate) fn to_logical(self) -> Option<PositionLogical> {
180 match self {
181 Self::Start(t) => Some(PositionLogical::Start(t)),
182 Self::End(t) => Some(PositionLogical::End(t)),
183 _ => None,
184 }
185 }
186}
187
188#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
207#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
208#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
209#[derive(csskit_derives::NodeWithMetadata)]
210pub enum PositionTwo {
211 Physical(PositionHorizontal, PositionVertical),
214 FlowRelative(PositionBlockAxis, PositionInlineAxis),
217 Logical(PositionLogical, PositionLogical),
219}
220
221impl<'a> Peek<'a> for PositionTwo {
222 const PEEK_KINDSET: KindSet = PositionOne::PEEK_KINDSET;
223
224 #[inline(always)]
225 fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
226 where
227 I: Iterator<Item = Cursor> + Clone,
228 {
229 PositionOne::peek(p, c)
230 }
231}
232
233impl PositionTwo {
234 pub(crate) fn from_two<'a, I>(_p: &mut Parser<'a, I>, first: PositionOne, second: PositionOne) -> ParserResult<Self>
235 where
236 I: Iterator<Item = Cursor> + Clone,
237 {
238 let h = first.to_horizontal();
240 let v = second.to_vertical();
241 if let (Some(h), Some(v)) = (h, v) {
242 return Ok(Self::Physical(h, v));
243 }
244 let v = first.to_vertical();
246 let h = second.to_horizontal();
247 if let (Some(v), Some(h)) = (v, h) {
248 return Ok(Self::Physical(h, v));
249 }
250 let block = first.to_block_axis();
252 let inline = second.to_inline_axis();
253 if let (Some(block), Some(inline)) = (block, inline) {
254 return Ok(Self::FlowRelative(block, inline));
255 }
256 let inline = first.to_inline_axis();
257 let block = second.to_block_axis();
258 if let (Some(inline), Some(block)) = (inline, block) {
259 return Ok(Self::FlowRelative(block, inline));
260 }
261 let a = first.to_logical();
263 let b = second.to_logical();
264 if let (Some(a), Some(b)) = (a, b) {
265 return Ok(Self::Logical(a, b));
266 }
267 Err(Diagnostic::new(second.into(), Diagnostic::unexpected))?
268 }
269}
270
271#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
287#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
288#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
289#[derive(csskit_derives::NodeWithMetadata)]
290pub enum PositionFour {
291 Physical(PositionHorizontalKeyword, LengthPercentage, PositionVerticalKeyword, LengthPercentage),
294 FlowRelative(PositionBlockAxisKeyword, LengthPercentage, PositionInlineAxisKeyword, LengthPercentage),
297 Logical(PositionLogical, LengthPercentage, PositionLogical, LengthPercentage),
299}
300
301impl PositionFour {
302 pub(crate) fn from_four<'a, I>(p: &mut Parser<'a, I>, first: PositionOne, second: PositionOne) -> ParserResult<Self>
303 where
304 I: Iterator<Item = Cursor> + Clone,
305 {
306 let second_lp = if let PositionOne::LengthPercentage(lp) = second { Some(lp) } else { None };
310
311 if let Some(lp1) = second_lp {
312 let third = p.parse::<PositionOne>()?;
313 let fourth = p.parse::<LengthPercentage>()?;
314 if let Some(h_kw) = first.to_horizontal_keyword()
316 && let Some(v_kw) = third.to_vertical_keyword()
317 {
318 return Ok(Self::Physical(h_kw, lp1, v_kw, fourth));
319 }
320 if let Some(v_kw) = first.to_vertical_keyword()
322 && let Some(h_kw) = third.to_horizontal_keyword()
323 {
324 return Ok(Self::Physical(h_kw, fourth, v_kw, lp1));
325 }
326 if let Some(b_kw) = first.to_block_axis_keyword()
328 && let Some(i_kw) = third.to_inline_axis_keyword()
329 {
330 return Ok(Self::FlowRelative(b_kw, lp1, i_kw, fourth));
331 }
332 if let Some(i_kw) = first.to_inline_axis_keyword()
334 && let Some(b_kw) = third.to_block_axis_keyword()
335 {
336 return Ok(Self::FlowRelative(b_kw, fourth, i_kw, lp1));
337 }
338 if let Some(a) = first.to_logical()
340 && let Some(b) = third.to_logical()
341 {
342 return Ok(Self::Logical(a, lp1, b, fourth));
343 }
344 Err(Diagnostic::new(third.into(), Diagnostic::unexpected))?
345 } else {
346 Err(Diagnostic::new(second.into(), Diagnostic::unexpected))?
349 }
350 }
351}
352
353#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
357#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
358#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
359#[derive(csskit_derives::NodeWithMetadata)]
360pub enum PositionHorizontal {
361 #[atom(CssAtomSet::Left)]
362 Left(T![Ident]),
363 #[atom(CssAtomSet::Right)]
364 Right(T![Ident]),
365 #[atom(CssAtomSet::Center)]
366 Center(T![Ident]),
367 #[atom(CssAtomSet::XStart)]
368 XStart(T![Ident]),
369 #[atom(CssAtomSet::XEnd)]
370 XEnd(T![Ident]),
371 LengthPercentage(LengthPercentage),
372}
373
374#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
378#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
379#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
380#[derive(csskit_derives::NodeWithMetadata)]
381pub enum PositionVertical {
382 #[atom(CssAtomSet::Top)]
383 Top(T![Ident]),
384 #[atom(CssAtomSet::Bottom)]
385 Bottom(T![Ident]),
386 #[atom(CssAtomSet::Center)]
387 Center(T![Ident]),
388 #[atom(CssAtomSet::YStart)]
389 YStart(T![Ident]),
390 #[atom(CssAtomSet::YEnd)]
391 YEnd(T![Ident]),
392 LengthPercentage(LengthPercentage),
393}
394
395#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
399#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
400#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
401#[derive(csskit_derives::NodeWithMetadata)]
402pub enum PositionBlockAxis {
403 #[atom(CssAtomSet::BlockStart)]
404 BlockStart(T![Ident]),
405 #[atom(CssAtomSet::BlockEnd)]
406 BlockEnd(T![Ident]),
407 #[atom(CssAtomSet::Center)]
408 Center(T![Ident]),
409}
410
411#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
415#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
416#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
417#[derive(csskit_derives::NodeWithMetadata)]
418pub enum PositionInlineAxis {
419 #[atom(CssAtomSet::InlineStart)]
420 InlineStart(T![Ident]),
421 #[atom(CssAtomSet::InlineEnd)]
422 InlineEnd(T![Ident]),
423 #[atom(CssAtomSet::Center)]
424 Center(T![Ident]),
425}
426
427#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
434#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
435#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
436#[derive(csskit_derives::NodeWithMetadata)]
437pub enum PositionLogical {
438 #[atom(CssAtomSet::Start)]
439 Start(T![Ident]),
440 #[atom(CssAtomSet::End)]
441 End(T![Ident]),
442}
443
444#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
448#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
449#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
450#[derive(csskit_derives::NodeWithMetadata)]
451pub enum PositionHorizontalKeyword {
452 #[atom(CssAtomSet::Left)]
453 Left(T![Ident]),
454 #[atom(CssAtomSet::Right)]
455 Right(T![Ident]),
456 #[atom(CssAtomSet::XStart)]
457 XStart(T![Ident]),
458 #[atom(CssAtomSet::XEnd)]
459 XEnd(T![Ident]),
460}
461
462#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
466#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
467#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
468#[derive(csskit_derives::NodeWithMetadata)]
469pub enum PositionVerticalKeyword {
470 #[atom(CssAtomSet::Top)]
471 Top(T![Ident]),
472 #[atom(CssAtomSet::Bottom)]
473 Bottom(T![Ident]),
474 #[atom(CssAtomSet::YStart)]
475 YStart(T![Ident]),
476 #[atom(CssAtomSet::YEnd)]
477 YEnd(T![Ident]),
478}
479
480#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
484#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
485#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
486#[derive(csskit_derives::NodeWithMetadata)]
487pub enum PositionBlockAxisKeyword {
488 #[atom(CssAtomSet::BlockStart)]
489 BlockStart(T![Ident]),
490 #[atom(CssAtomSet::BlockEnd)]
491 BlockEnd(T![Ident]),
492}
493
494#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
498#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
499#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
500#[derive(csskit_derives::NodeWithMetadata)]
501pub enum PositionInlineAxisKeyword {
502 #[atom(CssAtomSet::InlineStart)]
503 InlineStart(T![Ident]),
504 #[atom(CssAtomSet::InlineEnd)]
505 InlineEnd(T![Ident]),
506}
507
508#[cfg(test)]
509mod tests {
510 use super::*;
511 use crate::CssAtomSet;
512 use css_parse::{assert_parse, assert_parse_error, assert_parse_span};
513
514 #[test]
515 fn size_test() {
516 assert_eq!(std::mem::size_of::<Position>(), 68);
517 }
518
519 #[test]
520 fn test_writes() {
521 assert_parse!(CssAtomSet::ATOMS, Position, "left", Position::One(PositionOne::Left(_)));
523 assert_parse!(CssAtomSet::ATOMS, Position, "right", Position::One(PositionOne::Right(_)));
524 assert_parse!(CssAtomSet::ATOMS, Position, "top", Position::One(PositionOne::Top(_)));
525 assert_parse!(CssAtomSet::ATOMS, Position, "bottom", Position::One(PositionOne::Bottom(_)));
526 assert_parse!(CssAtomSet::ATOMS, Position, "center", Position::One(PositionOne::Center(_)));
527 assert_parse!(CssAtomSet::ATOMS, Position, "x-start", Position::One(PositionOne::XStart(_)));
528 assert_parse!(CssAtomSet::ATOMS, Position, "x-end", Position::One(PositionOne::XEnd(_)));
529 assert_parse!(CssAtomSet::ATOMS, Position, "y-start", Position::One(PositionOne::YStart(_)));
530 assert_parse!(CssAtomSet::ATOMS, Position, "y-end", Position::One(PositionOne::YEnd(_)));
531 assert_parse!(CssAtomSet::ATOMS, Position, "block-start", Position::One(PositionOne::BlockStart(_)));
532 assert_parse!(CssAtomSet::ATOMS, Position, "block-end", Position::One(PositionOne::BlockEnd(_)));
533 assert_parse!(CssAtomSet::ATOMS, Position, "inline-start", Position::One(PositionOne::InlineStart(_)));
534 assert_parse!(CssAtomSet::ATOMS, Position, "inline-end", Position::One(PositionOne::InlineEnd(_)));
535 assert_parse!(CssAtomSet::ATOMS, Position, "start", Position::One(PositionOne::Start(_)));
536 assert_parse!(CssAtomSet::ATOMS, Position, "end", Position::One(PositionOne::End(_)));
537 assert_parse!(
538 CssAtomSet::ATOMS,
539 Position,
540 "50%",
541 Position::One(PositionOne::LengthPercentage(LengthPercentage::Percent(_)))
542 );
543 assert_parse!(
545 CssAtomSet::ATOMS,
546 Position,
547 "center center",
548 Position::Two(PositionTwo::Physical(PositionHorizontal::Center(_), PositionVertical::Center(_)))
549 );
550 assert_parse!(
551 CssAtomSet::ATOMS,
552 Position,
553 "center top",
554 Position::Two(PositionTwo::Physical(PositionHorizontal::Center(_), PositionVertical::Top(_)))
555 );
556 assert_parse!(
557 CssAtomSet::ATOMS,
558 Position,
559 "50% 50%",
560 Position::Two(PositionTwo::Physical(
561 PositionHorizontal::LengthPercentage(LengthPercentage::Percent(_)),
562 PositionVertical::LengthPercentage(LengthPercentage::Percent(_))
563 ))
564 );
565 assert_parse!(
566 CssAtomSet::ATOMS,
567 Position,
568 "20px 30px",
569 Position::Two(PositionTwo::Physical(
570 PositionHorizontal::LengthPercentage(_),
571 PositionVertical::LengthPercentage(_)
572 ))
573 );
574 assert_parse!(
575 CssAtomSet::ATOMS,
576 Position,
577 "2% bottom",
578 Position::Two(PositionTwo::Physical(
579 PositionHorizontal::LengthPercentage(LengthPercentage::Percent(_)),
580 PositionVertical::Bottom(_)
581 ))
582 );
583 assert_parse!(
584 CssAtomSet::ATOMS,
585 Position,
586 "-70% -180%",
587 Position::Two(PositionTwo::Physical(
588 PositionHorizontal::LengthPercentage(LengthPercentage::Percent(_)),
589 PositionVertical::LengthPercentage(LengthPercentage::Percent(_))
590 ))
591 );
592 assert_parse!(
593 CssAtomSet::ATOMS,
594 Position,
595 "right 8.5%",
596 Position::Two(PositionTwo::Physical(
597 PositionHorizontal::Right(_),
598 PositionVertical::LengthPercentage(LengthPercentage::Percent(_))
599 ))
600 );
601 assert_parse!(
603 CssAtomSet::ATOMS,
604 Position,
605 "x-start y-end",
606 Position::Two(PositionTwo::Physical(PositionHorizontal::XStart(_), PositionVertical::YEnd(_)))
607 );
608 assert_parse!(
610 CssAtomSet::ATOMS,
611 Position,
612 "block-start inline-end",
613 Position::Two(PositionTwo::FlowRelative(
614 PositionBlockAxis::BlockStart(_),
615 PositionInlineAxis::InlineEnd(_)
616 ))
617 );
618 assert_parse!(
619 CssAtomSet::ATOMS,
620 Position,
621 "inline-end block-start",
622 Position::Two(PositionTwo::FlowRelative(
623 PositionBlockAxis::BlockStart(_),
624 PositionInlineAxis::InlineEnd(_)
625 ))
626 );
627 assert_parse!(
629 CssAtomSet::ATOMS,
630 Position,
631 "start end",
632 Position::Two(PositionTwo::Logical(PositionLogical::Start(_), PositionLogical::End(_)))
633 );
634 assert_parse!(
636 CssAtomSet::ATOMS,
637 Position,
638 "right -6px bottom 12vmin",
639 Position::Four(PositionFour::Physical(
640 PositionHorizontalKeyword::Right(_),
641 LengthPercentage::Length(_),
642 PositionVerticalKeyword::Bottom(_),
643 LengthPercentage::Length(_)
644 ))
645 );
646 assert_parse!(
647 CssAtomSet::ATOMS,
648 Position,
649 "bottom 12vmin right -6px",
650 Position::Four(PositionFour::Physical(
651 PositionHorizontalKeyword::Right(_),
652 LengthPercentage::Length(_),
653 PositionVerticalKeyword::Bottom(_),
654 LengthPercentage::Length(_)
655 ))
656 );
657 assert_parse!(
659 CssAtomSet::ATOMS,
660 Position,
661 "block-start 10px inline-end 20px",
662 Position::Four(PositionFour::FlowRelative(
663 PositionBlockAxisKeyword::BlockStart(_),
664 LengthPercentage::Length(_),
665 PositionInlineAxisKeyword::InlineEnd(_),
666 LengthPercentage::Length(_)
667 ))
668 );
669 assert_parse!(
671 CssAtomSet::ATOMS,
672 Position,
673 "start 10px end 20px",
674 Position::Four(PositionFour::Logical(
675 PositionLogical::Start(_),
676 LengthPercentage::Length(_),
677 PositionLogical::End(_),
678 LengthPercentage::Length(_)
679 ))
680 );
681 }
682
683 #[test]
684 fn test_errors() {
685 assert_parse_error!(CssAtomSet::ATOMS, Position, "left left");
686 assert_parse_error!(CssAtomSet::ATOMS, Position, "bottom top");
687 assert_parse_error!(CssAtomSet::ATOMS, Position, "10px 15px 20px 15px");
688 assert_parse_error!(CssAtomSet::ATOMS, Position, "right -6px bottom");
690 }
691
692 #[test]
693 fn test_spans() {
694 assert_parse_span!(
696 CssAtomSet::ATOMS,
697 Position,
698 r#"
699 right var(--foo)
700 ^^^^^
701 "#
702 );
703 assert_parse_span!(
705 CssAtomSet::ATOMS,
706 Position,
707 r#"
708 right -6px bottom 12rem 8px 20%
709 ^^^^^^^^^^^^^^^^^^^^^^^
710 "#
711 );
712 }
713}