1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value as JsonValue;
5
6#[derive(Debug, Serialize, Deserialize, Clone)]
7#[serde(rename_all = "camelCase")]
8pub struct ZoneId {
9 pub zone_name: String,
10}
11
12impl Default for ZoneId {
13 fn default() -> Self {
14 Self {
15 zone_name: "Notes".into(),
16 }
17 }
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone)]
22#[serde(rename_all = "camelCase")]
23pub struct CkField {
24 #[serde(rename = "type")]
25 pub kind: String,
26 pub value: JsonValue,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub is_encrypted: Option<bool>,
29}
30
31impl CkField {
32 pub fn string(v: impl Into<String>) -> Self {
33 Self {
34 kind: "STRING".into(),
35 value: JsonValue::String(v.into()),
36 is_encrypted: None,
37 }
38 }
39
40 pub fn string_encrypted(v: impl Into<String>) -> Self {
41 Self {
42 kind: "STRING".into(),
43 value: JsonValue::String(v.into()),
44 is_encrypted: Some(true),
45 }
46 }
47
48 pub fn string_null() -> Self {
49 Self {
50 kind: "STRING".into(),
51 value: JsonValue::Null,
52 is_encrypted: None,
53 }
54 }
55
56 pub fn string_list(v: Vec<String>) -> Self {
57 Self {
58 kind: "STRING_LIST".into(),
59 value: serde_json::to_value(v).unwrap(),
60 is_encrypted: None,
61 }
62 }
63
64 pub fn string_list_null() -> Self {
65 Self {
66 kind: "STRING_LIST".into(),
67 value: JsonValue::Null,
68 is_encrypted: None,
69 }
70 }
71
72 pub fn int64(v: i64) -> Self {
73 Self {
74 kind: "INT64".into(),
75 value: JsonValue::Number(v.into()),
76 is_encrypted: None,
77 }
78 }
79
80 pub fn timestamp(ms: i64) -> Self {
81 Self {
82 kind: "TIMESTAMP".into(),
83 value: JsonValue::Number(ms.into()),
84 is_encrypted: None,
85 }
86 }
87
88 pub fn timestamp_null() -> Self {
89 Self {
90 kind: "TIMESTAMP".into(),
91 value: JsonValue::Null,
92 is_encrypted: None,
93 }
94 }
95
96 pub fn bytes(b64: impl Into<String>) -> Self {
97 Self {
98 kind: "BYTES".into(),
99 value: JsonValue::String(b64.into()),
100 is_encrypted: None,
101 }
102 }
103
104 pub fn asset_id(receipt: AssetReceipt) -> Self {
105 Self {
106 kind: "ASSETID".into(),
107 value: serde_json::to_value(receipt).unwrap(),
108 is_encrypted: None,
109 }
110 }
111}
112
113#[derive(Debug, Serialize, Deserialize, Clone)]
114#[serde(rename_all = "camelCase")]
115pub struct AssetToken {
116 pub record_type: String,
117 pub record_name: String,
118 pub field_name: String,
119}
120
121#[derive(Debug, Serialize, Deserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct AssetUploadRequest {
124 pub zone_id: ZoneId,
125 pub tokens: Vec<AssetToken>,
126}
127
128#[derive(Debug, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct AssetUploadResponse {
131 pub tokens: Vec<AssetUploadToken>,
132}
133
134#[derive(Debug, Serialize, Deserialize)]
135#[serde(rename_all = "camelCase")]
136pub struct AssetUploadToken {
137 pub record_name: String,
138 pub field_name: String,
139 pub url: String,
140}
141
142#[derive(Debug, Serialize, Deserialize, Clone)]
143#[serde(rename_all = "camelCase")]
144pub struct AssetReceipt {
145 pub file_checksum: String,
146 pub receipt: String,
147 pub size: i64,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub wrapping_key: Option<String>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub reference_checksum: Option<String>,
152}
153
154#[derive(Debug, Serialize, Deserialize)]
155#[serde(rename_all = "camelCase")]
156pub struct AssetUploadResult {
157 pub single_file: AssetReceipt,
158}
159
160pub type Fields = HashMap<String, CkField>;
161
162#[derive(Debug, Serialize, Deserialize, Clone)]
163#[serde(rename_all = "camelCase")]
164pub struct CkRecord {
165 pub record_name: String,
166 #[serde(default)]
167 pub record_type: String,
168 #[serde(default)]
169 pub fields: Fields,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub record_change_tag: Option<String>,
172 #[serde(default)]
173 pub deleted: bool,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub server_error_code: Option<String>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub reason: Option<String>,
179}
180
181#[derive(Debug, Serialize)]
182#[serde(rename_all = "camelCase")]
183pub struct ModifyOperation {
184 pub operation_type: String, pub record: CkRecord,
186}
187
188#[derive(Debug, Serialize)]
189#[serde(rename_all = "camelCase")]
190pub struct ModifyRequest {
191 pub operations: Vec<ModifyOperation>,
192 pub zone_id: ZoneId,
193}
194
195#[derive(Debug, Deserialize)]
196#[serde(rename_all = "camelCase")]
197pub struct ModifyResponse {
198 pub records: Vec<CkRecord>,
199}
200
201#[derive(Debug, Serialize, Clone)]
202#[serde(rename_all = "camelCase")]
203pub struct CkQuery {
204 pub record_type: String,
205 #[serde(skip_serializing_if = "Vec::is_empty")]
206 pub filter_by: Vec<CkFilter>,
207 #[serde(skip_serializing_if = "Vec::is_empty")]
208 pub sort_by: Vec<CkSort>,
209}
210
211#[derive(Debug, Serialize, Clone)]
212#[serde(rename_all = "camelCase")]
213pub struct CkFilter {
214 pub field_name: String,
215 pub comparator: String,
216 pub field_value: CkFilterValue,
217}
218
219#[derive(Debug, Serialize, Clone)]
220pub struct CkFilterValue {
221 pub value: JsonValue,
222 #[serde(rename = "type")]
223 pub kind: String,
224}
225
226#[derive(Debug, Serialize, Clone)]
227#[serde(rename_all = "camelCase")]
228pub struct CkSort {
229 pub field_name: String,
230 pub ascending: bool,
231}
232
233#[derive(Debug, Serialize)]
234#[serde(rename_all = "camelCase")]
235pub struct QueryRequest {
236 pub zone_id: ZoneId,
237 pub query: CkQuery,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 pub results_limit: Option<usize>,
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub desired_keys: Option<Vec<String>>,
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub continuation_marker: Option<String>,
244}
245
246#[derive(Debug, Deserialize)]
247#[serde(rename_all = "camelCase")]
248pub struct QueryResponse {
249 pub records: Vec<CkRecord>,
250 #[serde(default)]
251 pub continuation_marker: Option<String>,
252}
253
254#[derive(Debug, Serialize)]
255#[serde(rename_all = "camelCase")]
256pub struct LookupRequest {
257 pub records: Vec<LookupRecord>,
258 pub zone_id: ZoneId,
259}
260
261#[derive(Debug, Serialize)]
262#[serde(rename_all = "camelCase")]
263pub struct LookupRecord {
264 pub record_name: String,
265}
266
267#[derive(Debug, Deserialize)]
268#[serde(rename_all = "camelCase")]
269pub struct LookupResponse {
270 pub records: Vec<CkRecord>,
271}
272
273impl CkRecord {
274 pub fn str_field(&self, name: &str) -> Option<&str> {
275 self.fields.get(name)?.value.as_str()
276 }
277
278 pub fn i64_field(&self, name: &str) -> Option<i64> {
279 self.fields.get(name)?.value.as_i64()
280 }
281
282 pub fn bool_field(&self, name: &str) -> Option<bool> {
283 self.i64_field(name).map(|v| v != 0)
284 }
285
286 pub fn string_list_field(&self, name: &str) -> Vec<String> {
287 self.fields
288 .get(name)
289 .and_then(|f| f.value.as_array())
290 .map(|arr| {
291 arr.iter()
292 .filter_map(|v| v.as_str().map(str::to_string))
293 .collect()
294 })
295 .unwrap_or_default()
296 }
297}