rust-db-blueprint 0.1.0

A Rust code generator — reads YAML draft files and generates Axum + SQLx models, migrations, handlers, routes, requests, tests, and seeds
Documentation
use indexmap::IndexMap;
use serde_yaml::Value;

use crate::models::*;
use super::statement_lexer::StatementLexer;

pub struct ControllerLexer;

impl ControllerLexer {
    pub fn analyze(controllers: &IndexMap<String, Value>) -> IndexMap<String, Controller> {
        let mut result = IndexMap::new();

        for (name, config) in controllers {
            let mut controller = Controller::new(name);

            if let Value::Mapping(mapping) = config {
                for (key, value) in mapping {
                    let key_str = key.as_str().unwrap_or("");

                    match key_str {
                        "resource" => {
                            controller.resource = value.as_str().map(|s| s.to_string());
                        }
                        "invokable" => {
                            controller.invokable = value.as_bool().unwrap_or(false);
                        }
                        "meta" | "config" => {
                            if let Value::Mapping(meta) = value {
                                Self::parse_meta(&mut controller, meta);
                            }
                        }
                        _ => {
                            // Parse controller method statements
                            if let Value::Sequence(seq) = value {
                                let statements: Vec<String> = seq
                                    .iter()
                                    .filter_map(|v| v.as_str().map(|s| s.to_string()))
                                    .collect();
                                let parsed = StatementLexer::parse(&statements);
                                controller.methods.insert(key_str.to_string(), parsed);
                            } else if let Value::String(s) = value {
                                let statements = vec![s.to_string()];
                                let parsed = StatementLexer::parse(&statements);
                                controller.methods.insert(key_str.to_string(), parsed);
                            }
                        }
                    }
                }
            }

            // Auto-populate resource methods if none explicitly defined
            if controller.methods.is_empty() && controller.is_resource() {
                Self::populate_resource_methods(&mut controller);
            }

            result.insert(name.to_string(), controller);
        }

        result
    }

    fn populate_resource_methods(controller: &mut Controller) {
        let is_api = controller.is_api_resource();

        let resource_actions: Vec<(&str, Vec<&str>)> = if is_api {
            vec![
                ("index", vec!["query:all"]),
                ("store", vec!["validate", "save"]),
                ("show", vec!["find"]),
                ("update", vec!["validate", "update"]),
                ("destroy", vec!["delete"]),
            ]
        } else {
            vec![
                ("index", vec!["query:all"]),
                ("create", vec![]),
                ("store", vec!["validate", "save"]),
                ("show", vec!["find"]),
                ("edit", vec!["find"]),
                ("update", vec!["validate", "update"]),
                ("destroy", vec!["delete"]),
            ]
        };

        for (name, stmts) in resource_actions {
            let parsed = StatementLexer::parse(
                &stmts.iter().map(|s| s.to_string()).collect::<Vec<_>>()
            );
            controller.methods.insert(name.to_string(), parsed);
        }
    }

    fn parse_meta(controller: &mut Controller, meta: &serde_yaml::Mapping) {
        for (key, value) in meta {
            let key_str = key.as_str().unwrap_or("");
            match key_str {
                "policies" | "policy" => {
                    controller.policies = value.as_bool().unwrap_or(false);
                }
                "parent" => {
                    controller.parent = value.as_str().map(|s| s.to_string());
                }
                "extends" => {
                    controller.extends = value.as_str().map(|s| s.to_string());
                }
                "traits" => {
                    controller.traits = value.as_str()
                        .map(|s| s.split(',').map(|t| t.trim().to_string()).collect())
                        .unwrap_or_default();
                }
                "implements" => {
                    controller.implements = value.as_str()
                        .map(|s| s.split(',').map(|t| t.trim().to_string()).collect())
                        .unwrap_or_default();
                }
                "namespace" => {
                    controller.namespace = value.as_str().map(|s| s.to_string());
                }
                _ => {}
            }
        }
    }
}