amaters_net/server_types.rs
1//! Shared types for the AmateRS gRPC server implementation.
2//!
3//! This module contains types that support `AqlServiceImpl` but are large
4//! enough to warrant their own file to keep `server.rs` under the 2000-line
5//! policy limit.
6
7use amaters_core::Update as UpdateOp;
8use amaters_core::types::{CipherBlob, Key};
9
10// ─── StreamConfig ─────────────────────────────────────────────────────────────
11
12/// Configuration for streaming query responses.
13///
14/// Controls chunk size, maximum result count, and timeout for streaming queries.
15#[derive(Debug, Clone)]
16pub struct StreamConfig {
17 /// Number of items per chunk (default: 100)
18 pub chunk_size: usize,
19 /// Maximum total results to return (None = unlimited)
20 pub max_results: Option<usize>,
21 /// Timeout for the entire streaming operation
22 pub timeout: std::time::Duration,
23}
24
25impl Default for StreamConfig {
26 fn default() -> Self {
27 Self {
28 chunk_size: 100,
29 max_results: None,
30 timeout: std::time::Duration::from_secs(30),
31 }
32 }
33}
34
35impl StreamConfig {
36 /// Create a new StreamConfig with the given chunk size.
37 pub fn with_chunk_size(mut self, chunk_size: usize) -> Self {
38 self.chunk_size = if chunk_size == 0 { 1 } else { chunk_size };
39 self
40 }
41
42 /// Set the maximum number of results.
43 pub fn with_max_results(mut self, max_results: usize) -> Self {
44 self.max_results = Some(max_results);
45 self
46 }
47
48 /// Set the timeout duration.
49 pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
50 self.timeout = timeout;
51 self
52 }
53}
54
55// ─── RollbackOp ───────────────────────────────────────────────────────────────
56
57/// An operation that can be undone during batch transaction rollback.
58///
59/// Stores the information needed to reverse a write operation if a later
60/// step in the same batch fails.
61#[derive(Debug)]
62#[allow(clippy::enum_variant_names)]
63pub(crate) enum RollbackOp {
64 /// Undo a Set operation: restore the old value or delete the key.
65 UndoSet {
66 key: Key,
67 /// The value that existed before the Set (`None` if the key was new).
68 old_value: Option<CipherBlob>,
69 },
70 /// Undo a Delete operation: re-insert the deleted value.
71 UndoDelete {
72 key: Key,
73 /// The value that existed before deletion.
74 old_value: Option<CipherBlob>,
75 },
76 /// Undo an Update operation: restore all key-value pairs to their
77 /// pre-update state.
78 UndoUpdate {
79 /// Snapshot of all key-value pairs before the update.
80 /// Keys with `None` values existed in the key list but had no value.
81 snapshots: Vec<(Key, Option<CipherBlob>)>,
82 },
83}
84
85// ─── apply_update_operation ───────────────────────────────────────────────────
86
87/// Apply a single update operation to a value blob.
88///
89/// - `Set`: replaces the value entirely with the new blob.
90/// - `Add`: concatenates each byte of the update blob to the corresponding byte
91/// of the current value (wrapping on overflow). If the blobs differ in
92/// length the shorter one is zero-extended.
93/// - `Mul`: multiplies each byte of the current value with the corresponding
94/// byte of the update blob (wrapping on overflow). If the blobs differ in
95/// length the shorter one is one-extended for multiplication identity.
96pub(crate) fn apply_update_operation(current: &CipherBlob, op: &UpdateOp) -> CipherBlob {
97 match op {
98 UpdateOp::Set(_col, blob) => blob.clone(),
99 UpdateOp::Add(_col, blob) => {
100 let a = current.as_bytes();
101 let b = blob.as_bytes();
102 let len = a.len().max(b.len());
103 let mut result = Vec::with_capacity(len);
104 for i in 0..len {
105 let va = if i < a.len() { a[i] } else { 0 };
106 let vb = if i < b.len() { b[i] } else { 0 };
107 result.push(va.wrapping_add(vb));
108 }
109 CipherBlob::new(result)
110 }
111 UpdateOp::Mul(_col, blob) => {
112 let a = current.as_bytes();
113 let b = blob.as_bytes();
114 let len = a.len().max(b.len());
115 let mut result = Vec::with_capacity(len);
116 for i in 0..len {
117 let va = if i < a.len() { a[i] } else { 1 };
118 let vb = if i < b.len() { b[i] } else { 1 };
119 result.push(va.wrapping_mul(vb));
120 }
121 CipherBlob::new(result)
122 }
123 }
124}