Skip to main content

commy_sdk_rust/
message.rs

1//! Message types for Commy client protocol
2
3use serde::{Deserialize, Serialize};
4
5/// Messages sent from client to server
6#[derive(Debug, Clone, Serialize, Deserialize)]
7#[serde(tag = "type", content = "data")]
8pub enum ClientMessage {
9    /// Authenticate with a tenant
10    Authenticate {
11        tenant_id: String,
12        client_version: String,
13        #[serde(flatten)]
14        credentials: AuthCredentials,
15    },
16
17    /// Create a new tenant (admin operation)
18    CreateTenant {
19        tenant_id: String,
20        tenant_name: String,
21    },
22
23    /// Delete a tenant (admin operation)
24    DeleteTenant { tenant_id: String },
25
26    /// Create a new service
27    CreateService {
28        tenant_id: String,
29        service_name: String,
30    },
31
32    /// Get existing service (errors if not found)
33    GetService {
34        tenant_id: String,
35        service_name: String,
36    },
37
38    /// Delete a service
39    DeleteService {
40        tenant_id: String,
41        service_name: String,
42    },
43
44    /// Allocate a variable in a service
45    AllocateVariable {
46        service_id: String,
47        variable_name: String,
48        initial_data: Vec<u8>,
49    },
50
51    /// Read variable data
52    ReadVariable {
53        service_id: String,
54        variable_name: String,
55    },
56
57    /// Write variable data
58    WriteVariable {
59        service_id: String,
60        variable_name: String,
61        data: Vec<u8>,
62    },
63
64    /// Deallocate a variable
65    DeallocateVariable {
66        service_id: String,
67        variable_name: String,
68    },
69
70    /// Subscribe to variable changes
71    Subscribe {
72        service_id: String,
73        variable_name: String,
74    },
75
76    /// Unsubscribe from variable changes
77    Unsubscribe {
78        service_id: String,
79        variable_name: String,
80    },
81
82    /// Heartbeat/keepalive
83    Heartbeat { client_id: String },
84
85    /// Disconnect gracefully
86    Disconnect { client_id: String },
87
88    /// Request service file path for memory mapping (local clients)
89    GetServiceFilePath {
90        tenant_id: String,
91        service_name: String,
92    },
93
94    /// Notify server of variable changes detected locally
95    ReportVariableChanges {
96        service_id: String,
97        changed_variables: Vec<String>,
98        new_values: Vec<(String, Vec<u8>)>,
99    },
100}
101
102/// Messages received from server
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(tag = "type", content = "data")]
105pub enum ServerMessage {
106    /// Authentication result
107    AuthenticationResult {
108        success: bool,
109        message: String,
110        server_version: String,
111        #[serde(skip_serializing_if = "Option::is_none")]
112        permissions: Option<Vec<String>>,
113    },
114
115    /// Service created or retrieved
116    Service {
117        service_id: String,
118        service_name: String,
119        tenant_id: String,
120        #[serde(skip_serializing_if = "Option::is_none")]
121        file_path: Option<String>,
122    },
123
124    /// Tenant created or retrieved
125    Tenant {
126        tenant_id: String,
127        tenant_name: String,
128    },
129
130    /// Tenant operation result (create/delete)
131    TenantResult {
132        success: bool,
133        tenant_id: String,
134        message: String,
135    },
136
137    /// Variable data response
138    VariableData {
139        service_id: String,
140        variable_name: String,
141        data: Vec<u8>,
142        version: u64,
143    },
144
145    /// Variable change notification
146    VariableChanged {
147        service_id: String,
148        variable_name: String,
149        data: Vec<u8>,
150        version: u64,
151    },
152
153    /// Operation result
154    Result {
155        request_id: String,
156        success: bool,
157        message: String,
158    },
159
160    /// Error response with explicit error type
161    Error { code: ErrorCode, message: String },
162
163    /// Server disconnecting
164    Disconnected { reason: String },
165
166    /// Service file path response (for local memory mapping)
167    ServiceFilePath {
168        service_id: String,
169        file_path: String,
170        file_size: u64,
171    },
172
173    /// Acknowledgment of variable changes
174    VariableChangesAcknowledged {
175        service_id: String,
176        changed_variables: Vec<String>,
177    },
178
179    /// Heartbeat response (keep-alive)
180    Heartbeat { timestamp: String },
181}
182
183/// Explicit error codes for API responses
184#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
185#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
186pub enum ErrorCode {
187    /// Resource not found (e.g., service doesn't exist)
188    NotFound,
189    /// Permission denied (unauthorized for this resource)
190    PermissionDenied,
191    /// Invalid authentication credentials
192    Unauthorized,
193    /// Resource already exists
194    AlreadyExists,
195    /// Invalid request parameters
196    InvalidRequest,
197    /// Internal server error
198    InternalError,
199    /// Connection lost or disconnected
200    ConnectionLost,
201    /// Operation timed out
202    Timeout,
203}
204
205/// Authentication credentials
206#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(tag = "method")]
208pub enum AuthCredentials {
209    /// API key authentication
210    #[serde(rename = "api_key")]
211    ApiKey { key: String },
212
213    /// JWT token authentication
214    #[serde(rename = "jwt")]
215    Jwt { token: String },
216
217    /// Username/password authentication
218    #[serde(rename = "basic")]
219    Basic { username: String, password: String },
220
221    /// Custom authentication
222    #[serde(rename = "custom")]
223    Custom { data: serde_json::Value },
224}
225
226/// Service metadata
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct ServiceMetadata {
229    pub service_id: String,
230    pub service_name: String,
231    pub tenant_id: String,
232    pub created_at: chrono::DateTime<chrono::Utc>,
233    pub file_path: Option<String>,
234}
235
236/// Variable metadata
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct VariableMetadata {
239    pub name: String,
240    pub service_id: String,
241    pub offset: u64,
242    pub size: u64,
243    pub version: u64,
244    pub created_at: chrono::DateTime<chrono::Utc>,
245}
246
247/// Permission set
248#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
249pub enum Permission {
250    Read,
251    Write,
252    Admin,
253    Execute,
254}
255
256/// Convert ErrorCode to CommyError
257impl From<ErrorCode> for crate::error::CommyError {
258    fn from(code: ErrorCode) -> Self {
259        match code {
260            ErrorCode::NotFound => {
261                crate::error::CommyError::NotFound("Resource not found".to_string())
262            }
263            ErrorCode::PermissionDenied => {
264                crate::error::CommyError::PermissionDenied("Permission denied".to_string())
265            }
266            ErrorCode::Unauthorized => {
267                crate::error::CommyError::Unauthorized("Unauthorized".to_string())
268            }
269            ErrorCode::AlreadyExists => {
270                crate::error::CommyError::AlreadyExists("Resource already exists".to_string())
271            }
272            ErrorCode::InvalidRequest => {
273                crate::error::CommyError::InvalidRequest("Invalid request".to_string())
274            }
275            ErrorCode::InternalError => {
276                crate::error::CommyError::Other("Internal server error".to_string())
277            }
278            ErrorCode::ConnectionLost => {
279                crate::error::CommyError::ConnectionLost("Connection lost".to_string())
280            }
281            ErrorCode::Timeout => crate::error::CommyError::Timeout,
282        }
283    }
284}