modo-db-macros 0.2.0

Procedural macros for modo-db
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::{ItemFn, LitInt, LitStr, Result, Token, parse2};

struct MigrationArgs {
    version: u64,
    description: String,
    group: Option<String>,
}

impl syn::parse::Parse for MigrationArgs {
    fn parse(input: syn::parse::ParseStream) -> Result<Self> {
        let mut version = None;
        let mut description = None;
        let mut group = None;

        while !input.is_empty() {
            let ident: syn::Ident = input.parse()?;
            input.parse::<Token![=]>()?;

            match ident.to_string().as_str() {
                "version" => {
                    let val: LitInt = input.parse()?;
                    version = Some(val.base10_parse::<u64>()?);
                }
                "description" => {
                    let val: LitStr = input.parse()?;
                    description = Some(val.value());
                }
                "group" => {
                    let val: LitStr = input.parse()?;
                    group = Some(val.value());
                }
                other => {
                    return Err(syn::Error::new_spanned(
                        ident,
                        format!("unknown migration attribute: {other}"),
                    ));
                }
            }

            if input.peek(Token![,]) {
                input.parse::<Token![,]>()?;
            }
        }

        let version = version.ok_or_else(|| input.error("missing `version` argument"))?;
        let description =
            description.ok_or_else(|| input.error("missing `description` argument"))?;

        Ok(MigrationArgs {
            version,
            description,
            group,
        })
    }
}

pub fn expand(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
    let args: MigrationArgs = parse2(attr)?;
    let func: ItemFn = parse2(item)?;

    let func_name = &func.sig.ident;
    let version = args.version;
    let description = &args.description;
    let group_str = args.group.as_deref().unwrap_or("default");

    Ok(quote! {
        #func

        modo_db::inventory::submit! {
            modo_db::MigrationRegistration {
                version: #version,
                description: #description,
                group: #group_str,
                handler: |db| Box::pin(#func_name(db)),
            }
        }
    })
}