use std::cmp::Reverse;
use super::ResolvedCrateConfig;
use crate::config::extras::Language;
impl ResolvedCrateConfig {
pub fn resolve_field_name(&self, lang: Language, type_name: &str, field_name: &str) -> Option<String> {
let explicit_key = format!("{type_name}.{field_name}");
let explicit = match lang {
Language::Python => self.python.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Node => self.node.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Ruby => self.ruby.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Php => self.php.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Elixir => self.elixir.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Wasm => self.wasm.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Ffi => self.ffi.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Gleam => self.gleam.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Go => self.go.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Java => self.java.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Kotlin => self.kotlin.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Csharp => self.csharp.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::R => self.r.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Zig => self.zig.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Dart => self.dart.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Swift => self.swift.as_ref().and_then(|c| c.rename_fields.get(&explicit_key)),
Language::Rust | Language::C => None,
};
if let Some(renamed) = explicit {
if renamed != field_name {
return Some(renamed.clone());
}
return None;
}
match lang {
Language::Python => crate::keywords::python_safe_name(field_name),
_ => None,
}
}
pub fn serde_rename_all_for_language(&self, lang: Language) -> String {
let override_val = match lang {
Language::Python => self.python.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Node => self.node.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Ruby => self.ruby.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Php => self.php.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Elixir => self.elixir.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Wasm => self.wasm.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Ffi => self.ffi.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Gleam => self.gleam.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Go => self.go.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Java => self.java.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Kotlin => self.kotlin.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Csharp => self.csharp.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::R => self.r.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Zig => self.zig.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Dart => self.dart.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Swift => self.swift.as_ref().and_then(|c| c.serde_rename_all.as_deref()),
Language::Rust | Language::C => None,
};
if let Some(val) = override_val {
return val.to_string();
}
match lang {
Language::Node | Language::Wasm | Language::Java | Language::Csharp => "camelCase".to_string(),
Language::Python
| Language::Ruby
| Language::Php
| Language::Go
| Language::Ffi
| Language::Elixir
| Language::R
| Language::Rust
| Language::Kotlin
| Language::Gleam
| Language::Zig
| Language::Swift
| Language::Dart
| Language::C => "snake_case".to_string(),
}
}
pub fn rewrite_path(&self, rust_path: &str) -> String {
let mut mappings: Vec<_> = self.path_mappings.iter().collect();
mappings.sort_by_key(|b| Reverse(b.0.len()));
for (from, to) in &mappings {
if rust_path.starts_with(from.as_str()) {
return format!("{}{}", to, &rust_path[from.len()..]);
}
}
rust_path.to_string()
}
pub fn resolved_version(&self) -> Option<String> {
let content = std::fs::read_to_string(&self.version_from).ok()?;
let value: toml::Value = toml::from_str(&content).ok()?;
if let Some(v) = value
.get("workspace")
.and_then(|w| w.get("package"))
.and_then(|p| p.get("version"))
.and_then(|v| v.as_str())
{
return Some(v.to_string());
}
value
.get("package")
.and_then(|p| p.get("version"))
.and_then(|v| v.as_str())
.map(|v| v.to_string())
}
}
#[cfg(test)]
mod tests {
use crate::config::extras::Language;
use crate::config::new_config::NewAlefConfig;
fn resolved_one(toml: &str) -> super::super::ResolvedCrateConfig {
let cfg: NewAlefConfig = toml::from_str(toml).unwrap();
cfg.resolve().unwrap().remove(0)
}
fn minimal() -> super::super::ResolvedCrateConfig {
resolved_one(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
"#,
)
}
#[test]
fn serde_rename_all_python_defaults_to_snake_case() {
let r = minimal();
assert_eq!(r.serde_rename_all_for_language(Language::Python), "snake_case");
}
#[test]
fn serde_rename_all_node_defaults_to_camel_case() {
let r = resolved_one(
r#"
[workspace]
languages = ["node"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
"#,
);
assert_eq!(r.serde_rename_all_for_language(Language::Node), "camelCase");
}
#[test]
fn serde_rename_all_java_defaults_to_camel_case() {
let r = resolved_one(
r#"
[workspace]
languages = ["java"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
"#,
);
assert_eq!(r.serde_rename_all_for_language(Language::Java), "camelCase");
}
#[test]
fn serde_rename_all_per_language_override_wins() {
let r = resolved_one(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
[crates.python]
serde_rename_all = "camelCase"
"#,
);
assert_eq!(r.serde_rename_all_for_language(Language::Python), "camelCase");
}
#[test]
fn resolved_resolve_field_name_keyword_escapes_python() {
use crate::keywords::python_safe_name;
let r = minimal();
let result = r.resolve_field_name(Language::Python, "MyType", "class");
assert_eq!(result, python_safe_name("class"));
}
#[test]
fn resolved_resolve_field_name_explicit_rename_wins_over_keyword_escape() {
let r = resolved_one(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
[crates.python]
rename_fields = { "MyType.class" = "klass" }
"#,
);
let result = r.resolve_field_name(Language::Python, "MyType", "class");
assert_eq!(result, Some("klass".to_string()));
}
#[test]
fn resolved_resolve_field_name_non_keyword_returns_none() {
let r = minimal();
let result = r.resolve_field_name(Language::Python, "MyType", "my_field");
assert_eq!(result, None);
}
#[test]
fn rewrite_path_applies_longest_prefix_first() {
let r = resolved_one(
r#"
[workspace]
languages = ["python"]
[[crates]]
name = "test-lib"
sources = ["src/lib.rs"]
path_mappings = { "foo::bar" = "baz::qux", "foo" = "zzz" }
"#,
);
assert_eq!(r.rewrite_path("foo::bar::Struct"), "baz::qux::Struct");
assert_eq!(r.rewrite_path("foo::Other"), "zzz::Other");
assert_eq!(r.rewrite_path("unrelated"), "unrelated");
}
}