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 util,
10};
11use std::borrow::Cow;
12
13impl<'s> TryFrom<(token::Dimension<'s>, Span)> for Dimension<'s> {
14 type Error = Error;
15
16 fn try_from((token, span): (token::Dimension<'s>, Span)) -> PResult<Self> {
17 let value_span = Span {
18 start: span.start,
19 end: span.start + token.value.raw.len(),
20 };
21 let unit_span = Span {
22 start: span.start + token.value.raw.len(),
23 end: span.end,
24 };
25
26 let value = (token.value, value_span).try_into()?;
27 let unit = Ident::from((token.unit, unit_span));
28 let unit_name = &unit.name;
29 let kind = if unit_name.eq_ignore_ascii_case("px")
30 || unit_name.eq_ignore_ascii_case("em")
31 || unit_name.eq_ignore_ascii_case("rem")
32 || unit_name.eq_ignore_ascii_case("ex")
33 || unit_name.eq_ignore_ascii_case("rex")
34 || unit_name.eq_ignore_ascii_case("cap")
35 || unit_name.eq_ignore_ascii_case("rcap")
36 || unit_name.eq_ignore_ascii_case("ch")
37 || unit_name.eq_ignore_ascii_case("rch")
38 || unit_name.eq_ignore_ascii_case("ic")
39 || unit_name.eq_ignore_ascii_case("ric")
40 || unit_name.eq_ignore_ascii_case("lh")
41 || unit_name.eq_ignore_ascii_case("rlh")
42 || unit_name.eq_ignore_ascii_case("vw")
43 || unit_name.eq_ignore_ascii_case("vh")
44 || unit_name.eq_ignore_ascii_case("vi")
45 || unit_name.eq_ignore_ascii_case("vb")
46 || unit_name.eq_ignore_ascii_case("vmin")
47 || unit_name.eq_ignore_ascii_case("vmax")
48 || unit_name.eq_ignore_ascii_case("lvw")
49 || unit_name.eq_ignore_ascii_case("lvh")
50 || unit_name.eq_ignore_ascii_case("lvi")
51 || unit_name.eq_ignore_ascii_case("lvb")
52 || unit_name.eq_ignore_ascii_case("lvmin")
53 || unit_name.eq_ignore_ascii_case("lvmax")
54 || unit_name.eq_ignore_ascii_case("svw")
55 || unit_name.eq_ignore_ascii_case("svh")
56 || unit_name.eq_ignore_ascii_case("svi")
57 || unit_name.eq_ignore_ascii_case("svb")
58 || unit_name.eq_ignore_ascii_case("vmin")
59 || unit_name.eq_ignore_ascii_case("vmax")
60 || unit_name.eq_ignore_ascii_case("dvw")
61 || unit_name.eq_ignore_ascii_case("dvh")
62 || unit_name.eq_ignore_ascii_case("dvi")
63 || unit_name.eq_ignore_ascii_case("dvb")
64 || unit_name.eq_ignore_ascii_case("dvmin")
65 || unit_name.eq_ignore_ascii_case("dvmax")
66 || unit_name.eq_ignore_ascii_case("cm")
67 || unit_name.eq_ignore_ascii_case("mm")
68 || unit_name.eq_ignore_ascii_case("Q")
69 || unit_name.eq_ignore_ascii_case("in")
70 || unit_name.eq_ignore_ascii_case("pc")
71 || unit_name.eq_ignore_ascii_case("pt")
72 {
73 DimensionKind::Length
74 } else if unit_name.eq_ignore_ascii_case("deg")
75 || unit_name.eq_ignore_ascii_case("grad")
76 || unit_name.eq_ignore_ascii_case("rad")
77 || unit_name.eq_ignore_ascii_case("turn")
78 {
79 DimensionKind::Angle
80 } else if unit_name.eq_ignore_ascii_case("s") || unit_name.eq_ignore_ascii_case("ms") {
81 DimensionKind::Duration
82 } else if unit_name.eq_ignore_ascii_case("Hz") || unit_name.eq_ignore_ascii_case("kHz") {
83 DimensionKind::Frequency
84 } else if unit_name.eq_ignore_ascii_case("dpi")
85 || unit_name.eq_ignore_ascii_case("dpcm")
86 || unit_name.eq_ignore_ascii_case("dppx")
87 {
88 DimensionKind::Resolution
89 } else if unit_name.eq_ignore_ascii_case("fr") {
90 DimensionKind::Flex
91 } else {
92 DimensionKind::Unknown
93 };
94
95 Ok(Dimension {
96 value,
97 unit,
98 kind,
99 span,
100 })
101 }
102}
103
104impl<'s> From<(token::Ident<'s>, Span)> for Ident<'s> {
105 fn from((token, span): (token::Ident<'s>, Span)) -> Self {
106 Ident {
107 name: token.name(),
108 raw: token.raw,
109 span,
110 }
111 }
112}
113
114impl<'s> From<(token::Placeholder<'s>, Span)> for Placeholder<'s> {
115 fn from((token, span): (token::Placeholder<'s>, Span)) -> Self {
116 Placeholder {
117 index: token.index,
118 suffix: token.suffix,
119 span,
120 }
121 }
122}
123
124impl<'s> From<(token::Ident<'s>, Span)> for InterpolableIdentStaticPart<'s> {
125 fn from((token, span): (token::Ident<'s>, Span)) -> Self {
126 InterpolableIdentStaticPart {
127 value: token.name(),
128 raw: token.raw,
129 span,
130 }
131 }
132}
133
134impl<'s> TryFrom<(token::Number<'s>, Span)> for Number<'s> {
135 type Error = Error;
136
137 fn try_from((token, span): (token::Number<'s>, Span)) -> PResult<Self> {
138 token
139 .raw
140 .parse()
141 .map_err(|_| Error {
142 kind: ErrorKind::InvalidNumber,
143 span: span.clone(),
144 })
145 .map(|value| Self {
146 value,
147 raw: token.raw,
148 span,
149 })
150 }
151}
152
153impl<'s> From<(token::StrTemplate<'s>, Span)> for InterpolableStrStaticPart<'s> {
154 fn from((token, span): (token::StrTemplate<'s>, Span)) -> Self {
155 let raw_without_quotes = if token.tail {
156 unsafe { token.raw.get_unchecked(0..token.raw.len() - 1) }
157 } else if token.head {
158 unsafe { token.raw.get_unchecked(1..token.raw.len()) }
159 } else {
160 token.raw
161 };
162 let value = if token.escaped {
163 util::handle_escape(raw_without_quotes)
164 } else {
165 Cow::from(raw_without_quotes)
166 };
167 Self {
168 value,
169 raw: token.raw,
170 span,
171 }
172 }
173}
174
175impl<'s> From<(token::UrlTemplate<'s>, Span)> for InterpolableUrlStaticPart<'s> {
176 fn from((token, span): (token::UrlTemplate<'s>, Span)) -> Self {
177 let value = if token.escaped {
178 util::handle_escape(token.raw)
179 } else {
180 Cow::from(token.raw)
181 };
182 Self {
183 value,
184 raw: token.raw,
185 span,
186 }
187 }
188}
189
190impl<'s> From<(token::Str<'s>, Span)> for Str<'s> {
191 fn from((str, span): (token::Str<'s>, Span)) -> Self {
192 let raw_without_quotes = unsafe { str.raw.get_unchecked(1..str.raw.len() - 1) };
193 let value = if str.escaped {
194 util::handle_escape(raw_without_quotes)
195 } else {
196 Cow::from(raw_without_quotes)
197 };
198 Self {
199 value,
200 raw: str.raw,
201 span,
202 }
203 }
204}