use super::prelude::*;
use crate::LengthPercentage;
#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum Position {
One(PositionOne),
Two(PositionTwo),
Four(PositionFour),
}
impl<'a> Peek<'a> for Position {
const PEEK_KINDSET: KindSet = PositionOne::PEEK_KINDSET;
#[inline(always)]
fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
where
I: Iterator<Item = Cursor> + Clone,
{
PositionOne::peek(p, c)
}
}
impl<'a> Parse<'a> for Position {
fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
where
I: Iterator<Item = Cursor> + Clone,
{
let first = p.parse::<PositionOne>()?;
if !p.peek::<PositionOne>() {
return Ok(Self::One(first));
}
let second = p.parse::<PositionOne>()?;
if !p.peek::<PositionOne>() {
return Ok(Self::Two(PositionTwo::from_two(p, first, second)?));
}
Ok(Self::Four(PositionFour::from_four(p, first, second)?))
}
}
#[derive(
Parse, Peek, IntoCursor, ToSpan, SemanticEq, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionOne {
#[atom(CssAtomSet::Left)]
Left(T![Ident]),
#[atom(CssAtomSet::Right)]
Right(T![Ident]),
#[atom(CssAtomSet::Center)]
Center(T![Ident]),
#[atom(CssAtomSet::Top)]
Top(T![Ident]),
#[atom(CssAtomSet::Bottom)]
Bottom(T![Ident]),
#[atom(CssAtomSet::XStart)]
XStart(T![Ident]),
#[atom(CssAtomSet::XEnd)]
XEnd(T![Ident]),
#[atom(CssAtomSet::YStart)]
YStart(T![Ident]),
#[atom(CssAtomSet::YEnd)]
YEnd(T![Ident]),
#[atom(CssAtomSet::BlockStart)]
BlockStart(T![Ident]),
#[atom(CssAtomSet::BlockEnd)]
BlockEnd(T![Ident]),
#[atom(CssAtomSet::InlineStart)]
InlineStart(T![Ident]),
#[atom(CssAtomSet::InlineEnd)]
InlineEnd(T![Ident]),
#[atom(CssAtomSet::Start)]
Start(T![Ident]),
#[atom(CssAtomSet::End)]
End(T![Ident]),
LengthPercentage(LengthPercentage),
}
impl PositionOne {
pub(crate) fn to_horizontal(self) -> Option<PositionHorizontal> {
match self {
Self::Left(t) => Some(PositionHorizontal::Left(t)),
Self::Right(t) => Some(PositionHorizontal::Right(t)),
Self::Center(t) => Some(PositionHorizontal::Center(t)),
Self::XStart(t) => Some(PositionHorizontal::XStart(t)),
Self::XEnd(t) => Some(PositionHorizontal::XEnd(t)),
Self::LengthPercentage(l) => Some(PositionHorizontal::LengthPercentage(l)),
_ => None,
}
}
pub(crate) fn to_vertical(self) -> Option<PositionVertical> {
match self {
Self::Top(t) => Some(PositionVertical::Top(t)),
Self::Bottom(t) => Some(PositionVertical::Bottom(t)),
Self::Center(t) => Some(PositionVertical::Center(t)),
Self::YStart(t) => Some(PositionVertical::YStart(t)),
Self::YEnd(t) => Some(PositionVertical::YEnd(t)),
Self::LengthPercentage(l) => Some(PositionVertical::LengthPercentage(l)),
_ => None,
}
}
pub(crate) fn to_horizontal_keyword(self) -> Option<PositionHorizontalKeyword> {
match self {
Self::Left(t) => Some(PositionHorizontalKeyword::Left(t)),
Self::Right(t) => Some(PositionHorizontalKeyword::Right(t)),
Self::XStart(t) => Some(PositionHorizontalKeyword::XStart(t)),
Self::XEnd(t) => Some(PositionHorizontalKeyword::XEnd(t)),
_ => None,
}
}
pub(crate) fn to_vertical_keyword(self) -> Option<PositionVerticalKeyword> {
match self {
Self::Top(t) => Some(PositionVerticalKeyword::Top(t)),
Self::Bottom(t) => Some(PositionVerticalKeyword::Bottom(t)),
Self::YStart(t) => Some(PositionVerticalKeyword::YStart(t)),
Self::YEnd(t) => Some(PositionVerticalKeyword::YEnd(t)),
_ => None,
}
}
pub(crate) fn to_block_axis(self) -> Option<PositionBlockAxis> {
match self {
Self::BlockStart(t) => Some(PositionBlockAxis::BlockStart(t)),
Self::BlockEnd(t) => Some(PositionBlockAxis::BlockEnd(t)),
Self::Center(t) => Some(PositionBlockAxis::Center(t)),
_ => None,
}
}
pub(crate) fn to_inline_axis(self) -> Option<PositionInlineAxis> {
match self {
Self::InlineStart(t) => Some(PositionInlineAxis::InlineStart(t)),
Self::InlineEnd(t) => Some(PositionInlineAxis::InlineEnd(t)),
Self::Center(t) => Some(PositionInlineAxis::Center(t)),
_ => None,
}
}
pub(crate) fn to_block_axis_keyword(self) -> Option<PositionBlockAxisKeyword> {
match self {
Self::BlockStart(t) => Some(PositionBlockAxisKeyword::BlockStart(t)),
Self::BlockEnd(t) => Some(PositionBlockAxisKeyword::BlockEnd(t)),
_ => None,
}
}
pub(crate) fn to_inline_axis_keyword(self) -> Option<PositionInlineAxisKeyword> {
match self {
Self::InlineStart(t) => Some(PositionInlineAxisKeyword::InlineStart(t)),
Self::InlineEnd(t) => Some(PositionInlineAxisKeyword::InlineEnd(t)),
_ => None,
}
}
pub(crate) fn to_logical(self) -> Option<PositionLogical> {
match self {
Self::Start(t) => Some(PositionLogical::Start(t)),
Self::End(t) => Some(PositionLogical::End(t)),
_ => None,
}
}
}
#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionTwo {
Physical(PositionHorizontal, PositionVertical),
FlowRelative(PositionBlockAxis, PositionInlineAxis),
Logical(PositionLogical, PositionLogical),
}
impl<'a> Peek<'a> for PositionTwo {
const PEEK_KINDSET: KindSet = PositionOne::PEEK_KINDSET;
#[inline(always)]
fn peek<I>(p: &Parser<'a, I>, c: Cursor) -> bool
where
I: Iterator<Item = Cursor> + Clone,
{
PositionOne::peek(p, c)
}
}
impl PositionTwo {
pub(crate) fn from_two<'a, I>(_p: &mut Parser<'a, I>, first: PositionOne, second: PositionOne) -> ParserResult<Self>
where
I: Iterator<Item = Cursor> + Clone,
{
let h = first.to_horizontal();
let v = second.to_vertical();
if let (Some(h), Some(v)) = (h, v) {
return Ok(Self::Physical(h, v));
}
let v = first.to_vertical();
let h = second.to_horizontal();
if let (Some(v), Some(h)) = (v, h) {
return Ok(Self::Physical(h, v));
}
let block = first.to_block_axis();
let inline = second.to_inline_axis();
if let (Some(block), Some(inline)) = (block, inline) {
return Ok(Self::FlowRelative(block, inline));
}
let inline = first.to_inline_axis();
let block = second.to_block_axis();
if let (Some(inline), Some(block)) = (inline, block) {
return Ok(Self::FlowRelative(block, inline));
}
let a = first.to_logical();
let b = second.to_logical();
if let (Some(a), Some(b)) = (a, b) {
return Ok(Self::Logical(a, b));
}
Err(Diagnostic::new(second.into(), Diagnostic::unexpected))?
}
}
#[derive(ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionFour {
Physical(PositionHorizontalKeyword, LengthPercentage, PositionVerticalKeyword, LengthPercentage),
FlowRelative(PositionBlockAxisKeyword, LengthPercentage, PositionInlineAxisKeyword, LengthPercentage),
Logical(PositionLogical, LengthPercentage, PositionLogical, LengthPercentage),
}
impl PositionFour {
pub(crate) fn from_four<'a, I>(p: &mut Parser<'a, I>, first: PositionOne, second: PositionOne) -> ParserResult<Self>
where
I: Iterator<Item = Cursor> + Clone,
{
let second_lp = if let PositionOne::LengthPercentage(lp) = second { Some(lp) } else { None };
if let Some(lp1) = second_lp {
let third = p.parse::<PositionOne>()?;
let fourth = p.parse::<LengthPercentage>()?;
if let Some(h_kw) = first.to_horizontal_keyword()
&& let Some(v_kw) = third.to_vertical_keyword()
{
return Ok(Self::Physical(h_kw, lp1, v_kw, fourth));
}
if let Some(v_kw) = first.to_vertical_keyword()
&& let Some(h_kw) = third.to_horizontal_keyword()
{
return Ok(Self::Physical(h_kw, fourth, v_kw, lp1));
}
if let Some(b_kw) = first.to_block_axis_keyword()
&& let Some(i_kw) = third.to_inline_axis_keyword()
{
return Ok(Self::FlowRelative(b_kw, lp1, i_kw, fourth));
}
if let Some(i_kw) = first.to_inline_axis_keyword()
&& let Some(b_kw) = third.to_block_axis_keyword()
{
return Ok(Self::FlowRelative(b_kw, fourth, i_kw, lp1));
}
if let Some(a) = first.to_logical()
&& let Some(b) = third.to_logical()
{
return Ok(Self::Logical(a, lp1, b, fourth));
}
Err(Diagnostic::new(third.into(), Diagnostic::unexpected))?
} else {
Err(Diagnostic::new(second.into(), Diagnostic::unexpected))?
}
}
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionHorizontal {
#[atom(CssAtomSet::Left)]
Left(T![Ident]),
#[atom(CssAtomSet::Right)]
Right(T![Ident]),
#[atom(CssAtomSet::Center)]
Center(T![Ident]),
#[atom(CssAtomSet::XStart)]
XStart(T![Ident]),
#[atom(CssAtomSet::XEnd)]
XEnd(T![Ident]),
LengthPercentage(LengthPercentage),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionVertical {
#[atom(CssAtomSet::Top)]
Top(T![Ident]),
#[atom(CssAtomSet::Bottom)]
Bottom(T![Ident]),
#[atom(CssAtomSet::Center)]
Center(T![Ident]),
#[atom(CssAtomSet::YStart)]
YStart(T![Ident]),
#[atom(CssAtomSet::YEnd)]
YEnd(T![Ident]),
LengthPercentage(LengthPercentage),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionBlockAxis {
#[atom(CssAtomSet::BlockStart)]
BlockStart(T![Ident]),
#[atom(CssAtomSet::BlockEnd)]
BlockEnd(T![Ident]),
#[atom(CssAtomSet::Center)]
Center(T![Ident]),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionInlineAxis {
#[atom(CssAtomSet::InlineStart)]
InlineStart(T![Ident]),
#[atom(CssAtomSet::InlineEnd)]
InlineEnd(T![Ident]),
#[atom(CssAtomSet::Center)]
Center(T![Ident]),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionLogical {
#[atom(CssAtomSet::Start)]
Start(T![Ident]),
#[atom(CssAtomSet::End)]
End(T![Ident]),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionHorizontalKeyword {
#[atom(CssAtomSet::Left)]
Left(T![Ident]),
#[atom(CssAtomSet::Right)]
Right(T![Ident]),
#[atom(CssAtomSet::XStart)]
XStart(T![Ident]),
#[atom(CssAtomSet::XEnd)]
XEnd(T![Ident]),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionVerticalKeyword {
#[atom(CssAtomSet::Top)]
Top(T![Ident]),
#[atom(CssAtomSet::Bottom)]
Bottom(T![Ident]),
#[atom(CssAtomSet::YStart)]
YStart(T![Ident]),
#[atom(CssAtomSet::YEnd)]
YEnd(T![Ident]),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionBlockAxisKeyword {
#[atom(CssAtomSet::BlockStart)]
BlockStart(T![Ident]),
#[atom(CssAtomSet::BlockEnd)]
BlockEnd(T![Ident]),
}
#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
#[derive(csskit_derives::NodeWithMetadata)]
pub enum PositionInlineAxisKeyword {
#[atom(CssAtomSet::InlineStart)]
InlineStart(T![Ident]),
#[atom(CssAtomSet::InlineEnd)]
InlineEnd(T![Ident]),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CssAtomSet;
use css_parse::{assert_parse, assert_parse_error, assert_parse_span};
#[test]
fn size_test() {
assert_eq!(std::mem::size_of::<Position>(), 68);
}
#[test]
fn test_writes() {
assert_parse!(CssAtomSet::ATOMS, Position, "left", Position::One(PositionOne::Left(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "right", Position::One(PositionOne::Right(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "top", Position::One(PositionOne::Top(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "bottom", Position::One(PositionOne::Bottom(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "center", Position::One(PositionOne::Center(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "x-start", Position::One(PositionOne::XStart(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "x-end", Position::One(PositionOne::XEnd(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "y-start", Position::One(PositionOne::YStart(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "y-end", Position::One(PositionOne::YEnd(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "block-start", Position::One(PositionOne::BlockStart(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "block-end", Position::One(PositionOne::BlockEnd(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "inline-start", Position::One(PositionOne::InlineStart(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "inline-end", Position::One(PositionOne::InlineEnd(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "start", Position::One(PositionOne::Start(_)));
assert_parse!(CssAtomSet::ATOMS, Position, "end", Position::One(PositionOne::End(_)));
assert_parse!(
CssAtomSet::ATOMS,
Position,
"50%",
Position::One(PositionOne::LengthPercentage(LengthPercentage::Percent(_)))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"center center",
Position::Two(PositionTwo::Physical(PositionHorizontal::Center(_), PositionVertical::Center(_)))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"center top",
Position::Two(PositionTwo::Physical(PositionHorizontal::Center(_), PositionVertical::Top(_)))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"50% 50%",
Position::Two(PositionTwo::Physical(
PositionHorizontal::LengthPercentage(LengthPercentage::Percent(_)),
PositionVertical::LengthPercentage(LengthPercentage::Percent(_))
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"20px 30px",
Position::Two(PositionTwo::Physical(
PositionHorizontal::LengthPercentage(_),
PositionVertical::LengthPercentage(_)
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"2% bottom",
Position::Two(PositionTwo::Physical(
PositionHorizontal::LengthPercentage(LengthPercentage::Percent(_)),
PositionVertical::Bottom(_)
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"-70% -180%",
Position::Two(PositionTwo::Physical(
PositionHorizontal::LengthPercentage(LengthPercentage::Percent(_)),
PositionVertical::LengthPercentage(LengthPercentage::Percent(_))
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"right 8.5%",
Position::Two(PositionTwo::Physical(
PositionHorizontal::Right(_),
PositionVertical::LengthPercentage(LengthPercentage::Percent(_))
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"x-start y-end",
Position::Two(PositionTwo::Physical(PositionHorizontal::XStart(_), PositionVertical::YEnd(_)))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"block-start inline-end",
Position::Two(PositionTwo::FlowRelative(
PositionBlockAxis::BlockStart(_),
PositionInlineAxis::InlineEnd(_)
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"inline-end block-start",
Position::Two(PositionTwo::FlowRelative(
PositionBlockAxis::BlockStart(_),
PositionInlineAxis::InlineEnd(_)
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"start end",
Position::Two(PositionTwo::Logical(PositionLogical::Start(_), PositionLogical::End(_)))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"right -6px bottom 12vmin",
Position::Four(PositionFour::Physical(
PositionHorizontalKeyword::Right(_),
LengthPercentage::Length(_),
PositionVerticalKeyword::Bottom(_),
LengthPercentage::Length(_)
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"bottom 12vmin right -6px",
Position::Four(PositionFour::Physical(
PositionHorizontalKeyword::Right(_),
LengthPercentage::Length(_),
PositionVerticalKeyword::Bottom(_),
LengthPercentage::Length(_)
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"block-start 10px inline-end 20px",
Position::Four(PositionFour::FlowRelative(
PositionBlockAxisKeyword::BlockStart(_),
LengthPercentage::Length(_),
PositionInlineAxisKeyword::InlineEnd(_),
LengthPercentage::Length(_)
))
);
assert_parse!(
CssAtomSet::ATOMS,
Position,
"start 10px end 20px",
Position::Four(PositionFour::Logical(
PositionLogical::Start(_),
LengthPercentage::Length(_),
PositionLogical::End(_),
LengthPercentage::Length(_)
))
);
}
#[test]
fn test_errors() {
assert_parse_error!(CssAtomSet::ATOMS, Position, "left left");
assert_parse_error!(CssAtomSet::ATOMS, Position, "bottom top");
assert_parse_error!(CssAtomSet::ATOMS, Position, "10px 15px 20px 15px");
assert_parse_error!(CssAtomSet::ATOMS, Position, "right -6px bottom");
}
#[test]
fn test_spans() {
assert_parse_span!(
CssAtomSet::ATOMS,
Position,
r#"
right var(--foo)
^^^^^
"#
);
assert_parse_span!(
CssAtomSet::ATOMS,
Position,
r#"
right -6px bottom 12rem 8px 20%
^^^^^^^^^^^^^^^^^^^^^^^
"#
);
}
}