Skip to main content

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}