use std::collections::HashMap;
use std::sync::OnceLock;
use parking_lot::Mutex;
#[derive(Debug, Clone)]
pub struct TableEntry {
pub create_sql: String,
pub fields: Vec<String>,
}
static REGISTRY: OnceLock<Mutex<HashMap<String, TableEntry>>> = OnceLock::new();
static STRUCT_TO_TABLE: OnceLock<Mutex<HashMap<String, String>>> = OnceLock::new();
fn registry() -> &'static Mutex<HashMap<String, TableEntry>> {
REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
}
fn struct_to_table() -> &'static Mutex<HashMap<String, String>> {
STRUCT_TO_TABLE.get_or_init(|| Mutex::new(HashMap::new()))
}
pub fn register(
struct_name: impl Into<String>,
table_name: impl Into<String>,
create_sql: impl Into<String>,
fields: Vec<String>,
) {
let struct_name = struct_name.into();
let table_name = table_name.into();
let entry = TableEntry {
create_sql: create_sql.into(),
fields,
};
registry().lock().insert(table_name.clone(), entry.clone());
struct_to_table()
.lock()
.insert(struct_name, table_name.clone());
}
pub fn get_by_table(table_name: &str) -> Option<TableEntry> {
registry().lock().get(table_name).cloned()
}
pub fn get_by_struct(struct_name: &str) -> Option<(String, TableEntry)> {
let table_name = struct_to_table().lock().get(struct_name).cloned()?;
let entry = registry().lock().get(&table_name).cloned()?;
Some((table_name, entry))
}
pub fn contains(table_name: &str) -> bool {
registry().lock().contains_key(table_name)
}
pub fn registered_names() -> Vec<String> {
registry().lock().keys().cloned().collect()
}
#[derive(Debug)]
pub struct Registry;
impl Registry {
pub fn seed_if_known(
table_name: &str,
db: &mut crate::db::CompileTimeDb,
) -> hyperdb_api::Result<bool> {
let Some(entry) = get_by_table(table_name) else {
return Ok(false);
};
db.conn.execute_command(&entry.create_sql)?;
Ok(true)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn register_and_retrieve_by_table() {
register(
"RegTestUser",
"reg_test_users",
"CREATE TABLE reg_test_users (id BIGINT, name TEXT)",
vec!["id".into(), "name".into()],
);
let entry = get_by_table("reg_test_users").expect("lookup by table name");
assert_eq!(entry.fields, &["id", "name"]);
assert!(entry.create_sql.contains("reg_test_users"));
}
#[test]
fn register_and_retrieve_by_struct() {
register(
"RegTestProfile",
"reg_test_profiles",
"CREATE TABLE reg_test_profiles (id BIGINT, bio TEXT)",
vec!["id".into(), "bio".into()],
);
let (table_name, entry) = get_by_struct("RegTestProfile").expect("lookup by struct name");
assert_eq!(table_name, "reg_test_profiles");
assert_eq!(entry.fields, &["id", "bio"]);
}
#[test]
fn contains_returns_false_for_unknown() {
assert!(!contains("reg_test_nonexistent_xyzzy"));
}
#[test]
fn registration_ordering_independent() {
register(
"RegTestOrder",
"reg_test_orders",
"CREATE TABLE reg_test_orders (id BIGINT, user_id BIGINT)",
vec!["id".into(), "user_id".into()],
);
register(
"RegTestCustomer",
"reg_test_customers",
"CREATE TABLE reg_test_customers (id BIGINT)",
vec!["id".into()],
);
assert!(contains("reg_test_orders"));
assert!(contains("reg_test_customers"));
assert!(get_by_struct("RegTestOrder").is_some());
assert!(get_by_struct("RegTestCustomer").is_some());
}
}