steamroom_cli/daemon/proto/
mod.rs1mod event;
5mod frame;
6mod params;
7mod request;
8mod response;
9mod status;
10
11pub use event::Event;
12pub use frame::Frame;
13pub use frame::PROTO_VERSION;
14pub use params::*;
15pub use request::Request;
16pub use response::ErrorKind;
17pub use response::Response;
18pub use status::JobRecord;
19pub use status::StatusSnapshot;
20
21use rkyv::Archive;
22use rkyv::Deserialize;
23use rkyv::Serialize;
24
25#[derive(
28 Archive,
29 Serialize,
30 Deserialize,
31 Debug,
32 Clone,
33 Copy,
34 PartialEq,
35 Eq,
36 Hash,
37 serde::Serialize,
38 serde::Deserialize,
39)]
40#[rkyv(derive(Debug))]
41pub struct JobId(pub u64);
42
43impl std::fmt::Display for JobId {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 write!(f, "#{}", self.0)
46 }
47}
48
49#[derive(
52 Archive,
53 Serialize,
54 Deserialize,
55 Debug,
56 Clone,
57 Copy,
58 PartialEq,
59 Eq,
60 serde::Serialize,
61 serde::Deserialize,
62)]
63#[rkyv(derive(Debug))]
64pub enum JobKind {
65 Download,
66 Info,
67 Files,
68 Manifests,
69 Diff,
70 Packages,
71 SaveManifest,
72 Workshop,
73 LocalInfo,
74}
75
76#[derive(Archive, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
80#[rkyv(derive(Debug))]
81pub enum OutputFormat {
82 Table,
83 Json,
84 Plain,
85}
86
87impl From<crate::cli::OutputFormat> for OutputFormat {
88 fn from(v: crate::cli::OutputFormat) -> Self {
89 match v {
90 crate::cli::OutputFormat::Table => Self::Table,
91 crate::cli::OutputFormat::Json => Self::Json,
92 crate::cli::OutputFormat::Plain => Self::Plain,
93 }
94 }
95}
96
97impl From<OutputFormat> for crate::cli::OutputFormat {
98 fn from(v: OutputFormat) -> Self {
99 match v {
100 OutputFormat::Table => Self::Table,
101 OutputFormat::Json => Self::Json,
102 OutputFormat::Plain => Self::Plain,
103 }
104 }
105}
106
107#[derive(Archive, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
110#[rkyv(derive(Debug))]
111pub enum LogLevel {
112 Error,
113 Warn,
114 Info,
115 Debug,
116 Trace,
117}
118
119impl From<tracing::Level> for LogLevel {
120 fn from(l: tracing::Level) -> Self {
121 match l {
122 tracing::Level::ERROR => Self::Error,
123 tracing::Level::WARN => Self::Warn,
124 tracing::Level::INFO => Self::Info,
125 tracing::Level::DEBUG => Self::Debug,
126 tracing::Level::TRACE => Self::Trace,
127 }
128 }
129}
130
131#[derive(Archive, Serialize, Deserialize, Debug, Clone, serde::Serialize, serde::Deserialize)]
133#[rkyv(derive(Debug))]
134pub struct ProgressUpdate {
135 pub bytes_done: u64,
136 pub bytes_total: u64,
137 pub files_done: u32,
138 pub files_total: u32,
139 pub rate_bytes_per_sec: u64,
140 pub eta_seconds: u32,
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn job_id_displays_with_hash_prefix() {
149 assert_eq!(format!("{}", JobId(42)), "#42");
150 }
151
152 #[test]
153 fn output_format_round_trips_through_cli_enum() {
154 for w in [OutputFormat::Table, OutputFormat::Json, OutputFormat::Plain] {
155 let cli: crate::cli::OutputFormat = w.into();
156 let back: OutputFormat = cli.into();
157 assert_eq!(w, back);
158 }
159 }
160
161 #[test]
162 fn log_level_maps_from_tracing() {
163 assert_eq!(LogLevel::from(tracing::Level::ERROR), LogLevel::Error);
164 assert_eq!(LogLevel::from(tracing::Level::TRACE), LogLevel::Trace);
165 }
166
167 #[test]
168 fn frame_round_trips_response_jobaccepted() {
169 let f = Frame::Response(Response::JobAccepted {
170 job_id: JobId(7),
171 position: 0,
172 });
173 let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&f).unwrap();
174 let back = rkyv::from_bytes::<Frame, rkyv::rancor::Error>(&bytes).unwrap();
175 match back {
176 Frame::Response(Response::JobAccepted { job_id, position }) => {
177 assert_eq!(job_id, JobId(7));
178 assert_eq!(position, 0);
179 }
180 other => panic!("wrong frame: {other:?}"),
181 }
182 }
183
184 #[test]
185 fn frame_round_trips_event_log() {
186 let f = Frame::Event(Event::Log {
187 job_id: Some(JobId(3)),
188 level: LogLevel::Warn,
189 target: "steamroom_cli".into(),
190 message: "stale".into(),
191 });
192 let bytes = rkyv::to_bytes::<rkyv::rancor::Error>(&f).unwrap();
193 let _back = rkyv::from_bytes::<Frame, rkyv::rancor::Error>(&bytes).unwrap();
194 }
195
196 #[test]
197 fn event_job_id_routes_correctly() {
198 let scoped = Event::Stdout {
199 job_id: JobId(9),
200 line: "x".into(),
201 };
202 assert_eq!(scoped.job_id(), Some(JobId(9)));
203 let qc = Event::QueueChanged {
204 snapshot: StatusSnapshot {
205 daemon_pid: 1,
206 daemon_started_at: 0,
207 account: None,
208 active: None,
209 queue: vec![],
210 recent: vec![],
211 },
212 };
213 assert_eq!(qc.job_id(), None);
214 }
215}