1#![warn(missing_docs)]
16
17pub const PROTOCOL_VERSION: &str = "0.1.0";
19
20#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
22#[non_exhaustive]
23pub enum HostCommand {
24 Init {
26 step_budget: u64,
28 #[serde(default)]
33 allowed_hosts: Vec<String>,
34 },
35 Run {
37 input: String,
39 },
40 StartRun {
42 input: String,
44 },
45 PollRun,
47 Signal {
49 signal: String,
51 },
52 Cancel,
54 Mount {
56 path: String,
58 },
59 ReadFile {
61 path: String,
63 },
64 WriteFile {
66 path: String,
68 data: Vec<u8>,
70 },
71 ListDir {
73 path: String,
75 },
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
80#[non_exhaustive]
81pub enum WorkerEvent {
82 Stdout(Vec<u8>),
84 Stderr(Vec<u8>),
86 Exit(i32),
88 Yielded,
90 Diagnostic(DiagnosticLevel, String),
92 FsChanged(String),
94 Version(String),
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
100#[non_exhaustive]
101pub enum DiagnosticLevel {
102 Info,
104 Warning,
106 Error,
108 Trace,
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn protocol_version() {
118 assert!(!PROTOCOL_VERSION.is_empty());
119 }
120
121 #[test]
122 fn host_command_variants() {
123 let cmd = HostCommand::Run {
124 input: "echo hello".into(),
125 };
126 assert!(matches!(cmd, HostCommand::Run { .. }));
127
128 let cmd = HostCommand::StartRun {
129 input: "echo hello".into(),
130 };
131 assert!(matches!(cmd, HostCommand::StartRun { .. }));
132
133 let cmd = HostCommand::Signal {
134 signal: "TERM".into(),
135 };
136 assert!(matches!(cmd, HostCommand::Signal { .. }));
137
138 assert_eq!(HostCommand::PollRun, HostCommand::PollRun);
139 }
140
141 #[test]
142 fn worker_event_variants() {
143 let evt = WorkerEvent::Exit(0);
144 assert_eq!(evt, WorkerEvent::Exit(0));
145
146 assert_eq!(WorkerEvent::Yielded, WorkerEvent::Yielded);
147
148 let evt = WorkerEvent::Diagnostic(DiagnosticLevel::Warning, "test".into());
149 assert!(matches!(
150 evt,
151 WorkerEvent::Diagnostic(DiagnosticLevel::Warning, _)
152 ));
153 }
154
155 #[test]
156 fn progressive_commands_roundtrip_json() {
157 let start = HostCommand::StartRun {
158 input: "echo hello".into(),
159 };
160 let encoded = serde_json::to_string(&start).unwrap();
161 assert_eq!(encoded, r#"{"StartRun":{"input":"echo hello"}}"#);
162 let decoded: HostCommand = serde_json::from_str(&encoded).unwrap();
163 assert_eq!(decoded, start);
164
165 let encoded = serde_json::to_string(&HostCommand::PollRun).unwrap();
166 assert_eq!(encoded, r#""PollRun""#);
167 let decoded: HostCommand = serde_json::from_str(&encoded).unwrap();
168 assert_eq!(decoded, HostCommand::PollRun);
169
170 let signal = HostCommand::Signal {
171 signal: "TERM".into(),
172 };
173 let encoded = serde_json::to_string(&signal).unwrap();
174 assert_eq!(encoded, r#"{"Signal":{"signal":"TERM"}}"#);
175 let decoded: HostCommand = serde_json::from_str(&encoded).unwrap();
176 assert_eq!(decoded, signal);
177 }
178
179 #[test]
180 fn yielded_event_roundtrips_json() {
181 let encoded = serde_json::to_string(&WorkerEvent::Yielded).unwrap();
182 assert_eq!(encoded, r#""Yielded""#);
183 let decoded: WorkerEvent = serde_json::from_str(&encoded).unwrap();
184 assert_eq!(decoded, WorkerEvent::Yielded);
185 }
186}