Skip to main content

nautilus_protocol/
methods.rs

1//! Nautilus protocol method definitions.
2//!
3//! This module defines stable method names and their request/response payloads.
4
5use serde::{Deserialize, Serialize};
6use serde_json::value::RawValue;
7use serde_json::Value;
8
9fn default_true() -> bool {
10    true
11}
12
13pub const ENGINE_HANDSHAKE: &str = "engine.handshake";
14pub const QUERY_FIND_MANY: &str = "query.findMany";
15pub const QUERY_FIND_FIRST: &str = "query.findFirst";
16pub const QUERY_FIND_UNIQUE: &str = "query.findUnique";
17pub const QUERY_FIND_UNIQUE_OR_THROW: &str = "query.findUniqueOrThrow";
18pub const QUERY_FIND_FIRST_OR_THROW: &str = "query.findFirstOrThrow";
19pub const QUERY_CREATE: &str = "query.create";
20pub const QUERY_CREATE_MANY: &str = "query.createMany";
21pub const QUERY_UPDATE: &str = "query.update";
22pub const QUERY_DELETE: &str = "query.delete";
23pub const QUERY_COUNT: &str = "query.count";
24/// Group records and compute aggregates (COUNT, AVG, SUM, MIN, MAX).
25pub const QUERY_GROUP_BY: &str = "query.groupBy";
26/// Method name for schema validation.
27pub const SCHEMA_VALIDATE: &str = "schema.validate";
28
29/// Start a new interactive transaction.
30pub const TRANSACTION_START: &str = "transaction.start";
31/// Commit an interactive transaction.
32pub const TRANSACTION_COMMIT: &str = "transaction.commit";
33/// Rollback an interactive transaction.
34pub const TRANSACTION_ROLLBACK: &str = "transaction.rollback";
35/// Execute a batch of operations atomically in a single transaction.
36pub const TRANSACTION_BATCH: &str = "transaction.batch";
37
38/// Handshake request parameters.
39///
40/// The handshake must be the first request sent by a client to validate
41/// protocol compatibility.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct HandshakeParams {
45    /// Protocol version the client is using.
46    pub protocol_version: u32,
47
48    /// Optional client name (e.g., "nautilus-js").
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub client_name: Option<String>,
51
52    /// Optional client version (e.g., "0.1.0").
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub client_version: Option<String>,
55}
56
57/// Handshake response.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59#[serde(rename_all = "camelCase")]
60pub struct HandshakeResult {
61    /// Engine version.
62    pub engine_version: String,
63
64    /// Protocol version the engine supports.
65    pub protocol_version: u32,
66}
67
68/// Find many request parameters.
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[serde(rename_all = "camelCase")]
71pub struct FindManyParams {
72    /// Protocol version (required in all requests).
73    pub protocol_version: u32,
74
75    /// Model name (e.g., "User", "Post").
76    pub model: String,
77
78    /// Query arguments (filters, ordering, pagination, etc.).
79    /// Structure is flexible and parsed by the engine.
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub args: Option<Value>,
82
83    /// Optional transaction ID — if present, this query runs inside the given transaction.
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub transaction_id: Option<String>,
86
87    /// Optional chunk size for streaming large result sets.
88    /// When set, the engine emits multiple partial responses of at most `chunk_size` rows each.
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub chunk_size: Option<usize>,
91}
92
93/// Find first request parameters (same shape as FindMany — optional full args).
94#[derive(Debug, Clone, Serialize, Deserialize)]
95#[serde(rename_all = "camelCase")]
96pub struct FindFirstParams {
97    pub protocol_version: u32,
98    pub model: String,
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub args: Option<Value>,
101    /// Optional transaction ID.
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub transaction_id: Option<String>,
104}
105
106/// Find unique request parameters.
107#[derive(Debug, Clone, Serialize, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct FindUniqueParams {
110    pub protocol_version: u32,
111    pub model: String,
112    pub filter: Value,
113    /// Optional transaction ID.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub transaction_id: Option<String>,
116}
117
118/// Find unique or throw request parameters (same shape as FindUnique).
119pub type FindUniqueOrThrowParams = FindUniqueParams;
120
121/// Find first or throw request parameters (same shape as FindFirst).
122pub type FindFirstOrThrowParams = FindFirstParams;
123
124/// Create request parameters.
125#[derive(Debug, Clone, Serialize, Deserialize)]
126#[serde(rename_all = "camelCase")]
127pub struct CreateParams {
128    pub protocol_version: u32,
129    pub model: String,
130    pub data: Value,
131    /// Optional transaction ID.
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub transaction_id: Option<String>,
134    /// Whether to return the created row(s). Defaults to `true`.
135    #[serde(default = "default_true")]
136    pub return_data: bool,
137}
138
139/// Create many request parameters.
140#[derive(Debug, Clone, Serialize, Deserialize)]
141#[serde(rename_all = "camelCase")]
142pub struct CreateManyParams {
143    pub protocol_version: u32,
144    pub model: String,
145    pub data: Vec<Value>,
146    /// Optional transaction ID.
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub transaction_id: Option<String>,
149    /// Whether to return the created row(s). Defaults to `true`.
150    #[serde(default = "default_true")]
151    pub return_data: bool,
152}
153
154/// Update request parameters.
155#[derive(Debug, Clone, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct UpdateParams {
158    pub protocol_version: u32,
159    pub model: String,
160    pub filter: Value,
161    pub data: Value,
162    /// Optional transaction ID.
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub transaction_id: Option<String>,
165    /// Whether to return the updated row(s). Defaults to `true`.
166    #[serde(default = "default_true")]
167    pub return_data: bool,
168}
169
170/// Delete request parameters.
171#[derive(Debug, Clone, Serialize, Deserialize)]
172#[serde(rename_all = "camelCase")]
173pub struct DeleteParams {
174    pub protocol_version: u32,
175    pub model: String,
176    pub filter: Value,
177    /// Optional transaction ID.
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub transaction_id: Option<String>,
180    /// Whether to return the deleted row(s). Defaults to `true`.
181    #[serde(default = "default_true")]
182    pub return_data: bool,
183}
184
185/// Count request parameters.
186#[derive(Debug, Clone, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase")]
188pub struct CountParams {
189    pub protocol_version: u32,
190    pub model: String,
191    /// Optional query arguments (where, take, skip).
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub args: Option<Value>,
194    /// Optional transaction ID.
195    #[serde(skip_serializing_if = "Option::is_none")]
196    pub transaction_id: Option<String>,
197}
198
199/// Group-by request parameters.
200#[derive(Debug, Clone, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub struct GroupByParams {
203    pub protocol_version: u32,
204    pub model: String,
205    /// Query arguments: by, where, having, take, skip, orderBy, count, avg, sum, min, max.
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub args: Option<Value>,
208    /// Optional transaction ID.
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub transaction_id: Option<String>,
211}
212
213/// Query result containing data rows.
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct QueryResult {
216    /// Result data as JSON objects.
217    pub data: Vec<Value>,
218}
219
220/// Mutation result with count of affected rows.
221#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct MutationResult {
223    /// Number of rows affected.
224    pub count: usize,
225
226    /// Optional returning data for mutations that support RETURNING.
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub data: Option<Vec<Value>>,
229}
230
231/// Method name for executing a raw SQL query (no parameter binding).
232pub const QUERY_RAW: &str = "query.rawQuery";
233/// Method name for executing a raw prepared-statement query (with bound params).
234pub const QUERY_RAW_STMT: &str = "query.rawStmtQuery";
235
236/// Raw SQL query request parameters.
237///
238/// Execute the SQL string as-is against the database and return the result rows
239/// as generic JSON objects.  No parameter binding is performed — embed literal
240/// values directly in the SQL string or use [`RawStmtQueryParams`] instead.
241#[derive(Debug, Clone, Serialize, Deserialize)]
242#[serde(rename_all = "camelCase")]
243pub struct RawQueryParams {
244    /// Protocol version (required in all requests).
245    pub protocol_version: u32,
246    /// Raw SQL string to execute.
247    pub sql: String,
248    /// Optional transaction ID.
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub transaction_id: Option<String>,
251}
252
253/// Raw prepared-statement query request parameters.
254///
255/// Execute the SQL string with bound parameters and return the result rows as
256/// generic JSON objects.  Use `$1`, `$2`, … (PostgreSQL) or `?` (MySQL /
257/// SQLite) as placeholders; parameters are bound in the order they appear in
258/// the `params` array.
259#[derive(Debug, Clone, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase")]
261pub struct RawStmtQueryParams {
262    /// Protocol version (required in all requests).
263    pub protocol_version: u32,
264    /// Raw SQL string containing parameter placeholders.
265    pub sql: String,
266    /// Ordered list of parameter values to bind.
267    #[serde(default)]
268    pub params: Vec<Value>,
269    /// Optional transaction ID.
270    #[serde(skip_serializing_if = "Option::is_none")]
271    pub transaction_id: Option<String>,
272}
273
274/// Schema validation request parameters.
275#[derive(Debug, Clone, Serialize, Deserialize)]
276#[serde(rename_all = "camelCase")]
277pub struct SchemaValidateParams {
278    pub protocol_version: u32,
279    pub schema: String,
280}
281
282/// Schema validation result.
283#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct SchemaValidateResult {
285    /// Whether the schema is valid.
286    pub valid: bool,
287
288    /// Validation errors if any.
289    #[serde(skip_serializing_if = "Option::is_none")]
290    pub errors: Option<Vec<String>>,
291}
292
293/// Transaction isolation level.
294#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
295#[serde(rename_all = "camelCase")]
296pub enum IsolationLevel {
297    /// Read uncommitted — allows dirty reads.
298    ReadUncommitted,
299    /// Read committed — default for most databases.
300    ReadCommitted,
301    /// Repeatable read — prevents non-repeatable reads.
302    RepeatableRead,
303    /// Serializable — strictest isolation level.
304    Serializable,
305}
306
307impl IsolationLevel {
308    /// Returns the SQL representation (e.g., `"READ COMMITTED"`).
309    pub fn as_sql(&self) -> &'static str {
310        match self {
311            IsolationLevel::ReadUncommitted => "READ UNCOMMITTED",
312            IsolationLevel::ReadCommitted => "READ COMMITTED",
313            IsolationLevel::RepeatableRead => "REPEATABLE READ",
314            IsolationLevel::Serializable => "SERIALIZABLE",
315        }
316    }
317}
318
319/// Start a new interactive transaction.
320#[derive(Debug, Clone, Serialize, Deserialize)]
321#[serde(rename_all = "camelCase")]
322pub struct TransactionStartParams {
323    pub protocol_version: u32,
324    /// Maximum duration in milliseconds before the transaction is automatically
325    /// rolled back. Defaults to 5000 (5 seconds) if omitted.
326    #[serde(skip_serializing_if = "Option::is_none")]
327    pub timeout_ms: Option<u64>,
328    /// Optional isolation level override.
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub isolation_level: Option<IsolationLevel>,
331}
332
333/// Result of starting a transaction.
334#[derive(Debug, Clone, Serialize, Deserialize)]
335#[serde(rename_all = "camelCase")]
336pub struct TransactionStartResult {
337    /// Unique transaction identifier. Pass this as `transactionId` in
338    /// subsequent query requests.
339    pub id: String,
340}
341
342/// Commit an interactive transaction.
343#[derive(Debug, Clone, Serialize, Deserialize)]
344#[serde(rename_all = "camelCase")]
345pub struct TransactionCommitParams {
346    pub protocol_version: u32,
347    /// Transaction ID returned by `transaction.start`.
348    pub id: String,
349}
350
351/// Result of committing a transaction (empty on success).
352#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct TransactionCommitResult {}
354
355/// Rollback an interactive transaction.
356#[derive(Debug, Clone, Serialize, Deserialize)]
357#[serde(rename_all = "camelCase")]
358pub struct TransactionRollbackParams {
359    pub protocol_version: u32,
360    /// Transaction ID returned by `transaction.start`.
361    pub id: String,
362}
363
364/// Result of rolling back a transaction (empty on success).
365#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct TransactionRollbackResult {}
367
368/// A single operation inside a batch transaction.
369#[derive(Debug, Clone, Serialize, Deserialize)]
370#[serde(rename_all = "camelCase")]
371pub struct BatchOperation {
372    /// JSON-RPC method name (e.g., `"query.create"`).
373    pub method: String,
374    /// Method-specific params (same shape as the standalone request).
375    pub params: Value,
376}
377
378/// Execute multiple operations atomically in one transaction.
379#[derive(Debug, Clone, Serialize, Deserialize)]
380#[serde(rename_all = "camelCase")]
381pub struct TransactionBatchParams {
382    pub protocol_version: u32,
383    /// Ordered list of operations to execute.
384    pub operations: Vec<BatchOperation>,
385    /// Optional isolation level for the batch transaction.
386    #[serde(skip_serializing_if = "Option::is_none")]
387    pub isolation_level: Option<IsolationLevel>,
388    /// Optional timeout in milliseconds (default: 5000).
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub timeout_ms: Option<u64>,
391}
392
393/// Result of a batch transaction.
394#[derive(Debug, Clone, Serialize, Deserialize)]
395pub struct TransactionBatchResult {
396    /// One result per operation, in the same order as the input.
397    pub results: Vec<Box<RawValue>>,
398}