Skip to main content

alef/core/config/resolved/
naming.rs

1//! Language-specific naming methods for `ResolvedCrateConfig`.
2
3use super::ResolvedCrateConfig;
4
5impl ResolvedCrateConfig {
6    /// Get the Python module name.
7    pub fn python_module_name(&self) -> String {
8        self.python
9            .as_ref()
10            .and_then(|p| p.module_name.as_ref())
11            .cloned()
12            .unwrap_or_else(|| format!("_{}", self.name.replace('-', "_")))
13    }
14
15    /// Get the Node package name.
16    pub fn node_package_name(&self) -> String {
17        self.node
18            .as_ref()
19            .and_then(|n| n.package_name.as_ref())
20            .cloned()
21            .unwrap_or_else(|| self.name.clone())
22    }
23
24    /// Get the WASM npm package name.
25    pub fn wasm_package_name(&self) -> String {
26        self.wasm
27            .as_ref()
28            .and_then(|w| w.package_name.as_ref())
29            .cloned()
30            .unwrap_or_else(|| {
31                let node_pkg = self.node_package_name();
32                let base = node_pkg.strip_suffix("-node").unwrap_or(node_pkg.as_str());
33                format!("{base}-wasm")
34            })
35    }
36
37    /// Get the Ruby gem name.
38    pub fn ruby_gem_name(&self) -> String {
39        self.ruby
40            .as_ref()
41            .and_then(|r| r.gem_name.as_ref())
42            .cloned()
43            .unwrap_or_else(|| self.name.replace('-', "_"))
44    }
45
46    /// Get the PHP extension name.
47    pub fn php_extension_name(&self) -> String {
48        self.php
49            .as_ref()
50            .and_then(|p| p.extension_name.as_ref())
51            .cloned()
52            .unwrap_or_else(|| self.name.replace('-', "_"))
53    }
54
55    /// Get the PHP binding Cargo crate name (used for deriving the shared library filename).
56    pub fn php_cargo_crate_name(&self) -> Option<&str> {
57        self.php.as_ref().and_then(|p| p.cargo_crate_name.as_deref())
58    }
59
60    /// Get the Elixir app name.
61    pub fn elixir_app_name(&self) -> String {
62        self.elixir
63            .as_ref()
64            .and_then(|e| e.app_name.as_ref())
65            .cloned()
66            .unwrap_or_else(|| self.name.replace('-', "_"))
67    }
68
69    /// Get the Zig module name.
70    pub fn zig_module_name(&self) -> String {
71        self.zig
72            .as_ref()
73            .and_then(|z| z.module_name.as_ref())
74            .cloned()
75            .unwrap_or_else(|| self.name.replace('-', "_"))
76    }
77
78    /// Get the Dart pubspec package name.
79    ///
80    /// Returns `[dart] pubspec_name` if set, otherwise derives a snake_case
81    /// name from the crate name by replacing hyphens with underscores.
82    pub fn dart_pubspec_name(&self) -> String {
83        self.dart
84            .as_ref()
85            .and_then(|d| d.pubspec_name.as_ref())
86            .cloned()
87            .unwrap_or_else(|| self.name.replace('-', "_"))
88    }
89
90    /// Get the Dart FRB bridge class name.
91    ///
92    /// Converts `[dart] lib_name` (falling back to `dart_pubspec_name()`) to
93    /// PascalCase and appends `"Bridge"`, matching the FRB v2 convention.
94    /// E.g. `lib_name = "sample-widget"` → `"SampleWidgetBridge"`.
95    pub fn dart_bridge_class_name(&self) -> String {
96        use heck::ToUpperCamelCase;
97        let lib_name = self
98            .dart
99            .as_ref()
100            .and_then(|d| d.lib_name.as_ref())
101            .cloned()
102            .unwrap_or_else(|| self.dart_pubspec_name());
103        format!("{}Bridge", lib_name.to_upper_camel_case())
104    }
105
106    /// Get the Swift module name.
107    ///
108    /// Returns `[swift] module_name` if configured, otherwise derives a PascalCase
109    /// name from the crate name (e.g. `"my-lib"` → `"MyLib"`).
110    pub fn swift_module(&self) -> String {
111        self.swift
112            .as_ref()
113            .and_then(|s| s.module_name.as_ref())
114            .cloned()
115            .unwrap_or_else(|| {
116                use heck::ToUpperCamelCase;
117                self.name.to_upper_camel_case()
118            })
119    }
120
121    /// Get the R package name.
122    pub fn r_package_name(&self) -> String {
123        self.r
124            .as_ref()
125            .and_then(|r| r.package_name.as_ref())
126            .cloned()
127            .unwrap_or_else(|| self.name.clone())
128    }
129
130    /// Get the WASM type name prefix (e.g. "Wasm" produces `WasmConversionOptions`).
131    /// Defaults to `"Wasm"`.
132    pub fn wasm_type_prefix(&self) -> String {
133        self.wasm
134            .as_ref()
135            .and_then(|w| w.type_prefix.as_ref())
136            .cloned()
137            .unwrap_or_else(|| "Wasm".to_string())
138    }
139
140    /// Get the Node/NAPI type name prefix (e.g. "Js" produces `JsConversionOptions`).
141    /// Defaults to `"Js"`.
142    pub fn node_type_prefix(&self) -> String {
143        self.node
144            .as_ref()
145            .and_then(|n| n.type_prefix.as_ref())
146            .cloned()
147            .unwrap_or_else(|| "Js".to_string())
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use crate::core::config::new_config::NewAlefConfig;
154
155    fn resolved_one(toml: &str) -> super::super::ResolvedCrateConfig {
156        let cfg: NewAlefConfig = toml::from_str(toml).unwrap();
157        cfg.resolve().unwrap().remove(0)
158    }
159
160    fn minimal() -> super::super::ResolvedCrateConfig {
161        resolved_one(
162            r#"
163[workspace]
164languages = ["python", "node"]
165
166[[crates]]
167name = "test-lib"
168sources = ["src/lib.rs"]
169"#,
170        )
171    }
172
173    #[test]
174    fn python_module_name_defaults_to_underscore_prefix() {
175        let r = minimal();
176        assert_eq!(r.python_module_name(), "_test_lib");
177    }
178
179    #[test]
180    fn python_module_name_explicit_override() {
181        let r = resolved_one(
182            r#"
183[workspace]
184languages = ["python"]
185
186[[crates]]
187name = "test-lib"
188sources = ["src/lib.rs"]
189
190[crates.python]
191module_name = "mymod"
192"#,
193        );
194        assert_eq!(r.python_module_name(), "mymod");
195    }
196
197    #[test]
198    fn node_package_name_defaults_to_crate_name() {
199        let r = minimal();
200        assert_eq!(r.node_package_name(), "test-lib");
201    }
202
203    #[test]
204    fn wasm_package_name_defaults_from_node_package_name() {
205        let r = resolved_one(
206            r#"
207[workspace]
208languages = ["node", "wasm"]
209
210[[crates]]
211name = "test-lib"
212sources = ["src/lib.rs"]
213
214[crates.node]
215package_name = "@scope/test-lib-node"
216"#,
217        );
218        assert_eq!(r.wasm_package_name(), "@scope/test-lib-wasm");
219    }
220
221    #[test]
222    fn wasm_package_name_uses_explicit_override() {
223        let r = resolved_one(
224            r#"
225[workspace]
226languages = ["wasm"]
227
228[[crates]]
229name = "test-lib"
230sources = ["src/lib.rs"]
231
232[crates.wasm]
233package_name = "@scope/explicit-wasm"
234"#,
235        );
236        assert_eq!(r.wasm_package_name(), "@scope/explicit-wasm");
237    }
238
239    #[test]
240    fn ruby_gem_name_replaces_hyphens() {
241        let r = minimal();
242        assert_eq!(r.ruby_gem_name(), "test_lib");
243    }
244
245    #[test]
246    fn php_extension_name_replaces_hyphens() {
247        let r = minimal();
248        assert_eq!(r.php_extension_name(), "test_lib");
249    }
250
251    #[test]
252    fn elixir_app_name_replaces_hyphens() {
253        let r = minimal();
254        assert_eq!(r.elixir_app_name(), "test_lib");
255    }
256
257    #[test]
258    fn zig_module_name_replaces_hyphens() {
259        let r = minimal();
260        assert_eq!(r.zig_module_name(), "test_lib");
261    }
262
263    #[test]
264    fn dart_pubspec_name_replaces_hyphens() {
265        let r = minimal();
266        assert_eq!(r.dart_pubspec_name(), "test_lib");
267    }
268
269    #[test]
270    fn swift_module_is_pascal_case() {
271        let r = minimal();
272        assert_eq!(r.swift_module(), "TestLib");
273    }
274
275    #[test]
276    fn r_package_name_defaults_to_crate_name() {
277        let r = minimal();
278        assert_eq!(r.r_package_name(), "test-lib");
279    }
280
281    #[test]
282    fn wasm_type_prefix_defaults_to_wasm() {
283        let r = minimal();
284        assert_eq!(r.wasm_type_prefix(), "Wasm");
285    }
286
287    #[test]
288    fn node_type_prefix_defaults_to_js() {
289        let r = minimal();
290        assert_eq!(r.node_type_prefix(), "Js");
291    }
292}