atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
2176d9a94bd9f6eaed0b8222251bd637ca2fb4a7
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/clinic.rsRust102 lines3.6 KB
1
use axum::http::{StatusCode, header};
2
use serde_json::json;
3
4
use super::{
5
    Floor, Outcome, Run,
6
    balance::{UNMEASURED_SHAME, UNMEASURED_SHAME_PER_INCH},
7
    credentials::{Scheme, etag_matches, parse_credentials},
8
    measure::{round2, volume},
9
};
10
use crate::{
11
    headers::{SERVER_TIMING, X_DIAGNOSIS},
12
    problem::Problem,
13
};
14
15
#[derive(Default)]
16
pub struct Clinic {
17
    pub(crate) intake_attempts: usize,
18
    pub(crate) measured: bool,
19
    pub(crate) prescribed: bool,
20
    pub(crate) cured: bool,
21
}
22
23
const INTAKE: [&str; 3] = [
24
    "The clinic does not accept excuses.",
25
    "The clinic accepts one token. You have not been carrying it.",
26
    "Be honest.",
27
];
28
29
impl Run {
30
    pub fn diagnose(
31
        &mut self,
32
        authorization: Option<&str>,
33
        if_none_match: Option<&str>,
34
    ) -> Result<Outcome, Problem> {
35
        if self.floor != Floor::Clinic {
36
            let floors = Floor::Clinic.number() - self.floor.number();
37
            return Err(Problem::new(
38
                StatusCode::TOO_EARLY,
39
                "no-appointment",
40
                format!("The clinic's next available appointment is {floors} floors away."),
41
            ));
42
        }
43
        let intake = Problem::new(
44
            StatusCode::UNAUTHORIZED,
45
            "intake",
46
            "The clinic requires authorisation.",
47
        );
48
        let Some(authorization) = authorization else {
49
            return Err(intake.header(header::WWW_AUTHENTICATE, "Bearer realm=\"clinic\""));
50
        };
51
        let truthful = parse_credentials(authorization, Scheme::Bearer)
52
            .is_some_and(|token| token.eq_ignore_ascii_case("honesty"));
53
        if !truthful {
54
            let hint = INTAKE[self.clinic.intake_attempts.min(INTAKE.len() - 1)];
55
            self.clinic.intake_attempts += 1;
56
            return Err(intake.header(
57
                header::WWW_AUTHENTICATE,
58
                format!(
59
                    "Bearer realm=\"clinic\", error=\"invalid_token\", error_description=\"{hint}\""
60
                ),
61
            ));
62
        }
63
64
        self.body.conditions.peyronie = false;
65
        self.body.conditions.priapism_turns = 0;
66
        let etag = self.origin_etag();
67
        if if_none_match.is_some_and(|inm| etag_matches(inm, &etag)) {
68
            self.clinic.cured = true;
69
            return Ok(Outcome::empty(StatusCode::NOT_MODIFIED)
70
                .header(header::ETAG, etag)
71
                .header(X_DIAGNOSIS, "normal"));
72
        }
73
        if !self.clinic.measured {
74
            self.clinic.measured = true;
75
            if !self.items.kit.ruler {
76
                self.standing.shame += UNMEASURED_SHAME
77
                    + (self.body.inflation * UNMEASURED_SHAME_PER_INCH).round() as u32;
78
            }
79
        }
80
        let volume = volume(self.body.length, self.body.girth);
81
        let foreskin = self.body.foreskin.unwrap_or(0.0);
82
        let timing = format!(
83
            "length;dur={:.2}, girth;dur={:.2}, foreskin;dur={foreskin:.2}, volume;desc=\"cubic inches\";dur={volume:.2}",
84
            self.body.length, self.body.girth
85
        );
86
        self.clinic.prescribed = true;
87
        self.touch();
88
        Ok(Outcome::json(
89
            StatusCode::OK,
90
            json!({
91
                "diagnosis": "within normal range",
92
                "detail": "Congratulations. You have been cured of a condition you never had. The run is over.",
93
                "measured_length": round2(self.body.length),
94
                "measured_girth": round2(self.body.girth),
95
                "volume": round2(volume),
96
                "prescription": "Please stop.",
97
            }),
98
        )
99
        .header(header::ETAG, etag)
100
        .header(SERVER_TIMING, timing))
101
    }
102
}