orma-derive 0.3.3

Derive macros for Orma
Documentation
use core::ops::Deref;
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::parse::ParseStream;

use syn::parse::Parse;
use syn::*;

#[derive(Debug)]
enum AttributeType {
    Table,
    Unknown,
}

struct NamedField(Field);

impl Deref for NamedField {
    type Target = Field;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<Field> for NamedField {
    fn from(field: Field) -> Self {
        Self(field)
    }
}

impl Into<Field> for NamedField {
    fn into(self) -> Field {
        self.0
    }
}

impl Parse for NamedField {
    fn parse(input: ParseStream) -> Result<Self> {
        Field::parse_named(input).map(NamedField::from)
    }
}

#[derive(Debug)]
struct DbDataAttributes {
    table: Option<String>,
}

impl From<&Ident> for AttributeType {
    fn from(ident: &Ident) -> Self {
        let str = ident.to_string();
        match str.as_str() {
            "table" => AttributeType::Table,
            _ => AttributeType::Unknown,
        }
    }
}

impl DbDataAttributes {
    fn default() -> Self {
        Self { table: None }
    }
}

fn lit_string(lit: &Lit) -> String {
    if let Lit::Str(lit_str) = lit {
        lit_str.value()
    } else {
        panic!("Invalid attr syntax {:?}", lit);
    }
}

fn parse_orma_attrs(attrs: &[NestedMeta]) -> DbDataAttributes {
    let mut ctx = DbDataAttributes::default();
    for attr in attrs {
        let meta = if let NestedMeta::Meta(meta) = attr {
            meta
        } else {
            panic!("{:?} is not a Meta", attr);
        };
        if let Meta::NameValue(name_value) = meta {
            let attr_type = AttributeType::from(name_value.path.get_ident().unwrap());
            if let AttributeType::Table = attr_type {
                ctx.table = Some(lit_string(&name_value.lit))
            }
        };
    }
    ctx
}

pub fn impl_orma(attrs: &[NestedMeta], input: &mut DeriveInput) -> TokenStream {
    let dbdata_attrs = parse_orma_attrs(attrs);
    let table_name = if let Some(table_name) = dbdata_attrs.table {
        table_name
    } else {
        panic!("No table name provided, {:?}", attrs);
    };
    let data = if let Data::Struct(ref mut it) = input.data {
        it
    } else {
        return Error::new(input.ident.span(), "Only structs are supported").to_compile_error();
    };

    let fields = if let Fields::Named(ref mut it) = data.fields {
        it
    } else {
        return Error::new(input.ident.span(), "Tuple structs are not supported")
            .to_compile_error();
    };

    let skip: Attribute = parse_quote! {#[serde(skip)]};
    fields.named.push(Field {
        attrs: vec![skip],
        vis: parse_quote! {pub},
        ident: Some(Ident::new("orma_id", Span::call_site())),
        colon_token: None,
        ty: parse_quote! {Option<::uuid::Uuid>},
    });

    let skip: Attribute = parse_quote! {#[serde(skip)]};
    fields.named.push(Field {
        attrs: vec![skip],
        vis: parse_quote! {pub},
        ident: Some(Ident::new("orma_version", Span::call_site())),
        colon_token: None,
        ty: parse_quote! {Option<i32>},
    });

    let ident = &input.ident;
    quote! {
        #input
        impl ::orma::DbData for #ident {
            fn table_name() -> &'static str {
                #table_name
            }
            fn id(&self) -> Option<::uuid::Uuid> {self.orma_id}
            fn version(&self) -> Option<i32> {self.orma_version}
            fn set_id(&mut self, uuid: ::uuid::Uuid) {
                self.orma_id = Some(uuid);
            }
            fn set_version(&mut self, version: i32) {
                self.orma_version = Some(version);
            }

        }
    }
}