derivit_core/
lib.rs

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}