use std::collections::HashMap;
use std::env;
use std::fs;
use std::path::Path;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let permission_sets_dir = Path::new(&manifest_dir).join("permission-sets");
println!("cargo:rerun-if-changed={}", permission_sets_dir.display());
let registry_code = generate_permission_set_registry(&permission_sets_dir);
let dest_path = Path::new(&out_dir).join("permission_sets_registry.rs");
fs::write(&dest_path, registry_code).unwrap();
println!(
"Generated permission sets registry at: {}",
dest_path.display()
);
}
fn generate_permission_set_registry(permission_sets_dir: &Path) -> String {
let mut code = String::new();
code.push_str(
r#"
// This file is auto-generated by build.rs
// Do not edit manually!
use std::collections::HashMap;
use std::sync::OnceLock;
"#,
);
let mut permission_sets = HashMap::new();
scan_permission_sets(permission_sets_dir, &mut permission_sets);
for (id, content) in &permission_sets {
let const_name = id_to_const_name(id);
code.push_str(&format!(
"const {}: &str = r###\"{}\"###;\n\n",
const_name, content
));
}
code.push_str(r#"
static PERMISSION_SETS_REGISTRY: OnceLock<HashMap<&'static str, alien_core::permissions::PermissionSet>> = OnceLock::new();
fn get_permission_sets_registry() -> &'static HashMap<&'static str, alien_core::permissions::PermissionSet> {
PERMISSION_SETS_REGISTRY.get_or_init(|| {
let mut registry = HashMap::new();
"#);
for id in permission_sets.keys() {
let const_name = id_to_const_name(id);
code.push_str(&format!(
r#" registry.insert("{}", parse_permission_set({}));
"#,
id, const_name
));
}
code.push_str(r#"
registry
})
}
fn parse_permission_set(jsonc_content: &str) -> alien_core::permissions::PermissionSet {
// Parse JSONC using json5
json5::from_str(jsonc_content)
.expect("Failed to parse permission set JSONC")
}
/// Normalize permission set ID to handle both hyphenated and underscore formats
fn normalize_permission_id(id: &str) -> String {
id.replace('_', "-")
}
/// Get a permission set by ID from the built-in registry
/// Supports both hyphenated (azure-container-apps-environment) and underscore (azure_container_apps_environment) formats
pub fn get_permission_set(id: &str) -> Option<&'static alien_core::permissions::PermissionSet> {
let registry = get_permission_sets_registry();
// Try exact match first
if let Some(perm_set) = registry.get(id) {
return Some(perm_set);
}
// Try normalized version (convert underscores to hyphens)
let normalized_id = normalize_permission_id(id);
registry.get(normalized_id.as_str())
}
/// Get all available permission set IDs
pub fn list_permission_set_ids() -> Vec<&'static str> {
get_permission_sets_registry().keys().copied().collect()
}
/// Check if a permission set exists in the registry
/// Supports both hyphenated (azure-container-apps-environment) and underscore (azure_container_apps_environment) formats
pub fn has_permission_set(id: &str) -> bool {
let registry = get_permission_sets_registry();
// Try exact match first
if registry.contains_key(id) {
return true;
}
// Try normalized version (convert underscores to hyphens)
let normalized_id = normalize_permission_id(id);
registry.contains_key(normalized_id.as_str())
}
"#);
code
}
fn scan_permission_sets(dir: &Path, permission_sets: &mut HashMap<String, String>) {
if !dir.exists() || !dir.is_dir() {
return;
}
for entry in fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
scan_permission_sets(&path, permission_sets);
} else if path.extension().and_then(|s| s.to_str()) == Some("jsonc") {
let content = fs::read_to_string(&path).unwrap();
match validate_permission_set(&content, &path) {
Ok(id) => {
permission_sets.insert(id, content);
}
Err(err) => {
panic!(
"Failed to parse permission set file at compile time: {}\nError: {}",
path.display(),
err
);
}
}
}
}
}
fn validate_permission_set(content: &str, path: &Path) -> Result<String, String> {
let permission_set: alien_core::permissions::PermissionSet =
json5::from_str(content).map_err(|e| format!("JSON parsing error: {}", e))?;
if permission_set.id.is_empty() {
return Err("Permission set ID cannot be empty".to_string());
}
if permission_set.description.is_empty() {
return Err("Permission set description cannot be empty".to_string());
}
println!(
"✓ Validated permission set: {} ({})",
permission_set.id,
path.display()
);
Ok(permission_set.id)
}
fn id_to_const_name(id: &str) -> String {
id.replace('/', "_").replace('-', "_").to_uppercase() + "_PERMISSION_SET"
}