cargo_generate_license_impl_license/
lib.rs1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse::Parse, Ident, Token, Visibility};
4
5struct UnitStruct {
6 vis: Visibility,
7 name: Ident,
8}
9
10impl Parse for UnitStruct {
11 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
12 let vis: Visibility = input.parse()?;
13 let _: Token!(struct) = input.parse()?;
14 let name = input.parse()?;
15 let _: Token!(;) = input.parse()?;
16 Ok(Self { vis, name })
17 }
18}
19struct Attrs {
20 path: syn::LitStr,
21 author: bool,
22 year: bool,
23 project: bool,
24}
25
26impl Parse for Attrs {
27 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
28 let path = input.parse()?;
29 let mut me = Self {
30 path,
31 author: false,
32 project: false,
33 year: false,
34 };
35 for _ in 0..=2 {
36 let Ok(_): syn::Result<Token!(,)> = input.parse() else { return Ok(me);};
37 let Ok(ident): syn::Result<Ident> = input.parse() else { return Ok(me);};
39 match ident.to_string().as_ref() {
40 "AUTHOR" => me.author = true,
41 "YEAR" => me.year = true,
42 "PROJECT" => me.project = true,
43 _ => {
44 return Err(syn::Error::new_spanned(
45 ident,
46 "expected fields AUTHOR, YEAR, and/or PROJECT",
47 ))
48 }
49 };
50 }
51 Ok(me)
52 }
53}
54
55fn derive_license_impl(attr: Attrs, struct_def: UnitStruct) -> TokenStream {
56 let type_name = &struct_def.name;
57 let path = attr.path;
58 let vis = &struct_def.vis;
59 let mut format_arguments = vec![];
60 if attr.author {
61 format_arguments.push(quote!(AUTHOR = name));
62 }
63 if attr.year {
64 format_arguments.push(quote!(YEAR = year));
65 }
66 if attr.project {
67 format_arguments.push(quote!(PROJECT = project));
68 }
69 quote!(
70 #vis struct #type_name;
71
72 impl License for #type_name {
73 fn notice(&self, year: u32, name: &str, project: &str) -> String {
74 format!(include_str!(#path) #(, #format_arguments)*)
75 }
76 }
77 )
78 .into()
79}
80
81#[proc_macro_attribute]
82pub fn impl_license(attrs: TokenStream, item: TokenStream) -> TokenStream {
83 derive_license_impl(
84 syn::parse(attrs).expect("parse attrs"),
85 syn::parse(item).expect("parse struct def"),
86 )
87}
88
89#[cfg(test)]
90mod tests {
91 use quote::ToTokens;
92
93 use super::*;
94
95 #[test]
96 fn test_attrs_parse() {
97 let attrs: Attrs = syn::parse_str(r#""path value", AUTHOR"#).expect("parse");
98 assert_eq!(
99 attrs.path.into_token_stream().to_string(),
100 r#""path value""#
101 );
102 assert!(attrs.author);
103 assert!(!attrs.project);
104 assert!(!attrs.year);
105 }
106}