an http based roguelike rpg about jelqing
Commit 27256acf2780
tarball3 files changed+80-36
@@ -142,32 +142,13 @@ impl Run {
142142
}143143
144144
let mut xml = String::from(145
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\">\n",145
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<D:multistatus xmlns:D=\"DAV:\" xmlns:J=\"https://jelq.dev/\">\n",146146
);147147
for (training, report) in reports {148
- let _ = write!(149
- xml,150
- " <D:response>\n <D:href>{}?technique={}</D:href>\n <D:status>HTTP/1.1 {} {}</D:status>\n",151
- self.path(""),152
- training.name(),153
- report.status.as_u16(),154
- crate::problem::reason(report.status),148
+ report.write(149
+ &mut xml,150
+ &format!("{}?technique={}", self.path(""), training.name()),155151
);156
- if let Some(location) = report.location {157
- let _ = writeln!(158
- xml,159
- " <D:location><D:href>{}</D:href></D:location>",160
- escape(&location)161
- );162
- }163
- if let Some(text) = report.text {164
- let _ = writeln!(165
- xml,166
- " <D:responsedescription>{}</D:responsedescription>",167
- escape(&text)168
- );169
- }170
- xml.push_str(" </D:response>\n");171152
}172153
xml.push_str(173154
" <D:responsedescription>Each technique has been reported separately.</D:responsedescription>\n</D:multistatus>\n",@@ -183,24 +164,79 @@ impl Run {
183164
/// What one technique of a combo would have answered on its own.184165
struct Report {185166
status: StatusCode,167
+ error: Option<&'static str>,168
+ detail: Option<String>,169
+ fields: Vec<(String, String)>,186170
location: Option<String>,187
- text: Option<String>,188171
}189172
190173
impl Report {191174
fn of(result: Result<Outcome, Problem>) -> Self {192
- match result {193
- Ok(outcome) => Self {194
- status: outcome.status(),195
- location: outcome.header_value(&header::LOCATION).map(str::to_owned),196
- text: outcome.body().map(Value::to_string),197
- },198
- Err(problem) => Self {199
- status: problem.status(),200
- location: None,201
- text: Some(Value::Object(problem.document()).to_string()),202
- },175
+ let (status, error, location, body) = match result {176
+ Ok(outcome) => (177
+ outcome.status(),178
+ None,179
+ outcome.header_value(&header::LOCATION).map(str::to_owned),180
+ outcome.body().and_then(Value::as_object).cloned(),181
+ ),182
+ Err(problem) => {183
+ let mut document = problem.document();184
+ for repeated in ["type", "title", "status"] {185
+ document.remove(repeated);186
+ }187
+ (problem.status(), Some(problem.slug()), None, Some(document))188
+ }189
+ };190
+ let mut body = body.unwrap_or_default();191
+ let detail = body192
+ .remove("detail")193
+ .and_then(|detail| detail.as_str().map(str::to_owned));194
+ let fields = body195
+ .into_iter()196
+ .map(|(key, value)| match value {197
+ Value::String(text) => (key, text),198
+ other => (key, other.to_string()),199
+ })200
+ .collect();201
+ Self {202
+ status,203
+ error,204
+ detail,205
+ fields,206
+ location,207
+ }208
+ }209
+210
+ /// In RFC 4918's order, with anything the game adds after it under its own namespace.211
+ fn write(&self, xml: &mut String, href: &str) {212
+ let _ = write!(213
+ xml,214
+ " <D:response>\n <D:href>{}</D:href>\n <D:status>HTTP/1.1 {} {}</D:status>\n",215
+ escape(href),216
+ self.status.as_u16(),217
+ crate::problem::reason(self.status),218
+ );219
+ if let Some(error) = self.error {220
+ let _ = writeln!(xml, " <D:error><J:{error}/></D:error>");221
+ }222
+ if let Some(detail) = &self.detail {223
+ let _ = writeln!(224
+ xml,225
+ " <D:responsedescription>{}</D:responsedescription>",226
+ escape(detail)227
+ );228
+ }229
+ if let Some(location) = &self.location {230
+ let _ = writeln!(231
+ xml,232
+ " <D:location><D:href>{}</D:href></D:location>",233
+ escape(location)234
+ );235
+ }236
+ for (key, value) in &self.fields {237
+ let _ = writeln!(xml, " <J:{key}>{}</J:{key}>", escape(value));203238
}239
+ xml.push_str(" </D:response>\n");204240
}205241
}206242
@@ -29,6 +29,10 @@ impl Problem {
2929
}))3030
}3131
32
+ pub fn slug(&self) -> &'static str {33
+ self.0.slug34
+ }35
+3236
pub fn status(&self) -> StatusCode {3337
self.0.status3438
}@@ -411,9 +411,13 @@ async fn a_combo_tells_each_technique_in_full() {
411411
continue;412412
}413413
assert!(414
- report.contains("<D:responsedescription>{\"") && report.contains("\"detail\""),414
+ report.contains("<D:responsedescription>") && !report.contains("{\""),415415
"{report}"416416
);417
+ let failed = ["HTTP/1.1 4", "HTTP/1.1 5"]418
+ .iter()419
+ .any(|s| report.contains(s));420
+ assert_eq!(failed, report.contains("<D:error><J:"), "{report}");417421
told += 1;418422
}419423
}