1extern crate core;
2
3use convert_case::{Case, Casing};
4use darling::{
5 util::{Flag, Override},
6 FromField, FromVariant,
7};
8use proc_macro2::{Ident, TokenStream};
9use quote::{format_ident, quote, quote_spanned};
10use syn::{
11 parse_macro_input, punctuated::Punctuated, spanned::Spanned, Data, DataEnum, DeriveInput,
12 Field, Fields, Path, Token, Variant,
13};
14
15fn to_discriminator(variant: &Variant, opts: &Opts) -> String {
17 if opts.index.is_present() {
18 return "".to_string();
19 }
20
21 opts.rename
22 .clone()
23 .unwrap_or_else(|| variant.ident.to_string().to_lowercase())
24}
25
26#[derive(FromVariant, Default)]
27#[darling(default, attributes(target))]
28struct Opts {
29 index: Flag,
30 rename: Option<String>,
31}
32
33#[derive(FromField, Default)]
34#[darling(default, attributes(target))]
35struct FieldOpts {
36 nested: Flag,
37 value: Flag,
38 default: Option<Override<String>>,
39}
40
41impl FieldOpts {
42 fn validate(self) -> Self {
43 if self.nested.is_present() && self.value.is_present() {
44 panic!("Cannot configure a field as both 'nested' and 'value'");
45 }
46 self
47 }
48}
49
50fn nested_field<P>(
52 expect_target: bool,
53 fields: &Punctuated<Field, P>,
54) -> (Vec<&Field>, Option<&Field>) {
55 let mut values = vec![];
56
57 for (i, field) in fields.iter().enumerate() {
58 let opts = FieldOpts::from_field(field)
59 .expect("Unable to parse field options")
60 .validate();
61
62 let last = i == fields.len() - 1;
63
64 if last {
65 if (expect_target || opts.nested.is_present()) && !opts.value.is_present() {
66 return (values, Some(field));
68 }
69 } else {
70 #[allow(clippy::collapsible_else_if)]
71 if opts.nested.is_present() {
72 panic!(
73 "Only the last field can be a nested target: {}",
74 field
75 .ident
76 .as_ref()
77 .map(ToString::to_string)
78 .unwrap_or_else(|| format!("{}", i))
79 );
80 }
81 }
82
83 values.push(field);
84 }
85
86 (values, None)
87}
88
89fn render_path(data: &DataEnum) -> impl Iterator<Item = TokenStream> + '_ {
91 data.variants.iter().map(|v| {
92 let name = &v.ident;
93
94 match &v.fields {
95 Fields::Unit => {
96 quote_spanned! { v.span() =>
97 Self::#name => {
98 self.render_self_into(__internal_path);
99 }
100 }
101 }
102 Fields::Unnamed(fields) => {
103 let (values, nested) = nested_field(true, &fields.unnamed);
104
105 let values = values
107 .iter()
108 .map(|_| quote!(_))
109 .chain(nested.map(|_| quote!(nested)));
110
111 let nested = match nested.is_some() {
112 true => {
113 quote! { nested.render_path_into(__internal_path); }
114 }
115 false => {
116 quote! {}
117 }
118 };
119
120 quote_spanned! { v.span() =>
121 Self::#name(#(#values),*) => {
122 self.render_self_into(__internal_path);
123 #nested
124 }
125 }
126 }
127 Fields::Named(fields) => {
128 let (capture, nested) = match nested_field(false, &fields.named) {
130 (_, Some(nested)) => {
131 let nested = nested.ident.as_ref().expect("Field must have a name");
132 (
133 quote! { #nested: nested, .. },
134 quote! { nested.render_path_into(__internal_path); },
135 )
136 }
137 (_, None) => (quote! {..}, quote! {}),
138 };
139
140 quote_spanned! { v.span() =>
141 Self::#name{ #capture } => {
142 self.render_self_into(__internal_path);
143 #nested
144 }
145 }
146 }
147 }
148 })
149}
150
151fn capture_values<P, F>(
153 expect_target: bool,
154 fields: &Punctuated<Field, P>,
155 skip_nested: TokenStream,
156 push: F,
157) -> (
158 impl Iterator<Item = TokenStream> + '_,
159 impl Iterator<Item = TokenStream> + '_,
160)
161where
162 F: Fn(&Ident) -> TokenStream + 'static,
163{
164 let (values, nested) = nested_field(expect_target, fields);
165
166 let captures = values
167 .clone()
168 .into_iter()
169 .enumerate()
170 .map(|(i, f)| {
171 let anon = format_ident!("arg_{}", i);
172 let name = f.ident.as_ref().unwrap_or(&anon);
173 quote! { #name }
174 })
175 .chain(nested.map(|_| skip_nested));
176
177 let values = values.into_iter().enumerate().map(move |(i, f)| {
178 let anon = format_ident!("arg_{}", i);
179 let name = f.ident.as_ref().unwrap_or(&anon);
180 push(name)
181 });
182
183 (captures, values)
184}
185
186fn render_self(data: &DataEnum) -> impl Iterator<Item = TokenStream> + '_ {
188 data.variants.iter().map(|v| {
189 let name = &v.ident;
190
191 let opts = Opts::from_variant(v).expect("Unable to parse options");
192 let disc = to_discriminator(v, &opts);
193
194 match &v.fields {
195 Fields::Unit => {
197 quote_spanned! { v.span() =>
198 Self::#name => { __internal_path.push(#disc.into()); }
199 }
200 }
201 Fields::Unnamed(fields) => {
203 let (captures, values) =
204 capture_values(true, &fields.unnamed, quote! { _ }, |name| {
205 quote! { __internal_path.push(#name.to_string()); }
206 });
207
208 quote_spanned! { v.span() =>
209 Self::#name(#(#captures),*) => {
210 __internal_path.push(#disc.into());
211 #(#values)*
212 }
213 }
214 }
215 Fields::Named(fields) => {
217 let (captures, values) =
218 capture_values(false, &fields.named, quote! { .. }, |name| {
219 quote! { __internal_path.push(#name.to_string()); }
220 });
221
222 quote_spanned! { v.span() =>
223 Self::#name { #(#captures),* } => {
224 __internal_path.push(#disc.into());
225 #(#values)*
226 }
227 }
228 }
229 }
230 })
231}
232
233fn parse_path(data: &DataEnum) -> impl Iterator<Item = TokenStream> + '_ {
235 data.variants.iter().map(|v| {
236 let name = &v.ident;
237
238 let opts = Opts::from_variant(v).expect("Unable to parse options");
239 let value = to_discriminator(v, &opts);
240
241 match &v.fields {
242 Fields::Unit => {
243 quote_spanned! { v.span() =>
244 [#value] => Some(Self::#name)
245 }
246 }
247 Fields::Unnamed(fields) => parse_rules(
248 v,
249 true,
250 &fields.unnamed,
251 |_, cap| from_str(cap),
252 |_, target| quote!(#target),
253 |name, values, target| quote!(Some(Self::#name(#(#values, )* #target))),
254 ),
255 Fields::Named(fields) => parse_rules(
256 v,
257 false,
258 &fields.named,
259 |name, cap| {
260 let name = name.expect("Must have a name");
261 let from = from_str(cap);
262 quote!(#name: #from)
263 },
264 |name, target| {
265 let name = name.expect("Must have a name");
266 quote!(#name: #target)
267 },
268 |name, values, target| quote!(Some(Self::#name { #(#values, )* #target})),
269 ),
270 }
271 })
272}
273
274fn parse_rules<P, F1, F2, F3>(
275 v: &Variant,
276 expect_target: bool,
277 fields: &Punctuated<Field, P>,
278 converter: F1,
279 target_converter: F2,
280 ctor: F3,
281) -> TokenStream
282where
283 F1: Fn(Option<&Ident>, &Ident) -> TokenStream,
284 F2: Fn(Option<&Ident>, TokenStream) -> TokenStream,
285 F3: Fn(&Ident, &Vec<TokenStream>, TokenStream) -> TokenStream,
286{
287 let name = &v.ident;
288
289 let (values, nested) = nested_field(expect_target, fields);
290
291 let opts = Opts::from_variant(v).expect("Unable to parse options");
292 let disc = to_discriminator(v, &opts);
293
294 let (captures, values): (Vec<_>, Vec<_>) = values
295 .iter()
296 .enumerate()
297 .map(|(i, f)| {
298 let name = f.ident.as_ref();
299 let cap = f
300 .ident
301 .as_ref()
302 .map(|f| format_ident!("value_{f}"))
303 .unwrap_or_else(|| format_ident!("arg_{i}"));
304 (quote!(#cap), converter(name, &cap))
305 })
306 .unzip();
307
308 match nested {
309 Some(nested) => {
310 let t = &nested.ty;
311
312 let field_opts = FieldOpts::from_field(nested).expect("Unable to parse field options");
313
314 let default = match &field_opts.default {
315 Some(Override::Inherit) => {
316 let init = ctor(
317 name,
318 &values,
319 target_converter(
320 nested.ident.as_ref(),
321 quote!(<#t as core::default::Default>::default()),
322 ),
323 );
324 quote! {
325 [#disc, #(#captures, )*] => #init,
326 }
327 }
328 Some(Override::Explicit(default)) => {
329 let default = syn::parse_str::<Path>(default).expect("Path to function");
330 let init = ctor(
331 name,
332 &values,
333 target_converter(nested.ident.as_ref(), quote!(#default ())),
334 );
335 quote! {
336 [#disc, #(#captures, )*] => #init,
337 }
338 }
339 None => {
340 quote! {}
341 }
342 };
343
344 let init = ctor(
345 name,
346 &values,
347 target_converter(nested.ident.as_ref(), quote!(target)),
348 );
349 quote_spanned! { v.span() =>
350 #default
351 [#disc, #(#captures, )* rest@..] => match #t::parse_path(rest) {
352 Some(target) => #init,
353 None => None,
354 }
355 }
356 }
357 None => {
358 let init = ctor(name, &values, quote!());
359 quote_spanned! { v.span() =>
360 [#disc, #(#captures),*] => #init
361 }
362 }
363 }
364}
365
366fn from_str(cap: &Ident) -> TokenStream {
367 quote!({
368 match std::str::FromStr::from_str(#cap) {
369 Ok(v) => v,
370 Err(err) => return None,
371 }
372 })
373}
374
375fn mappers(data: &DataEnum) -> impl Iterator<Item = TokenStream> + '_ {
377 data.variants.iter().map(|v| {
378 let name = &v.ident;
379
380 let fn_base_name = name.to_string().to_case(Case::Snake);
381
382 let map_name = format_ident!("map_{}", fn_base_name);
383 let mapper_name = format_ident!("mapper_{}", fn_base_name);
384 let with_name = format_ident!("with_{}", fn_base_name);
385
386 let none = Punctuated::<Field, Token![,]>::new();
387 let fields = match &v.fields {
388 Fields::Unnamed(fields) => fields.unnamed.iter(),
389 Fields::Named(fields) => fields.named.iter(),
390 Fields::Unit => none.iter(),
391 };
392
393 let (captures, types): (Vec<_>, Vec<_>) = fields
394 .enumerate()
395 .map(|(i, f)| {
396 let cap = f.ident.clone().unwrap_or_else(|| format_ident!("arg_{i}"));
397 let ty = &f.ty;
398 (quote!(#cap), quote!(#ty))
399 })
400 .unzip();
401
402 let (types, init) = match types.len() {
403 1 => {
404 let t = types.first().unwrap();
405 let c = captures.first().unwrap();
406 (quote!(#t), quote!(#c))
407 }
408 _ => (quote!((#(#types),*)), quote!((#(#captures),*))),
409 };
410
411 match &v.fields {
412 Fields::Unit => quote_spanned! { v.span() => },
413 Fields::Unnamed(fields) => {
414 let (_, nested ) = nested_field(true, &fields.unnamed);
415
416 let mapper = match nested {
417 Some(_) => {
418 quote!{
419 #[allow(unused)]
420 pub fn #mapper_name(_:()) -> yew_nested_router::prelude::Mapper<Self, #types> {
421 (Self::#map_name, Self::#name).into()
422 }
423 }
424 },
425 None => quote!(),
426 };
427
428 quote_spanned! { v.span() =>
429 #[allow(unused)]
430 pub fn #map_name(self) -> Option<#types> {
431 match self {
432 Self::#name(#(#captures),*) => Some(#init),
433 _ => None,
434 }
435 }
436
437 #mapper
438
439 #[allow(unused)]
440 pub fn #with_name<F, R>(f: F) -> impl Fn(Self) -> R
441 where
442 F: Fn(#types) -> R,
443 R: std::default::Default
444 {
445 move |s| s.#map_name().map(&f).unwrap_or_default()
446 }
447 }
448 },
449 Fields::Named(_) => quote_spanned! { v.span() =>
450 #[allow(unused)]
451 pub fn #map_name(self) -> Option<#types> {
452 match self {
453 Self::#name{#(#captures),*} => Some(#init),
454 _ => None,
455 }
456 }
457
458 #[allow(unused)]
459 pub fn #with_name<F, R>(f: F) -> impl Fn(Self) -> R
460 where
461 F: Fn(#types) -> R,
462 R: std::default::Default
463 {
464 move |s| s.#map_name().map(&f).unwrap_or_default()
465 }
466 },
467 }
468 })
469}
470
471fn predicates(data: &DataEnum) -> impl Iterator<Item = TokenStream> + '_ {
474 data.variants.iter().map(|v| {
475 let name = &v.ident;
476
477 let fn_name = name.to_string().to_case(Case::Snake);
478 let fn_name = format_ident!("is_{}", fn_name);
479
480 match &v.fields {
481 Fields::Unit => quote_spanned! { v.span() =>
482 #[allow(unused)]
483 #[allow(clippy::wrong_self_convention)]
484 pub fn #fn_name(self) -> bool {
485 matches!(self, Self::#name)
486 }
487 },
488 Fields::Unnamed(_) => {
489 quote_spanned! { v.span() =>
490 #[allow(unused)]
491 #[allow(clippy::wrong_self_convention)]
492 pub fn #fn_name(self) -> bool {
493 matches!(self, Self::#name( .. ))
494 }
495 }
496 }
497 Fields::Named(_) => {
498 quote_spanned! { v.span() =>
499 #[allow(unused)]
500 #[allow(clippy::wrong_self_convention)]
501 pub fn #fn_name(self) -> bool {
502 matches!(self, Self::#name{..})
503 }
504 }
505 }
506 }
507 })
508}
509
510#[proc_macro_derive(Target, attributes(target))]
512pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
513 let DeriveInput { ident, data, .. } = parse_macro_input!(input);
514
515 let data = match data {
516 Data::Enum(e) => e,
517 _ => panic!("Derive must be used on enum only"),
518 };
519
520 let render_path = render_path(&data);
521 let render_self = render_self(&data);
522 let parse_path = parse_path(&data);
523 let mappers = mappers(&data);
524 let predicates = predicates(&data);
525
526 let output = quote! {
527 impl yew_nested_router::target::Target for #ident {
528
529 fn render_self_into(&self, __internal_path: &mut Vec<String>) {
530 match self {
531 #(#render_self ,)*
532 }
533 }
534
535 fn render_path_into(&self, __internal_path: &mut Vec<String>) {
536 match self {
537 #(#render_path ,)*
538 }
539 }
540
541 fn parse_path(__internal_path: &[&str]) -> Option<Self> {
542 match __internal_path {
543 #(#parse_path ,)*
544 _ => None,
545 }
546 }
547
548 }
549
550 impl #ident {
551 #(#mappers)*
552 #(#predicates)*
553
554 #[inline]
555 pub fn any(self) -> bool { true }
556 }
557 };
558
559 output.into()
560}