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