rustant_core/gateway/
node_bridge.rs1use crate::gateway::events::ServerMessage;
4use crate::nodes::types::{Capability, NodeResult, NodeTask};
5use std::collections::HashMap;
6
7pub struct NodeBridge;
9
10impl NodeBridge {
11 pub fn new() -> Self {
12 Self
13 }
14
15 pub fn route_task_to_node(
17 &self,
18 task_name: &str,
19 args: &HashMap<String, String>,
20 ) -> Option<NodeTask> {
21 let capability = match task_name {
22 "shell" | "execute" | "run" => Some(Capability::Shell),
23 "applescript" | "osascript" => Some(Capability::AppleScript),
24 "screenshot" | "capture" => Some(Capability::Screenshot),
25 "clipboard" | "paste" => Some(Capability::Clipboard),
26 "filesystem" | "file" | "read_file" | "write_file" => Some(Capability::FileSystem),
27 "notify" | "notification" => Some(Capability::Notifications),
28 "browser" | "open_url" => Some(Capability::Browser),
29 _ => None,
30 };
31
32 capability.map(|cap| {
33 let command = args
34 .get("command")
35 .cloned()
36 .unwrap_or_else(|| task_name.to_string());
37 let mut task = NodeTask::new(cap, command);
38 if let Some(timeout) = args.get("timeout")
39 && let Ok(secs) = timeout.parse()
40 {
41 task = task.with_timeout(secs);
42 }
43 if let Some(task_args) = args.get("args") {
44 task = task.with_args(task_args.split_whitespace().map(String::from).collect());
45 }
46 task
47 })
48 }
49
50 pub fn node_result_to_server_message(result: &NodeResult) -> ServerMessage {
52 if result.success {
53 ServerMessage::Event {
54 event: crate::gateway::events::GatewayEvent::TaskCompleted {
55 task_id: result.task_id,
56 success: true,
57 summary: result.output.clone(),
58 },
59 }
60 } else {
61 ServerMessage::Event {
62 event: crate::gateway::events::GatewayEvent::TaskCompleted {
63 task_id: result.task_id,
64 success: false,
65 summary: result.output.clone(),
66 },
67 }
68 }
69 }
70}
71
72impl Default for NodeBridge {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use uuid::Uuid;
82
83 #[test]
84 fn test_node_bridge_route_shell_task() {
85 let bridge = NodeBridge::new();
86 let args = HashMap::from([("command".into(), "ls -la".into())]);
87 let task = bridge.route_task_to_node("shell", &args).unwrap();
88 assert_eq!(task.capability, Capability::Shell);
89 assert_eq!(task.command, "ls -la");
90 }
91
92 #[test]
93 fn test_node_bridge_route_applescript_task() {
94 let bridge = NodeBridge::new();
95 let args = HashMap::from([("command".into(), "tell app \"Finder\" to activate".into())]);
96 let task = bridge.route_task_to_node("applescript", &args).unwrap();
97 assert_eq!(task.capability, Capability::AppleScript);
98 }
99
100 #[test]
101 fn test_node_bridge_unknown_task_returns_none() {
102 let bridge = NodeBridge::new();
103 let args = HashMap::new();
104 assert!(bridge.route_task_to_node("unknown_task", &args).is_none());
105 }
106
107 #[test]
108 fn test_node_bridge_result_to_server_message() {
109 let result = NodeResult {
110 task_id: Uuid::new_v4(),
111 success: true,
112 output: "file1\nfile2".into(),
113 exit_code: Some(0),
114 duration_ms: 5,
115 };
116 let msg = NodeBridge::node_result_to_server_message(&result);
117 match msg {
118 ServerMessage::Event {
119 event:
120 crate::gateway::events::GatewayEvent::TaskCompleted {
121 success, summary, ..
122 },
123 } => {
124 assert!(success);
125 assert_eq!(summary, "file1\nfile2");
126 }
127 _ => panic!("Expected TaskCompleted event"),
128 }
129 }
130
131 #[test]
132 fn test_node_bridge_error_result() {
133 let result = NodeResult {
134 task_id: Uuid::new_v4(),
135 success: false,
136 output: "command not found".into(),
137 exit_code: Some(127),
138 duration_ms: 1,
139 };
140 let msg = NodeBridge::node_result_to_server_message(&result);
141 match msg {
142 ServerMessage::Event {
143 event:
144 crate::gateway::events::GatewayEvent::TaskCompleted {
145 success, summary, ..
146 },
147 } => {
148 assert!(!success);
149 assert_eq!(summary, "command not found");
150 }
151 _ => panic!("Expected TaskCompleted event"),
152 }
153 }
154}