1use darling::ast::Data;
2use darling::{FromDeriveInput, FromVariant};
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{Generics, Ident, LitStr};
6
7#[derive(Debug, FromVariant)]
8#[darling(attributes(action))]
9struct ActionField {
10 ident: Ident,
11
12 id: LitStr,
13 title: LitStr,
14 icon: LitStr,
15}
16
17#[derive(Debug, FromDeriveInput)]
18#[darling(attributes(action), supports(enum_unit))]
19struct Action {
20 ident: Ident,
21 data: Data<ActionField, ()>,
22 generics: Generics,
23}
24
25#[proc_macro_derive(Action, attributes(action))]
26pub fn derive_action(input: TokenStream) -> TokenStream {
27 let (ident, data, generics) = match Action::from_derive_input(&syn::parse_macro_input!(input)) {
28 Ok(Action {
29 ident,
30 data,
31 generics,
32 }) => (ident, data, generics),
33 Err(e) => return e.write_errors().into(),
34 };
35 let variants = data.take_enum().unwrap();
36
37 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
38
39 let variant_ids = variants.iter().map(|v| &v.ident);
40 let from_ids = variants.iter().map(|ActionField { id, ident, .. }| {
41 quote! { #id => ::std::option::Option::Some(Self::#ident), }
42 });
43 let to_ids = variants.iter().map(|ActionField { id, ident, .. }| {
44 quote! { Self::#ident => #id, }
45 });
46 let infos = variants.iter().map(
47 |ActionField {
48 ident, title, icon, ..
49 }| {
50 quote! {
51 Self::#ident => ::krunner::ActionInfo {
52 title: ::std::string::String::from(#title),
53 icon: ::std::string::String::from(#icon),
54 },
55 }
56 },
57 );
58
59 quote! {
60 #[automatically_derived]
61 impl #impl_generics ::krunner::Action for #ident #ty_generics #where_clause {
62 fn all() -> &'static [Self] {
63 &[#(Self::#variant_ids),*]
64 }
65 fn from_id(s: &str) -> ::std::option::Option<Self> {
66 match s {
67 #(#from_ids)*
68 _ => ::std::option::Option::None,
69 }
70 }
71 fn to_id(&self) -> ::std::string::String {
72 <::std::string::String as ::std::convert::From<&str>>::from(match self {
73 #(#to_ids)*
74 })
75 }
76 fn info(&self) -> ::krunner::ActionInfo {
77 match self {
78 #(#infos)*
79 }
80 }
81 }
82 }
83 .into()
84}