1#![allow(clippy::wrong_self_convention)]
2
3use std::path::PathBuf;
4
5use darling::{FromMeta, ToTokens};
6use syn::{parse::Parser, Attribute};
7
8pub mod getter;
9pub mod parser;
10pub mod setter;
11
12#[derive(Default, Clone)]
13pub struct FnGenerics {
14 pub bound: Option<syn::Generics>,
15}
16
17impl darling::FromMeta for FnGenerics {
18 fn from_value(value: &syn::Lit) -> darling::Result<Self> {
19 if let syn::Lit::Str(ref s) = value {
20 if s.value().is_empty() {
21 Ok(Self { bound: None })
22 } else {
23 let tt = format!("<{}>", s.value());
24 let bound = syn::parse_str::<syn::Generics>(&tt)?;
25 Ok(Self { bound: Some(bound) })
26 }
27 } else {
28 Err(darling::Error::custom("expected str literal").with_span(value))
29 }
30 }
31}
32
33#[derive(Default, Clone)]
34pub enum DebugOutput {
35 #[default]
36 StdOut,
37 StdErr,
38 File(PathBuf),
39}
40
41#[derive(Default, Clone)]
42pub struct Debug {
43 pub debug: DebugOutput,
44}
45
46impl From<DebugOutput> for Debug {
47 fn from(debug: DebugOutput) -> Self {
48 Self { debug }
49 }
50}
51
52impl darling::FromMeta for Debug {
53 fn from_meta(item: &syn::Meta) -> darling::Result<Self> {
54 match item {
55 syn::Meta::Path(_) => Ok(Self::default()),
56 syn::Meta::List(l) => Err(
57 darling::Error::custom(
58 "expected path or name value pair, but found attribute list for debug",
59 )
60 .with_span(l),
61 ),
62 syn::Meta::NameValue(val) => {
63 if let syn::Lit::Str(val) = &val.lit {
64 match val.value().as_str() {
65 "stdout" | "out" => Ok(Self::from(DebugOutput::StdOut)),
66 "stderr" | "err" | "error" => Ok(Self::from(DebugOutput::StdErr)),
67 file => Ok(Self::from(DebugOutput::File(PathBuf::from(file)))),
68 }
69 } else {
70 Err(darling::Error::custom("expected str literal").with_span(val))
71 }
72 }
73 }
74 }
75}
76
77impl Debug {
78 pub fn write(&self, ts: &proc_macro2::TokenStream) -> syn::Result<()> {
79 use std::fs::OpenOptions;
80 use std::io::Write;
81 syn::parse_file(&ts.to_string()).and_then(|file| {
82 let src = prettyplease::unparse(&file);
83 match &self.debug {
84 DebugOutput::StdOut => {
85 println!("{src}");
86 Ok(())
87 }
88 DebugOutput::StdErr => {
89 eprintln!("{src}");
90 Ok(())
91 }
92 DebugOutput::File(path) => {
93 let mut opts = OpenOptions::new();
94 opts
95 .write(true)
96 .create(true)
97 .truncate(true)
98 .read(true)
99 .open(path)
100 .and_then(|mut file| {
101 file.write_all(src.as_bytes())?;
102 file.flush()
103 })
104 .map_err(|e| syn::Error::new_spanned(ts, e.to_string()))
105 }
106 }
107 })
108 }
109}
110
111#[derive(Default, Clone)]
112pub struct Attributes {
113 pub attrs: Vec<syn::Attribute>,
114}
115
116impl core::fmt::Debug for Attributes {
117 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
118 let attrs = &self.attrs;
119 for attr in attrs.iter() {
120 quote::quote!(#attr).to_string().fmt(f)?;
121 }
122 Ok(())
123 }
124}
125
126impl FromMeta for Attributes {
127 fn from_list(items: &[syn::NestedMeta]) -> darling::Result<Self> {
128 let mut attrs = Vec::with_capacity(items.len());
129 for n in items {
130 attrs.extend(Attribute::parse_outer.parse2(quote::quote! { #[#n] })?);
131 }
132 Ok(Attributes { attrs })
133 }
134}
135
136impl ToTokens for Attributes {
137 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
138 for attr in self.attrs.iter() {
139 tokens.extend(quote::quote!(#attr));
140 }
141 }
142}