derive_sql_common/derive/
fields.rs

1use super::*;
2
3use attribute_derive::{Attribute};
4
5#[derive(Attribute)]
6#[attribute(ident = derive_sqlite)]
7struct FieldAttrs {
8  #[attribute(default = false)]
9  is_primary_key: bool,
10  #[attribute(default = false)]
11  is_unique: bool,
12  on_insert: Option<syn::PatPath>,
13  on_update: Option<syn::PatPath>,
14}
15
16pub struct Fields<'a> {
17  ident: &'a syn::Ident,
18  sql_type: SqlType,
19  attrs: FieldAttrs,
20  raw_type: String,
21}
22
23impl<'a> std::convert::TryFrom<&'a syn::Field> for Fields<'a> {
24  type Error = Box<dyn std::error::Error>;
25  fn try_from(f: &'a syn::Field) -> Result<Self, Self::Error> {
26    let sql_type: SqlType = f.into();
27    let raw_type: String = extract_type(&f.ty).ok_or(format!("Unable to retrieve raw type for {:#?}", &f.ty))?;
28    if matches!(sql_type, SqlType::Unsupported) { return Err("Type is not supported".into()); }
29    Ok( 
30      Fields {
31        ident: f.ident.as_ref().ok_or("Field does not have an ident")?,
32        sql_type,
33        attrs: FieldAttrs::from_attributes(&f.attrs)?,
34        raw_type,
35      } 
36    )
37  }
38}
39
40impl<'a> Fields<'a> {
41  pub fn name(&'a self) -> String { format!("{}", self.ident) }
42  pub fn ident(&'a self) -> &'a syn::Ident { self.ident }
43  pub fn raw_type(&'a self) -> &'a str { self.raw_type.as_str() }
44  pub fn sql_type(&'a self) -> &'a SqlType { &self.sql_type }
45  pub fn is_primary_key(&'a self) -> bool { self.attrs.is_primary_key }
46  pub fn is_unique(&self) -> bool { self.attrs.is_unique }
47  pub fn on_insert(&'a self) -> &'a Option<syn::PatPath> { &self.attrs.on_insert }
48  pub fn on_update(&'a self) -> &'a Option<syn::PatPath> { &self.attrs.on_update }
49  pub fn as_pub_static_member(&'a self) -> proc_macro2::TokenStream {
50    let key: syn::Ident = syn::Ident::new(self.name().to_ascii_uppercase().as_str(), self.ident.span()); 
51    let value = self.name();
52    quote::quote! {
53      pub const #key : &'static str = #value;
54    }
55  }
56}
57
58/// Retrieve the final type as a string, such "std::option::Option<f32>" gives "f32"
59fn extract_type(ty: &syn::Type) -> Option<String> {
60  match ty {
61    syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. } , .. }) => {
62      match segments.last() {
63        Some(syn::PathSegment { ident,
64            arguments: syn::PathArguments::AngleBracketed( syn::AngleBracketedGenericArguments { args, ..  } )
65        }) if ident == "Option" => {
66          match args.last() {
67            Some(syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { path, .. }))) => path.get_ident().map(|i| i.to_string()),
68            _ => None,
69          }
70        },
71        Some(syn::PathSegment { ident, ..}) => Some(ident.to_string()),
72        _ => None,
73      }
74    },
75    // syn::Type::Path(syn::TypePath { path, .. }) => path.get_ident().map(|i| i.to_string()),
76    _ => None,
77  }
78}
79