g1t/crates/contracts/src/outcome.rs

95 lines2,726 bytesCodeBlame
1use serde::de::DeserializeOwned;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "snake_case")]
6pub enum FailureCode {
7 NotFound,
8 Forbidden,
9 Unauthenticated,
10 Conflict,
11 Invalid,
12}
13
14impl FailureCode {
15 pub fn http_status(self) -> u16 {
16 match self {
17 FailureCode::NotFound => 404,
18 FailureCode::Forbidden => 403,
19 FailureCode::Unauthenticated => 401,
20 FailureCode::Conflict => 409,
21 FailureCode::Invalid => 422,
22 }
23 }
24}
25
26#[derive(Clone, Debug, Serialize, Deserialize)]
27pub struct Failure {
28 pub code: FailureCode,
29 pub message: String,
30}
31
32/// The result of a service call. Expected failures cross service boundaries
33/// as values, so callers have to handle them.
34///
35/// On the wire: `{"ok": true, "value": …}` or `{"ok": false, "error": …}`.
36#[derive(Clone, Debug)]
37pub enum Outcome<T> {
38 Ok(T),
39 Fail(Failure),
40}
41
42impl<T> Outcome<T> {
43 pub fn fail(code: FailureCode, message: impl Into<String>) -> Self {
44 Outcome::Fail(Failure {
45 code,
46 message: message.into(),
47 })
48 }
49
50 pub fn into_result(self) -> Result<T, Failure> {
51 match self {
52 Outcome::Ok(value) => Ok(value),
53 Outcome::Fail(failure) => Err(failure),
54 }
55 }
56}
57
58#[derive(Serialize, Deserialize)]
59#[serde(bound(serialize = "T: Serialize", deserialize = "T: DeserializeOwned"))]
60struct Wire<T> {
61 ok: bool,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
63 value: Option<T>,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
65 error: Option<Failure>,
66}
67
68impl<T: Serialize> Serialize for Outcome<T> {
69 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
70 match self {
71 Outcome::Ok(value) => Wire {
72 ok: true,
73 value: Some(value),
74 error: None,
75 },
76 Outcome::Fail(failure) => Wire {
77 ok: false,
78 value: None,
79 error: Some(failure.clone()),
80 },
81 }
82 .serialize(serializer)
83 }
84}
85
86impl<'de, T: DeserializeOwned> Deserialize<'de> for Outcome<T> {
87 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
88 let wire = Wire::<T>::deserialize(deserializer)?;
89 match (wire.ok, wire.value, wire.error) {
90 (true, Some(value), _) => Ok(Outcome::Ok(value)),
91 (false, _, Some(error)) => Ok(Outcome::Fail(error)),
92 _ => Err(serde::de::Error::custom("malformed outcome")),
93 }
94 }
95}