1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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);
}