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