atagen

jelq

public

an http based roguelike rpg about jelqing

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