arcane_engine/agent/
mod.rs1pub mod inspector;
2
3use std::sync::mpsc;
4
5#[derive(Debug)]
7pub enum InspectorRequest {
8 Health,
9 GetState { path: Option<String> },
10 Describe { verbosity: Option<String> },
11 ListActions,
12 ExecuteAction { name: String, payload: String },
13 Rewind { steps: u32 },
14 Simulate { action: String },
15 GetHistory,
16}
17
18#[derive(Debug)]
20pub struct InspectorResponse {
21 pub status: u16,
22 pub content_type: String,
23 pub body: String,
24}
25
26impl InspectorResponse {
27 pub fn json(body: String) -> Self {
28 Self {
29 status: 200,
30 content_type: "application/json".into(),
31 body,
32 }
33 }
34
35 pub fn text(body: String) -> Self {
36 Self {
37 status: 200,
38 content_type: "text/plain".into(),
39 body,
40 }
41 }
42
43 pub fn error(status: u16, message: String) -> Self {
44 Self {
45 status,
46 content_type: "application/json".into(),
47 body: format!("{{\"error\":\"{message}\"}}"),
48 }
49 }
50}
51
52pub type ResponseSender = mpsc::Sender<InspectorResponse>;
54
55pub type InspectorMessage = (InspectorRequest, ResponseSender);
57
58pub type RequestSender = mpsc::Sender<InspectorMessage>;
60
61pub type RequestReceiver = mpsc::Receiver<InspectorMessage>;
63
64pub fn inspector_channel() -> (RequestSender, RequestReceiver) {
66 mpsc::channel()
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn inspector_channel_round_trip() {
75 let (tx, rx) = inspector_channel();
76
77 let (resp_tx, resp_rx) = mpsc::channel();
79 tx.send((InspectorRequest::Health, resp_tx)).unwrap();
80
81 let (req, sender) = rx.recv().unwrap();
83 assert!(matches!(req, InspectorRequest::Health));
84 sender
85 .send(InspectorResponse::json("{\"status\":\"ok\"}".into()))
86 .unwrap();
87
88 let response = resp_rx.recv().unwrap();
90 assert_eq!(response.status, 200);
91 assert_eq!(response.content_type, "application/json");
92 assert!(response.body.contains("ok"));
93 }
94
95 #[test]
96 fn inspector_response_constructors() {
97 let json = InspectorResponse::json("{\"key\":1}".into());
98 assert_eq!(json.status, 200);
99 assert_eq!(json.content_type, "application/json");
100
101 let text = InspectorResponse::text("hello".into());
102 assert_eq!(text.status, 200);
103 assert_eq!(text.content_type, "text/plain");
104
105 let err = InspectorResponse::error(404, "not found".into());
106 assert_eq!(err.status, 404);
107 assert!(err.body.contains("not found"));
108 }
109
110 #[test]
111 fn inspector_request_variants() {
112 let requests = vec![
114 InspectorRequest::Health,
115 InspectorRequest::GetState { path: None },
116 InspectorRequest::GetState {
117 path: Some("player.hp".into()),
118 },
119 InspectorRequest::Describe {
120 verbosity: Some("full".into()),
121 },
122 InspectorRequest::ListActions,
123 InspectorRequest::ExecuteAction {
124 name: "move".into(),
125 payload: "{}".into(),
126 },
127 InspectorRequest::Rewind { steps: 3 },
128 InspectorRequest::Simulate {
129 action: "attack".into(),
130 },
131 InspectorRequest::GetHistory,
132 ];
133 assert_eq!(requests.len(), 9);
134 }
135}