worktable_macros 0.9.0

Proc-macro companion crate for worktable: the worktable! macro and its derives. Formerly published as worktable_codegen.
Documentation
use proc_macro2::TokenStream;
use syn::Error;

use crate::common::Parser;
use crate::generators::read_only;

pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
    let mut parser = Parser::new(input);
    let mut columns = None;
    let mut indexes = None;

    let name = parser.parse_name()?;
    let version = parser.parse_version()?.unwrap_or(1);

    while let Some(ident) = parser.peek_next() {
        match ident.to_string().as_str() {
            "columns" => columns = Some(parser.parse_columns()?),
            "indexes" => indexes = Some(parser.parse_indexes()?),
            "queries" => {
                return Err(Error::new(ident.span(), "worktable_version! does not support queries"));
            }
            "config" => {
                return Err(Error::new(ident.span(), "worktable_version! does not support config"));
            }
            "version" => {
                return Err(Error::new(
                    ident.span(),
                    "version must be specified before columns/indexes",
                ));
            }
            _ => return Err(Error::new(ident.span(), "Unexpected identifier")),
        }
    }

    let mut columns = columns.expect("columns must be defined");
    if let Some(i) = indexes {
        columns.indexes = i
    }

    read_only::expand(name, columns, version)
}

#[cfg(test)]
mod tests {
    use quote::quote;

    use crate::worktable_version::expand;

    #[test]
    fn test_basic_version_macro() {
        let input = quote! {
            name: UserV1,
            columns: {
                id: u64 primary_key,
                name: String,
            },
        };

        let res = expand(input).unwrap();
        let output = res.to_string();

        assert!(
            output.contains("index (read_only)"),
            "should generate read_only index attribute"
        );
        assert!(
            output.contains("table (read_only)"),
            "should generate read_only table attribute"
        );
    }

    #[test]
    fn test_version_with_indexes() {
        let input = quote! {
            name: UserV2,
            columns: {
                id: u64 primary_key,
                email: String,
            },
            indexes: {
                email_idx: email unique,
            },
        };

        let res = expand(input).unwrap();
        let output = res.to_string();

        assert!(output.contains("email_idx"), "should include index field");
    }

    #[test]
    fn test_rejects_queries() {
        let input = quote! {
            name: UserV1,
            columns: {
                id: u64 primary_key,
            },
            queries: {
                select: { ById() by id },
            },
        };

        let res = expand(input);
        assert!(res.is_err(), "should reject queries section");
    }

    #[test]
    fn test_rejects_config() {
        let input = quote! {
            name: UserV1,
            columns: {
                id: u64 primary_key,
            },
            config: {
                page_size: 8192,
            },
        };

        let res = expand(input);
        assert!(res.is_err(), "should reject config section");
    }

    #[test]
    fn test_explicit_version() {
        let input = quote! {
            name: UserV1,
            version: 2,
            columns: {
                id: u64 primary_key,
            },
        };

        let res = expand(input).unwrap();
        let output = res.to_string();

        assert!(
            output.contains("index (read_only)"),
            "should generate read_only index attribute"
        );
    }

    #[test]
    fn test_rejects_version_after_columns() {
        let input = quote! {
            name: UserV1,
            columns: {
                id: u64 primary_key,
            },
            version: 2,
        };

        let res = expand(input);
        assert!(res.is_err(), "should reject version after columns");
    }
}