1use std::borrow::Cow;
2
3#[cfg(feature = "validation")]
4use lightningcss::stylesheet::{ParserOptions, StyleSheet};
5use quote::quote;
6use xxhash_rust::xxh64::xxh64;
7
8#[proc_macro]
9pub fn css(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let data = pinkie_parser::parse(input.into());
11 let css = data.css;
12
13 #[cfg(feature = "validation")]
14 if let Err(e) = StyleSheet::parse(&format!(".dummy{{{css}}}"), ParserOptions::default()) {
15 let err = format!("css error: {}", e.kind);
16 if let Some(loc) = e.loc {
17 let offset = loc.column as usize - 8; let span = data
34 .spans
35 .into_iter()
36 .find_map(|(pos, span)| (offset <= pos).then_some(span));
37 if let Some(span) = span {
38 return quote::quote_spanned!(span => compile_error!(#err)).into();
39 }
40 }
41 return quote!(compile_error!(#err)).into();
42 };
43
44 let prefix = std::env::var("PINKIE_CSS_CLASS_PREFIX")
45 .map(Cow::Owned)
46 .unwrap_or("pinkie-".into());
47
48 let hash = &format!("{:08x}", xxh64(css.as_bytes(), 0))[0..8];
49 let class = format!("{prefix}{hash}");
50
51 let (line, location) = if cfg!(feature = "location") {
52 (
53 quote!(
54 const LINE: usize = line!() as usize;
55 ),
56 quote! {
57 location: ::pinkie::Location {
58 file: file!(),
59 line: LINE,
60 },
61 },
62 )
63 } else {
64 (quote!(), quote!())
65 };
66
67 quote! {{ #line;
68 const STYLE: ::pinkie::Style = ::pinkie::Style {
69 class: #class,
70 css: #css,
71 #location
72 };
73 ::pinkie::__submit!(STYLE);
74 STYLE
75 }}
76 .into()
77}