1use bitflags::bitflags;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct NodeAppInfo {
13 pub name: String,
15 pub version: String,
17 pub author: String,
19 pub description: String,
21 pub capabilities: Vec<String>,
23}
24
25impl NodeAppInfo {
26 pub fn capability_flags(&self) -> Capabilities {
28 let mut flags = Capabilities::empty();
29 for cap in &self.capabilities {
30 match cap.as_str() {
31 "http_handler" => flags |= Capabilities::HTTP_HANDLER,
32 "event_listener" => flags |= Capabilities::EVENT_LISTENER,
33 "filesystem_read" => flags |= Capabilities::FILESYSTEM_READ,
34 "filesystem_write" => flags |= Capabilities::FILESYSTEM_WRITE,
35 "network_outbound" => flags |= Capabilities::NETWORK_OUTBOUND,
36 "system_info" => flags |= Capabilities::SYSTEM_INFO,
37 "capability_provider" => flags |= Capabilities::CAPABILITY_PROVIDER,
38 _ => {
39 eprintln!("[node-app-api] Unknown capability '{}', ignoring", cap);
40 }
41 }
42 }
43 flags
44 }
45}
46
47bitflags! {
48 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
50 pub struct Capabilities: u32 {
51 const HTTP_HANDLER = 0b0000_0001; const EVENT_LISTENER = 0b0000_0010; const FILESYSTEM_READ = 0b0000_0100; const FILESYSTEM_WRITE = 0b0000_1000; const NETWORK_OUTBOUND = 0b0001_0000; const SYSTEM_INFO = 0b0010_0000; const CAPABILITY_PROVIDER = 0b0100_0000; }
66}
67
68impl Capabilities {
69 pub fn to_string_list(&self) -> Vec<String> {
71 let mut list = Vec::new();
72 if self.contains(Capabilities::HTTP_HANDLER) {
73 list.push("http_handler".to_string());
74 }
75 if self.contains(Capabilities::EVENT_LISTENER) {
76 list.push("event_listener".to_string());
77 }
78 if self.contains(Capabilities::FILESYSTEM_READ) {
79 list.push("filesystem_read".to_string());
80 }
81 if self.contains(Capabilities::FILESYSTEM_WRITE) {
82 list.push("filesystem_write".to_string());
83 }
84 if self.contains(Capabilities::NETWORK_OUTBOUND) {
85 list.push("network_outbound".to_string());
86 }
87 if self.contains(Capabilities::SYSTEM_INFO) {
88 list.push("system_info".to_string());
89 }
90 if self.contains(Capabilities::CAPABILITY_PROVIDER) {
91 list.push("capability_provider".to_string());
92 }
93 list
94 }
95
96 pub fn from_bits_safe(bits: u32) -> Self {
98 Capabilities::from_bits_truncate(bits)
99 }
100
101 pub fn from_string(s: &str) -> Option<Self> {
103 match s.to_lowercase().as_str() {
104 "http_handler" => Some(Capabilities::HTTP_HANDLER),
105 "event_listener" => Some(Capabilities::EVENT_LISTENER),
106 "filesystem_read" => Some(Capabilities::FILESYSTEM_READ),
107 "filesystem_write" => Some(Capabilities::FILESYSTEM_WRITE),
108 "network_outbound" => Some(Capabilities::NETWORK_OUTBOUND),
109 "system_info" => Some(Capabilities::SYSTEM_INFO),
110 "capability_provider" => Some(Capabilities::CAPABILITY_PROVIDER),
111 _ => None,
112 }
113 }
114
115 pub fn all_capability_names() -> &'static [&'static str] {
117 &[
118 "http_handler",
119 "event_listener",
120 "filesystem_read",
121 "filesystem_write",
122 "network_outbound",
123 "system_info",
124 "capability_provider",
125 ]
126 }
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize, Default)]
133pub struct CallerContext {
134 pub app_name: String,
136 pub granted_permissions: Vec<String>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct AppRequest {
143 pub id: String,
145 pub method: String,
147 pub path: String,
149 pub headers: HashMap<String, String>,
151 pub body: serde_json::Value,
153 #[serde(default)]
156 pub caller: Option<CallerContext>,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct AppResponse {
162 pub status: u16,
164 pub headers: HashMap<String, String>,
166 pub body: serde_json::Value,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct AppEvent {
173 pub name: String,
175 pub data: serde_json::Value,
177}
178
179#[derive(Debug, Clone, Serialize, Deserialize, Default)]
184pub struct CapabilityRequest {
185 pub id: String,
187 pub capability: String,
189 pub payload: serde_json::Value,
191 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub timeout_ms: Option<u64>,
194 #[serde(default, skip_serializing_if = "Option::is_none")]
197 pub trace_id: Option<String>,
198 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub span_id: Option<String>,
201 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub parent_span_id: Option<String>,
204 #[serde(default, skip_serializing_if = "Option::is_none")]
206 pub trace_depth: Option<u8>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct CapabilityResponse {
212 pub id: String,
214 pub success: bool,
216 pub payload: serde_json::Value,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct ProvidedCapability {
224 pub name: String,
226 pub description: String,
228 #[serde(default)]
230 pub request_schema: Option<serde_json::Value>,
231 #[serde(default)]
233 pub response_schema: Option<serde_json::Value>,
234 #[serde(default = "default_capability_priority")]
236 pub priority: i32,
237 #[serde(default)]
239 pub examples: Vec<CapabilityExample>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct CapabilityExample {
245 pub label: String,
247 pub request: serde_json::Value,
249}
250
251fn default_capability_priority() -> i32 {
252 100
253}