alef_core/config/resolved/
fields.rs1use std::cmp::Reverse;
4
5use super::ResolvedCrateConfig;
6use crate::config::extras::Language;
7
8impl ResolvedCrateConfig {
9 pub fn resolve_field_name(&self, lang: Language, type_name: &str, field_name: &str) -> Option<String> {
21 let explicit_key = format!("{type_name}.{field_name}");
23 let explicit = match lang {
24 Language::Python => self.python.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
25 Language::Node => self.node.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
26 Language::Ruby => self.ruby.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
27 Language::Php => self.php.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
28 Language::Elixir => self.elixir.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
29 Language::Wasm => self.wasm.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
30 Language::Ffi => self.ffi.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
31 Language::Gleam => self.gleam.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
32 Language::Go => self.go.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
33 Language::Java => self.java.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
34 Language::Kotlin => self.kotlin.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
35 Language::Csharp => self.csharp.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
36 Language::R => self.r.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
37 Language::Zig => self.zig.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
38 Language::Dart => self.dart.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
39 Language::Swift => self.swift.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
40 Language::Rust | Language::C => None,
41 };
42 if let Some(renamed) = explicit {
43 if renamed != field_name {
44 return Some(renamed.clone());
45 }
46 return None;
47 }
48
49 match lang {
51 Language::Python => crate::keywords::python_safe_name(field_name),
52 _ => None,
56 }
57 }
58
59 pub fn serde_rename_all_for_language(&self, lang: Language) -> String {
67 let override_val = match lang {
68 Language::Python => self.python.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
69 Language::Node => self.node.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
70 Language::Ruby => self.ruby.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
71 Language::Php => self.php.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
72 Language::Elixir => self.elixir.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
73 Language::Wasm => self.wasm.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
74 Language::Ffi => self.ffi.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
75 Language::Gleam => self.gleam.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
76 Language::Go => self.go.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
77 Language::Java => self.java.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
78 Language::Kotlin => self.kotlin.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
79 Language::Csharp => self.csharp.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
80 Language::R => self.r.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
81 Language::Zig => self.zig.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
82 Language::Dart => self.dart.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
83 Language::Swift => self.swift.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
84 Language::Rust | Language::C => None,
85 };
86
87 if let Some(val) = override_val {
88 return val.to_string();
89 }
90
91 match lang {
92 Language::Node
93 | Language::Wasm
94 | Language::Java
95 | Language::Csharp
96 | Language::Php
97 | Language::Kotlin
98 | Language::Swift
99 | Language::Dart => "camelCase".to_string(),
100 Language::Python
101 | Language::Ruby
102 | Language::Go
103 | Language::Ffi
104 | Language::Elixir
105 | Language::R
106 | Language::Rust
107 | Language::Gleam
108 | Language::Zig
109 | Language::C => "snake_case".to_string(),
110 }
111 }
112
113 pub fn rewrite_path(&self, rust_path: &str) -> String {
118 let mut mappings: Vec<_> = self.path_mappings.iter().collect();
119 mappings.sort_by_key(|b| Reverse(b.0.len()));
120
121 for (from, to) in &mappings {
122 if rust_path.starts_with(from.as_str()) {
123 return format!("{}{}", to, &rust_path[from.len()..]);
124 }
125 }
126 rust_path.to_string()
127 }
128
129 pub fn bridge_associated_types(&self) -> std::collections::HashSet<String> {
135 let mut set = std::collections::HashSet::new();
136 for bridge in &self.trait_bridges {
137 for name in bridge.associated_type_names() {
138 set.insert(name.to_string());
139 }
140 }
141 set
142 }
143
144 pub fn resolved_version(&self) -> Option<String> {
149 let content = std::fs::read_to_string(&self.version_from).ok()?;
150 let value: toml::Value = toml::from_str(&content).ok()?;
151 if let Some(v) = value
152 .get("workspace")
153 .and_then(|w| w.get("package"))
154 .and_then(|p| p.get("version"))
155 .and_then(|v| v.as_str())
156 {
157 return Some(v.to_string());
158 }
159 value
160 .get("package")
161 .and_then(|p| p.get("version"))
162 .and_then(|v| v.as_str())
163 .map(|v| v.to_string())
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use crate::config::extras::Language;
170 use crate::config::new_config::NewAlefConfig;
171
172 fn resolved_one(toml: &str) -> super::super::ResolvedCrateConfig {
173 let cfg: NewAlefConfig = toml::from_str(toml).unwrap();
174 cfg.resolve().unwrap().remove(0)
175 }
176
177 fn minimal() -> super::super::ResolvedCrateConfig {
178 resolved_one(
179 r#"
180[workspace]
181languages = ["python"]
182
183[[crates]]
184name = "test-lib"
185sources = ["src/lib.rs"]
186"#,
187 )
188 }
189
190 #[test]
191 fn serde_rename_all_python_defaults_to_snake_case() {
192 let r = minimal();
193 assert_eq!(r.serde_rename_all_for_language(Language::Python), "snake_case");
194 }
195
196 #[test]
197 fn serde_rename_all_node_defaults_to_camel_case() {
198 let r = resolved_one(
199 r#"
200[workspace]
201languages = ["node"]
202
203[[crates]]
204name = "test-lib"
205sources = ["src/lib.rs"]
206"#,
207 );
208 assert_eq!(r.serde_rename_all_for_language(Language::Node), "camelCase");
209 }
210
211 #[test]
212 fn serde_rename_all_java_defaults_to_camel_case() {
213 let r = resolved_one(
214 r#"
215[workspace]
216languages = ["java"]
217
218[[crates]]
219name = "test-lib"
220sources = ["src/lib.rs"]
221"#,
222 );
223 assert_eq!(r.serde_rename_all_for_language(Language::Java), "camelCase");
224 }
225
226 #[test]
227 fn serde_rename_all_per_language_override_wins() {
228 let r = resolved_one(
229 r#"
230[workspace]
231languages = ["python"]
232
233[[crates]]
234name = "test-lib"
235sources = ["src/lib.rs"]
236
237[crates.python]
238serde_rename_all = "camelCase"
239"#,
240 );
241 assert_eq!(r.serde_rename_all_for_language(Language::Python), "camelCase");
242 }
243
244 #[test]
245 fn resolved_resolve_field_name_keyword_escapes_python() {
246 use crate::keywords::python_safe_name;
247 let r = minimal();
248 let result = r.resolve_field_name(Language::Python, "MyType", "class");
250 assert_eq!(result, python_safe_name("class"));
251 }
252
253 #[test]
254 fn resolved_resolve_field_name_explicit_rename_wins_over_keyword_escape() {
255 let r = resolved_one(
256 r#"
257[workspace]
258languages = ["python"]
259
260[[crates]]
261name = "test-lib"
262sources = ["src/lib.rs"]
263
264[crates.python]
265rename_fields = { "MyType.class" = "klass" }
266"#,
267 );
268 let result = r.resolve_field_name(Language::Python, "MyType", "class");
269 assert_eq!(result, Some("klass".to_string()));
270 }
271
272 #[test]
273 fn resolved_resolve_field_name_non_keyword_returns_none() {
274 let r = minimal();
275 let result = r.resolve_field_name(Language::Python, "MyType", "my_field");
276 assert_eq!(result, None);
277 }
278
279 #[test]
280 fn rewrite_path_applies_longest_prefix_first() {
281 let r = resolved_one(
282 r#"
283[workspace]
284languages = ["python"]
285
286[[crates]]
287name = "test-lib"
288sources = ["src/lib.rs"]
289path_mappings = { "foo::bar" = "baz::qux", "foo" = "zzz" }
290"#,
291 );
292 assert_eq!(r.rewrite_path("foo::bar::Struct"), "baz::qux::Struct");
294 assert_eq!(r.rewrite_path("foo::Other"), "zzz::Other");
295 assert_eq!(r.rewrite_path("unrelated"), "unrelated");
296 }
297}