an http based roguelike rpg about jelqing
| 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 | } |