atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
53bce0d930007b21fb1f130865979bbd959edcc5
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/game/outcome.rsRust100 lines2.6 KB
1
use axum::http::{HeaderName, StatusCode, header};
2
use serde_json::{Value, json};
3
4
use crate::link::{self, Link};
5
6
pub enum Payload {
7
    Empty,
8
    Json(Value),
9
    Raw(&'static str, Vec<u8>),
10
}
11
12
/// One way out of a 300, offered in the body and in `Link` alike.
13
pub struct Choice {
14
    pub(crate) href: String,
15
    pub(crate) method: &'static str,
16
    pub(crate) title: &'static str,
17
}
18
19
/// A successful result of a game action, before it becomes an HTTP response.
20
pub struct Outcome {
21
    status: StatusCode,
22
    payload: Payload,
23
    headers: Vec<(HeaderName, String)>,
24
}
25
26
impl Outcome {
27
    pub fn empty(status: StatusCode) -> Self {
28
        Self {
29
            status,
30
            payload: Payload::Empty,
31
            headers: Vec::new(),
32
        }
33
    }
34
35
    pub fn json(status: StatusCode, body: Value) -> Self {
36
        Self {
37
            payload: Payload::Json(body),
38
            ..Self::empty(status)
39
        }
40
    }
41
42
    pub fn raw(status: StatusCode, content_type: &'static str, body: impl Into<Vec<u8>>) -> Self {
43
        Self {
44
            payload: Payload::Raw(content_type, body.into()),
45
            ..Self::empty(status)
46
        }
47
    }
48
49
    pub fn choices(detail: &str, choices: &[Choice]) -> Self {
50
        let listed: Vec<Value> = choices
51
            .iter()
52
            .map(|c| json!({ "href": c.href, "method": c.method, "title": c.title }))
53
            .collect();
54
        let links = link::join(choices.iter().map(|c| {
55
            Link::registered(&c.href, "alternate")
56
                .title(c.title)
57
                .allow(c.method)
58
        }));
59
        Self::json(
60
            StatusCode::MULTIPLE_CHOICES,
61
            json!({ "detail": detail, "choices": listed }),
62
        )
63
        .header(header::LINK, links)
64
    }
65
66
    pub fn header(mut self, name: HeaderName, value: impl Into<String>) -> Self {
67
        self.headers.push((name, value.into()));
68
        self
69
    }
70
71
    pub fn status(&self) -> StatusCode {
72
        self.status
73
    }
74
75
    pub fn body(&self) -> Option<&Value> {
76
        match &self.payload {
77
            Payload::Json(body) => Some(body),
78
            _ => None,
79
        }
80
    }
81
82
    pub fn header_value(&self, name: &HeaderName) -> Option<&str> {
83
        self.headers
84
            .iter()
85
            .find(|(n, _)| n == name)
86
            .map(|(_, v)| v.as_str())
87
    }
88
89
    pub(super) fn retold(mut self, status: StatusCode, detail: &str) -> Self {
90
        self.status = status;
91
        if let Payload::Json(body) = &mut self.payload {
92
            body["detail"] = Value::from(detail);
93
        }
94
        self
95
    }
96
97
    pub fn into_parts(self) -> (StatusCode, Payload, Vec<(HeaderName, String)>) {
98
        (self.status, self.payload, self.headers)
99
    }
100
}