crates/monetize-site-buy/src/urls.rs
//! **The site's own addresses in a buy** — where the vendor sends the buyer back, and where a
//! way back from the vendor goes on the site. Every one is on the SITE (its origin): the buyer
//! never lands on the shop's pages.
use crate::rules::{company, query_value};
/// **The site's two sign-up choices, as its page spells them** (Gunnar: `/signup?plan=10gb`
/// and `/signup?plan=private`; Holger: `/signup?plan=account` and `/signup?plan=private`).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Pages {
/// The account choice (the free account, a paid plan).
pub account: String,
/// The private-instance choice.
pub private: String,
/// Where an instance is managed (renewed, stopped), its company appended
/// (Gunnar: `/signup?plan=private&renew=`; Holger: `/private?renew=`).
pub manage: String,
}
impl Default for Pages {
fn default() -> Pages {
Pages { account: "/signup?plan=account".into(), private: "/signup?plan=private".into(), manage: "/signup?plan=private&renew=".into() }
}
}
/// `{origin}/welcome?session={session}[&next=…]`: where a paid sign-up comes back to (the
/// vendor rewrites `{session}`).
pub fn signup_success(origin: &str, next: Option<&str>) -> String {
let back = next.map(|n| format!("&next={}", query_value(n))).unwrap_or_default();
format!("{}/welcome?session={{session}}{back}", origin.trim_end_matches('/'))
}
/// **Where the vendor's Cancel goes on a sign-up**: back to the choice the person came from —
/// the private choice when the page's way back is it, the account's otherwise.
pub fn signup_cancel(origin: &str, next: &str, pages: &Pages) -> String {
let back = if next == pages.private { &pages.private } else { &pages.account };
format!("{}{back}", origin.trim_end_matches('/'))
}
/// **Where a visitor's new account is welcomed after a private order is paid** (pay once): the
/// site's welcome page, and after the passkey back to the order.
pub fn private_welcome(origin: &str, pages: &Pages, company: &str) -> String {
let next = query_value(&format!("{}&company={company}", pages.private));
format!("{}/welcome?session={{session}}&next={next}", origin.trim_end_matches('/'))
}
/// **Where a buyer who backs out of a private order goes**: the site's private choice.
pub fn private_cancel(origin: &str, pages: &Pages) -> String {
format!("{}{}", origin.trim_end_matches('/'), pages.private)
}
/// `/paid/estate/<company>` → the order's watch on the site's private choice.
pub fn paid_estate_to(pages: &Pages, co: &str) -> String {
if company(co) {
format!("{}&company={co}", pages.private)
} else {
pages.private.clone()
}
}
/// `/paid/estate/<company>/change-<n>` → the order's watch, told it is a change;
/// `/paid/estate/<company>/renewal-<n>` → the order's watch, as the first buy's way back
/// ([`paid_estate_to`]): the buyer lands with their company (BUYFLOW-4).
pub fn paid_change_to(pages: &Pages, co: &str, change: &str) -> String {
let numbered = |prefix: &str| change.strip_prefix(prefix).is_some_and(|n| !n.is_empty() && n.len() <= 6 && n.bytes().all(|b| b.is_ascii_digit()));
if company(co) && numbered("change-") {
format!("{}&company={co}&change=1", pages.private)
} else if company(co) && numbered("renewal-") {
paid_estate_to(pages, co)
} else {
pages.private.clone()
}
}
/// `/login?signup=<session>&user=<user>`: the passkey registration of a paid sign-up's account,
/// a rooted path on the site.
pub fn invite_url(session: &str, user: &str) -> String {
format!("/login?signup={}&user={}", query_value(session), query_value(user))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_way_back_is_on_the_site() {
let p = Pages { account: "/signup?plan=10gb".into(), private: "/signup?plan=private".into(), ..Pages::default() };
assert_eq!(signup_success("https://gunnar.rs/", Some("/signup?plan=private")), "https://gunnar.rs/welcome?session={session}&next=%2Fsignup%3Fplan%3Dprivate");
assert_eq!(signup_success("https://holger.rs", None), "https://holger.rs/welcome?session={session}");
assert_eq!(signup_cancel("https://gunnar.rs", "/signup?plan=private", &p), "https://gunnar.rs/signup?plan=private");
assert_eq!(signup_cancel("https://gunnar.rs", "/", &p), "https://gunnar.rs/signup?plan=10gb");
assert_eq!(private_welcome("https://holger.rs", &Pages::default(), "acme"), "https://holger.rs/welcome?session={session}&next=%2Fsignup%3Fplan%3Dprivate%26company%3Dacme");
assert_eq!(private_cancel("https://holger.rs", &Pages::default()), "https://holger.rs/signup?plan=private");
assert_eq!(paid_estate_to(&p, "acme"), "/signup?plan=private&company=acme");
assert_eq!(paid_estate_to(&p, "../x"), "/signup?plan=private");
assert_eq!(paid_change_to(&p, "acme", "change-2"), "/signup?plan=private&company=acme&change=1");
assert_eq!(paid_change_to(&p, "acme", "x"), "/signup?plan=private");
// BUYFLOW-4 finding 1: a renewal's way back carries its company, as the first buy's does.
assert_eq!(paid_change_to(&p, "acme", "renewal-3"), "/signup?plan=private&company=acme");
assert_eq!(paid_change_to(&p, "acme", "renewal-"), "/signup?plan=private");
assert_eq!(paid_change_to(&p, "../x", "renewal-3"), "/signup?plan=private");
assert_eq!(invite_url("cs_test_abcdefghij", "x14"), "/login?signup=cs_test_abcdefghij&user=x14");
}
}