use std::collections::{HashMap, HashSet};
use std::sync::{LazyLock, Mutex};
pub const CONVENTION_COLUMNS: [&str; 6] = [
"version",
"created_at",
"updated_at",
"deleted",
"deleted_at",
"deleted_by",
];
static CONFIG: LazyLock<Mutex<GenerateConfig>> =
LazyLock::new(|| Mutex::new(GenerateConfig::default()));
#[derive(Debug, Clone, Default)]
pub struct GenerateConfig {
pub(crate) type_overrides: HashMap<String, String>,
pub(crate) disabled_conventions: HashSet<String>,
pub(crate) lowercase_keywords: bool,
}
impl GenerateConfig {
pub fn new() -> Self {
Self::default()
}
pub fn type_overrides(mut self, overrides: &HashMap<String, String>) -> Self {
self.type_overrides
.extend(overrides.iter().map(|(k, v)| (k.clone(), v.clone())));
self
}
pub fn type_override(mut self, column: impl Into<String>, rust_type: impl Into<String>) -> Self {
self.type_overrides.insert(column.into(), rust_type.into());
self
}
pub fn disable_convention(mut self, rule: impl AsRef<str>) -> Self {
let rule = rule.as_ref().trim();
let column = rule.rsplit('.').next().unwrap();
if column != "*" && !CONVENTION_COLUMNS.contains(&column) {
panic!(
"disable_convention(\"{}\"):列名必须是 {:?} 之一或 *",
rule, CONVENTION_COLUMNS
);
}
self.disabled_conventions.insert(rule.to_string());
self
}
pub fn lowercase_keywords(mut self, enable: bool) -> Self {
self.lowercase_keywords = enable;
self
}
fn convention_enabled(&self, table: &str, column: &str) -> bool {
let rules = &self.disabled_conventions;
!(rules.contains("*")
|| rules.contains(column)
|| rules.contains(&format!("{}.*", table))
|| rules.contains(&format!("{}.{}", table, column)))
}
}
pub(crate) fn set(config: &GenerateConfig) {
*CONFIG.lock().unwrap() = config.clone();
}
pub(crate) fn type_override(column: &str) -> Option<String> {
CONFIG.lock().unwrap().type_overrides.get(column).cloned()
}
pub(crate) fn convention_enabled(table: &str, column: &str) -> bool {
CONFIG.lock().unwrap().convention_enabled(table, column)
}
pub(crate) fn kw(keyword: &str) -> String {
if CONFIG.lock().unwrap().lowercase_keywords {
keyword.to_lowercase()
} else {
keyword.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn disable_convention_rules() {
let config = GenerateConfig::new()
.disable_convention("user_token.version")
.disable_convention("created_at")
.disable_convention("log.*");
assert!(!config.convention_enabled("user_token", "version"));
assert!(config.convention_enabled("user", "version"));
assert!(!config.convention_enabled("user", "created_at"));
assert!(!config.convention_enabled("log", "deleted"));
assert!(config.convention_enabled("user", "deleted"));
assert!(!GenerateConfig::new().disable_convention("*").convention_enabled("a", "deleted"));
}
#[test]
#[should_panic]
fn disable_convention_rejects_unknown_column() {
GenerateConfig::new().disable_convention("user.name");
}
}