Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

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");
    }
}