Skip to main content

rust_store_core/naming/
mod.rs

1//! 命名归一与定向翻译(设计 §6)。**唯一实现**;`core-node` / `core-py` 仅透出。
2
3mod canonical;
4mod conflict;
5
6pub use canonical::{canonical, to_camel, to_pascal, to_snake};
7pub use conflict::{detect_conflicts, ERR_NAME_CONFLICT, RESERVED_KEYS};
8
9/// 目标命名风格(设计 §6.1)
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Style {
12    /// snake_case
13    Snake,
14    /// camelCase
15    Camel,
16    /// PascalCase(须导出)
17    Pascal,
18}
19
20/// 翻译目标:介质(物理库)/ 语言(代码,计算列跟随),见设计 §6.1
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum Target {
23    // 物理库
24    Mysql,
25    Postgres,
26    Sqlite,
27    Mongodb,
28    // 代码(计算列跟随)
29    Node,
30    Java,
31    Csharp,
32    Rust,
33    Go,
34    Python,
35}
36
37impl Target {
38    /// 字符串 → 目标;未知值 **Err**(禁静默回落,对齐 `Profile::from_str_or_err`)
39    pub fn from_str_or_err(s: &str) -> Result<Target, String> {
40        match s.to_lowercase().as_str() {
41            "mysql" => Ok(Target::Mysql),
42            "postgres" | "pg" | "postgresql" => Ok(Target::Postgres),
43            "sqlite" => Ok(Target::Sqlite),
44            "mongo" | "mongodb" => Ok(Target::Mongodb),
45            "node" | "nodejs" => Ok(Target::Node),
46            "java" => Ok(Target::Java),
47            "csharp" | "cs" | "c#" => Ok(Target::Csharp),
48            "rust" => Ok(Target::Rust),
49            "go" | "golang" => Ok(Target::Go),
50            "python" | "py" => Ok(Target::Python),
51            other => Err(format!("未知命名目标: {other}")),
52        }
53    }
54
55    /// 数据源 kind(`DataSource::as_str` 取值:mongo/mysql/postgres/sqlite)→ 目标
56    pub fn for_datasource_kind(kind: &str) -> Result<Target, String> {
57        Target::from_str_or_err(kind)
58    }
59
60    /// 目标 → 风格(设计 §6.1 风格表)
61    pub fn style(&self) -> Style {
62        match self {
63            Target::Mysql | Target::Postgres | Target::Sqlite | Target::Python => Style::Snake,
64            Target::Mongodb | Target::Node | Target::Java | Target::Csharp | Target::Rust => {
65                Style::Camel
66            }
67            Target::Go => Style::Pascal,
68        }
69    }
70}
71
72/// 逻辑名 → 目标风格物理名(可逆:先 `canonical` 再按 `style` 重组)
73pub fn translate(logical: &str, target: Target) -> String {
74    let tokens = canonical(logical);
75    match target.style() {
76        Style::Snake => to_snake(&tokens),
77        Style::Camel => to_camel(&tokens),
78        Style::Pascal => to_pascal(&tokens),
79    }
80}
81
82/// 字符串版翻译(绑定入口):目标串非法 ⇒ Err(禁静默回落)
83pub fn translate_by_str(logical: &str, target: &str) -> Result<String, String> {
84    Ok(translate(logical, Target::from_str_or_err(target)?))
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    /// 设计 §6.1 目标风格表逐项
92    #[test]
93    fn target_style_table() {
94        assert_eq!(translate("orderTotal", Target::Mysql), "order_total");
95        assert_eq!(translate("orderTotal", Target::Postgres), "order_total");
96        assert_eq!(translate("orderTotal", Target::Sqlite), "order_total");
97        assert_eq!(translate("orderTotal", Target::Mongodb), "orderTotal");
98        assert_eq!(translate("orderTotal", Target::Node), "orderTotal");
99        assert_eq!(translate("orderTotal", Target::Java), "orderTotal");
100        assert_eq!(translate("orderTotal", Target::Csharp), "orderTotal");
101        assert_eq!(translate("orderTotal", Target::Rust), "orderTotal");
102        assert_eq!(translate("orderTotal", Target::Go), "OrderTotal"); // V6
103        assert_eq!(translate("orderTotal", Target::Python), "order_total");
104    }
105
106    /// 设计 §6.2 管线:三写归一同一,各目标一致
107    #[test]
108    fn design_section_6_2_pipeline() {
109        for src in ["orderTotal", "order_total", "Order.Total"] {
110            assert_eq!(translate(src, Target::Mysql), "order_total");
111            assert_eq!(translate(src, Target::Mongodb), "orderTotal");
112            assert_eq!(translate(src, Target::Go), "OrderTotal");
113            assert_eq!(translate(src, Target::Python), "order_total");
114        }
115    }
116
117    #[test]
118    fn unknown_target_errors() {
119        assert!(Target::from_str_or_err("oracle").is_err());
120        assert!(translate_by_str("orderTotal", "oracle").is_err());
121    }
122}