dbnexus 0.1.3

An enterprise-grade database abstraction layer for Rust with built-in permission control and connection pooling
// Copyright (c) 2026 Kirky.X
//
// Licensed under the MIT License
// See LICENSE file in the project root for full license information.

//! Build script for compile-time permissions validation
//!
//! This script reads the permissions.yaml file and generates:
//! 1. A Rust module for the main crate with the list of defined roles
//! 2. A file that can be included in procedural macros for validation

use std::env;
use std::fs;
use std::path::Path;

fn main() {
    // 获取 permissions.yaml 文件路径
    let out_dir = env::var("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("generated_roles.rs");
    let _macro_include_path = Path::new(&out_dir).join("roles_include.rs");

    // 尝试读取 permissions.yaml
    let permissions_path = env::var("DB_PERMISSIONS_PATH").unwrap_or_else(|_| "permissions.yaml".to_string());

    // 简单解析 YAML 提取角色名
    let roles: Vec<String> = if Path::new(&permissions_path).exists() {
        match fs::read_to_string(&permissions_path) {
            Ok(content) => {
                // 简单解析:查找 roles: 后面的所有键
                let mut found_roles = Vec::new();
                let mut in_roles_section = false;
                for line in content.lines() {
                    let trimmed = line.trim();
                    if trimmed.starts_with("roles:") {
                        in_roles_section = true;
                        continue;
                    }
                    if in_roles_section {
                        // 检查是否是角色定义行(以 "- name:" 开头或直接是角色名加冒号)
                        if trimmed.starts_with("- name:") {
                            if let Some(name_start) = trimmed.find(":") {
                                let name_part = &trimmed[name_start + 1..];
                                let name = name_part.trim().trim_matches('"').trim_matches('\'');
                                if !name.is_empty() {
                                    found_roles.push(name.to_string());
                                }
                            }
                        } else if trimmed.starts_with('-') && !trimmed.contains(":") {
                            // 可能是列表项
                            let role_name = trimmed.trim_start_matches('-').trim();
                            if !role_name.is_empty() && !role_name.starts_with("name:") {
                                found_roles.push(role_name.to_string());
                            }
                        } else if !trimmed.is_empty()
                            && !trimmed.starts_with("tables:")
                            && !trimmed.starts_with("#")
                            && !trimmed.starts_with("    ")
                            && trimmed.ends_with(':')
                            && !trimmed.contains("name")
                        {
                            // 直接的角色名定义
                            let role_name = trimmed.trim_end_matches(':').trim();
                            if !role_name.is_empty() {
                                found_roles.push(role_name.to_string());
                            }
                        }
                    }
                }

                // 去重并排序
                found_roles.sort();
                found_roles.dedup();

                if found_roles.is_empty() {
                    eprintln!("Warning: No roles found in permissions.yaml. Using empty role list.");
                }

                found_roles
            }
            Err(e) => {
                eprintln!(
                    "Warning: Could not read permissions.yaml: {}. Using empty role list.",
                    e
                );
                Vec::new()
            }
        }
    } else {
        // 如果文件不存在,生成空角色列表(允许所有角色)
        Vec::new()
    };

    // 生成角色数组代码
    let roles_array_code = if roles.is_empty() {
        "// No roles defined in permissions.yaml - all roles are allowed at compile time".to_string()
    } else {
        roles
            .iter()
            .map(|r| format!("    \"{}\"", r))
            .collect::<Vec<_>>()
            .join(",\n")
    };

    // 生成 Rust 代码(用于主 crate)
    let generated_code = format!(
        r#"// Auto-generated by build.rs
// This file contains the list of roles defined in permissions.yaml

#[allow(dead_code)]
pub const DEFINED_ROLES: &[&str] = &[
    {}
];

#[allow(dead_code)]
pub fn is_role_defined(role: &str) -> bool {{
    DEFINED_ROLES.contains(&role)
}}

#[allow(dead_code)]
pub fn get_defined_roles() -> &'static [&'static str] {{
    DEFINED_ROLES
}}
"#,
        roles_array_code
    );

    fs::write(&dest_path, generated_code).unwrap();

    // 告诉 cargo 重新编译当 permissions.yaml 变化时
    println!("cargo:rerun-if-changed={}", permissions_path);
}