an http based roguelike rpg about jelqing
Blame: src/game/clinic.rs
782069860236 | atagen | 2026-10-10 12:44 UTC | 1 | use axum::http::{StatusCode, header}; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 2 | use serde_json::json; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 3 | |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 4 | use super::{Floor, Outcome, Run, body::volume, etag_matches, parse_credentials, round2}; |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 5 | use crate::{ |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 6 | headers::{SERVER_TIMING, X_DIAGNOSIS}, |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 7 | problem::Problem, |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 8 | }; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 9 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 10 | const INTAKE: [&str; 3] = [ |
782069860236 | atagen | 2026-10-10 12:44 UTC | 11 | "The clinic does not accept excuses.", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 12 | "The clinic accepts one token. You have not been carrying it.", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 13 | "Be honest.", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 14 | ]; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 15 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 16 | impl Run { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 17 | pub fn diagnose( |
782069860236 | atagen | 2026-10-10 12:44 UTC | 18 | &mut self, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 19 | authorization: Option<&str>, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 20 | if_none_match: Option<&str>, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 21 | ) -> Result<Outcome, Problem> { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 22 | if self.floor != Floor::Clinic { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 23 | let floors = Floor::Clinic.number() - self.floor.number(); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 24 | return Err(Problem::new( |
782069860236 | atagen | 2026-10-10 12:44 UTC | 25 | StatusCode::TOO_EARLY, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 26 | "no-appointment", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 27 | format!("The clinic's next available appointment is {floors} floors away."), |
782069860236 | atagen | 2026-10-10 12:44 UTC | 28 | )); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 29 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 30 | let intake = Problem::new( |
782069860236 | atagen | 2026-10-10 12:44 UTC | 31 | StatusCode::UNAUTHORIZED, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 32 | "intake", |
9683736a2c5e | atagen | 2026-10-10 13:15 UTC | 33 | "The clinic requires authorisation.", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 34 | ); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 35 | let Some(authorization) = authorization else { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 36 | return Err(intake.header(header::WWW_AUTHENTICATE, "Bearer realm=\"clinic\"")); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 37 | }; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 38 | let truthful = parse_credentials(authorization, "bearer") |
782069860236 | atagen | 2026-10-10 12:44 UTC | 39 | .is_some_and(|token| token.eq_ignore_ascii_case("honesty")); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 40 | if !truthful { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 41 | let hint = INTAKE[self.clinic.intake_attempts.min(INTAKE.len() - 1)]; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 42 | self.clinic.intake_attempts += 1; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 43 | return Err(intake.header( |
782069860236 | atagen | 2026-10-10 12:44 UTC | 44 | header::WWW_AUTHENTICATE, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 45 | format!( |
782069860236 | atagen | 2026-10-10 12:44 UTC | 46 | "Bearer realm=\"clinic\", error=\"invalid_token\", error_description=\"{hint}\"" |
782069860236 | atagen | 2026-10-10 12:44 UTC | 47 | ), |
782069860236 | atagen | 2026-10-10 12:44 UTC | 48 | )); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 49 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 50 | |
782069860236 | atagen | 2026-10-10 12:44 UTC | 51 | self.conditions.peyronie = false; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 52 | self.conditions.priapism_turns = 0; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 53 | let etag = self.origin_etag(); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 54 | if if_none_match.is_some_and(|inm| etag_matches(inm, &etag)) { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 55 | self.clinic.cured = true; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 56 | return Ok(Outcome::empty(StatusCode::NOT_MODIFIED) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 57 | .header(header::ETAG, etag) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 58 | .header(X_DIAGNOSIS, "normal")); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 59 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 60 | if !self.clinic.measured { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 61 | self.clinic.measured = true; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 62 | if !self.items.ruler { |
782069860236 | atagen | 2026-10-10 12:44 UTC | 63 | self.shame += 15; |
782069860236 | atagen | 2026-10-10 12:44 UTC | 64 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 65 | } |
159f0998263b | atagen | 2026-10-12 11:01 UTC | 66 | let volume = volume(self.length, self.girth); |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 67 | let foreskin = self.foreskin.unwrap_or(0.0); |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 68 | let timing = format!( |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 69 | "length;dur={:.2}, girth;dur={:.2}, foreskin;dur={foreskin:.2}, volume;desc=\"cubic inches\";dur={volume:.2}", |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 70 | self.length, self.girth |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 71 | ); |
c4d70621d377 | atagen | 2026-10-12 11:56 UTC | 72 | self.clinic.prescribed = true; |
c4d70621d377 | atagen | 2026-10-12 11:56 UTC | 73 | self.touch(); |
782069860236 | atagen | 2026-10-10 12:44 UTC | 74 | Ok(Outcome::json( |
782069860236 | atagen | 2026-10-10 12:44 UTC | 75 | StatusCode::OK, |
782069860236 | atagen | 2026-10-10 12:44 UTC | 76 | json!({ |
782069860236 | atagen | 2026-10-10 12:44 UTC | 77 | "diagnosis": "within normal range", |
c4d70621d377 | atagen | 2026-10-12 11:56 UTC | 78 | "detail": "Congratulations. You have been cured of a condition you never had. The run is over.", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 79 | "measured_length": round2(self.length), |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 80 | "measured_girth": round2(self.girth), |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 81 | "volume": round2(volume), |
782069860236 | atagen | 2026-10-10 12:44 UTC | 82 | "prescription": "Please stop.", |
782069860236 | atagen | 2026-10-10 12:44 UTC | 83 | }), |
782069860236 | atagen | 2026-10-10 12:44 UTC | 84 | ) |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 85 | .header(header::ETAG, etag) |
058650ec79d6 | atagen | 2026-10-12 10:49 UTC | 86 | .header(SERVER_TIMING, timing)) |
782069860236 | atagen | 2026-10-10 12:44 UTC | 87 | } |
782069860236 | atagen | 2026-10-10 12:44 UTC | 88 | } |