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    ///
81    /// Prefers the explicit `[java] group_id` override when set; otherwise falls back to
82    /// the Java package name (most projects publish under `groupId == java package`).
83    pub fn java_group_id(&self) -> String {
84        if let Some(gid) = self.java.as_ref().and_then(|j| j.group_id.as_ref()) {
85            return gid.clone();
86        }
87        self.java_package()
88    }
89
90    /// Get the Java Maven artifactId.
91    ///
92    /// Prefers the explicit `[java] artifact_id` override; otherwise falls back to the
93    /// crate name (`[[crates]] name`).
94    pub fn java_artifact_id(&self) -> String {
95        self.java
96            .as_ref()
97            .and_then(|j| j.artifact_id.as_ref())
98            .cloned()
99            .unwrap_or_else(|| self.name.clone())
100    }
101
102    /// Get the Kotlin package name, returning an error when neither
103    /// `[kotlin].package` nor a derivable repository URL is configured.
104    pub(crate) fn try_kotlin_package(&self) -> Result<String, String> {
105        if let Some(pkg) = self.kotlin.as_ref().and_then(|k| k.package.as_ref()) {
106            return Ok(pkg.clone());
107        }
108        if let Ok(repo) = self.try_github_repo() {
109            if let Some(pkg) = derive_reverse_dns_package(&repo) {
110                return Ok(pkg);
111            }
112        }
113        Err(format!(
114            "no Kotlin package configured — set `[kotlin] package = \"...\"` or `[scaffold] repository = \"https://<host>/<org>/...\"` for crate `{}`",
115            self.name
116        ))
117    }
118
119    /// Get the Kotlin package name with a vendor-neutral placeholder fallback.
120    pub fn kotlin_package(&self) -> String {
121        self.try_kotlin_package()
122            .unwrap_or_else(|_| "unconfigured.alef".to_string())
123    }
124
125    /// Get the C# namespace.
126    pub fn csharp_namespace(&self) -> String {
127        self.csharp
128            .as_ref()
129            .and_then(|c| c.namespace.as_ref())
130            .cloned()
131            .unwrap_or_else(|| {
132                use heck::ToPascalCase;
133                self.name.to_pascal_case()
134            })
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::config::new_config::NewAlefConfig;
141
142    fn resolved_one(toml: &str) -> super::super::ResolvedCrateConfig {
143        let cfg: NewAlefConfig = toml::from_str(toml).unwrap();
144        cfg.resolve().unwrap().remove(0)
145    }
146
147    fn with_repo(name: &str, repo: &str) -> super::super::ResolvedCrateConfig {
148        resolved_one(&format!(
149            r#"
150[workspace]
151languages = ["python"]
152
153[[crates]]
154name = "{name}"
155sources = ["src/lib.rs"]
156
157[crates.scaffold]
158repository = "{repo}"
159"#
160        ))
161    }
162
163    #[test]
164    fn go_module_derives_from_repo() {
165        let r = with_repo("my-lib", "https://github.com/foo/my-lib");
166        assert_eq!(r.go_module(), "github.com/foo/my-lib");
167    }
168
169    #[test]
170    fn go_module_explicit_wins_over_repo() {
171        let r = resolved_one(
172            r#"
173[workspace]
174languages = ["python"]
175
176[[crates]]
177name = "my-lib"
178sources = ["src/lib.rs"]
179
180[crates.scaffold]
181repository = "https://github.com/foo/my-lib"
182
183[crates.go]
184module = "custom.example.com/my-lib"
185"#,
186        );
187        assert_eq!(r.go_module(), "custom.example.com/my-lib");
188    }
189
190    #[test]
191    fn java_package_derives_from_repo() {
192        let r = with_repo("my-lib", "https://github.com/foo-org/my-lib");
193        assert_eq!(r.java_package(), "com.github.foo_org");
194    }
195
196    #[test]
197    fn java_package_explicit_wins() {
198        let r = resolved_one(
199            r#"
200[workspace]
201languages = ["python"]
202
203[[crates]]
204name = "my-lib"
205sources = ["src/lib.rs"]
206
207[crates.java]
208package = "dev.kreuzberg"
209"#,
210        );
211        assert_eq!(r.java_package(), "dev.kreuzberg");
212    }
213
214    #[test]
215    fn kotlin_package_falls_back_to_placeholder() {
216        let r = resolved_one(
217            r#"
218[workspace]
219languages = ["python"]
220
221[[crates]]
222name = "my-lib"
223sources = ["src/lib.rs"]
224"#,
225        );
226        assert_eq!(r.kotlin_package(), "unconfigured.alef");
227    }
228
229    #[test]
230    fn csharp_namespace_derives_pascal_case() {
231        let r = resolved_one(
232            r#"
233[workspace]
234languages = ["python"]
235
236[[crates]]
237name = "my-lib"
238sources = ["src/lib.rs"]
239"#,
240        );
241        assert_eq!(r.csharp_namespace(), "MyLib");
242    }
243
244    #[test]
245    fn java_group_id_equals_package() {
246        let r = with_repo("my-lib", "https://github.com/foo-org/my-lib");
247        assert_eq!(r.java_group_id(), r.java_package());
248    }
249}