maomi_skin/
lib.rs

1#![recursion_limit = "128"]
2
3use proc_macro2::{Span, TokenStream};
4use rustc_hash::FxHashMap;
5
6// pub mod parser;
7pub mod css_token;
8use css_token::*;
9pub mod module;
10pub mod pseudo;
11pub mod style_sheet;
12pub mod write_css;
13
14#[derive(Debug, Clone)]
15pub struct ParseError {
16    err: syn::Error,
17}
18
19impl ParseError {
20    pub fn new(span: Span, message: impl ToString) -> Self {
21        Self {
22            err: syn::Error::new(span, message.to_string()),
23        }
24    }
25
26    pub fn into_syn_error(self) -> syn::Error {
27        self.err
28    }
29}
30
31impl std::fmt::Display for ParseError {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        f.write_str(&self.err.to_string())
34    }
35}
36
37impl From<syn::Error> for ParseError {
38    fn from(err: syn::Error) -> Self {
39        Self { err }
40    }
41}
42
43pub trait ParseWithVars: Sized {
44    fn parse_with_vars(
45        input: syn::parse::ParseStream,
46        scope: &mut ScopeVars,
47    ) -> Result<Self, syn::Error>;
48}
49
50#[derive(Debug)]
51pub struct ScopeVars {
52    cur_mod: Option<ModPath>,
53    vars: FxHashMap<String, ScopeVarValue>,
54    var_refs: Vec<VarRef>,
55}
56
57impl ScopeVars {
58    fn insert_var(&mut self, var_name: &VarName, value: ScopeVarValue) -> Result<(), syn::Error> {
59        let mut inserted = false;
60        let span = var_name.span();
61        self.vars.entry(var_name.to_string()).or_insert_with(|| {
62            inserted = true;
63            value
64        });
65        if inserted {
66            Ok(())
67        } else {
68            Err(syn::Error::new(span, "duplicated identifier"))
69        }
70    }
71}
72
73#[derive(Debug, Clone)]
74pub enum ScopeVarValue {
75    Token(CssToken),
76    DynStr(VarDynRef),
77    DynNum(VarDynRef),
78    StyleDefinition(Vec<(VarName, ArgType)>),
79}
80
81impl ScopeVarValue {
82    fn type_name(&self) -> &'static str {
83        match self {
84            Self::Token(_) => "value",
85            Self::DynStr(_) => "&str",
86            Self::DynNum(_) => "{number}",
87            Self::StyleDefinition(_) => "StyleDefinition",
88        }
89    }
90}
91
92#[derive(Debug, Clone, Copy)]
93pub enum ArgType {
94    Str(Span),
95    Num(Span),
96}
97
98impl ArgType {
99    pub fn type_tokens(self) -> TokenStream {
100        match self {
101            Self::Str(span) => quote::quote_spanned!(span=> &str ),
102            Self::Num(span) => quote::quote_spanned!(span=> f32 ),
103        }
104    }
105}
106
107#[derive(Debug, Clone)]
108pub struct VarDynRef {
109    pub span: Span,
110    pub index: usize,
111}
112
113impl PartialEq for VarDynRef {
114    fn eq(&self, other: &Self) -> bool {
115        self.index == other.index
116    }
117}
118
119#[derive(Debug, Clone)]
120pub struct VarDynValue {
121    pub span: Span,
122    pub kind: VarDynValueKind,
123}
124
125#[derive(Debug, Clone)]
126pub enum VarDynValueKind {
127    Placeholder,
128    Str(String),
129    Num(Number),
130}
131
132impl VarDynValue {
133    pub fn placeholder(span: Span) -> Self {
134        Self {
135            span,
136            kind: VarDynValueKind::Placeholder,
137        }
138    }
139
140    fn type_name(&self) -> &'static str {
141        match &self.kind {
142            VarDynValueKind::Placeholder => "{unknown}",
143            VarDynValueKind::Str(_) => "&str",
144            VarDynValueKind::Num(_) => "{number}",
145        }
146    }
147}
148
149#[derive(Debug, Clone, PartialEq)]
150pub enum MaybeDyn<T> {
151    Static(T),
152    Dyn(VarDynRef),
153}
154
155impl ParseWithVars for MaybeDyn<String> {
156    fn parse_with_vars(
157        input: syn::parse::ParseStream,
158        scope: &mut ScopeVars,
159    ) -> Result<Self, syn::Error> {
160        use syn::*;
161        let la = input.lookahead1();
162        let value = if la.peek(LitStr) {
163            let s: LitStr = input.parse()?;
164            MaybeDyn::Static(s.value())
165        } else if la.peek(Ident) {
166            let var_name: VarName = input.parse()?;
167            if let Some(v) = scope.vars.get(&var_name.to_string()) {
168                match v {
169                    ScopeVarValue::DynStr(x) => {
170                        scope.var_refs.push(var_name.into_ref());
171                        MaybeDyn::Dyn(x.clone())
172                    }
173                    x => {
174                        return Err(syn::Error::new(
175                            var_name.span(),
176                            format!("expected &str, found {}", x.type_name()),
177                        ));
178                    }
179                }
180            } else {
181                return Err(syn::Error::new(var_name.span(), "variable not declared"));
182            }
183        } else {
184            return Err(la.error());
185        };
186        Ok(value)
187    }
188}
189
190impl MaybeDyn<String> {
191    fn value<'a>(&'a self, values: &'a [VarDynValue]) -> Result<&'a str, syn::Error> {
192        match self {
193            Self::Static(x) => Ok(x),
194            Self::Dyn(x) => {
195                let v = values.get(x.index).unwrap();
196                match &v.kind {
197                    VarDynValueKind::Str(x) => Ok(x),
198                    _ => Err(syn::Error::new(
199                        x.span,
200                        format!("expected &str, found {}", v.type_name()),
201                    )),
202                }
203            }
204        }
205    }
206}
207
208impl ParseWithVars for MaybeDyn<Number> {
209    fn parse_with_vars(
210        input: syn::parse::ParseStream,
211        scope: &mut ScopeVars,
212    ) -> Result<Self, syn::Error> {
213        use syn::*;
214        let la = input.lookahead1();
215        let value = if la.peek(LitInt) {
216            let v: LitInt = input.parse()?;
217            let value = v.base10_parse()?;
218            MaybeDyn::Static(Number::I32(value))
219        } else if la.peek(LitFloat) {
220            let v: LitFloat = input.parse()?;
221            let value = v.base10_parse()?;
222            MaybeDyn::Static(Number::F32(value))
223        } else if la.peek(Ident) {
224            let var_name: VarName = input.parse()?;
225            if let Some(v) = scope.vars.get(&var_name.to_string()) {
226                match v {
227                    ScopeVarValue::DynNum(x) => {
228                        scope.var_refs.push(var_name.into_ref());
229                        MaybeDyn::Dyn(x.clone())
230                    }
231                    x => {
232                        return Err(syn::Error::new(
233                            var_name.span(),
234                            format!("expected i32 or f32, found {}", x.type_name()),
235                        ));
236                    }
237                }
238            } else {
239                return Err(syn::Error::new(var_name.span(), "variable not declared"));
240            }
241        } else {
242            return Err(la.error());
243        };
244        Ok(value)
245    }
246}
247
248impl MaybeDyn<Number> {
249    fn value(&self, values: &[VarDynValue]) -> Result<Number, syn::Error> {
250        match self {
251            Self::Static(x) => Ok(x.clone()),
252            Self::Dyn(x) => {
253                let v = values.get(x.index).unwrap();
254                match &v.kind {
255                    VarDynValueKind::Num(x) => Ok(x.clone()),
256                    _ => Err(syn::Error::new(
257                        x.span,
258                        format!("expected {{number}}, found {}", v.type_name()),
259                    )),
260                }
261            }
262        }
263    }
264}
265
266#[derive(Debug, Clone, PartialEq)]
267pub enum Number {
268    I32(i32),
269    F32(f32),
270}
271
272#[derive(Debug, Clone, Default)]
273pub struct ModPath {
274    segs: Vec<syn::Ident>,
275}
276
277impl ModPath {
278    fn visible_in(&self, src: &Self) -> bool {
279        for (index, seg) in self.segs.iter().enumerate() {
280            if Some(seg) != src.segs.get(index) {
281                return false;
282            }
283        }
284        true
285    }
286}