1#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct PublicRegistry {
27 pub base: String,
30 pub store: Option<String>,
33}
34
35impl PublicRegistry {
36 pub fn versions_url(&self, name: &str) -> String {
38 self.with_store(format!("{}/{}/versions", self.base, name))
39 }
40
41 pub fn archive_url(&self, name: &str, version: &str) -> String {
44 self.with_store(format!("{}/{}/{}/archive", self.base, name, version))
45 }
46
47 pub fn contract_url(&self, name: &str, version: &str) -> String {
51 self.with_store(format!("{}/{}/{}/contract", self.base, name, version))
52 }
53
54 fn with_store(&self, url: String) -> String {
55 match &self.store {
56 Some(s) => format!("{url}?store={s}"),
57 None => url,
58 }
59 }
60}
61
62pub fn public(registry: &str) -> Option<PublicRegistry> {
67 let trimmed = registry.trim().trim_end_matches('/');
68 let (scheme, rest) = match trimmed.split_once("://") {
69 Some((s, r)) => (s, r),
70 None => ("https", trimmed),
71 };
72 let mut segs = rest.split('/').filter(|s| !s.is_empty());
73 let host = segs.next()?;
74 let tenant = segs.next()?; if host.is_empty() || tenant.is_empty() {
76 return None;
77 }
78 let store = segs.next().map(str::to_string);
79 Some(PublicRegistry {
80 base: format!("{scheme}://{host}/v1/public/{tenant}"),
81 store,
82 })
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn flat_store_registry() {
91 let r = public("vcs.lexlang.org/alpibrusl").unwrap();
92 assert_eq!(r.base, "https://vcs.lexlang.org/v1/public/alpibrusl");
93 assert_eq!(r.store, None);
94 assert_eq!(
95 r.versions_url("lex-agent"),
96 "https://vcs.lexlang.org/v1/public/alpibrusl/lex-agent/versions"
97 );
98 assert_eq!(
99 r.archive_url("lex-agent", "0.1.0"),
100 "https://vcs.lexlang.org/v1/public/alpibrusl/lex-agent/0.1.0/archive"
101 );
102 }
103
104 #[test]
105 fn named_store_registry_adds_store_query() {
106 let r = public("vcs.lexlang.org/lex-official/lex-nt").unwrap();
107 assert_eq!(r.base, "https://vcs.lexlang.org/v1/public/lex-official");
108 assert_eq!(r.store.as_deref(), Some("lex-nt"));
109 assert_eq!(
110 r.versions_url("lex-nt"),
111 "https://vcs.lexlang.org/v1/public/lex-official/lex-nt/versions?store=lex-nt"
112 );
113 assert_eq!(
114 r.contract_url("lex-nt", "1.2.0"),
115 "https://vcs.lexlang.org/v1/public/lex-official/lex-nt/1.2.0/contract?store=lex-nt"
116 );
117 }
118
119 #[test]
120 fn explicit_scheme_is_preserved() {
121 let r = public("http://localhost:4040/acme").unwrap();
122 assert_eq!(r.base, "http://localhost:4040/v1/public/acme");
123 }
124
125 #[test]
126 fn bare_host_has_no_public_surface() {
127 assert_eq!(public("vcs.lexlang.org"), None);
128 assert_eq!(public("https://vcs.lexlang.org"), None);
129 assert_eq!(public(""), None);
130 }
131}