Skip to main content

iii_sdk/
types.rs

1use std::{collections::HashMap, sync::Arc};
2
3use futures_util::future::BoxFuture;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::{
8    error::IIIError,
9    protocol::{RegisterFunctionMessage, RegisterTriggerTypeMessage},
10    triggers::TriggerHandler,
11};
12
13pub type RemoteFunctionHandler =
14    Arc<dyn Fn(Value) -> BoxFuture<'static, Result<Value, IIIError>> + Send + Sync>;
15
16// ============================================================================
17// Stream Update Types
18// ============================================================================
19
20/// Represents a path to a field in a JSON object
21#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
22pub struct FieldPath(pub String);
23
24impl FieldPath {
25    pub fn new(path: impl Into<String>) -> Self {
26        Self(path.into())
27    }
28
29    pub fn root() -> Self {
30        Self(String::new())
31    }
32}
33
34impl From<&str> for FieldPath {
35    fn from(value: &str) -> Self {
36        Self(value.to_string())
37    }
38}
39
40impl From<String> for FieldPath {
41    fn from(value: String) -> Self {
42        Self(value)
43    }
44}
45
46/// Operations that can be performed atomically on a stream value
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(tag = "type", rename_all = "lowercase")]
49pub enum UpdateOp {
50    /// Set a value at path (overwrite)
51    Set {
52        path: FieldPath,
53        value: Option<Value>,
54    },
55
56    /// Merge object into existing value (object-only)
57    Merge {
58        path: Option<FieldPath>,
59        value: Value,
60    },
61
62    /// Increment numeric value
63    Increment { path: FieldPath, by: i64 },
64
65    /// Decrement numeric value
66    Decrement { path: FieldPath, by: i64 },
67
68    /// Remove a field
69    Remove { path: FieldPath },
70}
71
72impl UpdateOp {
73    /// Create a Set operation
74    pub fn set(path: impl Into<FieldPath>, value: impl Into<Option<Value>>) -> Self {
75        Self::Set {
76            path: path.into(),
77            value: value.into(),
78        }
79    }
80
81    /// Create an Increment operation
82    pub fn increment(path: impl Into<FieldPath>, by: i64) -> Self {
83        Self::Increment {
84            path: path.into(),
85            by,
86        }
87    }
88
89    /// Create a Decrement operation
90    pub fn decrement(path: impl Into<FieldPath>, by: i64) -> Self {
91        Self::Decrement {
92            path: path.into(),
93            by,
94        }
95    }
96
97    /// Create a Remove operation
98    pub fn remove(path: impl Into<FieldPath>) -> Self {
99        Self::Remove { path: path.into() }
100    }
101
102    /// Create a Merge operation at root level
103    pub fn merge(value: impl Into<Value>) -> Self {
104        Self::Merge {
105            path: None,
106            value: value.into(),
107        }
108    }
109
110    /// Create a Merge operation at a specific path
111    pub fn merge_at(path: impl Into<FieldPath>, value: impl Into<Value>) -> Self {
112        Self::Merge {
113            path: Some(path.into()),
114            value: value.into(),
115        }
116    }
117}
118
119/// Result of an atomic update operation
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct UpdateResult {
122    /// The value before the update (None if key didn't exist)
123    pub old_value: Option<Value>,
124    /// The value after the update
125    pub new_value: Value,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct SetResult {
130    /// The value before the update (None if key didn't exist)
131    pub old_value: Option<Value>,
132    /// The value after the update
133    pub new_value: Value,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
137pub struct DeleteResult {
138    /// The value before the update (None if key didn't exist)
139    pub old_value: Option<Value>,
140}
141
142/// Input for the stream update function
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct StreamUpdateInput {
145    pub key: String,
146    pub ops: Vec<UpdateOp>,
147}
148
149#[derive(Clone)]
150pub struct RemoteFunctionData {
151    pub message: RegisterFunctionMessage,
152    pub handler: RemoteFunctionHandler,
153}
154
155#[derive(Clone)]
156pub struct RemoteTriggerTypeData {
157    pub message: RegisterTriggerTypeMessage,
158    pub handler: Arc<dyn TriggerHandler>,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ApiRequest<T = Value> {
163    #[serde(default)]
164    pub query_params: HashMap<String, String>,
165    #[serde(default)]
166    pub path_params: HashMap<String, String>,
167    #[serde(default)]
168    pub headers: HashMap<String, String>,
169    #[serde(default)]
170    pub path: String,
171    #[serde(default)]
172    pub method: String,
173    #[serde(default)]
174    pub body: T,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct ApiResponse<T = Value> {
179    pub status_code: u16,
180    #[serde(default)]
181    pub headers: HashMap<String, String>,
182    pub body: T,
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn api_request_defaults_when_missing_fields() {
191        let request: ApiRequest = serde_json::from_str("{}").unwrap();
192
193        assert!(request.query_params.is_empty());
194        assert!(request.path_params.is_empty());
195        assert!(request.headers.is_empty());
196        assert_eq!(request.path, "");
197        assert_eq!(request.method, "");
198        assert!(request.body.is_null());
199    }
200}