Skip to main content

artifactize_core/agent/session/
outcome.rs

1//! Mutually exclusive saved success/failure envelopes, with the original JSON keys.
2use crate::agent::verdict::ValidatedResult;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Debug, Clone, Default)]
7pub enum End {
8    #[default]
9    Empty,
10    /// The review's typed verdict; only the owner-defined fields stay JSON.
11    Completed(ValidatedResult),
12    Failed {
13        code: Option<crate::agent::error::Code>,
14        message: Option<String>,
15    },
16}
17#[derive(Debug, Clone, Default)]
18pub enum Answer {
19    #[default]
20    Empty,
21    Completed(String),
22    Failed {
23        code: Option<crate::agent::error::Code>,
24        message: Option<String>,
25    },
26}
27#[derive(Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29struct Wire<T> {
30    #[serde(skip_serializing_if = "Option::is_none")]
31    result: Option<T>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    text: Option<String>,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    error_code: Option<crate::agent::error::Code>,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    error: Option<String>,
38}
39// Every field is optional, whatever the result type.
40impl<T> Default for Wire<T> {
41    fn default() -> Self {
42        Self {
43            result: None,
44            text: None,
45            error_code: None,
46            error: None,
47        }
48    }
49}
50impl End {
51    pub fn result(&self) -> Option<&ValidatedResult> {
52        if let Self::Completed(value) = self {
53            Some(value)
54        } else {
55            None
56        }
57    }
58    pub fn failure(&self) -> Option<(Option<crate::agent::error::Code>, &str)> {
59        if let Self::Failed {
60            code,
61            message: Some(message),
62        } = self
63        {
64            Some((*code, message))
65        } else {
66            None
67        }
68    }
69}
70impl Answer {
71    pub fn text(&self) -> Option<&str> {
72        if let Self::Completed(value) = self {
73            Some(value)
74        } else {
75            None
76        }
77    }
78    pub fn failure(&self) -> Option<(Option<crate::agent::error::Code>, &str)> {
79        if let Self::Failed {
80            code,
81            message: Some(message),
82        } = self
83        {
84            Some((*code, message))
85        } else {
86            None
87        }
88    }
89}
90impl Serialize for End {
91    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
92        let wire = match self {
93            Self::Empty => Wire::default(),
94            Self::Completed(value) => Wire {
95                result: Some(value.clone()),
96                ..Wire::default()
97            },
98            Self::Failed { code, message } => Wire {
99                error_code: *code,
100                error: message.clone(),
101                ..Wire::default()
102            },
103        };
104        wire.serialize(serializer)
105    }
106}
107impl<'de> Deserialize<'de> for End {
108    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
109        let wire = Wire::<ValidatedResult>::deserialize(deserializer)?;
110        match (wire.result, wire.error_code, wire.error) {
111            (Some(value), None, None) => Ok(Self::Completed(value)),
112            (Some(_), _, _) => Err(serde::de::Error::custom(
113                "review end cannot succeed and fail",
114            )),
115            (None, None, None) => Ok(Self::Empty),
116            (None, code, message) => Ok(Self::Failed { code, message }),
117        }
118    }
119}
120impl Serialize for Answer {
121    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
122        let wire: Wire<Value> = match self {
123            Self::Empty => Wire::default(),
124            Self::Completed(value) => Wire {
125                text: Some(value.clone()),
126                ..Wire::default()
127            },
128            Self::Failed { code, message } => Wire {
129                error_code: *code,
130                error: message.clone(),
131                ..Wire::default()
132            },
133        };
134        wire.serialize(serializer)
135    }
136}
137impl<'de> Deserialize<'de> for Answer {
138    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
139        let wire = Wire::<Value>::deserialize(deserializer)?;
140        match (wire.text, wire.error_code, wire.error) {
141            (Some(value), None, None) => Ok(Self::Completed(value)),
142            (Some(_), _, _) => Err(serde::de::Error::custom("answer cannot succeed and fail")),
143            (None, None, None) => Ok(Self::Empty),
144            (None, code, message) => Ok(Self::Failed { code, message }),
145        }
146    }
147}
148#[cfg(test)]
149mod tests {
150    use super::*;
151    #[test]
152    fn original_success_error_and_empty_envelopes_round_trip() {
153        use crate::agent::error::Code;
154        use serde_json::json;
155
156        let empty: End = serde_json::from_value(json!({})).unwrap();
157        assert!(matches!(empty, End::Empty));
158        let completed: End = serde_json::from_value(json!({"result":{"verdict":"GREEN"}})).unwrap();
159        assert_eq!(completed.result().unwrap().verdict.as_str(), "GREEN");
160        let failed: End =
161            serde_json::from_value(json!({"errorCode":"TIMEOUT","error":"timed out"})).unwrap();
162        assert_eq!(failed.failure(), Some((Some(Code::Timeout), "timed out")));
163        for (end, expected) in [
164            (empty, json!({})),
165            (completed, json!({"result":{"verdict":"GREEN"}})),
166            (failed, json!({"errorCode":"TIMEOUT","error":"timed out"})),
167        ] {
168            assert_eq!(serde_json::to_value(end).unwrap(), expected);
169        }
170
171        let empty: Answer = serde_json::from_value(json!({})).unwrap();
172        assert!(matches!(empty, Answer::Empty));
173        let completed: Answer = serde_json::from_value(json!({"text":"answer"})).unwrap();
174        assert_eq!(completed.text(), Some("answer"));
175        let failed: Answer =
176            serde_json::from_value(json!({"errorCode":"TIMEOUT","error":"timed out"})).unwrap();
177        assert_eq!(failed.failure(), Some((Some(Code::Timeout), "timed out")));
178        for (answer, expected) in [
179            (empty, json!({})),
180            (completed, json!({"text":"answer"})),
181            (failed, json!({"errorCode":"TIMEOUT","error":"timed out"})),
182        ] {
183            assert_eq!(serde_json::to_value(answer).unwrap(), expected);
184        }
185        assert!(
186            serde_json::from_value::<Answer>(serde_json::json!({"text":"answer","error":"failed"}))
187                .is_err()
188        );
189        assert!(
190            serde_json::from_value::<End>(
191                serde_json::json!({"result":{"verdict":"RED"},"error":"failed"})
192            )
193            .is_err()
194        );
195        assert!(serde_json::from_value::<End>(serde_json::json!({"result":{}})).is_err());
196    }
197}