atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
fcc6cf6d5edcfdaf719c2b9cd40c6b77532340e6
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/http/links.rsRust122 lines3.7 KB
1
use std::fmt::Write as _;
2
3
use super::{meta, run::RunResource, runs};
4
use crate::game::{Branch, FORUM_THREAD, 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
        ("re-read", &runs::REREAD, 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.thread {
70
        Link {
71
            href: FORUM_THREAD,
72
            rel: "thread",
73
            allow: &meta::THREAD.allow_any(),
74
            title: None,
75
            lubricant: None,
76
        }
77
        .write(&mut out);
78
    }
79
    if reach.branch_pending {
80
        for (branch, title) in [
81
            (Branch::Forum, "Read the forum"),
82
            (Branch::CloseTab, "Close the tab"),
83
        ] {
84
            Link {
85
                href: &run.path(&format!("/branches/{}", branch.slug())),
86
                rel: "branch",
87
                allow: "POST",
88
                title: Some(title),
89
                lubricant: None,
90
            }
91
            .write(&mut out);
92
        }
93
    }
94
    let run_path = run.path("");
95
    for technique in run.techniques_now() {
96
        let name = technique.name();
97
        Link {
98
            href: &format!("{run_path}?technique={name}"),
99
            rel: "technique",
100
            allow: "JELQ",
101
            title: Some(name),
102
            lubricant: technique.lubricant(),
103
        }
104
        .write(&mut out);
105
    }
106
    if reach.shopping {
107
        for item in SHOP {
108
            let Some(price) = run.price(item) else {
109
                continue;
110
            };
111
            Link {
112
                href: &run.path(&format!("/shop/{}", item.slug())),
113
                rel: "shop-item",
114
                allow: "POST",
115
                title: Some(&format!("{} for {price}", item.slug())),
116
                lubricant: None,
117
            }
118
            .write(&mut out);
119
        }
120
    }
121
    out
122
}