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