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