1pub mod inventory;
7pub mod relay;
8pub mod ssh;
9
10use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
15pub struct RemoteOutput {
16 pub stdout: String,
17 pub stderr: String,
18 pub exit_code: i32,
19 pub duration_ms: u64,
20 pub machine_id: String,
21}
22
23impl RemoteOutput {
24 pub fn success(&self) -> bool {
26 self.exit_code == 0
27 }
28}
29
30pub trait RemoteExecutor: Send + Sync {
32 fn execute(
34 &self,
35 machine_id: &str,
36 command: &str,
37 working_dir: Option<&str>,
38 ) -> anyhow::Result<RemoteOutput>;
39
40 fn ping(&self, machine_id: &str) -> anyhow::Result<bool>;
42
43 fn executor_type(&self) -> &str;
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn test_remote_output_success() {
53 let output = RemoteOutput {
54 stdout: "hello".into(),
55 stderr: String::new(),
56 exit_code: 0,
57 duration_ms: 100,
58 machine_id: "m1".into(),
59 };
60 assert!(output.success());
61 }
62
63 #[test]
64 fn test_remote_output_failure() {
65 let output = RemoteOutput {
66 stdout: String::new(),
67 stderr: "not found".into(),
68 exit_code: 1,
69 duration_ms: 50,
70 machine_id: "m1".into(),
71 };
72 assert!(!output.success());
73 }
74
75 #[test]
76 fn test_remote_output_serialization() {
77 let output = RemoteOutput {
78 stdout: "ok".into(),
79 stderr: String::new(),
80 exit_code: 0,
81 duration_ms: 42,
82 machine_id: "test-machine".into(),
83 };
84 let json = serde_json::to_string(&output).unwrap();
85 let back: RemoteOutput = serde_json::from_str(&json).unwrap();
86 assert_eq!(output.exit_code, back.exit_code);
87 assert_eq!(output.machine_id, back.machine_id);
88 }
89}