atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
cddf59e37d5f7b2a7e0f8b644252196caf5a7e97
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/http/links.rsRust112 lines3.5 KB
1
use std::fmt::Write as _;
2
3
use super::{run::RunResource, runs};
4
use crate::game::{Branch, Run, SHOP};
5
6
pub const REL_BASE: &str = "https://jelq.dev/rels/";
7
8
/// One RFC 8288 link. `allow` and `lubricant` are extension attributes (§3), so a client never
9
/// has to guess what a target accepts.
10
struct Link<'a> {
11
    href: &'a str,
12
    rel: &'a str,
13
    allow: &'a str,
14
    title: Option<&'a str>,
15
    lubricant: Option<&'a str>,
16
}
17
18
impl Link<'_> {
19
    fn write(&self, out: &mut String) {
20
        if !out.is_empty() {
21
            out.push_str(", ");
22
        }
23
        let _ = write!(out, "<{}>; rel=\"{REL_BASE}{}\"", self.href, self.rel);
24
        if let Some(title) = self.title {
25
            let _ = write!(out, "; title=\"{title}\"");
26
        }
27
        let _ = write!(out, "; allow=\"{}\"", self.allow);
28
        if let Some(rule) = self.lubricant {
29
            let _ = write!(out, "; lubricant=\"{rule}\"");
30
        }
31
    }
32
}
33
34
pub fn header(run: &Run) -> String {
35
    let reach = run.reach();
36
    let mut out = String::new();
37
    let resources: [(&str, &RunResource, bool); 15] = [
38
        ("", &runs::RUN, true),
39
        ("floor", &runs::FLOOR, true),
40
        ("length", &runs::LENGTH, true),
41
        ("foreskin", &runs::FORESKIN, reach.foreskin),
42
        ("inventory", &runs::INVENTORY, true),
43
        ("techniques", &runs::TECHNIQUES, true),
44
        ("drawer", &runs::DRAWER, reach.drawer),
45
        ("kitchen", &runs::KITCHEN, reach.kitchen),
46
        ("thread", &runs::THREAD, reach.thread),
47
        ("grip", &runs::GRIP, true),
48
        ("shop", &runs::SHOP, reach.shop),
49
        ("portal", &runs::PORTAL, reach.portal),
50
        ("elder", &runs::ELDER, reach.elder),
51
        ("footage", &runs::FOOTAGE, reach.footage),
52
        ("diagnosis", &runs::DIAGNOSIS, reach.diagnosis),
53
    ];
54
    for (rel, target, _) in resources.iter().filter(|(_, _, relevant)| *relevant) {
55
        let href = if rel.is_empty() {
56
            run.path("")
57
        } else {
58
            run.path(&format!("/{rel}"))
59
        };
60
        Link {
61
            href: &href,
62
            rel: if rel.is_empty() { "run" } else { rel },
63
            allow: &target.resource.allow(run),
64
            title: None,
65
            lubricant: None,
66
        }
67
        .write(&mut out);
68
    }
69
    if reach.branch_pending {
70
        for (branch, title) in [
71
            (Branch::Forum, "Read the forum"),
72
            (Branch::CloseTab, "Close the tab"),
73
        ] {
74
            Link {
75
                href: &run.path(&format!("/branches/{}", branch.slug())),
76
                rel: "branch",
77
                allow: "POST",
78
                title: Some(title),
79
                lubricant: None,
80
            }
81
            .write(&mut out);
82
        }
83
    }
84
    let run_path = run.path("");
85
    for technique in run.techniques_now() {
86
        let name = technique.name();
87
        Link {
88
            href: &format!("{run_path}?technique={name}"),
89
            rel: "technique",
90
            allow: "JELQ",
91
            title: Some(name),
92
            lubricant: technique.lubricant(),
93
        }
94
        .write(&mut out);
95
    }
96
    if reach.shopping {
97
        for item in SHOP {
98
            let Some(price) = run.price(item) else {
99
                continue;
100
            };
101
            Link {
102
                href: &run.path(&format!("/shop/{}", item.slug())),
103
                rel: "shop-item",
104
                allow: "POST",
105
                title: Some(&format!("{} for {price}", item.slug())),
106
                lubricant: None,
107
            }
108
            .write(&mut out);
109
        }
110
    }
111
    out
112
}