Skip to main content

volt_client_grpc/
proto.rs

1//! Protobuf definitions and utilities.
2//!
3//! This module contains the protobuf message definitions used by the Volt API.
4//!
5//! The module uses tonic-build to generate Rust code from .proto files located in
6//! the tdxvolt-core repository. If the proto files are not available at build time,
7//! manual fallback definitions are used instead.
8
9use serde::de::Deserializer;
10use serde::{Deserialize, Serialize};
11
12// Include the generated protobuf code from tonic-build
13// The generated code will be in OUT_DIR at build time
14
15/// Generated protobuf modules from .proto files
16pub mod generated {
17    /// TDX Volt API types
18    pub mod tdx {
19        pub mod volt_api {
20            pub mod volt {
21                pub mod v1 {
22                    include!(concat!(env!("OUT_DIR"), "/tdx.volt_api.volt.v1.rs"));
23                }
24            }
25            pub mod data {
26                pub mod v1 {
27                    include!(concat!(env!("OUT_DIR"), "/tdx.volt_api.data.v1.rs"));
28                }
29            }
30            pub mod relay {
31                pub mod v1 {
32                    include!(concat!(env!("OUT_DIR"), "/tdx.volt_api.relay.v1.rs"));
33                }
34            }
35            pub mod proto_db_sync {
36                pub mod v1 {
37                    include!(concat!(
38                        env!("OUT_DIR"),
39                        "/tdx.volt_api.proto_db_sync.v1.rs"
40                    ));
41                }
42            }
43        }
44    }
45}
46
47// Re-export the main generated types for convenience
48pub use generated::tdx::volt_api::data::v1 as data;
49pub use generated::tdx::volt_api::proto_db_sync::v1 as proto_db_sync;
50pub use generated::tdx::volt_api::relay::v1 as relay;
51pub use generated::tdx::volt_api::volt::v1 as volt;
52
53// Re-export the generated clients
54pub use data::sqlite_database_api_client::SqliteDatabaseApiClient;
55pub use data::sqlite_server_api_client::SqliteServerApiClient;
56pub use relay::relay_api_client::RelayApiClient;
57pub use volt::sync_api_client::SyncApiClient;
58pub use volt::volt_api_client::VoltApiClient;
59
60// ==============================================================================
61// Manual fallback types (used when proto files are not available or for
62// compatibility with existing code that uses serde-based serialization)
63// ==============================================================================
64
65/// Status message for API responses
66#[derive(Debug, Clone, Default, Serialize, Deserialize)]
67pub struct Status {
68    /// Status code (0 = success)
69    #[serde(default)]
70    pub code: i32,
71    /// Status message (empty = success)
72    #[serde(default)]
73    pub message: String,
74    /// Additional description
75    #[serde(default)]
76    pub description: Option<String>,
77}
78
79impl Status {
80    pub fn is_ok(&self) -> bool {
81        self.code == 0 && self.message.is_empty()
82    }
83
84    pub fn ok() -> Self {
85        Self::default()
86    }
87
88    pub fn error(code: i32, message: impl Into<String>) -> Self {
89        Self {
90            code,
91            message: message.into(),
92            description: None,
93        }
94    }
95}
96
97/// Connect request hello message
98#[derive(Debug, Clone, Default, Serialize, Deserialize)]
99pub struct ConnectHello {
100    /// Ping interval in milliseconds
101    #[serde(default)]
102    pub ping_interval: u64,
103    /// Timestamp of the hello message
104    #[serde(default)]
105    pub timestamp: u64,
106}
107
108/// Connect request message
109#[derive(Debug, Clone, Default, Serialize, Deserialize)]
110pub struct ConnectRequest {
111    /// Hello message (first message)
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub hello: Option<ConnectHello>,
114    /// Ping message
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub ping: Option<PingMessage>,
117}
118
119/// Ping message
120#[derive(Debug, Clone, Default, Serialize, Deserialize)]
121pub struct PingMessage {
122    /// Timestamp
123    pub timestamp: u64,
124}
125
126/// Connect acknowledge message
127#[derive(Debug, Clone, Default, Serialize, Deserialize)]
128pub struct ConnectAcknowledge {
129    /// Connection ID assigned by the server
130    pub connection_id: String,
131    /// Server's ping interval
132    pub ping_interval: u64,
133}
134
135/// Connect response payload types
136#[derive(Debug, Clone, Serialize, Deserialize)]
137#[serde(tag = "payload")]
138pub enum ConnectResponsePayload {
139    #[serde(rename = "ping")]
140    Ping(PingMessage),
141    #[serde(rename = "acknowledge")]
142    Acknowledge(ConnectAcknowledge),
143    #[serde(rename = "invoke_request")]
144    InvokeRequest(InvokeRequest),
145    #[serde(rename = "evt")]
146    Event(serde_json::Value),
147}
148
149/// Invoke request message
150#[derive(Debug, Clone, Default, Serialize, Deserialize)]
151pub struct InvokeRequest {
152    /// Invoke ID
153    pub invoke_id: String,
154    /// Additional payload data
155    #[serde(flatten)]
156    pub payload: serde_json::Value,
157}
158
159/// Authenticate request message
160#[derive(Debug, Clone, Default, Serialize, Deserialize)]
161pub struct AuthenticateRequest {
162    /// Client name
163    pub client_name: String,
164    /// Host address (IP)
165    #[serde(default)]
166    pub host: Option<String>,
167    /// Public key (for session-only auth)
168    #[serde(default)]
169    pub public_key: Option<String>,
170    /// DID public key (for DID auth)
171    #[serde(default)]
172    pub did_public_key: Option<String>,
173    /// Existing DID
174    #[serde(default)]
175    pub did: Option<String>,
176    /// DID document (when creating own DID)
177    #[serde(default)]
178    pub did_document: Option<String>,
179    /// DID document signature
180    #[serde(default)]
181    pub did_document_signature: Option<String>,
182    /// Challenge code
183    #[serde(default)]
184    pub challenge: Option<String>,
185    /// Exchange token
186    #[serde(default)]
187    pub exchange_token: Option<String>,
188    /// Verifiable presentations
189    #[serde(default)]
190    pub verifiable_presentation: Vec<VerifiablePresentation>,
191}
192
193/// Verifiable presentation
194#[derive(Debug, Clone, Default, Serialize, Deserialize)]
195pub struct VerifiablePresentation {
196    /// Credentials in the presentation
197    pub credential: Vec<String>,
198    /// Signature over the presentation
199    pub signature: String,
200}
201
202/// Authenticate response message
203#[derive(Debug, Clone, Default, Serialize, Deserialize)]
204pub struct AuthenticateResponse {
205    /// Status
206    #[serde(default)]
207    pub status: Option<Status>,
208    /// Policy decision
209    #[serde(default, deserialize_with = "string_or_default")]
210    pub decision: String,
211    /// Certificate issued to the client
212    #[serde(default)]
213    pub cert: Option<String>,
214    /// CA certificate chain
215    #[serde(default)]
216    pub chain: Option<String>,
217    /// Session ID
218    #[serde(default)]
219    pub session_id: Option<String>,
220    /// Identity DID
221    #[serde(default)]
222    pub identity_did: Option<String>,
223    /// Credentials
224    #[serde(default)]
225    pub credential: Vec<String>,
226}
227
228/// Resource request
229#[derive(Debug, Clone, Default, Serialize, Deserialize)]
230pub struct ResourceRequest {
231    /// Resource ID
232    pub resource_id: String,
233}
234
235fn string_or_default<'de, D>(deserializer: D) -> Result<String, D::Error>
236where
237    D: Deserializer<'de>,
238{
239    Ok(Option::<String>::deserialize(deserializer)?.unwrap_or_default())
240}
241
242/// Access request
243#[derive(Debug, Clone, Default, Serialize, Deserialize)]
244pub struct AccessRequest {
245    /// Target resource ID
246    pub resource_id: String,
247    /// Access type
248    pub access: String,
249}
250
251/// Access response
252#[derive(Debug, Clone, Default, Serialize, Deserialize)]
253pub struct AccessResponse {
254    /// Status
255    #[serde(default)]
256    pub status: Option<Status>,
257    /// Policy decision
258    pub decision: String,
259}
260
261/// Generic API response with status
262#[derive(Debug, Clone, Default, Serialize, Deserialize)]
263pub struct ApiResponse {
264    /// Status
265    #[serde(default)]
266    pub status: Option<Status>,
267    /// Response payload
268    #[serde(flatten)]
269    pub payload: serde_json::Value,
270}
271
272impl ApiResponse {
273    pub fn is_ok(&self) -> bool {
274        self.status.as_ref().map(|s| s.is_ok()).unwrap_or(true)
275    }
276
277    pub fn error_message(&self) -> Option<&str> {
278        self.status
279            .as_ref()
280            .filter(|s| !s.is_ok())
281            .map(|s| s.message.as_str())
282    }
283}
284
285/// SQL execute start request
286#[derive(Debug, Clone, Default, Serialize, Deserialize)]
287pub struct SqlExecuteStart {
288    /// Database ID
289    pub database_id: String,
290    /// Whether to start a transaction
291    #[serde(default)]
292    pub transaction: bool,
293    /// SQL statement
294    pub statement: String,
295    /// Page size limit
296    #[serde(default)]
297    pub page_size: Option<u32>,
298}
299
300/// SQL execute request
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct SqlExecuteRequest {
303    /// Start request (first message)
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub start: Option<SqlExecuteStart>,
306    /// Continue request
307    #[serde(skip_serializing_if = "Option::is_none")]
308    pub next: Option<bool>,
309    /// Commit transaction
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub commit: Option<bool>,
312    /// Rollback transaction  
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub rollback: Option<bool>,
315}
316
317/// SQL column definition
318#[derive(Debug, Clone, Default, Serialize, Deserialize)]
319pub struct SqlColumn {
320    /// Column name
321    pub name: String,
322    /// Column description
323    #[serde(default)]
324    pub description: Option<String>,
325    /// Column type
326    #[serde(rename = "type")]
327    pub column_type: String,
328}
329
330/// SQL row header
331#[derive(Debug, Clone, Default, Serialize, Deserialize)]
332pub struct SqlRowHeader {
333    /// Column definitions
334    pub column: Vec<SqlColumn>,
335}
336
337/// SQL variant value
338#[derive(Debug, Clone, Serialize, Deserialize)]
339#[serde(rename_all = "snake_case")]
340pub enum SqlVariant {
341    Text(String),
342    Integer(i64),
343    Real(f64),
344    Blob(Vec<u8>),
345    Null(bool),
346}
347
348/// SQL variant row
349#[derive(Debug, Clone, Default, Serialize, Deserialize)]
350pub struct SqlVariantRow {
351    /// Column values
352    pub column: Vec<SqlVariant>,
353}
354
355/// SQL execute response
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct SqlExecuteResponse {
358    /// Status
359    #[serde(default)]
360    pub status: Option<Status>,
361    /// Row header (first response)
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub header: Option<SqlRowHeader>,
364    /// Data row
365    #[serde(skip_serializing_if = "Option::is_none")]
366    pub row: Option<SqlVariantRow>,
367}
368
369/// File download request
370#[derive(Debug, Clone, Default, Serialize, Deserialize)]
371pub struct DownloadFileRequest {
372    /// Resource ID of the file
373    pub resource_id: String,
374}
375
376/// File download response
377#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct DownloadFileResponse {
379    /// Status
380    #[serde(default)]
381    pub status: Option<Status>,
382    /// File block (chunk of data)
383    #[serde(skip_serializing_if = "Option::is_none")]
384    pub block: Option<Vec<u8>>,
385}