use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum ExitInfo {
Signal { exit_code: i32, signal: String },
Panic {
exit_code: i32,
message: String,
location: String,
},
Error { exit_code: i32, message: String },
}
impl ExitInfo {
pub fn from_file(path: &Path) -> Option<Self> {
let content = std::fs::read_to_string(path).ok()?;
serde_json::from_str(&content).ok()
}
pub fn exit_code(&self) -> i32 {
match self {
ExitInfo::Signal { exit_code, .. } => *exit_code,
ExitInfo::Panic { exit_code, .. } => *exit_code,
ExitInfo::Error { exit_code, .. } => *exit_code,
}
}
pub fn signal_name(&self) -> Option<&str> {
match self {
ExitInfo::Signal { signal, .. } => Some(signal),
ExitInfo::Panic { .. } | ExitInfo::Error { .. } => None,
}
}
pub fn panic_message(&self) -> Option<&str> {
match self {
ExitInfo::Panic { message, .. } => Some(message),
ExitInfo::Signal { .. } | ExitInfo::Error { .. } => None,
}
}
pub fn error_message(&self) -> Option<&str> {
match self {
ExitInfo::Error { message, .. } => Some(message),
ExitInfo::Signal { .. } | ExitInfo::Panic { .. } => None,
}
}
pub fn is_signal(&self) -> bool {
matches!(self, ExitInfo::Signal { .. })
}
pub fn is_panic(&self) -> bool {
matches!(self, ExitInfo::Panic { .. })
}
pub fn is_error(&self) -> bool {
matches!(self, ExitInfo::Error { .. })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signal_serialization() {
let info = ExitInfo::Signal {
exit_code: 134,
signal: "SIGABRT".to_string(),
};
let json = serde_json::to_string(&info).unwrap();
assert!(json.contains(r#""type":"signal""#));
assert!(json.contains(r#""exit_code":134"#));
assert!(json.contains(r#""signal":"SIGABRT""#));
let parsed: ExitInfo = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.exit_code(), 134);
assert_eq!(parsed.signal_name(), Some("SIGABRT"));
assert!(parsed.is_signal());
}
#[test]
fn test_panic_serialization() {
let info = ExitInfo::Panic {
exit_code: 101,
message: "explicit panic".to_string(),
location: "main.rs:42:5".to_string(),
};
let json = serde_json::to_string(&info).unwrap();
assert!(json.contains(r#""type":"panic""#));
assert!(json.contains(r#""exit_code":101"#));
assert!(json.contains(r#""message":"explicit panic""#));
let parsed: ExitInfo = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.exit_code(), 101);
assert_eq!(parsed.panic_message(), Some("explicit panic"));
assert!(parsed.is_panic());
}
#[test]
fn test_from_file_not_found() {
let result = ExitInfo::from_file(Path::new("/nonexistent/path"));
assert!(result.is_none());
}
#[test]
fn test_from_file_invalid_json() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("exit");
std::fs::write(&path, "not valid json").unwrap();
let result = ExitInfo::from_file(&path);
assert!(result.is_none());
}
#[test]
fn test_from_file_valid() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("exit");
std::fs::write(
&path,
r#"{"type":"signal","exit_code":134,"signal":"SIGABRT"}"#,
)
.unwrap();
let result = ExitInfo::from_file(&path);
assert!(result.is_some());
let info = result.unwrap();
assert_eq!(info.exit_code(), 134);
assert_eq!(info.signal_name(), Some("SIGABRT"));
}
#[test]
fn test_error_serialization() {
let info = ExitInfo::Error {
exit_code: 1,
message: "Failed to create VM instance".to_string(),
};
let json = serde_json::to_string(&info).unwrap();
assert!(json.contains(r#""type":"error""#));
assert!(json.contains(r#""exit_code":1"#));
assert!(json.contains(r#""message":"Failed to create VM instance""#));
let parsed: ExitInfo = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.exit_code(), 1);
assert_eq!(parsed.error_message(), Some("Failed to create VM instance"));
assert!(parsed.is_error());
}
#[test]
fn test_error_from_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("exit");
std::fs::write(
&path,
r#"{"type":"error","exit_code":1,"message":"test error"}"#,
)
.unwrap();
let result = ExitInfo::from_file(&path);
assert!(result.is_some());
let info = result.unwrap();
assert_eq!(info.exit_code(), 1);
assert_eq!(info.error_message(), Some("test error"));
assert!(info.is_error());
}
}