oxc_css_parser/parser/
convert.rs1use crate::{
2 Span,
3 ast::{
4 Dimension, DimensionKind, Ident, InterpolableIdentStaticPart, InterpolableStrStaticPart,
5 InterpolableUrlStaticPart, Number, Placeholder, Str,
6 },
7 error::{Error, ErrorKind, PResult},
8 tokenizer::token,
9};
10
11impl<'a> TryFrom<(token::Dimension<'a>, Span)> for Dimension<'a> {
12 type Error = Error;
13
14 fn try_from((token, span): (token::Dimension<'a>, Span)) -> PResult<Self> {
15 let value_span = Span { start: span.start, end: span.start + token.value.raw.len() };
16 let unit_span = Span { start: span.start + token.value.raw.len(), end: span.end };
17
18 let value = (token.value, value_span).try_into()?;
19 let unit = Ident::from((token.unit, unit_span));
20 let kind = dimension_kind(unit.name);
21
22 Ok(Dimension { value, unit, kind, span })
23 }
24}
25
26pub(super) fn dimension_kind(unit_name: &str) -> DimensionKind {
27 if unit_name.eq_ignore_ascii_case("px")
28 || unit_name.eq_ignore_ascii_case("em")
29 || unit_name.eq_ignore_ascii_case("rem")
30 || unit_name.eq_ignore_ascii_case("ex")
31 || unit_name.eq_ignore_ascii_case("rex")
32 || unit_name.eq_ignore_ascii_case("cap")
33 || unit_name.eq_ignore_ascii_case("rcap")
34 || unit_name.eq_ignore_ascii_case("ch")
35 || unit_name.eq_ignore_ascii_case("rch")
36 || unit_name.eq_ignore_ascii_case("ic")
37 || unit_name.eq_ignore_ascii_case("ric")
38 || unit_name.eq_ignore_ascii_case("lh")
39 || unit_name.eq_ignore_ascii_case("rlh")
40 || unit_name.eq_ignore_ascii_case("vw")
41 || unit_name.eq_ignore_ascii_case("vh")
42 || unit_name.eq_ignore_ascii_case("vi")
43 || unit_name.eq_ignore_ascii_case("vb")
44 || unit_name.eq_ignore_ascii_case("vmin")
45 || unit_name.eq_ignore_ascii_case("vmax")
46 || unit_name.eq_ignore_ascii_case("lvw")
47 || unit_name.eq_ignore_ascii_case("lvh")
48 || unit_name.eq_ignore_ascii_case("lvi")
49 || unit_name.eq_ignore_ascii_case("lvb")
50 || unit_name.eq_ignore_ascii_case("lvmin")
51 || unit_name.eq_ignore_ascii_case("lvmax")
52 || unit_name.eq_ignore_ascii_case("svw")
53 || unit_name.eq_ignore_ascii_case("svh")
54 || unit_name.eq_ignore_ascii_case("svi")
55 || unit_name.eq_ignore_ascii_case("svb")
56 || unit_name.eq_ignore_ascii_case("vmin")
57 || unit_name.eq_ignore_ascii_case("vmax")
58 || unit_name.eq_ignore_ascii_case("dvw")
59 || unit_name.eq_ignore_ascii_case("dvh")
60 || unit_name.eq_ignore_ascii_case("dvi")
61 || unit_name.eq_ignore_ascii_case("dvb")
62 || unit_name.eq_ignore_ascii_case("dvmin")
63 || unit_name.eq_ignore_ascii_case("dvmax")
64 || unit_name.eq_ignore_ascii_case("cm")
65 || unit_name.eq_ignore_ascii_case("mm")
66 || unit_name.eq_ignore_ascii_case("Q")
67 || unit_name.eq_ignore_ascii_case("in")
68 || unit_name.eq_ignore_ascii_case("pc")
69 || unit_name.eq_ignore_ascii_case("pt")
70 {
71 DimensionKind::Length
72 } else if unit_name.eq_ignore_ascii_case("deg")
73 || unit_name.eq_ignore_ascii_case("grad")
74 || unit_name.eq_ignore_ascii_case("rad")
75 || unit_name.eq_ignore_ascii_case("turn")
76 {
77 DimensionKind::Angle
78 } else if unit_name.eq_ignore_ascii_case("s") || unit_name.eq_ignore_ascii_case("ms") {
79 DimensionKind::Duration
80 } else if unit_name.eq_ignore_ascii_case("Hz") || unit_name.eq_ignore_ascii_case("kHz") {
81 DimensionKind::Frequency
82 } else if unit_name.eq_ignore_ascii_case("dpi")
83 || unit_name.eq_ignore_ascii_case("dpcm")
84 || unit_name.eq_ignore_ascii_case("dppx")
85 {
86 DimensionKind::Resolution
87 } else if unit_name.eq_ignore_ascii_case("fr") {
88 DimensionKind::Flex
89 } else {
90 DimensionKind::Unknown
91 }
92}
93
94impl<'a> From<(token::Ident<'a>, Span)> for Ident<'a> {
95 fn from((token, span): (token::Ident<'a>, Span)) -> Self {
96 Ident { name: token.raw, raw: token.raw, span }
97 }
98}
99
100impl<'a> From<(token::Placeholder<'a>, Span)> for Placeholder<'a> {
101 fn from((token, span): (token::Placeholder<'a>, Span)) -> Self {
102 Placeholder { index: token.index, suffix: token.suffix, span }
103 }
104}
105
106impl<'a> From<(token::Ident<'a>, Span)> for InterpolableIdentStaticPart<'a> {
107 fn from((token, span): (token::Ident<'a>, Span)) -> Self {
108 InterpolableIdentStaticPart { value: token.raw, raw: token.raw, span }
109 }
110}
111
112impl<'a> TryFrom<(token::Number<'a>, Span)> for Number<'a> {
113 type Error = Error;
114
115 fn try_from((token, span): (token::Number<'a>, Span)) -> PResult<Self> {
116 token
117 .raw
118 .parse()
119 .map_err(|_| Error { kind: ErrorKind::InvalidNumber, span: span.clone() })
120 .map(|value| Self { value, raw: token.raw, span })
121 }
122}
123
124impl<'a> From<(token::StrTemplate<'a>, Span)> for InterpolableStrStaticPart<'a> {
125 fn from((token, span): (token::StrTemplate<'a>, Span)) -> Self {
126 let raw_without_quotes = if token.tail {
127 unsafe { token.raw.get_unchecked(0..token.raw.len() - 1) }
128 } else if token.head {
129 unsafe { token.raw.get_unchecked(1..token.raw.len()) }
130 } else {
131 token.raw
132 };
133 let value = raw_without_quotes;
134 Self { value, raw: token.raw, span }
135 }
136}
137
138impl<'a> From<(token::UrlTemplate<'a>, Span)> for InterpolableUrlStaticPart<'a> {
139 fn from((token, span): (token::UrlTemplate<'a>, Span)) -> Self {
140 let value = token.raw;
141 Self { value, raw: token.raw, span }
142 }
143}
144
145impl<'a> From<(token::Str<'a>, Span)> for Str<'a> {
146 fn from((str, span): (token::Str<'a>, Span)) -> Self {
147 let raw_without_quotes = unsafe { str.raw.get_unchecked(1..str.raw.len() - 1) };
148 let value = raw_without_quotes;
149 Self { value, raw: str.raw, span }
150 }
151}