worktable_macros 0.9.0

Proc-macro companion crate for worktable: the worktable! macro and its derives. Formerly published as worktable_codegen.
Documentation
use indexmap::IndexMap;
use std::collections::HashMap;

use crate::common::model::GeneratorType;
use crate::common::model::index::Index;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::spanned::Spanned;

fn is_sized(ident: &Ident) -> bool {
    !matches!(ident.to_string().as_str(), "String")
}

#[derive(Debug, Clone)]
pub struct Columns {
    pub is_sized: bool,
    pub columns_map: HashMap<Ident, TokenStream>,
    pub field_positions: HashMap<Ident, usize>,
    pub indexes: IndexMap<Ident, Index>,
    pub primary_keys: Vec<Ident>,
    pub generator_type: GeneratorType,
}

#[derive(Debug)]
pub struct Row {
    pub name: Ident,
    pub type_: Ident,
    pub is_primary_key: bool,
    pub gen_type: GeneratorType,
    pub optional: bool,
}

impl Columns {
    pub fn try_from_rows(rows: Vec<Row>, input: &TokenStream) -> syn::Result<Self> {
        let mut columns_map = HashMap::new();
        let mut field_positions = HashMap::new();
        let mut sized = true;
        let mut pk = vec![];
        let mut gen_type = None;

        for (pos, row) in rows.into_iter().enumerate() {
            let type_ = &row.type_;
            if sized {
                sized = is_sized(type_)
            }
            let type_ = if row.optional {
                quote! { core::option::Option<#type_> }
            } else {
                quote! { #type_ }
            };
            columns_map.insert(row.name.clone(), type_);
            field_positions.insert(row.name.clone(), pos);

            if row.is_primary_key {
                if let Some(t) = gen_type {
                    if t != row.gen_type {
                        return Err(syn::Error::new(input.span(), "Generator type must be same"));
                    }
                } else {
                    gen_type = Some(row.gen_type)
                }
                pk.push(row.name);
            }
        }

        if pk.is_empty() {
            return Err(syn::Error::new(input.span(), "Primary key must be set"));
        }

        Ok(Self {
            is_sized: sized,
            columns_map,
            indexes: Default::default(),
            primary_keys: pk,
            generator_type: gen_type.expect("set"),
            field_positions,
        })
    }
}