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, Patch, Timestamp, TnId},
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<u32>,
238 pub comments: Option<u32>,
239}
240
241#[derive(Debug, Clone, Default)]
243pub struct UpdateActionDataOptions {
244 pub subject: Patch<String>,
245 pub reactions: Patch<u32>,
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>, }
252
253#[derive(Debug, Clone, Default)]
255pub struct FinalizeActionOptions<'a> {
256 pub attachments: Option<&'a [&'a str]>,
257 pub subject: Option<&'a str>,
258 pub audience_tag: Option<&'a str>,
259 pub key: Option<&'a str>,
260}
261
262#[derive(Debug, Clone)]
263pub struct CreateOutboundActionOptions {
264 pub recipient_tag: String,
265 pub typ: String,
266}
267
268fn deserialize_split<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
269where
270 D: serde::Deserializer<'de>,
271{
272 let s = String::deserialize(deserializer)?;
273 Ok(Some(s.split(',').map(|v| v.trim().to_string()).collect()))
274}
275
276#[derive(Debug, Default, Deserialize)]
278#[serde(deny_unknown_fields)]
279pub struct ListActionOptions {
280 pub limit: Option<u32>,
282 pub cursor: Option<String>,
284 pub sort: Option<String>,
286 #[serde(rename = "sortDir")]
288 pub sort_dir: Option<String>,
289 #[serde(default, rename = "type", deserialize_with = "deserialize_split")]
290 pub typ: Option<Vec<String>>,
291 #[serde(default, deserialize_with = "deserialize_split")]
292 pub status: Option<Vec<String>>,
293 pub tag: Option<String>,
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, Clone, Serialize, Deserialize)]
380#[serde(rename_all = "camelCase")]
381pub struct ReactionData {
382 pub id: Box<str>,
383 pub action_id: Box<str>,
384 pub reactor_id_tag: Box<str>,
385 pub r#type: Box<str>,
386 pub content: Option<Box<str>>,
387 #[serde(serialize_with = "serialize_timestamp_iso")]
388 pub created_at: Timestamp,
389}
390
391#[derive(Debug)]
394pub enum FileId<S: AsRef<str>> {
395 FileId(S),
396 FId(u64),
397}
398
399pub enum ActionId<S: AsRef<str>> {
400 ActionId(S),
401 AId(u64),
402}
403
404#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
407pub enum FileStatus {
408 #[serde(rename = "A")]
409 Active,
410 #[serde(rename = "P")]
411 Pending,
412 #[serde(rename = "D")]
413 Deleted,
414}
415
416#[skip_serializing_none]
418#[derive(Debug, Clone, Default, Serialize)]
419#[serde(rename_all = "camelCase")]
420pub struct FileUserData {
421 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
422 pub accessed_at: Option<Timestamp>,
423 #[serde(serialize_with = "serialize_timestamp_iso_opt")]
424 pub modified_at: Option<Timestamp>,
425 pub pinned: bool,
426 pub starred: bool,
427}
428
429#[skip_serializing_none]
430#[derive(Debug, Clone, Serialize)]
431#[serde(rename_all = "camelCase")]
432pub struct FileView {
433 pub file_id: Box<str>,
434 pub parent_id: Option<Box<str>>, pub owner: Option<ProfileInfo>,
436 pub creator: Option<ProfileInfo>,
437 pub preset: Option<Box<str>>,
438 pub content_type: Option<Box<str>>,
439 pub file_name: Box<str>,
440 pub file_tp: Option<Box<str>>, #[serde(serialize_with = "serialize_timestamp_iso")]
442 pub created_at: Timestamp,
443 #[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
444 pub accessed_at: Option<Timestamp>, #[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
446 pub modified_at: Option<Timestamp>, pub status: FileStatus,
448 pub tags: Option<Vec<Box<str>>>,
449 pub visibility: Option<char>, pub access_level: Option<crate::types::AccessLevel>, pub user_data: Option<FileUserData>, pub x: Option<serde_json::Value>, }
454
455#[skip_serializing_none]
456#[derive(Debug, Clone, Serialize)]
457pub struct FileVariant<S: AsRef<str> + Debug> {
458 #[serde(rename = "variantId")]
459 pub variant_id: S,
460 pub variant: S,
461 pub format: S,
462 pub size: u64,
463 pub resolution: (u32, u32),
464 pub available: bool,
465 pub duration: Option<f64>,
467 pub bitrate: Option<u32>,
469 #[serde(rename = "pageCount")]
471 pub page_count: Option<u32>,
472}
473
474impl<S: AsRef<str> + Debug> PartialEq for FileVariant<S> {
475 fn eq(&self, other: &Self) -> bool {
476 self.variant_id.as_ref() == other.variant_id.as_ref()
477 && self.variant.as_ref() == other.variant.as_ref()
478 && self.format.as_ref() == other.format.as_ref()
479 && self.size == other.size
480 && self.resolution == other.resolution
481 && self.available == other.available
482 && self.duration == other.duration
483 && self.bitrate == other.bitrate
484 && self.page_count == other.page_count
485 }
486}
487
488impl<S: AsRef<str> + Debug> Eq for FileVariant<S> {}
489
490impl<S: AsRef<str> + Debug + Ord> PartialOrd for FileVariant<S> {
491 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
492 Some(self.cmp(other))
493 }
494}
495
496impl<S: AsRef<str> + Debug + Ord> Ord for FileVariant<S> {
497 fn cmp(&self, other: &Self) -> Ordering {
498 self.size
500 .cmp(&other.size)
501 .then_with(|| self.resolution.0.cmp(&other.resolution.0))
502 .then_with(|| self.resolution.1.cmp(&other.resolution.1))
503 .then_with(|| self.size.cmp(&other.size))
504 }
505}
506
507#[derive(Debug, Default, Deserialize)]
512#[serde(deny_unknown_fields)]
513pub struct ListFileOptions {
514 pub limit: Option<u32>,
516 pub cursor: Option<String>,
518 #[serde(rename = "fileId")]
519 pub file_id: Option<String>,
520 #[serde(rename = "parentId")]
521 pub parent_id: Option<String>, pub tag: Option<String>,
523 pub preset: Option<String>,
524 pub variant: Option<String>,
525 pub status: Option<FileStatus>,
527 #[serde(rename = "fileTp")]
528 pub file_type: Option<String>,
529 #[serde(rename = "contentType")]
531 pub content_type: Option<String>,
532 pub pinned: Option<bool>,
534 pub starred: Option<bool>,
536 pub sort: Option<String>,
538 #[serde(rename = "sortDir")]
540 pub sort_dir: Option<String>,
541 #[serde(skip)]
543 pub user_id_tag: Option<String>,
544}
545
546#[derive(Debug, Clone, Default)]
547pub struct CreateFile {
548 pub orig_variant_id: Option<Box<str>>,
549 pub file_id: Option<Box<str>>,
550 pub parent_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]
591#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct PushSubscriptionData {
593 pub endpoint: String,
595 #[serde(rename = "expirationTime")]
597 pub expiration_time: Option<i64>,
598 pub keys: PushSubscriptionKeys,
600}
601
602#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct PushSubscriptionKeys {
605 pub p256dh: String,
607 pub auth: String,
609}
610
611#[derive(Debug, Clone, Serialize)]
613#[serde(rename_all = "camelCase")]
614pub struct PushSubscription {
615 pub id: u64,
617 pub subscription: PushSubscriptionData,
619 #[serde(serialize_with = "serialize_timestamp_iso")]
621 pub created_at: Timestamp,
622}
623
624pub struct Task {
627 pub task_id: u64,
628 pub tn_id: TnId,
629 pub kind: Box<str>,
630 pub status: char,
631 pub created_at: Timestamp,
632 pub next_at: Option<Timestamp>,
633 pub input: Box<str>,
634 pub output: Box<str>,
635 pub deps: Box<[u64]>,
636 pub retry: Option<Box<str>>,
637 pub cron: Option<Box<str>>,
638}
639
640#[derive(Debug, Default)]
641pub struct TaskPatch {
642 pub input: Patch<String>,
643 pub next_at: Patch<Timestamp>,
644 pub deps: Patch<Vec<u64>>,
645 pub retry: Patch<String>,
646 pub cron: Patch<String>,
647}
648
649#[derive(Debug, Default)]
650pub struct ListTaskOptions {}
651
652#[async_trait]
653pub trait MetaAdapter: Debug + Send + Sync {
654 async fn read_tenant(&self, tn_id: TnId) -> ClResult<Tenant<Box<str>>>;
659
660 async fn create_tenant(&self, tn_id: TnId, id_tag: &str) -> ClResult<TnId>;
662
663 async fn update_tenant(&self, tn_id: TnId, tenant: &UpdateTenantData) -> ClResult<()>;
665
666 async fn delete_tenant(&self, tn_id: TnId) -> ClResult<()>;
668
669 async fn list_tenants(&self, opts: &ListTenantsMetaOptions) -> ClResult<Vec<TenantListMeta>>;
671
672 async fn list_profiles(
674 &self,
675 tn_id: TnId,
676 opts: &ListProfileOptions,
677 ) -> ClResult<Vec<Profile<Box<str>>>>;
678
679 async fn get_relationships(
686 &self,
687 tn_id: TnId,
688 target_id_tags: &[&str],
689 ) -> ClResult<HashMap<String, (bool, bool)>>;
690
691 async fn read_profile(
695 &self,
696 tn_id: TnId,
697 id_tag: &str,
698 ) -> ClResult<(Box<str>, Profile<Box<str>>)>;
699
700 async fn read_profile_roles(
702 &self,
703 tn_id: TnId,
704 id_tag: &str,
705 ) -> ClResult<Option<Box<[Box<str>]>>>;
706
707 async fn create_profile(
708 &self,
709 tn_id: TnId,
710 profile: &Profile<&str>,
711 etag: &str,
712 ) -> ClResult<()>;
713 async fn update_profile(
714 &self,
715 tn_id: TnId,
716 id_tag: &str,
717 profile: &UpdateProfileData,
718 ) -> ClResult<()>;
719
720 async fn read_profile_public_key(
724 &self,
725 id_tag: &str,
726 key_id: &str,
727 ) -> ClResult<(Box<str>, Timestamp)>;
728 async fn add_profile_public_key(
729 &self,
730 id_tag: &str,
731 key_id: &str,
732 public_key: &str,
733 ) -> ClResult<()>;
734 async fn process_profile_refresh<'a>(
740 &self,
741 callback: Box<dyn Fn(TnId, &'a str, Option<&'a str>) -> ClResult<()> + Send>,
742 );
743
744 async fn list_stale_profiles(
749 &self,
750 max_age_secs: i64,
751 limit: u32,
752 ) -> ClResult<Vec<(TnId, Box<str>, Option<Box<str>>)>>;
753
754 async fn get_action_id(&self, tn_id: TnId, a_id: u64) -> ClResult<Box<str>>;
757 async fn list_actions(
758 &self,
759 tn_id: TnId,
760 opts: &ListActionOptions,
761 ) -> ClResult<Vec<ActionView>>;
762 async fn list_action_tokens(
763 &self,
764 tn_id: TnId,
765 opts: &ListActionOptions,
766 ) -> ClResult<Box<[Box<str>]>>;
767
768 async fn create_action(
769 &self,
770 tn_id: TnId,
771 action: &Action<&str>,
772 key: Option<&str>,
773 ) -> ClResult<ActionId<Box<str>>>;
774
775 async fn finalize_action(
776 &self,
777 tn_id: TnId,
778 a_id: u64,
779 action_id: &str,
780 options: FinalizeActionOptions<'_>,
781 ) -> ClResult<()>;
782
783 async fn create_inbound_action(
784 &self,
785 tn_id: TnId,
786 action_id: &str,
787 token: &str,
788 ack_token: Option<&str>,
789 ) -> ClResult<()>;
790
791 async fn get_action_root_id(&self, tn_id: TnId, action_id: &str) -> ClResult<Box<str>>;
793
794 async fn get_action_data(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionData>>;
796
797 async fn get_action_by_key(
799 &self,
800 tn_id: TnId,
801 action_key: &str,
802 ) -> ClResult<Option<Action<Box<str>>>>;
803
804 async fn store_action_token(&self, tn_id: TnId, action_id: &str, token: &str) -> ClResult<()>;
806
807 async fn get_action_token(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<Box<str>>>;
809
810 async fn update_action_data(
812 &self,
813 tn_id: TnId,
814 action_id: &str,
815 opts: &UpdateActionDataOptions,
816 ) -> ClResult<()>;
817
818 async fn update_inbound_action(
820 &self,
821 tn_id: TnId,
822 action_id: &str,
823 status: Option<char>,
824 ) -> ClResult<()>;
825
826 async fn get_related_action_tokens(
829 &self,
830 tn_id: TnId,
831 aprv_action_id: &str,
832 ) -> ClResult<Vec<(Box<str>, Box<str>)>>;
833
834 async fn create_outbound_action(
836 &self,
837 tn_id: TnId,
838 action_id: &str,
839 token: &str,
840 opts: &CreateOutboundActionOptions,
841 ) -> ClResult<()>;
842
843 async fn get_file_id(&self, tn_id: TnId, f_id: u64) -> ClResult<Box<str>>;
846 async fn list_files(&self, tn_id: TnId, opts: &ListFileOptions) -> ClResult<Vec<FileView>>;
847 async fn list_file_variants(
848 &self,
849 tn_id: TnId,
850 file_id: FileId<&str>,
851 ) -> ClResult<Vec<FileVariant<Box<str>>>>;
852 async fn list_available_variants(&self, tn_id: TnId, file_id: &str) -> ClResult<Vec<Box<str>>>;
854 async fn read_file_variant(
855 &self,
856 tn_id: TnId,
857 variant_id: &str,
858 ) -> ClResult<FileVariant<Box<str>>>;
859 async fn read_file_id_by_variant(&self, tn_id: TnId, variant_id: &str) -> ClResult<Box<str>>;
861 async fn read_f_id_by_file_id(&self, tn_id: TnId, file_id: &str) -> ClResult<u64>;
863 async fn create_file(&self, tn_id: TnId, opts: CreateFile) -> ClResult<FileId<Box<str>>>;
864 async fn create_file_variant<'a>(
865 &'a self,
866 tn_id: TnId,
867 f_id: u64,
868 opts: FileVariant<&'a str>,
869 ) -> ClResult<&'a str>;
870 async fn update_file_id(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
871
872 async fn finalize_file(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
874
875 async fn list_tasks(&self, opts: ListTaskOptions) -> ClResult<Vec<Task>>;
878 async fn list_task_ids(&self, kind: &str, keys: &[Box<str>]) -> ClResult<Vec<u64>>;
879 async fn create_task(
880 &self,
881 kind: &'static str,
882 key: Option<&str>,
883 input: &str,
884 deps: &[u64],
885 ) -> ClResult<u64>;
886 async fn update_task_finished(&self, task_id: u64, output: &str) -> ClResult<()>;
887 async fn update_task_error(
888 &self,
889 task_id: u64,
890 output: &str,
891 next_at: Option<Timestamp>,
892 ) -> ClResult<()>;
893
894 async fn find_task_by_key(&self, key: &str) -> ClResult<Option<Task>>;
896
897 async fn update_task(&self, task_id: u64, patch: &TaskPatch) -> ClResult<()>;
899
900 async fn get_profile_info(&self, tn_id: TnId, id_tag: &str) -> ClResult<ProfileData>;
904
905 async fn get_action(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionView>>;
909
910 async fn update_action(
912 &self,
913 tn_id: TnId,
914 action_id: &str,
915 content: Option<&str>,
916 attachments: Option<&[&str]>,
917 ) -> ClResult<()>;
918
919 async fn delete_action(&self, tn_id: TnId, action_id: &str) -> ClResult<()>;
921
922 async fn add_reaction(
924 &self,
925 tn_id: TnId,
926 action_id: &str,
927 reactor_id_tag: &str,
928 reaction_type: &str,
929 content: Option<&str>,
930 ) -> ClResult<()>;
931
932 async fn list_reactions(&self, tn_id: TnId, action_id: &str) -> ClResult<Vec<ReactionData>>;
934
935 async fn delete_file(&self, tn_id: TnId, file_id: &str) -> ClResult<()>;
939
940 async fn list_settings(
944 &self,
945 tn_id: TnId,
946 prefix: Option<&[String]>,
947 ) -> ClResult<std::collections::HashMap<String, serde_json::Value>>;
948
949 async fn read_setting(&self, tn_id: TnId, name: &str) -> ClResult<Option<serde_json::Value>>;
951
952 async fn update_setting(
954 &self,
955 tn_id: TnId,
956 name: &str,
957 value: Option<serde_json::Value>,
958 ) -> ClResult<()>;
959
960 async fn list_refs(&self, tn_id: TnId, opts: &ListRefsOptions) -> ClResult<Vec<RefData>>;
964
965 async fn get_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<Option<(Box<str>, Box<str>)>>;
967
968 async fn create_ref(
970 &self,
971 tn_id: TnId,
972 ref_id: &str,
973 opts: &CreateRefOptions,
974 ) -> ClResult<RefData>;
975
976 async fn delete_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<()>;
978
979 async fn use_ref(
982 &self,
983 ref_id: &str,
984 expected_types: &[&str],
985 ) -> ClResult<(TnId, Box<str>, RefData)>;
986
987 async fn validate_ref(
990 &self,
991 ref_id: &str,
992 expected_types: &[&str],
993 ) -> ClResult<(TnId, Box<str>, RefData)>;
994
995 async fn list_tags(
1005 &self,
1006 tn_id: TnId,
1007 prefix: Option<&str>,
1008 with_counts: bool,
1009 limit: Option<u32>,
1010 ) -> ClResult<Vec<TagInfo>>;
1011
1012 async fn add_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
1014
1015 async fn remove_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
1017
1018 async fn update_file_data(
1022 &self,
1023 tn_id: TnId,
1024 file_id: &str,
1025 opts: &UpdateFileOptions,
1026 ) -> ClResult<()>;
1027
1028 async fn read_file(&self, tn_id: TnId, file_id: &str) -> ClResult<Option<FileView>>;
1030
1031 async fn record_file_access(&self, tn_id: TnId, id_tag: &str, file_id: &str) -> ClResult<()>;
1036
1037 async fn record_file_modification(
1039 &self,
1040 tn_id: TnId,
1041 id_tag: &str,
1042 file_id: &str,
1043 ) -> ClResult<()>;
1044
1045 async fn update_file_user_data(
1047 &self,
1048 tn_id: TnId,
1049 id_tag: &str,
1050 file_id: &str,
1051 pinned: Option<bool>,
1052 starred: Option<bool>,
1053 ) -> ClResult<FileUserData>;
1054
1055 async fn get_file_user_data(
1057 &self,
1058 tn_id: TnId,
1059 id_tag: &str,
1060 file_id: &str,
1061 ) -> ClResult<Option<FileUserData>>;
1062
1063 async fn list_push_subscriptions(&self, tn_id: TnId) -> ClResult<Vec<PushSubscription>>;
1071
1072 async fn create_push_subscription(
1078 &self,
1079 tn_id: TnId,
1080 subscription: &PushSubscriptionData,
1081 ) -> ClResult<u64>;
1082
1083 async fn delete_push_subscription(&self, tn_id: TnId, subscription_id: u64) -> ClResult<()>;
1088}
1089
1090#[cfg(test)]
1091mod tests {
1092 use super::*;
1093 use serde_urlencoded;
1094
1095 #[test]
1096 fn test_deserialize_list_action_options_with_multiple_statuses() {
1097 let query = "status=C,N&type=POST,REPLY";
1098 let opts: ListActionOptions =
1099 serde_urlencoded::from_str(query).expect("should deserialize");
1100
1101 assert!(opts.status.is_some());
1102 let statuses = opts.status.expect("status should be Some");
1103 assert_eq!(statuses.len(), 2);
1104 assert_eq!(statuses[0].as_str(), "C");
1105 assert_eq!(statuses[1].as_str(), "N");
1106
1107 assert!(opts.typ.is_some());
1108 let types = opts.typ.expect("type should be Some");
1109 assert_eq!(types.len(), 2);
1110 assert_eq!(types[0].as_str(), "POST");
1111 assert_eq!(types[1].as_str(), "REPLY");
1112 }
1113
1114 #[test]
1115 fn test_deserialize_list_action_options_without_status() {
1116 let query = "issuer=alice";
1117 let opts: ListActionOptions =
1118 serde_urlencoded::from_str(query).expect("should deserialize");
1119
1120 assert!(opts.status.is_none());
1121 assert!(opts.typ.is_none());
1122 assert_eq!(opts.issuer.as_deref(), Some("alice"));
1123 }
1124
1125 #[test]
1126 fn test_deserialize_list_action_options_single_status() {
1127 let query = "status=C";
1128 let opts: ListActionOptions =
1129 serde_urlencoded::from_str(query).expect("should deserialize");
1130
1131 assert!(opts.status.is_some());
1132 let statuses = opts.status.expect("status should be Some");
1133 assert_eq!(statuses.len(), 1);
1134 assert_eq!(statuses[0].as_str(), "C");
1135 }
1136}
1137
1138