1mod codegen;
2pub(crate) mod grammar;
3mod macro_inputs;
4use crate::codegen::string_rendering::class_name_to_struct_identifier;
5use crate::codegen::{render_classes, render_generate_dwind_class};
6use crate::grammar::{parse_class_string, parse_selector};
7use crate::macro_inputs::DwindInputSignal;
8use dwind_base::media_queries::Breakpoint;
9use macro_inputs::{DwGenerateInput, DwGenerateMapInput, DwindInput};
10use proc_macro::TokenStream;
11use quote::quote;
12use std::rc::Rc;
13
14#[proc_macro]
29pub fn dwclass(input: TokenStream) -> TokenStream {
30 let DwindInput {
31 self_ident,
32 classes,
33 } = syn::parse::<DwindInput>(input).unwrap();
34
35 let classes = parse_class_string(classes.value().as_str()).unwrap();
36 let classes = render_classes(classes);
37
38 let (generator_classes, normal_classes): (Vec<_>, Vec<_>) = classes
39 .into_iter()
40 .map(|class| match class {
41 (tokens, breakpoint, false) => (None, Some((tokens, breakpoint))),
42 (tokens, breakpoint, true) => (Some((tokens, breakpoint)), None),
43 })
44 .unzip();
45
46 let v = Rc::new(Some(""));
47
48 v.as_ref().as_ref().map(|_v| {});
49
50 let classes = normal_classes.into_iter().filter_map(|v| v).map(|(class, breakpoint)| {
51 if let Some(breakpoint) = breakpoint {
52 let bp = breakpoint.breakpoint;
53
54 if breakpoint.is_media_query {
55 let Breakpoint::MediaQuery(mq) = bp else { unreachable!() };
56
57 quote! {
58 .class_signal(#class, dominator::media_query(#mq))
59 }
60 } else {
61 let bp_value = match bp {
62 Breakpoint::VerySmall => quote! { dwind::prelude::media_queries::Breakpoint::VerySmall },
63 Breakpoint::Small => quote! { dwind::prelude::media_queries::Breakpoint::Small },
64 Breakpoint::Medium => quote! { dwind::prelude::media_queries::Breakpoint::Medium },
65 Breakpoint::Large => quote! { dwind::prelude::media_queries::Breakpoint::Large },
66 Breakpoint::VeryLarge => quote! { dwind::prelude::media_queries::Breakpoint::VeryLarge },
67 _ => { panic!("media query breakpoint not supported here")}
68 };
69
70 if let Some(_modifier) = breakpoint.modifier {
71 quote! {
72 .class_signal(#class, dwind::prelude::media_queries::breakpoint_less_than_signal(#bp_value))
73 }
74 } else {
75 quote! {
76 .class_signal(#class, dwind::prelude::media_queries::breakpoint_active_signal(#bp_value))
77 }
78 }
79 }
80 } else {
81 quote! {
82 .class(#class)
83 }
84 }
85 });
86
87 let gen_classes = generator_classes.into_iter().filter_map(|v| v).map(|(class_tokens, breakpoint)| {
88 if let Some(breakpoint) = breakpoint {
89 let bp = breakpoint.breakpoint;
90
91 let apply = if breakpoint.is_media_query {
92 let Breakpoint::MediaQuery(mq) = bp else { unreachable!() };
93
94 quote! {
95 .class_signal(&*foobar, dominator::media_query(#mq))
96 }
97 } else {
98 let bp_value = match bp {
99 Breakpoint::VerySmall => quote! { dwind::prelude::media_queries::Breakpoint::VerySmall },
100 Breakpoint::Small => quote! { dwind::prelude::media_queries::Breakpoint::Small },
101 Breakpoint::Medium => quote! { dwind::prelude::media_queries::Breakpoint::Medium },
102 Breakpoint::Large => quote! { dwind::prelude::media_queries::Breakpoint::Large },
103 Breakpoint::VeryLarge => quote! { dwind::prelude::media_queries::Breakpoint::VeryLarge },
104 _ => { panic!("media query breakpoint not supported here")}
105 };
106
107 if let Some(_modifier) = breakpoint.modifier {
108 quote! {
109 .class_signal(&*foobar, dwind::prelude::media_queries::breakpoint_less_than_signal(#bp_value))
110 }
111 } else {
112 quote! {
113 .class_signal(&*foobar, dwind::prelude::media_queries::breakpoint_active_signal(#bp_value))
114 }
115 }
116 };
117
118 quote! {
119 let #self_ident = {
120 #[doc(hidden)]
121 pub static foobar: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(|| {
122 #class_tokens
123 });
124
125 #self_ident . #apply
126 };
127 }
128 } else {
129 quote! {
130 let #self_ident = {
131 #[doc(hidden)]
132 pub static foobar: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(|| {
133 #class_tokens
134 });
135
136 #self_ident . class(&*foobar)
137 };
138 }
139 }
140 });
141
142 quote! {
143 {
144 #(#gen_classes)*
145 #self_ident #(#classes)*
146 }
147 }
148 .into()
149}
150
151#[proc_macro]
166pub fn dwclass_signal(input: TokenStream) -> TokenStream {
167 let DwindInputSignal {
168 input: DwindInput {
169 self_ident,
170 classes,
171 },
172 signal,
173 } = syn::parse::<DwindInputSignal>(input).unwrap();
174
175 let classes = parse_class_string(classes.value().as_str()).unwrap();
176 let classes = render_classes(classes);
177
178 let (generator_classes, normal_classes): (Vec<_>, Vec<_>) = classes
179 .into_iter()
180 .map(|class| match class {
181 (tokens, breakpoint, false) => (None, Some((tokens, breakpoint))),
182 (tokens, breakpoint, true) => (Some((tokens, breakpoint)), None),
183 })
184 .unzip();
185
186 let classes = normal_classes
187 .into_iter()
188 .filter_map(|v| v)
189 .map(|(class, _breakpoint)| {
190 quote! {
191 .class_signal(#class, #signal)
192 }
193 });
194
195 let gen_classes = generator_classes.into_iter().filter_map(|v| v).map(|(class_tokens, _breakpoint)| {
196 quote! {
197 let #self_ident = {
198 #[doc(hidden)]
199 pub static foobar: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(|| {
200 #class_tokens
201 });
202
203 #self_ident . class_signal(&*foobar, #signal)
204 };
205 }
206 });
207
208 quote! {
209 {
210 #(#gen_classes)*
211 #self_ident #(#classes)*
212 }
213 }
214 .into()
215}
216
217#[proc_macro]
249pub fn dwgenerate(input: TokenStream) -> TokenStream {
250 let DwGenerateInput {
251 class_definition,
252 class_name,
253 } = syn::parse(input).unwrap();
254
255 let class_definition = parse_selector(class_definition.value().as_str())
256 .map(|v| v.1)
257 .unwrap();
258
259 let class_name = class_name_to_struct_identifier(&class_name.value());
260
261 let rendered = render_generate_dwind_class(class_name, class_definition);
262
263 rendered.into()
264}
265
266#[proc_macro]
293pub fn dwgenerate_map(input: TokenStream) -> TokenStream {
294 let input: DwGenerateMapInput = syn::parse(input).unwrap();
295
296 let DwGenerateMapInput {
297 base_output_ident,
298 dwind_class_lit,
299 args,
300 } = input;
301
302 let output = args.tuples.into_iter().map(|input_tuple| {
303 let ident_name = class_name_to_struct_identifier(&format!(
305 "{}-{}",
306 base_output_ident.value(),
307 input_tuple.first.value()
308 ));
309
310 let class_literal = format!(
312 "{}[{}]",
313 dwind_class_lit.value(),
314 input_tuple.second.value()
315 );
316
317 let class = parse_selector(class_literal.as_str()).unwrap().1;
319
320 render_generate_dwind_class(ident_name, class)
321 });
322
323 quote! {
324 #(#output)*
325 }
326 .into()
327}