use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SessionHandle(pub String);
impl SessionHandle {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SnapshotId(pub String);
impl SnapshotId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecOutput {
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
pub duration: Duration,
pub timed_out: bool,
}
impl ExecOutput {
pub fn new(
stdout: impl Into<String>,
stderr: impl Into<String>,
exit_code: i32,
duration: Duration,
timed_out: bool,
) -> Self {
Self { stdout: stdout.into(), stderr: stderr.into(), exit_code, duration, timed_out }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DirEntry {
pub name: String,
#[serde(rename = "type")]
pub entry_type: EntryType,
}
impl DirEntry {
pub fn new(name: impl Into<String>, entry_type: EntryType) -> Self {
Self { name: name.into(), entry_type }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntryType {
File,
Directory,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn session_handle_equality() {
let a = SessionHandle("session-1".to_string());
let b = SessionHandle("session-1".to_string());
let c = SessionHandle("session-2".to_string());
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn snapshot_id_equality() {
let a = SnapshotId("snap-1".to_string());
let b = SnapshotId("snap-1".to_string());
let c = SnapshotId("snap-2".to_string());
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn session_handle_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(SessionHandle("a".to_string()));
set.insert(SessionHandle("b".to_string()));
set.insert(SessionHandle("a".to_string()));
assert_eq!(set.len(), 2);
}
#[test]
fn snapshot_id_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(SnapshotId("x".to_string()));
set.insert(SnapshotId("y".to_string()));
set.insert(SnapshotId("x".to_string()));
assert_eq!(set.len(), 2);
}
#[test]
fn exec_output_serialization_roundtrip() {
let output = ExecOutput {
stdout: "hello".to_string(),
stderr: "warn".to_string(),
exit_code: 1,
duration: Duration::from_millis(500),
timed_out: false,
};
let json = serde_json::to_string(&output).unwrap();
let deserialized: ExecOutput = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.stdout, "hello");
assert_eq!(deserialized.stderr, "warn");
assert_eq!(deserialized.exit_code, 1);
assert_eq!(deserialized.duration, Duration::from_millis(500));
assert!(!deserialized.timed_out);
}
#[test]
fn dir_entry_serialization_roundtrip() {
let entry = DirEntry { name: "main.rs".to_string(), entry_type: EntryType::File };
let json = serde_json::to_string(&entry).unwrap();
assert!(json.contains(r#""type":"file""#));
let deserialized: DirEntry = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, entry);
}
#[test]
fn entry_type_serialization() {
let file_json = serde_json::to_string(&EntryType::File).unwrap();
let dir_json = serde_json::to_string(&EntryType::Directory).unwrap();
assert_eq!(file_json, r#""file""#);
assert_eq!(dir_json, r#""directory""#);
}
#[test]
fn session_handle_serialization_roundtrip() {
let handle = SessionHandle("test-session".to_string());
let json = serde_json::to_string(&handle).unwrap();
let deserialized: SessionHandle = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, handle);
}
#[test]
fn snapshot_id_serialization_roundtrip() {
let id = SnapshotId("test-snapshot".to_string());
let json = serde_json::to_string(&id).unwrap();
let deserialized: SnapshotId = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, id);
}
}