1pub const TRASH_PARENT_ID: &str = "__trash__";
8
9use async_trait::async_trait;
10use serde::{Deserialize, Serialize};
11use serde_with::skip_serializing_none;
12use std::{cmp::Ordering, collections::HashMap, fmt::Debug};
13
14use crate::{
15 prelude::*,
16 types::{serialize_timestamp_iso, serialize_timestamp_iso_opt},
17};
18
19#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
22pub enum ProfileType {
23 #[default]
24 #[serde(rename = "person")]
25 Person,
26 #[serde(rename = "community")]
27 Community,
28}
29
30#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
31pub enum ProfileStatus {
32 #[serde(rename = "A")]
33 Active,
34 #[serde(rename = "T")]
35 Trusted,
36 #[serde(rename = "B")]
37 Blocked,
38 #[serde(rename = "M")]
39 Muted,
40 #[serde(rename = "S")]
41 Suspended,
42 #[serde(rename = "X")]
43 Banned,
44}
45
46impl ProfileStatus {
47 pub fn as_str(&self) -> &'static str {
49 match self {
50 ProfileStatus::Active => "active",
51 ProfileStatus::Trusted => "trusted",
52 ProfileStatus::Blocked => "blocked",
53 ProfileStatus::Muted => "muted",
54 ProfileStatus::Suspended => "suspended",
55 ProfileStatus::Banned => "banned",
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
63#[serde(rename_all = "lowercase")]
64pub enum ProfileTrust {
65 Always,
67 Never,
69}
70
71#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
72pub enum ProfileConnectionStatus {
73 #[default]
74 Disconnected,
75 RequestPending,
76 Connected,
77}
78
79impl ProfileConnectionStatus {
80 pub fn is_connected(&self) -> bool {
81 matches!(self, ProfileConnectionStatus::Connected)
82 }
83}
84
85impl std::fmt::Display for ProfileConnectionStatus {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 ProfileConnectionStatus::Disconnected => write!(f, "disconnected"),
89 ProfileConnectionStatus::RequestPending => write!(f, "pending"),
90 ProfileConnectionStatus::Connected => write!(f, "connected"),
91 }
92 }
93}
94
95#[skip_serializing_none]
99#[derive(Debug, Clone, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub struct RefData {
102 pub ref_id: Box<str>,
103 pub r#type: Box<str>,
104 pub description: Option<Box<str>>,
105 #[serde(serialize_with = "serialize_timestamp_iso")]
106 pub created_at: Timestamp,
107 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
108 pub expires_at: Option<Timestamp>,
109 pub count: Option<u32>,
111 pub resource_id: Option<Box<str>>,
113 pub access_level: Option<char>,
115 pub params: Option<Box<str>>,
117}
118
119pub struct ListRefsOptions {
120 pub typ: Option<String>,
121 pub filter: Option<String>, pub resource_id: Option<String>,
124}
125
126#[derive(Default)]
127pub struct CreateRefOptions {
128 pub typ: String,
129 pub description: Option<String>,
130 pub expires_at: Option<Timestamp>,
131 pub count: Option<u32>,
132 pub resource_id: Option<String>,
134 pub access_level: Option<char>,
136 pub params: Option<String>,
138}
139
140#[skip_serializing_none]
141#[derive(Debug, Serialize)]
142#[serde(rename_all = "camelCase")]
143pub struct Tenant<S: AsRef<str>> {
144 #[serde(rename = "id")]
145 pub tn_id: TnId,
146 pub id_tag: S,
147 pub name: S,
148 #[serde(rename = "type")]
149 pub typ: ProfileType,
150 pub profile_pic: Option<S>,
151 pub cover_pic: Option<S>,
152 #[serde(serialize_with = "serialize_timestamp_iso")]
153 pub created_at: Timestamp,
154 pub x: HashMap<S, S>,
155}
156
157#[derive(Debug, Default)]
159pub struct ListTenantsMetaOptions {
160 pub limit: Option<u32>,
161 pub offset: Option<u32>,
162}
163
164#[skip_serializing_none]
166#[derive(Debug, Clone, Serialize)]
167#[serde(rename_all = "camelCase")]
168pub struct TenantListMeta {
169 pub tn_id: TnId,
170 pub id_tag: Box<str>,
171 pub name: Box<str>,
172 #[serde(rename = "type")]
173 pub typ: ProfileType,
174 pub profile_pic: Option<Box<str>>,
175 #[serde(serialize_with = "serialize_timestamp_iso")]
176 pub created_at: Timestamp,
177}
178
179#[derive(Debug, Default, Deserialize)]
180pub struct UpdateTenantData {
181 #[serde(rename = "idTag", default)]
182 pub id_tag: Patch<String>,
183 #[serde(default)]
184 pub name: Patch<String>,
185 #[serde(rename = "type", default)]
186 pub typ: Patch<ProfileType>,
187 #[serde(rename = "profilePic", default)]
188 pub profile_pic: Patch<String>,
189 #[serde(rename = "coverPic", default)]
190 pub cover_pic: Patch<String>,
191 #[serde(default)]
193 pub x: Option<std::collections::HashMap<String, Option<String>>>,
194}
195
196#[derive(Debug)]
197pub struct Profile<S: AsRef<str>> {
198 pub id_tag: S,
199 pub name: S,
200 pub typ: ProfileType,
201 pub profile_pic: Option<S>,
202 pub status: Option<ProfileStatus>,
203 pub synced_at: Option<Timestamp>,
204 pub following: bool,
205 pub connected: ProfileConnectionStatus,
206 pub roles: Option<Box<[Box<str>]>>,
207 pub trust: Option<ProfileTrust>,
208}
209
210#[derive(Debug, Default, Deserialize)]
211pub struct ListProfileOptions {
212 #[serde(rename = "type")]
213 pub typ: Option<ProfileType>,
214 pub status: Option<Box<[ProfileStatus]>>,
215 pub connected: Option<ProfileConnectionStatus>,
216 pub following: Option<bool>,
217 pub q: Option<String>,
218 pub id_tag: Option<String>,
219 pub trust_set: Option<bool>,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
227#[serde(rename_all = "camelCase")]
228pub struct ProfileData {
229 pub id_tag: Box<str>,
230 pub name: Box<str>,
231 #[serde(rename = "type")]
232 pub r#type: Box<str>, pub profile_pic: Option<Box<str>>,
234 #[serde(default, skip_serializing_if = "Option::is_none")]
236 pub status: Option<Box<str>>,
237 #[serde(serialize_with = "serialize_timestamp_iso")]
238 pub created_at: Timestamp,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct ProfileList {
244 pub profiles: Vec<ProfileData>,
245 pub total: usize,
246 pub limit: usize,
247 pub offset: usize,
248}
249
250#[derive(Debug, Default, Deserialize)]
251pub struct UpdateProfileData {
252 #[serde(default)]
254 pub name: Patch<Box<str>>,
255 #[serde(default, rename = "profilePic")]
256 pub profile_pic: Patch<Option<Box<str>>>,
257 #[serde(default)]
258 pub roles: Patch<Option<Vec<Box<str>>>>,
259
260 #[serde(default)]
262 pub status: Patch<ProfileStatus>,
263
264 #[serde(default)]
266 pub synced: Patch<bool>,
267 #[serde(default)]
268 pub following: Patch<bool>,
269 #[serde(default)]
270 pub connected: Patch<ProfileConnectionStatus>,
271 #[serde(default)]
272 pub trust: Patch<ProfileTrust>,
273
274 #[serde(default)]
276 pub etag: Patch<Box<str>>,
277}
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub enum UpsertResult {
282 Created,
284 Updated,
286}
287
288#[derive(Default)]
309pub struct UpsertProfileFields {
310 pub name: Patch<Box<str>>,
311 pub typ: Patch<ProfileType>,
312 pub profile_pic: Patch<Option<Box<str>>>,
313 pub roles: Patch<Option<Vec<Box<str>>>>,
314 pub status: Patch<ProfileStatus>,
315 pub synced: Patch<bool>,
316 pub following: Patch<bool>,
317 pub connected: Patch<ProfileConnectionStatus>,
318 pub trust: Patch<ProfileTrust>,
319 pub etag: Patch<Box<str>>,
320}
321
322impl UpsertProfileFields {
323 pub fn from_update(update: UpdateProfileData) -> Self {
328 Self {
329 name: update.name,
330 typ: Patch::Undefined,
331 profile_pic: update.profile_pic,
332 roles: update.roles,
333 status: update.status,
334 synced: update.synced,
335 following: update.following,
336 connected: update.connected,
337 trust: update.trust,
338 etag: update.etag,
339 }
340 }
341}
342
343#[derive(Debug, Clone)]
348pub struct ActionData {
349 pub subject: Option<Box<str>>,
350 pub reactions: Option<Box<str>>,
351 pub comments: Option<u32>,
352}
353
354#[derive(Debug, Clone, Default)]
356pub struct UpdateActionDataOptions {
357 pub subject: Patch<String>,
358 pub reactions: Patch<String>,
359 pub comments: Patch<u32>,
360 pub comments_read: Patch<u32>,
361 pub status: Patch<char>,
362 pub visibility: Patch<char>,
363 pub x: Patch<serde_json::Value>, pub content: Patch<String>,
365 pub attachments: Patch<String>, pub flags: Patch<String>,
367 pub sub_typ: Patch<String>,
368 pub created_at: Patch<Timestamp>,
375}
376
377#[derive(Debug, Clone, Default)]
379pub struct FinalizeActionOptions<'a> {
380 pub attachments: Option<&'a [&'a str]>,
381 pub subject: Option<&'a str>,
382 pub audience_tag: Option<&'a str>,
383 pub key: Option<&'a str>,
384}
385
386fn deserialize_split<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
387where
388 D: serde::Deserializer<'de>,
389{
390 let s = String::deserialize(deserializer)?;
391 let values: Vec<String> =
392 s.split(',').map(|v| v.trim().to_string()).filter(|v| !v.is_empty()).collect();
393 if values.is_empty() { Ok(None) } else { Ok(Some(values)) }
394}
395
396#[derive(Debug, Clone, Copy, Deserialize)]
402#[serde(rename_all = "lowercase")]
403pub enum AudienceType {
404 Personal,
405 Community,
406}
407
408#[derive(Debug, Default, Deserialize)]
410#[serde(deny_unknown_fields)]
411pub struct ListActionOptions {
412 pub limit: Option<u32>,
414 pub cursor: Option<String>,
416 pub sort: Option<String>,
418 #[serde(rename = "sortDir")]
420 pub sort_dir: Option<String>,
421 #[serde(default, rename = "type", deserialize_with = "deserialize_split")]
422 pub typ: Option<Vec<String>>,
423 #[serde(default, deserialize_with = "deserialize_split")]
424 pub status: Option<Vec<String>>,
425 pub tag: Option<String>,
426 pub search: Option<String>,
427 #[serde(default, deserialize_with = "deserialize_split")]
428 pub visibility: Option<Vec<String>>,
429 pub issuer: Option<String>,
430 pub audience: Option<String>,
431 #[serde(rename = "audienceType")]
432 pub audience_type: Option<AudienceType>,
433 pub involved: Option<String>,
434 #[serde(skip)]
436 pub viewer_id_tag: Option<String>,
437 #[serde(rename = "actionId")]
438 pub action_id: Option<String>,
439 #[serde(rename = "parentId")]
440 pub parent_id: Option<String>,
441 #[serde(rename = "rootId")]
442 pub root_id: Option<String>,
443 pub subject: Option<String>,
444 #[serde(rename = "createdAfter")]
445 pub created_after: Option<Timestamp>,
446}
447
448#[skip_serializing_none]
449#[derive(Debug, Clone, Serialize)]
450pub struct ProfileInfo {
451 #[serde(rename = "idTag")]
452 pub id_tag: Box<str>,
453 pub name: Box<str>,
454 #[serde(rename = "type")]
455 pub typ: ProfileType,
456 #[serde(rename = "profilePic")]
457 pub profile_pic: Option<Box<str>>,
458}
459
460pub struct Action<S: AsRef<str>> {
461 pub action_id: S,
462 pub typ: S,
463 pub sub_typ: Option<S>,
464 pub issuer_tag: S,
465 pub parent_id: Option<S>,
466 pub root_id: Option<S>,
467 pub audience_tag: Option<S>,
468 pub content: Option<S>,
469 pub attachments: Option<Vec<S>>,
470 pub subject: Option<S>,
471 pub created_at: Timestamp,
472 pub expires_at: Option<Timestamp>,
473 pub visibility: Option<char>, pub flags: Option<S>, pub x: Option<serde_json::Value>, }
477
478#[skip_serializing_none]
479#[derive(Debug, Clone, Serialize)]
480pub struct AttachmentView {
481 #[serde(rename = "fileId")]
482 pub file_id: Box<str>,
483 pub dim: Option<(u32, u32)>,
484 #[serde(rename = "localVariants")]
485 pub local_variants: Option<Vec<Box<str>>>,
486}
487
488#[skip_serializing_none]
489#[derive(Debug, Clone, Serialize)]
490#[serde(rename_all = "camelCase")]
491pub struct ActionView {
492 pub action_id: Box<str>,
493 #[serde(rename = "type")]
494 pub typ: Box<str>,
495 #[serde(rename = "subType")]
496 pub sub_typ: Option<Box<str>>,
497 pub parent_id: Option<Box<str>>,
498 pub root_id: Option<Box<str>>,
499 pub issuer: ProfileInfo,
500 pub audience: Option<ProfileInfo>,
501 pub content: Option<serde_json::Value>,
502 pub attachments: Option<Vec<AttachmentView>>,
503 pub subject: Option<Box<str>>,
504 pub subject_profile: Option<ProfileInfo>,
505 #[serde(serialize_with = "serialize_timestamp_iso")]
506 pub created_at: Timestamp,
507 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
508 pub expires_at: Option<Timestamp>,
509 pub status: Option<Box<str>>,
510 pub stat: Option<serde_json::Value>,
511 pub visibility: Option<char>,
512 pub flags: Option<Box<str>>, pub x: Option<serde_json::Value>, }
515
516#[derive(Debug)]
519pub enum FileId<S: AsRef<str>> {
520 FileId(S),
521 FId(u64),
522}
523
524pub enum ActionId<S: AsRef<str>> {
525 ActionId(S),
526 AId(u64),
527}
528
529#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
532pub enum FileStatus {
533 #[serde(rename = "A")]
534 Active,
535 #[serde(rename = "P")]
536 Pending,
537 #[serde(rename = "D")]
538 Deleted,
539}
540
541#[skip_serializing_none]
543#[derive(Debug, Clone, Default, Serialize)]
544#[serde(rename_all = "camelCase")]
545pub struct FileUserData {
546 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
547 pub accessed_at: Option<Timestamp>,
548 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
549 pub modified_at: Option<Timestamp>,
550 pub pinned: bool,
551 pub starred: bool,
552}
553
554#[skip_serializing_none]
555#[derive(Debug, Clone, Serialize)]
556#[serde(rename_all = "camelCase")]
557pub struct FileView {
558 pub file_id: Box<str>,
559 pub parent_id: Option<Box<str>>, pub root_id: Option<Box<str>>, pub owner: Option<ProfileInfo>,
562 pub creator: Option<ProfileInfo>,
563 pub preset: Option<Box<str>>,
564 pub content_type: Option<Box<str>>,
565 pub file_name: Box<str>,
566 pub file_tp: Option<Box<str>>, #[serde(serialize_with = "serialize_timestamp_iso")]
568 pub created_at: Timestamp,
569 #[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
570 pub accessed_at: Option<Timestamp>, #[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
572 pub modified_at: Option<Timestamp>, pub status: FileStatus,
574 pub tags: Option<Vec<Box<str>>>,
575 pub visibility: Option<char>, pub hidden: bool,
577 pub access_level: Option<crate::types::AccessLevel>, pub user_data: Option<FileUserData>, pub x: Option<serde_json::Value>, }
581
582#[skip_serializing_none]
583#[derive(Debug, Clone, Serialize)]
584pub struct FileVariant<S: AsRef<str> + Debug> {
585 #[serde(rename = "variantId")]
586 pub variant_id: S,
587 pub variant: S,
588 pub format: S,
589 pub size: u64,
590 pub resolution: (u32, u32),
591 pub available: bool,
592 pub duration: Option<f64>,
594 pub bitrate: Option<u32>,
596 #[serde(rename = "pageCount")]
598 pub page_count: Option<u32>,
599}
600
601impl<S: AsRef<str> + Debug> PartialEq for FileVariant<S> {
602 fn eq(&self, other: &Self) -> bool {
603 self.variant_id.as_ref() == other.variant_id.as_ref()
604 && self.variant.as_ref() == other.variant.as_ref()
605 && self.format.as_ref() == other.format.as_ref()
606 && self.size == other.size
607 && self.resolution == other.resolution
608 && self.available == other.available
609 && self.duration == other.duration
610 && self.bitrate == other.bitrate
611 && self.page_count == other.page_count
612 }
613}
614
615impl<S: AsRef<str> + Debug> Eq for FileVariant<S> {}
616
617impl<S: AsRef<str> + Debug + Ord> PartialOrd for FileVariant<S> {
618 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
619 Some(self.cmp(other))
620 }
621}
622
623impl<S: AsRef<str> + Debug + Ord> Ord for FileVariant<S> {
624 fn cmp(&self, other: &Self) -> Ordering {
625 self.size
626 .cmp(&other.size)
627 .then_with(|| self.resolution.0.cmp(&other.resolution.0))
628 .then_with(|| self.resolution.1.cmp(&other.resolution.1))
629 .then_with(|| self.variant.as_ref().cmp(other.variant.as_ref()))
630 }
631}
632
633#[derive(Debug, Default, Deserialize)]
638#[serde(deny_unknown_fields)]
639pub struct ListFileOptions {
640 pub limit: Option<u32>,
642 pub cursor: Option<String>,
644 #[serde(default, rename = "fileId", deserialize_with = "deserialize_split")]
645 pub file_id: Option<Vec<String>>,
646 #[serde(rename = "parentId")]
647 pub parent_id: Option<String>, #[serde(rename = "rootId")]
649 pub root_id: Option<String>, pub tag: Option<String>,
651 pub preset: Option<String>,
652 pub variant: Option<String>,
653 pub status: Option<FileStatus>,
655 #[serde(default, rename = "fileTp", deserialize_with = "deserialize_split")]
656 pub file_type: Option<Vec<String>>,
657 #[serde(default, rename = "contentType", deserialize_with = "deserialize_split")]
659 pub content_type: Option<Vec<String>>,
660 #[serde(rename = "fileName")]
662 pub file_name: Option<String>,
663 #[serde(rename = "ownerIdTag")]
665 pub owner_id_tag: Option<String>,
666 #[serde(rename = "notOwnerIdTag")]
668 pub not_owner_id_tag: Option<String>,
669 pub pinned: Option<bool>,
671 pub starred: Option<bool>,
673 pub hidden: Option<bool>,
675 pub sort: Option<String>,
677 #[serde(rename = "sortDir")]
679 pub sort_dir: Option<String>,
680 #[serde(skip)]
682 pub user_id_tag: Option<String>,
683 #[serde(skip)]
686 pub scope_file_id: Option<String>,
687 #[serde(skip)]
691 pub visible_levels: Option<Vec<char>>,
692}
693
694#[derive(Debug, Clone, Default)]
695pub struct CreateFile {
696 pub orig_variant_id: Option<Box<str>>,
697 pub file_id: Option<Box<str>>,
698 pub parent_id: Option<Box<str>>, pub root_id: Option<Box<str>>, pub owner_tag: Option<Box<str>>, pub creator_tag: Option<Box<str>>, pub preset: Option<Box<str>>,
703 pub content_type: Box<str>,
704 pub file_name: Box<str>,
705 pub file_tp: Option<Box<str>>, pub created_at: Option<Timestamp>,
707 pub tags: Option<Vec<Box<str>>>,
708 pub x: Option<serde_json::Value>,
709 pub visibility: Option<char>, pub hidden: bool,
711 pub status: Option<FileStatus>, }
713
714#[derive(Debug, Clone, Deserialize)]
715pub struct CreateFileVariant {
716 pub variant: Box<str>,
717 pub format: Box<str>,
718 pub resolution: (u32, u32),
719 pub size: u64,
720 pub available: bool,
721}
722
723#[derive(Debug, Clone, Default, Deserialize)]
725pub struct UpdateFileOptions {
726 #[serde(default, rename = "fileName")]
727 pub file_name: Patch<String>,
728 #[serde(default, rename = "parentId")]
729 pub parent_id: Patch<String>, #[serde(default)]
731 pub visibility: Patch<char>,
732 #[serde(default)]
733 pub status: Patch<char>,
734 #[serde(default)]
735 pub hidden: Patch<bool>,
736}
737
738#[skip_serializing_none]
742#[derive(Debug, Clone, Serialize)]
743#[serde(rename_all = "camelCase")]
744pub struct ShareEntry {
745 pub id: i64,
746 pub resource_type: char,
747 pub resource_id: Box<str>,
748 pub subject_type: char,
749 pub subject_id: Box<str>,
750 pub permission: char,
751 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
752 pub expires_at: Option<Timestamp>,
753 pub created_by: Box<str>,
754 #[serde(serialize_with = "serialize_timestamp_iso")]
755 pub created_at: Timestamp,
756 pub subject_file_name: Option<Box<str>>,
758 pub subject_content_type: Option<Box<str>>,
759 pub subject_file_tp: Option<Box<str>>,
760}
761
762#[derive(Debug, Deserialize)]
763#[serde(rename_all = "camelCase")]
764pub struct CreateShareEntry {
765 pub subject_type: char,
766 pub subject_id: String,
767 pub permission: char,
768 pub expires_at: Option<Timestamp>,
769}
770
771#[skip_serializing_none]
776#[derive(Debug, Clone, Serialize, Deserialize)]
777pub struct PushSubscriptionData {
778 pub endpoint: String,
780 #[serde(rename = "expirationTime")]
782 pub expiration_time: Option<i64>,
783 pub keys: PushSubscriptionKeys,
785}
786
787#[derive(Debug, Clone, Serialize, Deserialize)]
789pub struct PushSubscriptionKeys {
790 pub p256dh: String,
792 pub auth: String,
794}
795
796#[derive(Debug, Clone, Serialize)]
798#[serde(rename_all = "camelCase")]
799pub struct PushSubscription {
800 pub id: u64,
802 pub subscription: PushSubscriptionData,
804 #[serde(serialize_with = "serialize_timestamp_iso")]
806 pub created_at: Timestamp,
807}
808
809pub struct Task {
812 pub task_id: u64,
813 pub tn_id: TnId,
814 pub kind: Box<str>,
815 pub status: char,
816 pub created_at: Timestamp,
817 pub next_at: Option<Timestamp>,
818 pub input: Box<str>,
819 pub output: Box<str>,
820 pub deps: Box<[u64]>,
821 pub retry: Option<Box<str>>,
822 pub cron: Option<Box<str>>,
823}
824
825#[derive(Debug, Default)]
826pub struct TaskPatch {
827 pub input: Patch<String>,
828 pub next_at: Patch<Timestamp>,
829 pub deps: Patch<Vec<u64>>,
830 pub retry: Patch<String>,
831 pub cron: Patch<String>,
832}
833
834#[derive(Debug, Default)]
835pub struct ListTaskOptions {}
836
837#[derive(Debug)]
842pub struct InstallApp {
843 pub app_name: Box<str>,
844 pub publisher_tag: Box<str>,
845 pub version: Box<str>,
846 pub action_id: Box<str>,
847 pub file_id: Box<str>,
848 pub blob_id: Box<str>,
849 pub capabilities: Option<Vec<Box<str>>>,
850}
851
852#[derive(Debug, Serialize)]
854#[serde(rename_all = "camelCase")]
855pub struct InstalledApp {
856 pub app_name: Box<str>,
857 pub publisher_tag: Box<str>,
858 pub version: Box<str>,
859 pub action_id: Box<str>,
860 pub file_id: Box<str>,
861 pub blob_id: Box<str>,
862 pub status: Box<str>,
863 pub capabilities: Option<Vec<Box<str>>>,
864 pub auto_update: bool,
865 #[serde(serialize_with = "serialize_timestamp_iso")]
866 pub installed_at: Timestamp,
867}
868
869#[derive(Debug, Clone, Serialize)]
874#[serde(rename_all = "camelCase")]
875pub struct AddressBook {
876 pub ab_id: u64,
877 pub name: Box<str>,
878 pub description: Option<Box<str>>,
879 pub ctag: Box<str>,
881 #[serde(serialize_with = "serialize_timestamp_iso")]
882 pub created_at: Timestamp,
883 #[serde(serialize_with = "serialize_timestamp_iso")]
884 pub updated_at: Timestamp,
885}
886
887#[derive(Debug, Default)]
888pub struct UpdateAddressBookData {
889 pub name: Patch<String>,
890 pub description: Patch<String>,
891}
892
893#[derive(Debug, Clone, Default)]
897pub struct ContactExtracted {
898 pub fn_name: Option<Box<str>>,
899 pub given_name: Option<Box<str>>,
900 pub family_name: Option<Box<str>>,
901 pub email: Option<Box<str>>,
902 pub emails: Option<Box<str>>,
903 pub tel: Option<Box<str>>,
904 pub tels: Option<Box<str>>,
905 pub org: Option<Box<str>>,
906 pub title: Option<Box<str>>,
907 pub note: Option<Box<str>>,
908 pub photo_uri: Option<Box<str>>,
909 pub profile_id_tag: Option<Box<str>>,
910}
911
912#[derive(Debug, Clone)]
914pub struct Contact {
915 pub c_id: u64,
916 pub ab_id: u64,
917 pub uid: Box<str>,
918 pub etag: Box<str>,
919 pub vcard: Box<str>,
920 pub extracted: ContactExtracted,
921 pub created_at: Timestamp,
922 pub updated_at: Timestamp,
923}
924
925#[derive(Debug, Clone)]
928pub struct ContactView {
929 pub c_id: u64,
930 pub ab_id: u64,
931 pub uid: Box<str>,
932 pub etag: Box<str>,
933 pub extracted: ContactExtracted,
934 pub created_at: Timestamp,
935 pub updated_at: Timestamp,
936}
937
938#[derive(Debug, Clone)]
941pub struct ContactSyncEntry {
942 pub uid: Box<str>,
943 pub etag: Box<str>,
944 pub deleted: bool,
945 pub updated_at: Timestamp,
946}
947
948#[derive(Debug, Default)]
949pub struct ListContactOptions {
950 pub q: Option<String>,
952 pub cursor: Option<String>,
954 pub limit: Option<u32>,
956}
957
958#[derive(Debug, Clone, Serialize)]
963#[serde(rename_all = "camelCase")]
964pub struct Calendar {
965 pub cal_id: u64,
966 pub name: Box<str>,
967 pub description: Option<Box<str>>,
968 pub color: Option<Box<str>>,
970 pub timezone: Option<Box<str>>,
972 pub components: Box<str>,
974 pub ctag: Box<str>,
976 #[serde(serialize_with = "serialize_timestamp_iso")]
977 pub created_at: Timestamp,
978 #[serde(serialize_with = "serialize_timestamp_iso")]
979 pub updated_at: Timestamp,
980}
981
982#[derive(Debug, Default)]
983pub struct CreateCalendarData {
984 pub name: String,
985 pub description: Option<String>,
986 pub color: Option<String>,
987 pub timezone: Option<String>,
988 pub components: Option<String>,
990}
991
992#[derive(Debug, Default)]
993pub struct UpdateCalendarData {
994 pub name: Patch<String>,
995 pub description: Patch<String>,
996 pub color: Patch<String>,
997 pub timezone: Patch<String>,
998 pub components: Patch<String>,
999}
1000
1001#[derive(Debug, Clone, Default)]
1004pub struct CalendarObjectExtracted {
1005 pub component: Box<str>,
1007 pub summary: Option<Box<str>>,
1008 pub location: Option<Box<str>>,
1009 pub description: Option<Box<str>>,
1010 pub dtstart: Option<Timestamp>,
1012 pub dtend: Option<Timestamp>,
1014 pub all_day: bool,
1016 pub status: Option<Box<str>>,
1018 pub priority: Option<u8>,
1020 pub organizer: Option<Box<str>>,
1021 pub rrule: Option<Box<str>>,
1023 pub exdate: Vec<Timestamp>,
1025 pub recurrence_id: Option<Timestamp>,
1027 pub sequence: i64,
1028}
1029
1030#[derive(Debug, Clone, Copy)]
1035pub struct CalendarObjectWrite<'a> {
1036 pub uid: &'a str,
1037 pub ical: &'a str,
1038 pub etag: &'a str,
1039 pub extracted: &'a CalendarObjectExtracted,
1040}
1041
1042#[derive(Debug, Clone)]
1044pub struct CalendarObject {
1045 pub co_id: u64,
1046 pub cal_id: u64,
1047 pub uid: Box<str>,
1048 pub etag: Box<str>,
1049 pub ical: Box<str>,
1050 pub extracted: CalendarObjectExtracted,
1051 pub created_at: Timestamp,
1052 pub updated_at: Timestamp,
1053}
1054
1055#[derive(Debug, Clone)]
1057pub struct CalendarObjectView {
1058 pub co_id: u64,
1059 pub cal_id: u64,
1060 pub uid: Box<str>,
1061 pub etag: Box<str>,
1062 pub extracted: CalendarObjectExtracted,
1063 pub created_at: Timestamp,
1064 pub updated_at: Timestamp,
1065}
1066
1067#[derive(Debug, Clone)]
1070pub struct CalendarObjectSyncEntry {
1071 pub uid: Box<str>,
1072 pub etag: Box<str>,
1073 pub deleted: bool,
1074 pub updated_at: Timestamp,
1075}
1076
1077#[derive(Debug, Default)]
1078pub struct ListCalendarObjectOptions {
1079 pub component: Option<String>,
1081 pub q: Option<String>,
1083 pub start: Option<Timestamp>,
1085 pub end: Option<Timestamp>,
1087 pub cursor: Option<String>,
1088 pub limit: Option<u32>,
1089 pub include_exceptions: bool,
1092}
1093
1094#[async_trait]
1095pub trait MetaAdapter: Debug + Send + Sync {
1096 async fn read_tenant(&self, tn_id: TnId) -> ClResult<Tenant<Box<str>>>;
1101
1102 async fn create_tenant(&self, tn_id: TnId, id_tag: &str) -> ClResult<TnId>;
1104
1105 async fn update_tenant(&self, tn_id: TnId, tenant: &UpdateTenantData) -> ClResult<()>;
1107
1108 async fn delete_tenant(&self, tn_id: TnId) -> ClResult<()>;
1110
1111 async fn list_tenants(&self, opts: &ListTenantsMetaOptions) -> ClResult<Vec<TenantListMeta>>;
1113
1114 async fn list_profiles(
1116 &self,
1117 tn_id: TnId,
1118 opts: &ListProfileOptions,
1119 ) -> ClResult<Vec<Profile<Box<str>>>>;
1120
1121 async fn get_relationships(
1128 &self,
1129 tn_id: TnId,
1130 target_id_tags: &[&str],
1131 ) -> ClResult<HashMap<String, (bool, bool)>>;
1132
1133 async fn read_profile(
1137 &self,
1138 tn_id: TnId,
1139 id_tag: &str,
1140 ) -> ClResult<(Box<str>, Profile<Box<str>>)>;
1141
1142 async fn read_profile_roles(
1144 &self,
1145 tn_id: TnId,
1146 id_tag: &str,
1147 ) -> ClResult<Option<Box<[Box<str>]>>>;
1148
1149 async fn upsert_profile(
1156 &self,
1157 tn_id: TnId,
1158 id_tag: &str,
1159 fields: &UpsertProfileFields,
1160 ) -> ClResult<UpsertResult>;
1161
1162 async fn read_profile_public_key(
1166 &self,
1167 id_tag: &str,
1168 key_id: &str,
1169 ) -> ClResult<(Box<str>, Timestamp)>;
1170 async fn add_profile_public_key(
1176 &self,
1177 id_tag: &str,
1178 key_id: &str,
1179 public_key: &str,
1180 expires_at: Option<Timestamp>,
1181 ) -> ClResult<()>;
1182 async fn list_stale_profiles(
1193 &self,
1194 max_age_secs: i64,
1195 disable_after_secs: i64,
1196 limit: u32,
1197 ) -> ClResult<Vec<(TnId, Box<str>, Option<Box<str>>)>>;
1198
1199 async fn get_action_id(&self, tn_id: TnId, a_id: u64) -> ClResult<Box<str>>;
1202 async fn list_actions(
1203 &self,
1204 tn_id: TnId,
1205 opts: &ListActionOptions,
1206 ) -> ClResult<Vec<ActionView>>;
1207 async fn list_action_tokens(
1208 &self,
1209 tn_id: TnId,
1210 opts: &ListActionOptions,
1211 ) -> ClResult<Box<[Box<str>]>>;
1212
1213 async fn create_action(
1214 &self,
1215 tn_id: TnId,
1216 action: &Action<&str>,
1217 key: Option<&str>,
1218 ) -> ClResult<ActionId<Box<str>>>;
1219
1220 async fn finalize_action(
1221 &self,
1222 tn_id: TnId,
1223 a_id: u64,
1224 action_id: &str,
1225 options: FinalizeActionOptions<'_>,
1226 ) -> ClResult<()>;
1227
1228 async fn create_inbound_action(
1229 &self,
1230 tn_id: TnId,
1231 action_id: &str,
1232 token: &str,
1233 ack_token: Option<&str>,
1234 ) -> ClResult<()>;
1235
1236 async fn get_action_root_id(&self, tn_id: TnId, action_id: &str) -> ClResult<Box<str>>;
1238
1239 async fn get_action_data(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionData>>;
1241
1242 async fn get_action_by_key(
1244 &self,
1245 tn_id: TnId,
1246 action_key: &str,
1247 ) -> ClResult<Option<Action<Box<str>>>>;
1248
1249 async fn store_action_token(&self, tn_id: TnId, action_id: &str, token: &str) -> ClResult<()>;
1251
1252 async fn get_action_token(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<Box<str>>>;
1254
1255 async fn update_action_data(
1257 &self,
1258 tn_id: TnId,
1259 action_id: &str,
1260 opts: &UpdateActionDataOptions,
1261 ) -> ClResult<()>;
1262
1263 async fn update_inbound_action(
1265 &self,
1266 tn_id: TnId,
1267 action_id: &str,
1268 status: Option<char>,
1269 ) -> ClResult<()>;
1270
1271 async fn get_related_action_tokens(
1274 &self,
1275 tn_id: TnId,
1276 aprv_action_id: &str,
1277 ) -> ClResult<Vec<(Box<str>, Box<str>)>>;
1278
1279 async fn get_file_id(&self, tn_id: TnId, f_id: u64) -> ClResult<Box<str>>;
1282 async fn list_files(&self, tn_id: TnId, opts: &ListFileOptions) -> ClResult<Vec<FileView>>;
1283 async fn list_file_variants(
1284 &self,
1285 tn_id: TnId,
1286 file_id: FileId<&str>,
1287 ) -> ClResult<Vec<FileVariant<Box<str>>>>;
1288 async fn list_available_variants(&self, tn_id: TnId, file_id: &str) -> ClResult<Vec<Box<str>>>;
1290 async fn read_file_variant(
1291 &self,
1292 tn_id: TnId,
1293 variant_id: &str,
1294 ) -> ClResult<FileVariant<Box<str>>>;
1295 async fn read_file_id_by_variant(&self, tn_id: TnId, variant_id: &str) -> ClResult<Box<str>>;
1297 async fn read_f_id_by_file_id(&self, tn_id: TnId, file_id: &str) -> ClResult<u64>;
1299 async fn create_file(&self, tn_id: TnId, opts: CreateFile) -> ClResult<FileId<Box<str>>>;
1300 async fn create_file_variant<'a>(
1301 &'a self,
1302 tn_id: TnId,
1303 f_id: u64,
1304 opts: FileVariant<&'a str>,
1305 ) -> ClResult<&'a str>;
1306 async fn update_file_id(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
1307
1308 async fn finalize_file(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
1310
1311 async fn list_tasks(&self, opts: ListTaskOptions) -> ClResult<Vec<Task>>;
1314 async fn list_task_ids(&self, kind: &str, keys: &[Box<str>]) -> ClResult<Vec<u64>>;
1315 async fn create_task(
1316 &self,
1317 kind: &'static str,
1318 key: Option<&str>,
1319 input: &str,
1320 deps: &[u64],
1321 ) -> ClResult<u64>;
1322 async fn update_task_finished(&self, task_id: u64, output: &str) -> ClResult<()>;
1323 async fn update_task_error(
1324 &self,
1325 task_id: u64,
1326 output: &str,
1327 next_at: Option<Timestamp>,
1328 ) -> ClResult<()>;
1329
1330 async fn find_task_by_key(&self, key: &str) -> ClResult<Option<Task>>;
1332
1333 async fn update_task(&self, task_id: u64, patch: &TaskPatch) -> ClResult<()>;
1335
1336 async fn find_completed_deps(&self, deps: &[u64]) -> ClResult<Vec<u64>>;
1338
1339 async fn get_profile_info(&self, tn_id: TnId, id_tag: &str) -> ClResult<ProfileData>;
1343
1344 async fn get_action(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionView>>;
1348
1349 async fn update_action(
1351 &self,
1352 tn_id: TnId,
1353 action_id: &str,
1354 content: Option<&str>,
1355 attachments: Option<&[&str]>,
1356 ) -> ClResult<()>;
1357
1358 async fn delete_action(&self, tn_id: TnId, action_id: &str) -> ClResult<()>;
1360
1361 async fn count_reactions(&self, tn_id: TnId, subject_id: &str) -> ClResult<String>;
1364
1365 async fn delete_file(&self, tn_id: TnId, file_id: &str) -> ClResult<()>;
1369
1370 async fn list_children_by_root(&self, tn_id: TnId, root_id: &str) -> ClResult<Vec<Box<str>>>;
1372
1373 async fn list_settings(
1377 &self,
1378 tn_id: TnId,
1379 prefix: Option<&[String]>,
1380 ) -> ClResult<std::collections::HashMap<String, serde_json::Value>>;
1381
1382 async fn read_setting(&self, tn_id: TnId, name: &str) -> ClResult<Option<serde_json::Value>>;
1384
1385 async fn update_setting(
1387 &self,
1388 tn_id: TnId,
1389 name: &str,
1390 value: Option<serde_json::Value>,
1391 ) -> ClResult<()>;
1392
1393 async fn list_refs(&self, tn_id: TnId, opts: &ListRefsOptions) -> ClResult<Vec<RefData>>;
1397
1398 async fn get_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<Option<(Box<str>, Box<str>)>>;
1400
1401 async fn create_ref(
1403 &self,
1404 tn_id: TnId,
1405 ref_id: &str,
1406 opts: &CreateRefOptions,
1407 ) -> ClResult<RefData>;
1408
1409 async fn delete_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<()>;
1411
1412 async fn use_ref(
1415 &self,
1416 ref_id: &str,
1417 expected_types: &[&str],
1418 ) -> ClResult<(TnId, Box<str>, RefData)>;
1419
1420 async fn validate_ref(
1423 &self,
1424 ref_id: &str,
1425 expected_types: &[&str],
1426 ) -> ClResult<(TnId, Box<str>, RefData)>;
1427
1428 async fn list_tags(
1438 &self,
1439 tn_id: TnId,
1440 prefix: Option<&str>,
1441 with_counts: bool,
1442 limit: Option<u32>,
1443 ) -> ClResult<Vec<TagInfo>>;
1444
1445 async fn add_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
1447
1448 async fn remove_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
1450
1451 async fn update_file_data(
1455 &self,
1456 tn_id: TnId,
1457 file_id: &str,
1458 opts: &UpdateFileOptions,
1459 ) -> ClResult<()>;
1460
1461 async fn read_file(&self, tn_id: TnId, file_id: &str) -> ClResult<Option<FileView>>;
1463
1464 async fn record_file_access(&self, tn_id: TnId, id_tag: &str, file_id: &str) -> ClResult<()>;
1469
1470 async fn record_file_modification(
1472 &self,
1473 tn_id: TnId,
1474 id_tag: &str,
1475 file_id: &str,
1476 ) -> ClResult<()>;
1477
1478 async fn update_file_user_data(
1480 &self,
1481 tn_id: TnId,
1482 id_tag: &str,
1483 file_id: &str,
1484 pinned: Option<bool>,
1485 starred: Option<bool>,
1486 ) -> ClResult<FileUserData>;
1487
1488 async fn get_file_user_data(
1490 &self,
1491 tn_id: TnId,
1492 id_tag: &str,
1493 file_id: &str,
1494 ) -> ClResult<Option<FileUserData>>;
1495
1496 async fn list_push_subscriptions(&self, tn_id: TnId) -> ClResult<Vec<PushSubscription>>;
1504
1505 async fn create_push_subscription(
1511 &self,
1512 tn_id: TnId,
1513 subscription: &PushSubscriptionData,
1514 ) -> ClResult<u64>;
1515
1516 async fn delete_push_subscription(&self, tn_id: TnId, subscription_id: u64) -> ClResult<()>;
1521
1522 async fn create_share_entry(
1527 &self,
1528 tn_id: TnId,
1529 resource_type: char,
1530 resource_id: &str,
1531 created_by: &str,
1532 entry: &CreateShareEntry,
1533 ) -> ClResult<ShareEntry>;
1534
1535 async fn delete_share_entry(&self, tn_id: TnId, id: i64) -> ClResult<()>;
1537
1538 async fn list_share_entries(
1540 &self,
1541 tn_id: TnId,
1542 resource_type: char,
1543 resource_id: &str,
1544 ) -> ClResult<Vec<ShareEntry>>;
1545
1546 async fn list_share_entries_by_subject(
1549 &self,
1550 tn_id: TnId,
1551 subject_type: Option<char>,
1552 subject_id: &str,
1553 ) -> ClResult<Vec<ShareEntry>>;
1554
1555 async fn check_share_access(
1558 &self,
1559 tn_id: TnId,
1560 resource_type: char,
1561 resource_id: &str,
1562 subject_type: char,
1563 subject_id: &str,
1564 ) -> ClResult<Option<char>>;
1565
1566 async fn read_share_entry(&self, tn_id: TnId, id: i64) -> ClResult<Option<ShareEntry>>;
1568
1569 async fn install_app(&self, tn_id: TnId, install: &InstallApp) -> ClResult<()>;
1574
1575 async fn uninstall_app(&self, tn_id: TnId, app_name: &str, publisher_tag: &str)
1577 -> ClResult<()>;
1578
1579 async fn list_installed_apps(
1581 &self,
1582 tn_id: TnId,
1583 search: Option<&str>,
1584 ) -> ClResult<Vec<InstalledApp>>;
1585
1586 async fn get_installed_app(
1588 &self,
1589 tn_id: TnId,
1590 app_name: &str,
1591 publisher_tag: &str,
1592 ) -> ClResult<Option<InstalledApp>>;
1593
1594 async fn create_address_book(
1599 &self,
1600 tn_id: TnId,
1601 name: &str,
1602 description: Option<&str>,
1603 ) -> ClResult<AddressBook>;
1604
1605 async fn list_address_books(&self, tn_id: TnId) -> ClResult<Vec<AddressBook>>;
1607
1608 async fn get_address_book(&self, tn_id: TnId, ab_id: u64) -> ClResult<Option<AddressBook>>;
1610
1611 async fn get_address_book_by_name(
1613 &self,
1614 tn_id: TnId,
1615 name: &str,
1616 ) -> ClResult<Option<AddressBook>>;
1617
1618 async fn update_address_book(
1620 &self,
1621 tn_id: TnId,
1622 ab_id: u64,
1623 patch: &UpdateAddressBookData,
1624 ) -> ClResult<()>;
1625
1626 async fn delete_address_book(&self, tn_id: TnId, ab_id: u64) -> ClResult<()>;
1628
1629 async fn list_contacts(
1632 &self,
1633 tn_id: TnId,
1634 ab_id: Option<u64>,
1635 opts: &ListContactOptions,
1636 ) -> ClResult<Vec<ContactView>>;
1637
1638 async fn get_contact(&self, tn_id: TnId, ab_id: u64, uid: &str) -> ClResult<Option<Contact>>;
1640
1641 async fn upsert_contact(
1644 &self,
1645 tn_id: TnId,
1646 ab_id: u64,
1647 uid: &str,
1648 vcard: &str,
1649 etag: &str,
1650 extracted: &ContactExtracted,
1651 ) -> ClResult<Box<str>>;
1652
1653 async fn delete_contact(&self, tn_id: TnId, ab_id: u64, uid: &str) -> ClResult<()>;
1656
1657 async fn get_contacts_by_uids(
1659 &self,
1660 tn_id: TnId,
1661 ab_id: u64,
1662 uids: &[&str],
1663 ) -> ClResult<Vec<Contact>>;
1664
1665 async fn list_contacts_since(
1671 &self,
1672 tn_id: TnId,
1673 ab_id: u64,
1674 since: Option<Timestamp>,
1675 limit: Option<u32>,
1676 ) -> ClResult<Vec<ContactSyncEntry>>;
1677
1678 async fn list_contacts_by_profile(
1680 &self,
1681 tn_id: TnId,
1682 profile_id_tag: &str,
1683 ) -> ClResult<Vec<Contact>>;
1684
1685 async fn create_calendar(&self, tn_id: TnId, input: &CreateCalendarData) -> ClResult<Calendar>;
1690
1691 async fn list_calendars(&self, tn_id: TnId) -> ClResult<Vec<Calendar>>;
1693
1694 async fn get_calendar(&self, tn_id: TnId, cal_id: u64) -> ClResult<Option<Calendar>>;
1696
1697 async fn get_calendar_by_name(&self, tn_id: TnId, name: &str) -> ClResult<Option<Calendar>>;
1699
1700 async fn update_calendar(
1702 &self,
1703 tn_id: TnId,
1704 cal_id: u64,
1705 patch: &UpdateCalendarData,
1706 ) -> ClResult<()>;
1707
1708 async fn delete_calendar(&self, tn_id: TnId, cal_id: u64) -> ClResult<()>;
1710
1711 async fn list_calendar_objects(
1713 &self,
1714 tn_id: TnId,
1715 cal_id: u64,
1716 opts: &ListCalendarObjectOptions,
1717 ) -> ClResult<Vec<CalendarObjectView>>;
1718
1719 async fn get_calendar_object(
1723 &self,
1724 tn_id: TnId,
1725 cal_id: u64,
1726 uid: &str,
1727 ) -> ClResult<Option<CalendarObject>>;
1728
1729 async fn get_calendar_object_override(
1731 &self,
1732 tn_id: TnId,
1733 cal_id: u64,
1734 uid: &str,
1735 recurrence_id: Timestamp,
1736 ) -> ClResult<Option<CalendarObject>>;
1737
1738 async fn list_calendar_object_overrides(
1740 &self,
1741 tn_id: TnId,
1742 cal_id: u64,
1743 uid: &str,
1744 ) -> ClResult<Vec<CalendarObject>>;
1745
1746 async fn delete_calendar_object_override(
1748 &self,
1749 tn_id: TnId,
1750 cal_id: u64,
1751 uid: &str,
1752 recurrence_id: Timestamp,
1753 ) -> ClResult<()>;
1754
1755 async fn upsert_calendar_object(
1759 &self,
1760 tn_id: TnId,
1761 cal_id: u64,
1762 uid: &str,
1763 ical: &str,
1764 etag: &str,
1765 extracted: &CalendarObjectExtracted,
1766 ) -> ClResult<Box<str>>;
1767
1768 async fn delete_calendar_object(&self, tn_id: TnId, cal_id: u64, uid: &str) -> ClResult<()>;
1771
1772 async fn split_calendar_object_series(
1783 &self,
1784 tn_id: TnId,
1785 cal_id: u64,
1786 master: CalendarObjectWrite<'_>,
1787 tail: CalendarObjectWrite<'_>,
1788 split_at: Timestamp,
1789 ) -> ClResult<(Box<str>, Box<str>)>;
1790
1791 async fn get_calendar_objects_by_uids(
1793 &self,
1794 tn_id: TnId,
1795 cal_id: u64,
1796 uids: &[&str],
1797 ) -> ClResult<Vec<CalendarObject>>;
1798
1799 async fn list_calendar_objects_since(
1802 &self,
1803 tn_id: TnId,
1804 cal_id: u64,
1805 since: Option<Timestamp>,
1806 limit: Option<u32>,
1807 ) -> ClResult<Vec<CalendarObjectSyncEntry>>;
1808
1809 async fn query_calendar_objects_in_range(
1814 &self,
1815 tn_id: TnId,
1816 cal_id: u64,
1817 component: Option<&str>,
1818 start: Option<Timestamp>,
1819 end: Option<Timestamp>,
1820 ) -> ClResult<Vec<CalendarObject>>;
1821}
1822
1823#[cfg(test)]
1824mod tests {
1825 use super::*;
1826 #[test]
1827 fn test_deserialize_list_action_options_with_multiple_statuses() {
1828 let query = "status=C,N&type=POST,REPLY";
1829 let opts: ListActionOptions =
1830 serde_urlencoded::from_str(query).expect("should deserialize");
1831
1832 assert!(opts.status.is_some());
1833 let statuses = opts.status.expect("status should be Some");
1834 assert_eq!(statuses.len(), 2);
1835 assert_eq!(statuses[0].as_str(), "C");
1836 assert_eq!(statuses[1].as_str(), "N");
1837
1838 assert!(opts.typ.is_some());
1839 let types = opts.typ.expect("type should be Some");
1840 assert_eq!(types.len(), 2);
1841 assert_eq!(types[0].as_str(), "POST");
1842 assert_eq!(types[1].as_str(), "REPLY");
1843 }
1844
1845 #[test]
1846 fn test_deserialize_list_action_options_without_status() {
1847 let query = "issuer=alice";
1848 let opts: ListActionOptions =
1849 serde_urlencoded::from_str(query).expect("should deserialize");
1850
1851 assert!(opts.status.is_none());
1852 assert!(opts.typ.is_none());
1853 assert_eq!(opts.issuer.as_deref(), Some("alice"));
1854 }
1855
1856 #[test]
1857 fn test_deserialize_list_action_options_single_status() {
1858 let query = "status=C";
1859 let opts: ListActionOptions =
1860 serde_urlencoded::from_str(query).expect("should deserialize");
1861
1862 assert!(opts.status.is_some());
1863 let statuses = opts.status.expect("status should be Some");
1864 assert_eq!(statuses.len(), 1);
1865 assert_eq!(statuses[0].as_str(), "C");
1866 }
1867
1868 #[test]
1869 fn test_deserialize_list_action_options_audience_type() {
1870 let opts: ListActionOptions = serde_urlencoded::from_str("audienceType=personal")
1871 .expect("should deserialize personal");
1872 assert!(matches!(opts.audience_type, Some(AudienceType::Personal)));
1873
1874 let opts: ListActionOptions = serde_urlencoded::from_str("audienceType=community")
1875 .expect("should deserialize community");
1876 assert!(matches!(opts.audience_type, Some(AudienceType::Community)));
1877
1878 let opts: ListActionOptions =
1879 serde_urlencoded::from_str("issuer=alice").expect("should deserialize");
1880 assert!(opts.audience_type.is_none());
1881
1882 let res: Result<ListActionOptions, _> = serde_urlencoded::from_str("audienceType=garbage");
1883 assert!(res.is_err(), "garbage audienceType should error");
1884 }
1885
1886 #[test]
1887 fn test_deserialize_list_action_options_multi_visibility() {
1888 let opts: ListActionOptions =
1889 serde_urlencoded::from_str("visibility=F,C").expect("should deserialize");
1890 let v = opts.visibility.expect("visibility should be Some");
1891 assert_eq!(v.len(), 2);
1892 assert_eq!(v[0].as_str(), "F");
1893 assert_eq!(v[1].as_str(), "C");
1894
1895 let opts: ListActionOptions =
1896 serde_urlencoded::from_str("visibility=P").expect("should deserialize");
1897 let v = opts.visibility.expect("visibility should be Some");
1898 assert_eq!(v.len(), 1);
1899 assert_eq!(v[0].as_str(), "P");
1900
1901 let opts: ListActionOptions =
1902 serde_urlencoded::from_str("issuer=alice").expect("should deserialize");
1903 assert!(opts.visibility.is_none());
1904 }
1905
1906 #[test]
1907 fn test_deserialize_list_action_options_visibility_with_direct() {
1908 let opts: ListActionOptions =
1909 serde_urlencoded::from_str("visibility=D,F").expect("should deserialize");
1910 let v = opts.visibility.expect("visibility should be Some");
1911 assert_eq!(v.len(), 2);
1912 assert_eq!(v[0].as_str(), "D");
1913 assert_eq!(v[1].as_str(), "F");
1914 }
1915}
1916
1917