use std::collections::{HashMap, HashSet};
use std::sync::{OnceLock, RwLock};
fn registry() -> &'static RwLock<HashMap<String, HashSet<&'static str>>> {
static REGISTRY: OnceLock<RwLock<HashMap<String, HashSet<&'static str>>>> = OnceLock::new();
REGISTRY.get_or_init(|| RwLock::new(HashMap::new()))
}
fn key(table: &str) -> String {
table.rsplit('.').next().unwrap_or(table).trim_matches('"').to_ascii_lowercase()
}
pub fn register(table: &str, fields: &'static [&'static str]) {
if fields.is_empty() {
return;
}
let mut map = registry().write().unwrap_or_else(|e| e.into_inner());
map.entry(key(table)).or_default().extend(fields.iter().copied());
}
pub fn secret_fields_of(table: &str) -> Vec<&'static str> {
let map = registry().read().unwrap_or_else(|e| e.into_inner());
map.get(&key(table)).map(|s| s.iter().copied().collect()).unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_table_registered_in_any_spelling_answers_its_secrets() {
register("registry_probe.accounts", &["passwordHash"]);
register("ACCOUNTS_OTHER", &[]);
assert_eq!(secret_fields_of("accounts"), vec!["passwordHash"]);
assert_eq!(secret_fields_of("\"registry_probe\".\"accounts\""), vec!["passwordHash"]);
assert!(secret_fields_of("accounts_other").is_empty());
assert!(secret_fields_of("never_registered").is_empty());
}
}