Skip to main content

alef_core/config/resolved/
identifiers.rs

1//! Go/Java/Kotlin/C# identifier methods and reverse-DNS derivations.
2
3use super::ResolvedCrateConfig;
4use crate::config::derive::{derive_go_module_from_repo, derive_reverse_dns_package};
5
6impl ResolvedCrateConfig {
7    /// Get the GitHub repository URL, returning an error when no source has it set.
8    ///
9    /// Resolution order:
10    /// 1. `[e2e.registry] github_repo`
11    /// 2. `[scaffold] repository`
12    pub fn try_github_repo(&self) -> Result<String, String> {
13        if let Some(e2e) = &self.e2e {
14            if let Some(url) = &e2e.registry.github_repo {
15                return Ok(url.clone());
16            }
17        }
18        if let Some(url) = self.scaffold.as_ref().and_then(|s| s.repository.as_ref()) {
19            return Ok(url.clone());
20        }
21        Err(format!(
22            "no repository URL configured — set `[scaffold] repository = \"...\"` (or `[e2e.registry] github_repo`) for crate `{}`",
23            self.name
24        ))
25    }
26
27    /// Get the GitHub repository URL with a vendor-neutral placeholder fallback.
28    pub fn github_repo(&self) -> String {
29        self.try_github_repo()
30            .unwrap_or_else(|_| format!("https://example.invalid/{}", self.name))
31    }
32
33    /// Get the Go module path, returning an error when neither `[go].module`
34    /// nor a derivable repository URL is configured.
35    pub(crate) fn try_go_module(&self) -> Result<String, String> {
36        if let Some(module) = self.go.as_ref().and_then(|g| g.module.as_ref()) {
37            return Ok(module.clone());
38        }
39        if let Ok(repo) = self.try_github_repo() {
40            if let Some(module) = derive_go_module_from_repo(&repo) {
41                return Ok(module);
42            }
43        }
44        Err(format!(
45            "no Go module configured — set `[go] module = \"...\"` or `[scaffold] repository = \"https://<host>/<org>/...\"` for crate `{}`",
46            self.name
47        ))
48    }
49
50    /// Get the Go module path with a vendor-neutral placeholder fallback.
51    pub fn go_module(&self) -> String {
52        self.try_go_module()
53            .unwrap_or_else(|_| format!("example.invalid/{}", self.name))
54    }
55
56    /// Get the Java package name, returning an error when neither `[java].package`
57    /// nor a derivable repository URL is configured.
58    pub(crate) fn try_java_package(&self) -> Result<String, String> {
59        if let Some(pkg) = self.java.as_ref().and_then(|j| j.package.as_ref()) {
60            return Ok(pkg.clone());
61        }
62        if let Ok(repo) = self.try_github_repo() {
63            if let Some(pkg) = derive_reverse_dns_package(&repo) {
64                return Ok(pkg);
65            }
66        }
67        Err(format!(
68            "no Java package configured — set `[java] package = \"...\"` or `[scaffold] repository = \"https://<host>/<org>/...\"` for crate `{}`",
69            self.name
70        ))
71    }
72
73    /// Get the Java package name with a vendor-neutral placeholder fallback.
74    pub fn java_package(&self) -> String {
75        self.try_java_package()
76            .unwrap_or_else(|_| "unconfigured.alef".to_string())
77    }
78
79    /// Get the Java Maven groupId.
80    pub fn java_group_id(&self) -> String {
81        self.java_package()
82    }
83
84    /// Get the Kotlin package name, returning an error when neither
85    /// `[kotlin].package` nor a derivable repository URL is configured.
86    pub(crate) fn try_kotlin_package(&self) -> Result<String, String> {
87        if let Some(pkg) = self.kotlin.as_ref().and_then(|k| k.package.as_ref()) {
88            return Ok(pkg.clone());
89        }
90        if let Ok(repo) = self.try_github_repo() {
91            if let Some(pkg) = derive_reverse_dns_package(&repo) {
92                return Ok(pkg);
93            }
94        }
95        Err(format!(
96            "no Kotlin package configured — set `[kotlin] package = \"...\"` or `[scaffold] repository = \"https://<host>/<org>/...\"` for crate `{}`",
97            self.name
98        ))
99    }
100
101    /// Get the Kotlin package name with a vendor-neutral placeholder fallback.
102    pub fn kotlin_package(&self) -> String {
103        self.try_kotlin_package()
104            .unwrap_or_else(|_| "unconfigured.alef".to_string())
105    }
106
107    /// Get the C# namespace.
108    pub fn csharp_namespace(&self) -> String {
109        self.csharp
110            .as_ref()
111            .and_then(|c| c.namespace.as_ref())
112            .cloned()
113            .unwrap_or_else(|| {
114                use heck::ToPascalCase;
115                self.name.to_pascal_case()
116            })
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use crate::config::new_config::NewAlefConfig;
123
124    fn resolved_one(toml: &str) -> super::super::ResolvedCrateConfig {
125        let cfg: NewAlefConfig = toml::from_str(toml).unwrap();
126        cfg.resolve().unwrap().remove(0)
127    }
128
129    fn with_repo(name: &str, repo: &str) -> super::super::ResolvedCrateConfig {
130        resolved_one(&format!(
131            r#"
132[workspace]
133languages = ["python"]
134
135[[crates]]
136name = "{name}"
137sources = ["src/lib.rs"]
138
139[crates.scaffold]
140repository = "{repo}"
141"#
142        ))
143    }
144
145    #[test]
146    fn go_module_derives_from_repo() {
147        let r = with_repo("my-lib", "https://github.com/foo/my-lib");
148        assert_eq!(r.go_module(), "github.com/foo/my-lib");
149    }
150
151    #[test]
152    fn go_module_explicit_wins_over_repo() {
153        let r = resolved_one(
154            r#"
155[workspace]
156languages = ["python"]
157
158[[crates]]
159name = "my-lib"
160sources = ["src/lib.rs"]
161
162[crates.scaffold]
163repository = "https://github.com/foo/my-lib"
164
165[crates.go]
166module = "custom.example.com/my-lib"
167"#,
168        );
169        assert_eq!(r.go_module(), "custom.example.com/my-lib");
170    }
171
172    #[test]
173    fn java_package_derives_from_repo() {
174        let r = with_repo("my-lib", "https://github.com/foo-org/my-lib");
175        assert_eq!(r.java_package(), "com.github.foo_org");
176    }
177
178    #[test]
179    fn java_package_explicit_wins() {
180        let r = resolved_one(
181            r#"
182[workspace]
183languages = ["python"]
184
185[[crates]]
186name = "my-lib"
187sources = ["src/lib.rs"]
188
189[crates.java]
190package = "dev.kreuzberg"
191"#,
192        );
193        assert_eq!(r.java_package(), "dev.kreuzberg");
194    }
195
196    #[test]
197    fn kotlin_package_falls_back_to_placeholder() {
198        let r = resolved_one(
199            r#"
200[workspace]
201languages = ["python"]
202
203[[crates]]
204name = "my-lib"
205sources = ["src/lib.rs"]
206"#,
207        );
208        assert_eq!(r.kotlin_package(), "unconfigured.alef");
209    }
210
211    #[test]
212    fn csharp_namespace_derives_pascal_case() {
213        let r = resolved_one(
214            r#"
215[workspace]
216languages = ["python"]
217
218[[crates]]
219name = "my-lib"
220sources = ["src/lib.rs"]
221"#,
222        );
223        assert_eq!(r.csharp_namespace(), "MyLib");
224    }
225
226    #[test]
227    fn java_group_id_equals_package() {
228        let r = with_repo("my-lib", "https://github.com/foo-org/my-lib");
229        assert_eq!(r.java_group_id(), r.java_package());
230    }
231}