monetize_embed/sold.rs
1//! **What a sold private Gunnar is called** — ONE function, called by the shop
2//! that sells it (monetize's estate order: the ready mail's first-login link,
3//! the DNS records, `laid.json`'s `names`) and by the kit that installs it
4//! (its certificate, its passkeys' relying party, its DNS records).
5//!
6//! A sold estate lives under the SELLING shop's apex (owner, 2026-10-08):
7//! gunnar.rs sells `acme.gunnar.rs`; test2's shop sells `acme.test2.gunnar.rs`
8//! — the certificate, the passkey RP, the DNS, the link, the mail and
9//! `laid.json` all say the same name. Wiring trace A43: monetize named it
10//! `<co>.<apex>` while the kit named it `<co>.gunnar.rs` whatever the apex, so
11//! a test2 buyer's first-login link led to a name nothing served.
12
13/// The names of one sold estate. `host` first: it is the console, the passkeys'
14/// relying party, the name the certificate is for.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct SoldNames {
17 pub company: String,
18 pub apex: String,
19 /// `<company>.<apex>`.
20 pub host: String,
21}
22
23impl SoldNames {
24 /// The names of `company` sold by the shop at `apex` (`gunnar.rs`,
25 /// `test2.gunnar.rs`). Pure; neither is checked here (the shop's name rule
26 /// and the kit's are).
27 pub fn of(company: &str, apex: &str) -> SoldNames {
28 let (company, apex) = (company.trim().to_string(), apex.trim().trim_end_matches('.').to_ascii_lowercase());
29 let host = format!("{company}.{apex}");
30 SoldNames { company, apex, host }
31 }
32
33 /// **Every name the estate serves under the shop's apex**, the kit's
34 /// `subzone` shape: `<host>`, `www.<host>`, `git.<host>` — each one an A
35 /// record to its front. No `monetize.<host>` (G16, ONE monetize): a sold
36 /// estate runs no monetize; its boxes pull from the ONE.
37 pub fn names(&self) -> Vec<String> {
38 ["", "www.", "git."].iter().map(|p| format!("{p}{}", self.host)).collect()
39 }
40
41 /// **The word the kit puts between a name's first label and its DNS zone**
42 /// (`zone` = `gunnar.rs`): `acme` at gunnar.rs, `acme.test2` at
43 /// test2.gunnar.rs — so `git.gunnar.rs` becomes `git.acme.test2.gunnar.rs`.
44 /// `None` when the host is not inside `zone` (the kit cannot write it).
45 pub fn word_in(&self, zone: &str) -> Option<String> {
46 let zone = zone.trim().trim_end_matches('.').to_ascii_lowercase();
47 self.host.strip_suffix(&format!(".{zone}")).filter(|w| !w.is_empty()).map(str::to_string)
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 /// ★ A43: the shop and the kit name a sold estate under the SELLING shop's apex.
56 #[test]
57 fn a_sold_estate_is_named_under_the_selling_shops_apex() {
58 let rs = SoldNames::of("acme", "gunnar.rs");
59 // G16 (ONE monetize): a sold estate runs no monetize, so it serves no `monetize.<host>`.
60 assert_eq!(rs.names(), vec!["acme.gunnar.rs", "www.acme.gunnar.rs", "git.acme.gunnar.rs"]);
61 assert_eq!(rs.word_in("gunnar.rs").as_deref(), Some("acme"));
62 let t2 = SoldNames::of("acme", "test2.gunnar.rs");
63 assert_eq!(t2.host, "acme.test2.gunnar.rs");
64 assert_eq!(t2.names(), vec!["acme.test2.gunnar.rs", "www.acme.test2.gunnar.rs", "git.acme.test2.gunnar.rs"]);
65 assert_eq!(t2.word_in("gunnar.rs").as_deref(), Some("acme.test2"));
66 assert_eq!(t2.word_in("vetra.se"), None, "a host outside the zone has no word in it");
67 assert_eq!(SoldNames::of("acme", "Test2.Gunnar.RS.").host, "acme.test2.gunnar.rs");
68 }
69}