1pub const TRASH_PARENT_ID: &str = "__trash__";
5
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8use serde_with::skip_serializing_none;
9use std::{cmp::Ordering, collections::HashMap, fmt::Debug};
10
11use crate::{
12 prelude::*,
13 types::{serialize_timestamp_iso, serialize_timestamp_iso_opt},
14};
15
16#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
19pub enum ProfileType {
20 #[serde(rename = "person")]
21 Person,
22 #[serde(rename = "community")]
23 Community,
24}
25
26#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
27pub enum ProfileStatus {
28 #[serde(rename = "A")]
29 Active,
30 #[serde(rename = "T")]
31 Trusted,
32 #[serde(rename = "B")]
33 Blocked,
34 #[serde(rename = "M")]
35 Muted,
36 #[serde(rename = "S")]
37 Suspended,
38 #[serde(rename = "X")]
39 Banned,
40}
41
42#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
43pub enum ProfileConnectionStatus {
44 #[default]
45 Disconnected,
46 RequestPending,
47 Connected,
48}
49
50impl ProfileConnectionStatus {
51 pub fn is_connected(&self) -> bool {
52 matches!(self, ProfileConnectionStatus::Connected)
53 }
54}
55
56impl std::fmt::Display for ProfileConnectionStatus {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 match self {
59 ProfileConnectionStatus::Disconnected => write!(f, "disconnected"),
60 ProfileConnectionStatus::RequestPending => write!(f, "pending"),
61 ProfileConnectionStatus::Connected => write!(f, "connected"),
62 }
63 }
64}
65
66#[skip_serializing_none]
70#[derive(Debug, Clone, Serialize)]
71#[serde(rename_all = "camelCase")]
72pub struct RefData {
73 pub ref_id: Box<str>,
74 pub r#type: Box<str>,
75 pub description: Option<Box<str>>,
76 #[serde(serialize_with = "serialize_timestamp_iso")]
77 pub created_at: Timestamp,
78 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
79 pub expires_at: Option<Timestamp>,
80 pub count: Option<u32>,
82 pub resource_id: Option<Box<str>>,
84 pub access_level: Option<char>,
86}
87
88pub struct ListRefsOptions {
89 pub typ: Option<String>,
90 pub filter: Option<String>, pub resource_id: Option<String>,
93}
94
95pub struct CreateRefOptions {
96 pub typ: String,
97 pub description: Option<String>,
98 pub expires_at: Option<Timestamp>,
99 pub count: Option<u32>,
100 pub resource_id: Option<String>,
102 pub access_level: Option<char>,
104}
105
106#[skip_serializing_none]
107#[derive(Debug, Serialize)]
108#[serde(rename_all = "camelCase")]
109pub struct Tenant<S: AsRef<str>> {
110 #[serde(rename = "id")]
111 pub tn_id: TnId,
112 pub id_tag: S,
113 pub name: S,
114 #[serde(rename = "type")]
115 pub typ: ProfileType,
116 pub profile_pic: Option<S>,
117 pub cover_pic: Option<S>,
118 #[serde(serialize_with = "serialize_timestamp_iso")]
119 pub created_at: Timestamp,
120 pub x: HashMap<S, S>,
121}
122
123#[derive(Debug, Default)]
125pub struct ListTenantsMetaOptions {
126 pub limit: Option<u32>,
127 pub offset: Option<u32>,
128}
129
130#[skip_serializing_none]
132#[derive(Debug, Clone, Serialize)]
133#[serde(rename_all = "camelCase")]
134pub struct TenantListMeta {
135 pub tn_id: TnId,
136 pub id_tag: Box<str>,
137 pub name: Box<str>,
138 #[serde(rename = "type")]
139 pub typ: ProfileType,
140 pub profile_pic: Option<Box<str>>,
141 #[serde(serialize_with = "serialize_timestamp_iso")]
142 pub created_at: Timestamp,
143}
144
145#[derive(Debug, Default, Deserialize)]
146pub struct UpdateTenantData {
147 #[serde(rename = "idTag", default)]
148 pub id_tag: Patch<String>,
149 #[serde(default)]
150 pub name: Patch<String>,
151 #[serde(rename = "type", default)]
152 pub typ: Patch<ProfileType>,
153 #[serde(rename = "profilePic", default)]
154 pub profile_pic: Patch<String>,
155 #[serde(rename = "coverPic", default)]
156 pub cover_pic: Patch<String>,
157}
158
159#[derive(Debug)]
160pub struct Profile<S: AsRef<str>> {
161 pub id_tag: S,
162 pub name: S,
163 pub typ: ProfileType,
164 pub profile_pic: Option<S>,
165 pub following: bool,
166 pub connected: ProfileConnectionStatus,
167 pub roles: Option<Box<[Box<str>]>>,
168}
169
170#[derive(Debug, Default, Deserialize)]
171pub struct ListProfileOptions {
172 #[serde(rename = "type")]
173 pub typ: Option<ProfileType>,
174 pub status: Option<Box<[ProfileStatus]>>,
175 pub connected: Option<ProfileConnectionStatus>,
176 pub following: Option<bool>,
177 pub q: Option<String>,
178 pub id_tag: Option<String>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct ProfileData {
185 pub id_tag: Box<str>,
186 pub name: Box<str>,
187 #[serde(rename = "type")]
188 pub r#type: Box<str>, pub profile_pic: Option<Box<str>>,
190 #[serde(serialize_with = "serialize_timestamp_iso")]
191 pub created_at: Timestamp,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct ProfileList {
197 pub profiles: Vec<ProfileData>,
198 pub total: usize,
199 pub limit: usize,
200 pub offset: usize,
201}
202
203#[derive(Debug, Default, Deserialize)]
204pub struct UpdateProfileData {
205 #[serde(default)]
207 pub name: Patch<Box<str>>,
208 #[serde(default, rename = "profilePic")]
209 pub profile_pic: Patch<Option<Box<str>>>,
210 #[serde(default)]
211 pub roles: Patch<Option<Vec<Box<str>>>>,
212
213 #[serde(default)]
215 pub status: Patch<ProfileStatus>,
216
217 #[serde(default)]
219 pub synced: Patch<bool>,
220 #[serde(default)]
221 pub following: Patch<bool>,
222 #[serde(default)]
223 pub connected: Patch<ProfileConnectionStatus>,
224
225 #[serde(default)]
227 pub etag: Patch<Box<str>>,
228}
229
230#[derive(Debug, Clone)]
235pub struct ActionData {
236 pub subject: Option<Box<str>>,
237 pub reactions: Option<Box<str>>,
238 pub comments: Option<u32>,
239}
240
241#[derive(Debug, Clone, Default)]
243pub struct UpdateActionDataOptions {
244 pub subject: Patch<String>,
245 pub reactions: Patch<String>,
246 pub comments: Patch<u32>,
247 pub comments_read: Patch<u32>,
248 pub status: Patch<char>,
249 pub visibility: Patch<char>,
250 pub x: Patch<serde_json::Value>, pub content: Patch<String>,
252 pub attachments: Patch<String>, pub flags: Patch<String>,
254 pub created_at: Patch<Timestamp>,
255}
256
257#[derive(Debug, Clone, Default)]
259pub struct FinalizeActionOptions<'a> {
260 pub attachments: Option<&'a [&'a str]>,
261 pub subject: Option<&'a str>,
262 pub audience_tag: Option<&'a str>,
263 pub key: Option<&'a str>,
264}
265
266fn deserialize_split<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
267where
268 D: serde::Deserializer<'de>,
269{
270 let s = String::deserialize(deserializer)?;
271 Ok(Some(s.split(',').map(|v| v.trim().to_string()).collect()))
272}
273
274#[derive(Debug, Default, Deserialize)]
276#[serde(deny_unknown_fields)]
277pub struct ListActionOptions {
278 pub limit: Option<u32>,
280 pub cursor: Option<String>,
282 pub sort: Option<String>,
284 #[serde(rename = "sortDir")]
286 pub sort_dir: Option<String>,
287 #[serde(default, rename = "type", deserialize_with = "deserialize_split")]
288 pub typ: Option<Vec<String>>,
289 #[serde(default, deserialize_with = "deserialize_split")]
290 pub status: Option<Vec<String>>,
291 pub tag: Option<String>,
292 pub search: Option<String>,
293 pub visibility: Option<char>,
294 pub issuer: Option<String>,
295 pub audience: Option<String>,
296 pub involved: Option<String>,
297 #[serde(skip)]
299 pub viewer_id_tag: Option<String>,
300 #[serde(rename = "actionId")]
301 pub action_id: Option<String>,
302 #[serde(rename = "parentId")]
303 pub parent_id: Option<String>,
304 #[serde(rename = "rootId")]
305 pub root_id: Option<String>,
306 pub subject: Option<String>,
307 #[serde(rename = "createdAfter")]
308 pub created_after: Option<Timestamp>,
309}
310
311#[skip_serializing_none]
312#[derive(Debug, Clone, Serialize)]
313pub struct ProfileInfo {
314 #[serde(rename = "idTag")]
315 pub id_tag: Box<str>,
316 pub name: Box<str>,
317 #[serde(rename = "type")]
318 pub typ: ProfileType,
319 #[serde(rename = "profilePic")]
320 pub profile_pic: Option<Box<str>>,
321}
322
323pub struct Action<S: AsRef<str>> {
324 pub action_id: S,
325 pub typ: S,
326 pub sub_typ: Option<S>,
327 pub issuer_tag: S,
328 pub parent_id: Option<S>,
329 pub root_id: Option<S>,
330 pub audience_tag: Option<S>,
331 pub content: Option<S>,
332 pub attachments: Option<Vec<S>>,
333 pub subject: Option<S>,
334 pub created_at: Timestamp,
335 pub expires_at: Option<Timestamp>,
336 pub visibility: Option<char>, pub flags: Option<S>, pub x: Option<serde_json::Value>, }
340
341#[skip_serializing_none]
342#[derive(Debug, Clone, Serialize)]
343pub struct AttachmentView {
344 #[serde(rename = "fileId")]
345 pub file_id: Box<str>,
346 pub dim: Option<(u32, u32)>,
347 #[serde(rename = "localVariants")]
348 pub local_variants: Option<Vec<Box<str>>>,
349}
350
351#[skip_serializing_none]
352#[derive(Debug, Clone, Serialize)]
353#[serde(rename_all = "camelCase")]
354pub struct ActionView {
355 pub action_id: Box<str>,
356 #[serde(rename = "type")]
357 pub typ: Box<str>,
358 #[serde(rename = "subType")]
359 pub sub_typ: Option<Box<str>>,
360 pub parent_id: Option<Box<str>>,
361 pub root_id: Option<Box<str>>,
362 pub issuer: ProfileInfo,
363 pub audience: Option<ProfileInfo>,
364 pub content: Option<serde_json::Value>,
365 pub attachments: Option<Vec<AttachmentView>>,
366 pub subject: Option<Box<str>>,
367 #[serde(serialize_with = "serialize_timestamp_iso")]
368 pub created_at: Timestamp,
369 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
370 pub expires_at: Option<Timestamp>,
371 pub status: Option<Box<str>>,
372 pub stat: Option<serde_json::Value>,
373 pub visibility: Option<char>,
374 pub flags: Option<Box<str>>, pub x: Option<serde_json::Value>, }
377
378#[derive(Debug)]
381pub enum FileId<S: AsRef<str>> {
382 FileId(S),
383 FId(u64),
384}
385
386pub enum ActionId<S: AsRef<str>> {
387 ActionId(S),
388 AId(u64),
389}
390
391#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
394pub enum FileStatus {
395 #[serde(rename = "A")]
396 Active,
397 #[serde(rename = "P")]
398 Pending,
399 #[serde(rename = "D")]
400 Deleted,
401}
402
403#[skip_serializing_none]
405#[derive(Debug, Clone, Default, Serialize)]
406#[serde(rename_all = "camelCase")]
407pub struct FileUserData {
408 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
409 pub accessed_at: Option<Timestamp>,
410 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
411 pub modified_at: Option<Timestamp>,
412 pub pinned: bool,
413 pub starred: bool,
414}
415
416#[skip_serializing_none]
417#[derive(Debug, Clone, Serialize)]
418#[serde(rename_all = "camelCase")]
419pub struct FileView {
420 pub file_id: Box<str>,
421 pub parent_id: Option<Box<str>>, pub root_id: Option<Box<str>>, pub owner: Option<ProfileInfo>,
424 pub creator: Option<ProfileInfo>,
425 pub preset: Option<Box<str>>,
426 pub content_type: Option<Box<str>>,
427 pub file_name: Box<str>,
428 pub file_tp: Option<Box<str>>, #[serde(serialize_with = "serialize_timestamp_iso")]
430 pub created_at: Timestamp,
431 #[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
432 pub accessed_at: Option<Timestamp>, #[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
434 pub modified_at: Option<Timestamp>, pub status: FileStatus,
436 pub tags: Option<Vec<Box<str>>>,
437 pub visibility: Option<char>, pub access_level: Option<crate::types::AccessLevel>, pub user_data: Option<FileUserData>, pub x: Option<serde_json::Value>, }
442
443#[skip_serializing_none]
444#[derive(Debug, Clone, Serialize)]
445pub struct FileVariant<S: AsRef<str> + Debug> {
446 #[serde(rename = "variantId")]
447 pub variant_id: S,
448 pub variant: S,
449 pub format: S,
450 pub size: u64,
451 pub resolution: (u32, u32),
452 pub available: bool,
453 pub duration: Option<f64>,
455 pub bitrate: Option<u32>,
457 #[serde(rename = "pageCount")]
459 pub page_count: Option<u32>,
460}
461
462impl<S: AsRef<str> + Debug> PartialEq for FileVariant<S> {
463 fn eq(&self, other: &Self) -> bool {
464 self.variant_id.as_ref() == other.variant_id.as_ref()
465 && self.variant.as_ref() == other.variant.as_ref()
466 && self.format.as_ref() == other.format.as_ref()
467 && self.size == other.size
468 && self.resolution == other.resolution
469 && self.available == other.available
470 && self.duration == other.duration
471 && self.bitrate == other.bitrate
472 && self.page_count == other.page_count
473 }
474}
475
476impl<S: AsRef<str> + Debug> Eq for FileVariant<S> {}
477
478impl<S: AsRef<str> + Debug + Ord> PartialOrd for FileVariant<S> {
479 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
480 Some(self.cmp(other))
481 }
482}
483
484impl<S: AsRef<str> + Debug + Ord> Ord for FileVariant<S> {
485 fn cmp(&self, other: &Self) -> Ordering {
486 self.size
488 .cmp(&other.size)
489 .then_with(|| self.resolution.0.cmp(&other.resolution.0))
490 .then_with(|| self.resolution.1.cmp(&other.resolution.1))
491 .then_with(|| self.size.cmp(&other.size))
492 }
493}
494
495#[derive(Debug, Default, Deserialize)]
500#[serde(deny_unknown_fields)]
501pub struct ListFileOptions {
502 pub limit: Option<u32>,
504 pub cursor: Option<String>,
506 #[serde(rename = "fileId")]
507 pub file_id: Option<String>,
508 #[serde(rename = "parentId")]
509 pub parent_id: Option<String>, #[serde(rename = "rootId")]
511 pub root_id: Option<String>, pub tag: Option<String>,
513 pub preset: Option<String>,
514 pub variant: Option<String>,
515 pub status: Option<FileStatus>,
517 #[serde(rename = "fileTp")]
518 pub file_type: Option<String>,
519 #[serde(rename = "contentType")]
521 pub content_type: Option<String>,
522 pub pinned: Option<bool>,
524 pub starred: Option<bool>,
526 pub sort: Option<String>,
528 #[serde(rename = "sortDir")]
530 pub sort_dir: Option<String>,
531 #[serde(skip)]
533 pub user_id_tag: Option<String>,
534 #[serde(skip)]
537 pub scope_file_id: Option<String>,
538 #[serde(skip)]
542 pub visible_levels: Option<Vec<char>>,
543}
544
545#[derive(Debug, Clone, Default)]
546pub struct CreateFile {
547 pub orig_variant_id: Option<Box<str>>,
548 pub file_id: Option<Box<str>>,
549 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>>,
554 pub content_type: Box<str>,
555 pub file_name: Box<str>,
556 pub file_tp: Option<Box<str>>, pub created_at: Option<Timestamp>,
558 pub tags: Option<Vec<Box<str>>>,
559 pub x: Option<serde_json::Value>,
560 pub visibility: Option<char>, pub status: Option<FileStatus>, }
563
564#[derive(Debug, Clone, Deserialize)]
565pub struct CreateFileVariant {
566 pub variant: Box<str>,
567 pub format: Box<str>,
568 pub resolution: (u32, u32),
569 pub size: u64,
570 pub available: bool,
571}
572
573#[derive(Debug, Clone, Default, Deserialize)]
575pub struct UpdateFileOptions {
576 #[serde(default, rename = "fileName")]
577 pub file_name: Patch<String>,
578 #[serde(default, rename = "parentId")]
579 pub parent_id: Patch<String>, #[serde(default)]
581 pub visibility: Patch<char>,
582 #[serde(default)]
583 pub status: Patch<char>,
584}
585
586#[skip_serializing_none]
590#[derive(Debug, Clone, Serialize)]
591#[serde(rename_all = "camelCase")]
592pub struct ShareEntry {
593 pub id: i64,
594 pub resource_type: char,
595 pub resource_id: Box<str>,
596 pub subject_type: char,
597 pub subject_id: Box<str>,
598 pub permission: char,
599 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
600 pub expires_at: Option<Timestamp>,
601 pub created_by: Box<str>,
602 #[serde(serialize_with = "serialize_timestamp_iso")]
603 pub created_at: Timestamp,
604 pub subject_file_name: Option<Box<str>>,
606 pub subject_content_type: Option<Box<str>>,
607 pub subject_file_tp: Option<Box<str>>,
608}
609
610#[derive(Debug, Deserialize)]
611#[serde(rename_all = "camelCase")]
612pub struct CreateShareEntry {
613 pub subject_type: char,
614 pub subject_id: String,
615 pub permission: char,
616 pub expires_at: Option<Timestamp>,
617}
618
619#[skip_serializing_none]
624#[derive(Debug, Clone, Serialize, Deserialize)]
625pub struct PushSubscriptionData {
626 pub endpoint: String,
628 #[serde(rename = "expirationTime")]
630 pub expiration_time: Option<i64>,
631 pub keys: PushSubscriptionKeys,
633}
634
635#[derive(Debug, Clone, Serialize, Deserialize)]
637pub struct PushSubscriptionKeys {
638 pub p256dh: String,
640 pub auth: String,
642}
643
644#[derive(Debug, Clone, Serialize)]
646#[serde(rename_all = "camelCase")]
647pub struct PushSubscription {
648 pub id: u64,
650 pub subscription: PushSubscriptionData,
652 #[serde(serialize_with = "serialize_timestamp_iso")]
654 pub created_at: Timestamp,
655}
656
657pub struct Task {
660 pub task_id: u64,
661 pub tn_id: TnId,
662 pub kind: Box<str>,
663 pub status: char,
664 pub created_at: Timestamp,
665 pub next_at: Option<Timestamp>,
666 pub input: Box<str>,
667 pub output: Box<str>,
668 pub deps: Box<[u64]>,
669 pub retry: Option<Box<str>>,
670 pub cron: Option<Box<str>>,
671}
672
673#[derive(Debug, Default)]
674pub struct TaskPatch {
675 pub input: Patch<String>,
676 pub next_at: Patch<Timestamp>,
677 pub deps: Patch<Vec<u64>>,
678 pub retry: Patch<String>,
679 pub cron: Patch<String>,
680}
681
682#[derive(Debug, Default)]
683pub struct ListTaskOptions {}
684
685#[derive(Debug)]
690pub struct InstallApp {
691 pub app_name: Box<str>,
692 pub publisher_tag: Box<str>,
693 pub version: Box<str>,
694 pub action_id: Box<str>,
695 pub file_id: Box<str>,
696 pub blob_id: Box<str>,
697 pub capabilities: Option<Vec<Box<str>>>,
698}
699
700#[derive(Debug, Serialize)]
702#[serde(rename_all = "camelCase")]
703pub struct InstalledApp {
704 pub app_name: Box<str>,
705 pub publisher_tag: Box<str>,
706 pub version: Box<str>,
707 pub action_id: Box<str>,
708 pub file_id: Box<str>,
709 pub blob_id: Box<str>,
710 pub status: Box<str>,
711 pub capabilities: Option<Vec<Box<str>>>,
712 pub auto_update: bool,
713 #[serde(serialize_with = "serialize_timestamp_iso")]
714 pub installed_at: Timestamp,
715}
716
717#[async_trait]
718pub trait MetaAdapter: Debug + Send + Sync {
719 async fn read_tenant(&self, tn_id: TnId) -> ClResult<Tenant<Box<str>>>;
724
725 async fn create_tenant(&self, tn_id: TnId, id_tag: &str) -> ClResult<TnId>;
727
728 async fn update_tenant(&self, tn_id: TnId, tenant: &UpdateTenantData) -> ClResult<()>;
730
731 async fn delete_tenant(&self, tn_id: TnId) -> ClResult<()>;
733
734 async fn list_tenants(&self, opts: &ListTenantsMetaOptions) -> ClResult<Vec<TenantListMeta>>;
736
737 async fn list_profiles(
739 &self,
740 tn_id: TnId,
741 opts: &ListProfileOptions,
742 ) -> ClResult<Vec<Profile<Box<str>>>>;
743
744 async fn get_relationships(
751 &self,
752 tn_id: TnId,
753 target_id_tags: &[&str],
754 ) -> ClResult<HashMap<String, (bool, bool)>>;
755
756 async fn read_profile(
760 &self,
761 tn_id: TnId,
762 id_tag: &str,
763 ) -> ClResult<(Box<str>, Profile<Box<str>>)>;
764
765 async fn read_profile_roles(
767 &self,
768 tn_id: TnId,
769 id_tag: &str,
770 ) -> ClResult<Option<Box<[Box<str>]>>>;
771
772 async fn create_profile(
773 &self,
774 tn_id: TnId,
775 profile: &Profile<&str>,
776 etag: &str,
777 ) -> ClResult<()>;
778 async fn update_profile(
779 &self,
780 tn_id: TnId,
781 id_tag: &str,
782 profile: &UpdateProfileData,
783 ) -> ClResult<()>;
784
785 async fn read_profile_public_key(
789 &self,
790 id_tag: &str,
791 key_id: &str,
792 ) -> ClResult<(Box<str>, Timestamp)>;
793 async fn add_profile_public_key(
794 &self,
795 id_tag: &str,
796 key_id: &str,
797 public_key: &str,
798 ) -> ClResult<()>;
799 async fn process_profile_refresh<'a>(
805 &self,
806 callback: Box<dyn Fn(TnId, &'a str, Option<&'a str>) -> ClResult<()> + Send>,
807 );
808
809 async fn list_stale_profiles(
814 &self,
815 max_age_secs: i64,
816 limit: u32,
817 ) -> ClResult<Vec<(TnId, Box<str>, Option<Box<str>>)>>;
818
819 async fn get_action_id(&self, tn_id: TnId, a_id: u64) -> ClResult<Box<str>>;
822 async fn list_actions(
823 &self,
824 tn_id: TnId,
825 opts: &ListActionOptions,
826 ) -> ClResult<Vec<ActionView>>;
827 async fn list_action_tokens(
828 &self,
829 tn_id: TnId,
830 opts: &ListActionOptions,
831 ) -> ClResult<Box<[Box<str>]>>;
832
833 async fn create_action(
834 &self,
835 tn_id: TnId,
836 action: &Action<&str>,
837 key: Option<&str>,
838 ) -> ClResult<ActionId<Box<str>>>;
839
840 async fn finalize_action(
841 &self,
842 tn_id: TnId,
843 a_id: u64,
844 action_id: &str,
845 options: FinalizeActionOptions<'_>,
846 ) -> ClResult<()>;
847
848 async fn create_inbound_action(
849 &self,
850 tn_id: TnId,
851 action_id: &str,
852 token: &str,
853 ack_token: Option<&str>,
854 ) -> ClResult<()>;
855
856 async fn get_action_root_id(&self, tn_id: TnId, action_id: &str) -> ClResult<Box<str>>;
858
859 async fn get_action_data(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionData>>;
861
862 async fn get_action_by_key(
864 &self,
865 tn_id: TnId,
866 action_key: &str,
867 ) -> ClResult<Option<Action<Box<str>>>>;
868
869 async fn store_action_token(&self, tn_id: TnId, action_id: &str, token: &str) -> ClResult<()>;
871
872 async fn get_action_token(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<Box<str>>>;
874
875 async fn update_action_data(
877 &self,
878 tn_id: TnId,
879 action_id: &str,
880 opts: &UpdateActionDataOptions,
881 ) -> ClResult<()>;
882
883 async fn update_inbound_action(
885 &self,
886 tn_id: TnId,
887 action_id: &str,
888 status: Option<char>,
889 ) -> ClResult<()>;
890
891 async fn get_related_action_tokens(
894 &self,
895 tn_id: TnId,
896 aprv_action_id: &str,
897 ) -> ClResult<Vec<(Box<str>, Box<str>)>>;
898
899 async fn get_file_id(&self, tn_id: TnId, f_id: u64) -> ClResult<Box<str>>;
902 async fn list_files(&self, tn_id: TnId, opts: &ListFileOptions) -> ClResult<Vec<FileView>>;
903 async fn list_file_variants(
904 &self,
905 tn_id: TnId,
906 file_id: FileId<&str>,
907 ) -> ClResult<Vec<FileVariant<Box<str>>>>;
908 async fn list_available_variants(&self, tn_id: TnId, file_id: &str) -> ClResult<Vec<Box<str>>>;
910 async fn read_file_variant(
911 &self,
912 tn_id: TnId,
913 variant_id: &str,
914 ) -> ClResult<FileVariant<Box<str>>>;
915 async fn read_file_id_by_variant(&self, tn_id: TnId, variant_id: &str) -> ClResult<Box<str>>;
917 async fn read_f_id_by_file_id(&self, tn_id: TnId, file_id: &str) -> ClResult<u64>;
919 async fn create_file(&self, tn_id: TnId, opts: CreateFile) -> ClResult<FileId<Box<str>>>;
920 async fn create_file_variant<'a>(
921 &'a self,
922 tn_id: TnId,
923 f_id: u64,
924 opts: FileVariant<&'a str>,
925 ) -> ClResult<&'a str>;
926 async fn update_file_id(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
927
928 async fn finalize_file(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
930
931 async fn list_tasks(&self, opts: ListTaskOptions) -> ClResult<Vec<Task>>;
934 async fn list_task_ids(&self, kind: &str, keys: &[Box<str>]) -> ClResult<Vec<u64>>;
935 async fn create_task(
936 &self,
937 kind: &'static str,
938 key: Option<&str>,
939 input: &str,
940 deps: &[u64],
941 ) -> ClResult<u64>;
942 async fn update_task_finished(&self, task_id: u64, output: &str) -> ClResult<()>;
943 async fn update_task_error(
944 &self,
945 task_id: u64,
946 output: &str,
947 next_at: Option<Timestamp>,
948 ) -> ClResult<()>;
949
950 async fn find_task_by_key(&self, key: &str) -> ClResult<Option<Task>>;
952
953 async fn update_task(&self, task_id: u64, patch: &TaskPatch) -> ClResult<()>;
955
956 async fn get_profile_info(&self, tn_id: TnId, id_tag: &str) -> ClResult<ProfileData>;
960
961 async fn get_action(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionView>>;
965
966 async fn update_action(
968 &self,
969 tn_id: TnId,
970 action_id: &str,
971 content: Option<&str>,
972 attachments: Option<&[&str]>,
973 ) -> ClResult<()>;
974
975 async fn delete_action(&self, tn_id: TnId, action_id: &str) -> ClResult<()>;
977
978 async fn count_reactions(&self, tn_id: TnId, subject_id: &str) -> ClResult<String>;
981
982 async fn delete_file(&self, tn_id: TnId, file_id: &str) -> ClResult<()>;
986
987 async fn list_children_by_root(&self, tn_id: TnId, root_id: &str) -> ClResult<Vec<Box<str>>>;
989
990 async fn list_settings(
994 &self,
995 tn_id: TnId,
996 prefix: Option<&[String]>,
997 ) -> ClResult<std::collections::HashMap<String, serde_json::Value>>;
998
999 async fn read_setting(&self, tn_id: TnId, name: &str) -> ClResult<Option<serde_json::Value>>;
1001
1002 async fn update_setting(
1004 &self,
1005 tn_id: TnId,
1006 name: &str,
1007 value: Option<serde_json::Value>,
1008 ) -> ClResult<()>;
1009
1010 async fn list_refs(&self, tn_id: TnId, opts: &ListRefsOptions) -> ClResult<Vec<RefData>>;
1014
1015 async fn get_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<Option<(Box<str>, Box<str>)>>;
1017
1018 async fn create_ref(
1020 &self,
1021 tn_id: TnId,
1022 ref_id: &str,
1023 opts: &CreateRefOptions,
1024 ) -> ClResult<RefData>;
1025
1026 async fn delete_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<()>;
1028
1029 async fn use_ref(
1032 &self,
1033 ref_id: &str,
1034 expected_types: &[&str],
1035 ) -> ClResult<(TnId, Box<str>, RefData)>;
1036
1037 async fn validate_ref(
1040 &self,
1041 ref_id: &str,
1042 expected_types: &[&str],
1043 ) -> ClResult<(TnId, Box<str>, RefData)>;
1044
1045 async fn list_tags(
1055 &self,
1056 tn_id: TnId,
1057 prefix: Option<&str>,
1058 with_counts: bool,
1059 limit: Option<u32>,
1060 ) -> ClResult<Vec<TagInfo>>;
1061
1062 async fn add_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
1064
1065 async fn remove_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
1067
1068 async fn update_file_data(
1072 &self,
1073 tn_id: TnId,
1074 file_id: &str,
1075 opts: &UpdateFileOptions,
1076 ) -> ClResult<()>;
1077
1078 async fn read_file(&self, tn_id: TnId, file_id: &str) -> ClResult<Option<FileView>>;
1080
1081 async fn record_file_access(&self, tn_id: TnId, id_tag: &str, file_id: &str) -> ClResult<()>;
1086
1087 async fn record_file_modification(
1089 &self,
1090 tn_id: TnId,
1091 id_tag: &str,
1092 file_id: &str,
1093 ) -> ClResult<()>;
1094
1095 async fn update_file_user_data(
1097 &self,
1098 tn_id: TnId,
1099 id_tag: &str,
1100 file_id: &str,
1101 pinned: Option<bool>,
1102 starred: Option<bool>,
1103 ) -> ClResult<FileUserData>;
1104
1105 async fn get_file_user_data(
1107 &self,
1108 tn_id: TnId,
1109 id_tag: &str,
1110 file_id: &str,
1111 ) -> ClResult<Option<FileUserData>>;
1112
1113 async fn list_push_subscriptions(&self, tn_id: TnId) -> ClResult<Vec<PushSubscription>>;
1121
1122 async fn create_push_subscription(
1128 &self,
1129 tn_id: TnId,
1130 subscription: &PushSubscriptionData,
1131 ) -> ClResult<u64>;
1132
1133 async fn delete_push_subscription(&self, tn_id: TnId, subscription_id: u64) -> ClResult<()>;
1138
1139 async fn create_share_entry(
1144 &self,
1145 tn_id: TnId,
1146 resource_type: char,
1147 resource_id: &str,
1148 created_by: &str,
1149 entry: &CreateShareEntry,
1150 ) -> ClResult<ShareEntry>;
1151
1152 async fn delete_share_entry(&self, tn_id: TnId, id: i64) -> ClResult<()>;
1154
1155 async fn list_share_entries(
1157 &self,
1158 tn_id: TnId,
1159 resource_type: char,
1160 resource_id: &str,
1161 ) -> ClResult<Vec<ShareEntry>>;
1162
1163 async fn list_share_entries_by_subject(
1166 &self,
1167 tn_id: TnId,
1168 subject_type: Option<char>,
1169 subject_id: &str,
1170 ) -> ClResult<Vec<ShareEntry>>;
1171
1172 async fn check_share_access(
1175 &self,
1176 tn_id: TnId,
1177 resource_type: char,
1178 resource_id: &str,
1179 subject_type: char,
1180 subject_id: &str,
1181 ) -> ClResult<Option<char>>;
1182
1183 async fn read_share_entry(&self, tn_id: TnId, id: i64) -> ClResult<Option<ShareEntry>>;
1185
1186 async fn install_app(&self, tn_id: TnId, install: &InstallApp) -> ClResult<()>;
1191
1192 async fn uninstall_app(&self, tn_id: TnId, app_name: &str, publisher_tag: &str)
1194 -> ClResult<()>;
1195
1196 async fn list_installed_apps(
1198 &self,
1199 tn_id: TnId,
1200 search: Option<&str>,
1201 ) -> ClResult<Vec<InstalledApp>>;
1202
1203 async fn get_installed_app(
1205 &self,
1206 tn_id: TnId,
1207 app_name: &str,
1208 publisher_tag: &str,
1209 ) -> ClResult<Option<InstalledApp>>;
1210}
1211
1212#[cfg(test)]
1213mod tests {
1214 use super::*;
1215 #[test]
1216 fn test_deserialize_list_action_options_with_multiple_statuses() {
1217 let query = "status=C,N&type=POST,REPLY";
1218 let opts: ListActionOptions =
1219 serde_urlencoded::from_str(query).expect("should deserialize");
1220
1221 assert!(opts.status.is_some());
1222 let statuses = opts.status.expect("status should be Some");
1223 assert_eq!(statuses.len(), 2);
1224 assert_eq!(statuses[0].as_str(), "C");
1225 assert_eq!(statuses[1].as_str(), "N");
1226
1227 assert!(opts.typ.is_some());
1228 let types = opts.typ.expect("type should be Some");
1229 assert_eq!(types.len(), 2);
1230 assert_eq!(types[0].as_str(), "POST");
1231 assert_eq!(types[1].as_str(), "REPLY");
1232 }
1233
1234 #[test]
1235 fn test_deserialize_list_action_options_without_status() {
1236 let query = "issuer=alice";
1237 let opts: ListActionOptions =
1238 serde_urlencoded::from_str(query).expect("should deserialize");
1239
1240 assert!(opts.status.is_none());
1241 assert!(opts.typ.is_none());
1242 assert_eq!(opts.issuer.as_deref(), Some("alice"));
1243 }
1244
1245 #[test]
1246 fn test_deserialize_list_action_options_single_status() {
1247 let query = "status=C";
1248 let opts: ListActionOptions =
1249 serde_urlencoded::from_str(query).expect("should deserialize");
1250
1251 assert!(opts.status.is_some());
1252 let statuses = opts.status.expect("status should be Some");
1253 assert_eq!(statuses.len(), 1);
1254 assert_eq!(statuses[0].as_str(), "C");
1255 }
1256}
1257
1258