zinit_client/
models.rs

1use chrono::{DateTime, Utc};
2use futures::Stream;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::fmt;
6use std::pin::Pin;
7
8use crate::error::Result;
9
10/// Protocol type used by the zinit server
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Protocol {
13    /// JSON-RPC protocol (new servers v0.2.25+)
14    JsonRpc,
15    /// Raw command protocol (old servers v0.2.14)
16    RawCommands,
17}
18
19impl fmt::Display for Protocol {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            Protocol::JsonRpc => write!(f, "JSON-RPC"),
23            Protocol::RawCommands => write!(f, "Raw Commands"),
24        }
25    }
26}
27
28/// Server capabilities based on version and protocol
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct ServerCapabilities {
31    /// Protocol used by the server
32    pub protocol: Protocol,
33    /// Whether the server supports dynamic service creation
34    pub supports_create: bool,
35    /// Whether the server supports service deletion
36    pub supports_delete: bool,
37    /// Whether the server supports getting service configuration
38    pub supports_get_config: bool,
39    /// Whether the server supports service statistics
40    pub supports_stats: bool,
41    /// Whether the server supports log streaming
42    pub supports_log_streaming: bool,
43    /// Whether the server supports HTTP/RPC server management
44    pub supports_http_server: bool,
45}
46
47impl ServerCapabilities {
48    /// Full capabilities for new servers (JSON-RPC)
49    pub fn full() -> Self {
50        Self {
51            protocol: Protocol::JsonRpc,
52            supports_create: true,
53            supports_delete: true,
54            supports_get_config: true,
55            supports_stats: true,
56            supports_log_streaming: true,
57            supports_http_server: true,
58        }
59    }
60
61    /// Legacy capabilities for old servers (raw commands)
62    pub fn legacy() -> Self {
63        Self {
64            protocol: Protocol::RawCommands,
65            supports_create: false,
66            supports_delete: false,
67            supports_get_config: false,
68            supports_stats: false,
69            supports_log_streaming: true, // Basic log support
70            supports_http_server: false,
71        }
72    }
73}
74
75/// Service state
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77#[serde(rename_all = "PascalCase")]
78pub enum ServiceState {
79    /// Service state is unknown
80    Unknown,
81    /// Service is blocked by dependencies
82    Blocked,
83    /// Service is spawned but not yet running
84    Spawned,
85    /// Service is running
86    Running,
87    /// Service completed successfully (for oneshot services)
88    Success,
89    /// Service exited with an error
90    Error,
91    /// Service test command failed
92    TestFailure,
93}
94
95impl fmt::Display for ServiceState {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match self {
98            ServiceState::Unknown => write!(f, "Unknown"),
99            ServiceState::Blocked => write!(f, "Blocked"),
100            ServiceState::Spawned => write!(f, "Spawned"),
101            ServiceState::Running => write!(f, "Running"),
102            ServiceState::Success => write!(f, "Success"),
103            ServiceState::Error => write!(f, "Error"),
104            ServiceState::TestFailure => write!(f, "TestFailure"),
105        }
106    }
107}
108
109/// Service target state
110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
111#[serde(rename_all = "PascalCase")]
112pub enum ServiceTarget {
113    /// Service should be running
114    Up,
115    /// Service should be stopped
116    Down,
117}
118
119impl fmt::Display for ServiceTarget {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        match self {
122            ServiceTarget::Up => write!(f, "Up"),
123            ServiceTarget::Down => write!(f, "Down"),
124        }
125    }
126}
127
128/// Service status information
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct ServiceStatus {
131    /// Service name
132    pub name: String,
133    /// Process ID (0 if not running)
134    pub pid: u32,
135    /// Current state
136    pub state: ServiceState,
137    /// Target state
138    pub target: ServiceTarget,
139    /// Dependencies and their states
140    pub after: HashMap<String, String>,
141}
142
143/// Log entry from a service
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct LogEntry {
146    /// Timestamp of the log entry
147    pub timestamp: DateTime<Utc>,
148    /// Service that generated the log
149    pub service: String,
150    /// Log message
151    pub message: String,
152}
153
154/// Stream of log entries
155pub struct LogStream {
156    /// Inner stream of log entries
157    pub(crate) inner: Pin<Box<dyn Stream<Item = Result<LogEntry>> + Send>>,
158}
159
160impl Stream for LogStream {
161    type Item = Result<LogEntry>;
162
163    fn poll_next(
164        mut self: Pin<&mut Self>,
165        cx: &mut std::task::Context<'_>,
166    ) -> std::task::Poll<Option<Self::Item>> {
167        Pin::new(&mut self.inner).poll_next(cx)
168    }
169}
170
171/// Response from the Zinit API
172#[derive(Debug, Clone, Deserialize)]
173pub(crate) struct Response {
174    /// Response state (ok or error)
175    pub state: ResponseState,
176    /// Response body
177    pub body: serde_json::Value,
178}
179
180/// Response state
181#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
182#[serde(rename_all = "lowercase")]
183pub(crate) enum ResponseState {
184    /// Success response
185    Ok,
186    /// Error response
187    Error,
188}
189
190/// JSON-RPC request structure
191#[derive(Debug, Clone, Serialize)]
192pub(crate) struct JsonRpcRequest {
193    /// JSON-RPC version
194    pub jsonrpc: String,
195    /// Method name
196    pub method: String,
197    /// Parameters
198    pub params: serde_json::Value,
199    /// Request ID
200    pub id: u64,
201}
202
203impl JsonRpcRequest {
204    /// Create a new JSON-RPC request
205    pub fn new(method: &str, params: serde_json::Value, id: u64) -> Self {
206        Self {
207            jsonrpc: "2.0".to_string(),
208            method: method.to_string(),
209            params,
210            id,
211        }
212    }
213}
214
215/// JSON-RPC response structure
216#[derive(Debug, Clone, Deserialize)]
217pub(crate) struct JsonRpcResponse {
218    /// JSON-RPC version
219    #[allow(dead_code)]
220    pub jsonrpc: String,
221    /// Request ID
222    #[allow(dead_code)]
223    pub id: Option<u64>,
224    /// Result (if successful)
225    pub result: Option<serde_json::Value>,
226    /// Error (if failed)
227    pub error: Option<JsonRpcError>,
228}
229
230/// JSON-RPC error structure
231#[derive(Debug, Clone, Deserialize)]
232pub(crate) struct JsonRpcError {
233    /// Error code
234    pub code: i32,
235    /// Error message
236    pub message: String,
237    /// Additional error data
238    pub data: Option<serde_json::Value>,
239}