use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "command", rename_all = "snake_case")]
#[non_exhaustive]
pub enum Command {
Hello,
Login {
user_name: String,
#[serde(default)]
password: String,
},
Logout,
LoadFile {
path: String,
},
Start {
#[serde(default = "main_sequence")]
sequence: String,
},
Run {
sequence_file: String,
#[serde(default = "main_sequence")]
sequence: String,
},
Terminate {
execution_id: i32,
},
ReadValue {
execution_id: i32,
lookup: String,
},
Shutdown,
}
fn main_sequence() -> String {
"MainSequence".to_owned()
}
impl Command {
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
Self::Hello => "hello",
Self::Login { .. } => "login",
Self::Logout => "logout",
Self::LoadFile { .. } => "load_file",
Self::Start { .. } => "start",
Self::Run { .. } => "run",
Self::Terminate { .. } => "terminate",
Self::ReadValue { .. } => "read_value",
Self::Shutdown => "shutdown",
}
}
#[must_use]
pub const fn is_control(&self) -> bool {
matches!(
self,
Self::Run { .. }
| Self::Start { .. }
| Self::Terminate { .. }
| Self::Login { .. }
| Self::Logout
| Self::Shutdown
)
}
}
#[cfg(test)]
mod tests {
use super::Command;
#[test]
fn the_wire_form_is_tagged_by_a_command_field() {
let text = serde_json::to_string(&Command::Hello).unwrap_or_default();
assert_eq!(text, r#"{"command":"hello"}"#);
}
#[test]
fn a_run_may_omit_the_sequence_name() {
let parsed: Command =
serde_json::from_str(r#"{"command":"run","sequence_file":"C:\\x.seq"}"#)
.unwrap_or(Command::Shutdown);
assert_eq!(
parsed,
Command::Run {
sequence_file: r"C:\x.seq".to_owned(),
sequence: "MainSequence".to_owned(),
}
);
}
#[test]
fn commands_round_trip() {
for command in [
Command::Hello,
Command::Terminate { execution_id: 1 },
Command::ReadValue {
execution_id: 1,
lookup: "Locals.Result".to_owned(),
},
Command::Shutdown,
] {
let text = serde_json::to_string(&command).unwrap_or_default();
let back: Command = serde_json::from_str(&text).unwrap_or(Command::Hello);
assert_eq!(back, command, "round trip failed for {}", command.name());
}
}
#[test]
fn reads_are_not_control_but_runs_are() {
assert!(!Command::Hello.is_control());
assert!(
!Command::ReadValue {
execution_id: 1,
lookup: "Locals".to_owned()
}
.is_control()
);
assert!(Command::Terminate { execution_id: 1 }.is_control());
assert!(Command::Shutdown.is_control());
}
}