use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "response", rename_all = "snake_case")]
#[non_exhaustive]
pub enum Response {
Hello {
engine: String,
is_64bit: bool,
},
Started {
execution_id: i32,
},
Done {
command: String,
},
LoggedIn {
user_name: String,
full_name: String,
},
Loaded {
path: String,
sequences: i32,
},
Value {
lookup: String,
value: String,
},
Failed {
command: String,
reason: String,
},
}
impl Response {
#[must_use]
pub const fn is_failure(&self) -> bool {
matches!(self, Self::Failed { .. })
}
}
#[cfg(test)]
mod tests {
use super::Response;
#[test]
fn the_wire_form_is_tagged_by_a_response_field() {
let text =
serde_json::to_string(&Response::Started { execution_id: 7 }).unwrap_or_default();
assert_eq!(text, r#"{"response":"started","execution_id":7}"#);
}
#[test]
fn a_failure_carries_the_reason_rather_than_just_a_flag() {
let failure = Response::Failed {
command: "terminate".to_owned(),
reason: "no such execution".to_owned(),
};
assert!(failure.is_failure());
let text = serde_json::to_string(&failure).unwrap_or_default();
assert!(text.contains("no such execution"), "{text}");
}
#[test]
fn responses_round_trip() {
for response in [
Response::Hello {
engine: "2026 Q1".to_owned(),
is_64bit: true,
},
Response::Done {
command: "shutdown".to_owned(),
},
Response::Value {
lookup: "Locals.Result".to_owned(),
value: r#"{"a":1}"#.to_owned(),
},
] {
let text = serde_json::to_string(&response).unwrap_or_default();
let back: Response = serde_json::from_str(&text).unwrap_or(Response::Done {
command: String::new(),
});
assert_eq!(back, response);
}
}
}