Skip to main content

oxc_css_parser/parser/
convert.rs

1use 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}