kanade_shared/wire/
command.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Serialize, Deserialize, Debug, Clone)]
4pub struct Command {
5 pub id: String,
6 pub version: String,
7 pub request_id: String,
8 pub job_id: Option<String>,
9 pub shell: Shell,
10 pub script: String,
11 pub timeout_secs: u64,
12 pub jitter_secs: Option<u64>,
13 #[serde(default)]
17 pub run_as: RunAs,
18}
19
20#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
21#[serde(rename_all = "lowercase")]
22pub enum Shell {
23 Powershell,
24 Cmd,
25}
26
27#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
43#[serde(rename_all = "snake_case")]
44pub enum RunAs {
45 #[default]
48 System,
49 User,
54 SystemGui,
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 fn sample_command() -> Command {
65 Command {
66 id: "echo-test".into(),
67 version: "1.0.0".into(),
68 request_id: "req-1".into(),
69 job_id: Some("dep-1".into()),
70 shell: Shell::Powershell,
71 script: "echo hi".into(),
72 timeout_secs: 30,
73 jitter_secs: Some(5),
74 run_as: RunAs::System,
75 }
76 }
77
78 #[test]
79 fn shell_serialises_lowercase() {
80 let json = serde_json::to_string(&Shell::Powershell).unwrap();
81 assert_eq!(json, "\"powershell\"");
82 let json = serde_json::to_string(&Shell::Cmd).unwrap();
83 assert_eq!(json, "\"cmd\"");
84 }
85
86 #[test]
87 fn run_as_serialises_snake_case() {
88 for (mode, expected) in [
89 (RunAs::System, "\"system\""),
90 (RunAs::User, "\"user\""),
91 (RunAs::SystemGui, "\"system_gui\""),
92 ] {
93 let json = serde_json::to_string(&mode).unwrap();
94 assert_eq!(json, expected, "serialise {mode:?}");
95 let back: RunAs = serde_json::from_str(expected).unwrap();
96 assert_eq!(back, mode, "round-trip {expected}");
97 }
98 }
99
100 #[test]
101 fn run_as_defaults_to_system() {
102 assert_eq!(RunAs::default(), RunAs::System);
103 }
104
105 #[test]
106 fn command_round_trips_through_json() {
107 let orig = sample_command();
108 let json = serde_json::to_string(&orig).expect("encode");
109 let decoded: Command = serde_json::from_str(&json).expect("decode");
110 assert_eq!(decoded.id, orig.id);
111 assert_eq!(decoded.version, orig.version);
112 assert_eq!(decoded.request_id, orig.request_id);
113 assert_eq!(decoded.job_id, orig.job_id);
114 assert_eq!(decoded.shell, orig.shell);
115 assert_eq!(decoded.script, orig.script);
116 assert_eq!(decoded.timeout_secs, orig.timeout_secs);
117 assert_eq!(decoded.jitter_secs, orig.jitter_secs);
118 assert_eq!(decoded.run_as, orig.run_as);
119 }
120
121 #[test]
122 fn command_round_trips_each_run_as_variant() {
123 for mode in [RunAs::System, RunAs::User, RunAs::SystemGui] {
124 let cmd = Command {
125 run_as: mode,
126 ..sample_command()
127 };
128 let json = serde_json::to_string(&cmd).unwrap();
129 let back: Command = serde_json::from_str(&json).unwrap();
130 assert_eq!(back.run_as, mode);
131 }
132 }
133
134 #[test]
135 fn command_accepts_missing_optional_fields() {
136 let json = r#"{
137 "id": "x",
138 "version": "1.0.0",
139 "request_id": "r",
140 "shell": "cmd",
141 "script": "echo",
142 "timeout_secs": 5
143 }"#;
144 let cmd: Command = serde_json::from_str(json).expect("decode");
145 assert!(cmd.job_id.is_none());
146 assert!(cmd.jitter_secs.is_none());
147 assert_eq!(cmd.shell, Shell::Cmd);
148 assert_eq!(cmd.run_as, RunAs::System);
150 }
151}