pub const TRASH_PARENT_ID: &str = "__trash__";
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{cmp::Ordering, collections::HashMap, fmt::Debug};
use crate::{
prelude::*,
types::{serialize_timestamp_iso, serialize_timestamp_iso_opt},
};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub enum ProfileType {
#[default]
#[serde(rename = "person")]
Person,
#[serde(rename = "community")]
Community,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ProfileStatus {
#[serde(rename = "A")]
Active,
#[serde(rename = "T")]
Trusted,
#[serde(rename = "B")]
Blocked,
#[serde(rename = "M")]
Muted,
#[serde(rename = "S")]
Suspended,
#[serde(rename = "X")]
Banned,
}
impl ProfileStatus {
pub fn as_str(&self) -> &'static str {
match self {
ProfileStatus::Active => "active",
ProfileStatus::Trusted => "trusted",
ProfileStatus::Blocked => "blocked",
ProfileStatus::Muted => "muted",
ProfileStatus::Suspended => "suspended",
ProfileStatus::Banned => "banned",
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ProfileTrust {
Always,
Never,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, PartialEq, Eq)]
pub enum ProfileConnectionStatus {
#[default]
Disconnected,
RequestPending,
Connected,
}
impl ProfileConnectionStatus {
pub fn is_connected(&self) -> bool {
matches!(self, ProfileConnectionStatus::Connected)
}
}
impl std::fmt::Display for ProfileConnectionStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ProfileConnectionStatus::Disconnected => write!(f, "disconnected"),
ProfileConnectionStatus::RequestPending => write!(f, "pending"),
ProfileConnectionStatus::Connected => write!(f, "connected"),
}
}
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RefData {
pub ref_id: Box<str>,
pub r#type: Box<str>,
pub description: Option<Box<str>>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
#[serde(serialize_with = "serialize_timestamp_iso_opt")]
pub expires_at: Option<Timestamp>,
pub count: Option<u32>,
pub resource_id: Option<Box<str>>,
pub access_level: Option<char>,
pub params: Option<Box<str>>,
}
pub struct ListRefsOptions {
pub typ: Option<String>,
pub filter: Option<String>, pub resource_id: Option<String>,
}
#[derive(Default)]
pub struct CreateRefOptions {
pub typ: String,
pub description: Option<String>,
pub expires_at: Option<Timestamp>,
pub count: Option<u32>,
pub resource_id: Option<String>,
pub access_level: Option<char>,
pub params: Option<String>,
}
#[skip_serializing_none]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Tenant<S: AsRef<str>> {
#[serde(rename = "id")]
pub tn_id: TnId,
pub id_tag: S,
pub name: S,
#[serde(rename = "type")]
pub typ: ProfileType,
pub profile_pic: Option<S>,
pub cover_pic: Option<S>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
pub x: HashMap<S, S>,
}
#[derive(Debug, Default)]
pub struct ListTenantsMetaOptions {
pub limit: Option<u32>,
pub offset: Option<u32>,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TenantListMeta {
pub tn_id: TnId,
pub id_tag: Box<str>,
pub name: Box<str>,
#[serde(rename = "type")]
pub typ: ProfileType,
pub profile_pic: Option<Box<str>>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
}
#[derive(Debug, Default, Deserialize)]
pub struct UpdateTenantData {
#[serde(rename = "idTag", default)]
pub id_tag: Patch<String>,
#[serde(default)]
pub name: Patch<String>,
#[serde(rename = "type", default)]
pub typ: Patch<ProfileType>,
#[serde(rename = "profilePic", default)]
pub profile_pic: Patch<String>,
#[serde(rename = "coverPic", default)]
pub cover_pic: Patch<String>,
#[serde(default)]
pub x: Option<std::collections::HashMap<String, Option<String>>>,
}
#[derive(Debug)]
pub struct Profile<S: AsRef<str>> {
pub id_tag: S,
pub name: S,
pub typ: ProfileType,
pub profile_pic: Option<S>,
pub status: Option<ProfileStatus>,
pub synced_at: Option<Timestamp>,
pub following: bool,
pub connected: ProfileConnectionStatus,
pub roles: Option<Box<[Box<str>]>>,
pub trust: Option<ProfileTrust>,
}
#[derive(Debug, Default, Deserialize)]
pub struct ListProfileOptions {
#[serde(rename = "type")]
pub typ: Option<ProfileType>,
pub status: Option<Box<[ProfileStatus]>>,
pub connected: Option<ProfileConnectionStatus>,
pub following: Option<bool>,
pub q: Option<String>,
pub id_tag: Option<String>,
pub trust_set: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProfileData {
pub id_tag: Box<str>,
pub name: Box<str>,
#[serde(rename = "type")]
pub r#type: Box<str>, pub profile_pic: Option<Box<str>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<Box<str>>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileList {
pub profiles: Vec<ProfileData>,
pub total: usize,
pub limit: usize,
pub offset: usize,
}
#[derive(Debug, Default, Deserialize)]
pub struct UpdateProfileData {
#[serde(default)]
pub name: Patch<Box<str>>,
#[serde(default, rename = "profilePic")]
pub profile_pic: Patch<Option<Box<str>>>,
#[serde(default)]
pub roles: Patch<Option<Vec<Box<str>>>>,
#[serde(default)]
pub status: Patch<ProfileStatus>,
#[serde(default)]
pub synced: Patch<bool>,
#[serde(default)]
pub following: Patch<bool>,
#[serde(default)]
pub connected: Patch<ProfileConnectionStatus>,
#[serde(default)]
pub trust: Patch<ProfileTrust>,
#[serde(default)]
pub etag: Patch<Box<str>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UpsertResult {
Created,
Updated,
}
#[derive(Default)]
pub struct UpsertProfileFields {
pub name: Patch<Box<str>>,
pub typ: Patch<ProfileType>,
pub profile_pic: Patch<Option<Box<str>>>,
pub roles: Patch<Option<Vec<Box<str>>>>,
pub status: Patch<ProfileStatus>,
pub synced: Patch<bool>,
pub following: Patch<bool>,
pub connected: Patch<ProfileConnectionStatus>,
pub trust: Patch<ProfileTrust>,
pub etag: Patch<Box<str>>,
}
impl UpsertProfileFields {
pub fn from_update(update: UpdateProfileData) -> Self {
Self {
name: update.name,
typ: Patch::Undefined,
profile_pic: update.profile_pic,
roles: update.roles,
status: update.status,
synced: update.synced,
following: update.following,
connected: update.connected,
trust: update.trust,
etag: update.etag,
}
}
}
#[derive(Debug, Clone)]
pub struct ActionData {
pub subject: Option<Box<str>>,
pub reactions: Option<Box<str>>,
pub comments: Option<u32>,
}
#[derive(Debug, Clone, Default)]
pub struct UpdateActionDataOptions {
pub subject: Patch<String>,
pub reactions: Patch<String>,
pub comments: Patch<u32>,
pub comments_read: Patch<u32>,
pub status: Patch<char>,
pub visibility: Patch<char>,
pub x: Patch<serde_json::Value>, pub content: Patch<String>,
pub attachments: Patch<String>, pub flags: Patch<String>,
pub sub_typ: Patch<String>,
pub created_at: Patch<Timestamp>,
}
#[derive(Debug, Clone, Default)]
pub struct FinalizeActionOptions<'a> {
pub attachments: Option<&'a [&'a str]>,
pub subject: Option<&'a str>,
pub audience_tag: Option<&'a str>,
pub key: Option<&'a str>,
}
fn deserialize_split<'de, D>(deserializer: D) -> Result<Option<Vec<String>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let values: Vec<String> =
s.split(',').map(|v| v.trim().to_string()).filter(|v| !v.is_empty()).collect();
if values.is_empty() { Ok(None) } else { Ok(Some(values)) }
}
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AudienceType {
Personal,
Community,
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ListActionOptions {
pub limit: Option<u32>,
pub cursor: Option<String>,
pub sort: Option<String>,
#[serde(rename = "sortDir")]
pub sort_dir: Option<String>,
#[serde(default, rename = "type", deserialize_with = "deserialize_split")]
pub typ: Option<Vec<String>>,
#[serde(default, deserialize_with = "deserialize_split")]
pub status: Option<Vec<String>>,
pub tag: Option<String>,
pub search: Option<String>,
#[serde(default, deserialize_with = "deserialize_split")]
pub visibility: Option<Vec<String>>,
pub issuer: Option<String>,
pub audience: Option<String>,
#[serde(rename = "audienceType")]
pub audience_type: Option<AudienceType>,
pub involved: Option<String>,
#[serde(skip)]
pub viewer_id_tag: Option<String>,
#[serde(rename = "actionId")]
pub action_id: Option<String>,
#[serde(rename = "parentId")]
pub parent_id: Option<String>,
#[serde(rename = "rootId")]
pub root_id: Option<String>,
pub subject: Option<String>,
#[serde(rename = "createdAfter")]
pub created_after: Option<Timestamp>,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
pub struct ProfileInfo {
#[serde(rename = "idTag")]
pub id_tag: Box<str>,
pub name: Box<str>,
#[serde(rename = "type")]
pub typ: ProfileType,
#[serde(rename = "profilePic")]
pub profile_pic: Option<Box<str>>,
}
pub struct Action<S: AsRef<str>> {
pub action_id: S,
pub typ: S,
pub sub_typ: Option<S>,
pub issuer_tag: S,
pub parent_id: Option<S>,
pub root_id: Option<S>,
pub audience_tag: Option<S>,
pub content: Option<S>,
pub attachments: Option<Vec<S>>,
pub subject: Option<S>,
pub created_at: Timestamp,
pub expires_at: Option<Timestamp>,
pub visibility: Option<char>, pub flags: Option<S>, pub x: Option<serde_json::Value>, }
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
pub struct AttachmentView {
#[serde(rename = "fileId")]
pub file_id: Box<str>,
pub dim: Option<(u32, u32)>,
#[serde(rename = "localVariants")]
pub local_variants: Option<Vec<Box<str>>>,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ActionView {
pub action_id: Box<str>,
#[serde(rename = "type")]
pub typ: Box<str>,
#[serde(rename = "subType")]
pub sub_typ: Option<Box<str>>,
pub parent_id: Option<Box<str>>,
pub root_id: Option<Box<str>>,
pub issuer: ProfileInfo,
pub audience: Option<ProfileInfo>,
pub content: Option<serde_json::Value>,
pub attachments: Option<Vec<AttachmentView>>,
pub subject: Option<Box<str>>,
pub subject_profile: Option<ProfileInfo>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
#[serde(serialize_with = "serialize_timestamp_iso_opt")]
pub expires_at: Option<Timestamp>,
pub status: Option<Box<str>>,
pub stat: Option<serde_json::Value>,
pub visibility: Option<char>,
pub flags: Option<Box<str>>, pub x: Option<serde_json::Value>, }
#[derive(Debug)]
pub enum FileId<S: AsRef<str>> {
FileId(S),
FId(u64),
}
pub enum ActionId<S: AsRef<str>> {
ActionId(S),
AId(u64),
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub enum FileStatus {
#[serde(rename = "A")]
Active,
#[serde(rename = "P")]
Pending,
#[serde(rename = "D")]
Deleted,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileUserData {
#[serde(serialize_with = "serialize_timestamp_iso_opt")]
pub accessed_at: Option<Timestamp>,
#[serde(serialize_with = "serialize_timestamp_iso_opt")]
pub modified_at: Option<Timestamp>,
pub pinned: bool,
pub starred: bool,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileView {
pub file_id: Box<str>,
pub parent_id: Option<Box<str>>, pub root_id: Option<Box<str>>, pub owner: Option<ProfileInfo>,
pub creator: Option<ProfileInfo>,
pub preset: Option<Box<str>>,
pub content_type: Option<Box<str>>,
pub file_name: Box<str>,
pub file_tp: Option<Box<str>>, #[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
#[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
pub accessed_at: Option<Timestamp>, #[serde(serialize_with = "crate::types::serialize_timestamp_iso_opt")]
pub modified_at: Option<Timestamp>, pub status: FileStatus,
pub tags: Option<Vec<Box<str>>>,
pub visibility: Option<char>, pub hidden: bool,
pub access_level: Option<crate::types::AccessLevel>, pub user_data: Option<FileUserData>, pub x: Option<serde_json::Value>, }
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
pub struct FileVariant<S: AsRef<str> + Debug> {
#[serde(rename = "variantId")]
pub variant_id: S,
pub variant: S,
pub format: S,
pub size: u64,
pub resolution: (u32, u32),
pub available: bool,
pub duration: Option<f64>,
pub bitrate: Option<u32>,
#[serde(rename = "pageCount")]
pub page_count: Option<u32>,
}
impl<S: AsRef<str> + Debug> PartialEq for FileVariant<S> {
fn eq(&self, other: &Self) -> bool {
self.variant_id.as_ref() == other.variant_id.as_ref()
&& self.variant.as_ref() == other.variant.as_ref()
&& self.format.as_ref() == other.format.as_ref()
&& self.size == other.size
&& self.resolution == other.resolution
&& self.available == other.available
&& self.duration == other.duration
&& self.bitrate == other.bitrate
&& self.page_count == other.page_count
}
}
impl<S: AsRef<str> + Debug> Eq for FileVariant<S> {}
impl<S: AsRef<str> + Debug + Ord> PartialOrd for FileVariant<S> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<S: AsRef<str> + Debug + Ord> Ord for FileVariant<S> {
fn cmp(&self, other: &Self) -> Ordering {
self.size
.cmp(&other.size)
.then_with(|| self.resolution.0.cmp(&other.resolution.0))
.then_with(|| self.resolution.1.cmp(&other.resolution.1))
.then_with(|| self.variant.as_ref().cmp(other.variant.as_ref()))
}
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ListFileOptions {
pub limit: Option<u32>,
pub cursor: Option<String>,
#[serde(default, rename = "fileId", deserialize_with = "deserialize_split")]
pub file_id: Option<Vec<String>>,
#[serde(rename = "parentId")]
pub parent_id: Option<String>, #[serde(rename = "rootId")]
pub root_id: Option<String>, pub tag: Option<String>,
pub preset: Option<String>,
pub variant: Option<String>,
pub status: Option<FileStatus>,
#[serde(default, rename = "fileTp", deserialize_with = "deserialize_split")]
pub file_type: Option<Vec<String>>,
#[serde(default, rename = "contentType", deserialize_with = "deserialize_split")]
pub content_type: Option<Vec<String>>,
#[serde(rename = "fileName")]
pub file_name: Option<String>,
#[serde(rename = "ownerIdTag")]
pub owner_id_tag: Option<String>,
#[serde(rename = "notOwnerIdTag")]
pub not_owner_id_tag: Option<String>,
pub pinned: Option<bool>,
pub starred: Option<bool>,
pub hidden: Option<bool>,
pub sort: Option<String>,
#[serde(rename = "sortDir")]
pub sort_dir: Option<String>,
#[serde(skip)]
pub user_id_tag: Option<String>,
#[serde(skip)]
pub scope_file_id: Option<String>,
#[serde(skip)]
pub visible_levels: Option<Vec<char>>,
}
#[derive(Debug, Clone, Default)]
pub struct CreateFile {
pub orig_variant_id: Option<Box<str>>,
pub file_id: Option<Box<str>>,
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>>,
pub content_type: Box<str>,
pub file_name: Box<str>,
pub file_tp: Option<Box<str>>, pub created_at: Option<Timestamp>,
pub tags: Option<Vec<Box<str>>>,
pub x: Option<serde_json::Value>,
pub visibility: Option<char>, pub hidden: bool,
pub status: Option<FileStatus>, }
#[derive(Debug, Clone, Deserialize)]
pub struct CreateFileVariant {
pub variant: Box<str>,
pub format: Box<str>,
pub resolution: (u32, u32),
pub size: u64,
pub available: bool,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct UpdateFileOptions {
#[serde(default, rename = "fileName")]
pub file_name: Patch<String>,
#[serde(default, rename = "parentId")]
pub parent_id: Patch<String>, #[serde(default)]
pub visibility: Patch<char>,
#[serde(default)]
pub status: Patch<char>,
#[serde(default)]
pub hidden: Patch<bool>,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ShareEntry {
pub id: i64,
pub resource_type: char,
pub resource_id: Box<str>,
pub subject_type: char,
pub subject_id: Box<str>,
pub permission: char,
#[serde(serialize_with = "serialize_timestamp_iso_opt")]
pub expires_at: Option<Timestamp>,
pub created_by: Box<str>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
pub subject_file_name: Option<Box<str>>,
pub subject_content_type: Option<Box<str>>,
pub subject_file_tp: Option<Box<str>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateShareEntry {
pub subject_type: char,
pub subject_id: String,
pub permission: char,
pub expires_at: Option<Timestamp>,
}
#[skip_serializing_none]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PushSubscriptionData {
pub endpoint: String,
#[serde(rename = "expirationTime")]
pub expiration_time: Option<i64>,
pub keys: PushSubscriptionKeys,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PushSubscriptionKeys {
pub p256dh: String,
pub auth: String,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PushSubscription {
pub id: u64,
pub subscription: PushSubscriptionData,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
}
pub struct Task {
pub task_id: u64,
pub tn_id: TnId,
pub kind: Box<str>,
pub status: char,
pub created_at: Timestamp,
pub next_at: Option<Timestamp>,
pub input: Box<str>,
pub output: Box<str>,
pub deps: Box<[u64]>,
pub retry: Option<Box<str>>,
pub cron: Option<Box<str>>,
}
#[derive(Debug, Default)]
pub struct TaskPatch {
pub input: Patch<String>,
pub next_at: Patch<Timestamp>,
pub deps: Patch<Vec<u64>>,
pub retry: Patch<String>,
pub cron: Patch<String>,
}
#[derive(Debug, Default)]
pub struct ListTaskOptions {}
#[derive(Debug)]
pub struct InstallApp {
pub app_name: Box<str>,
pub publisher_tag: Box<str>,
pub version: Box<str>,
pub action_id: Box<str>,
pub file_id: Box<str>,
pub blob_id: Box<str>,
pub capabilities: Option<Vec<Box<str>>>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InstalledApp {
pub app_name: Box<str>,
pub publisher_tag: Box<str>,
pub version: Box<str>,
pub action_id: Box<str>,
pub file_id: Box<str>,
pub blob_id: Box<str>,
pub status: Box<str>,
pub capabilities: Option<Vec<Box<str>>>,
pub auto_update: bool,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub installed_at: Timestamp,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AddressBook {
pub ab_id: u64,
pub name: Box<str>,
pub description: Option<Box<str>>,
pub ctag: Box<str>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub updated_at: Timestamp,
}
#[derive(Debug, Default)]
pub struct UpdateAddressBookData {
pub name: Patch<String>,
pub description: Patch<String>,
}
#[derive(Debug, Clone, Default)]
pub struct ContactExtracted {
pub fn_name: Option<Box<str>>,
pub given_name: Option<Box<str>>,
pub family_name: Option<Box<str>>,
pub email: Option<Box<str>>,
pub emails: Option<Box<str>>,
pub tel: Option<Box<str>>,
pub tels: Option<Box<str>>,
pub org: Option<Box<str>>,
pub title: Option<Box<str>>,
pub note: Option<Box<str>>,
pub photo_uri: Option<Box<str>>,
pub profile_id_tag: Option<Box<str>>,
}
#[derive(Debug, Clone)]
pub struct Contact {
pub c_id: u64,
pub ab_id: u64,
pub uid: Box<str>,
pub etag: Box<str>,
pub vcard: Box<str>,
pub extracted: ContactExtracted,
pub created_at: Timestamp,
pub updated_at: Timestamp,
}
#[derive(Debug, Clone)]
pub struct ContactView {
pub c_id: u64,
pub ab_id: u64,
pub uid: Box<str>,
pub etag: Box<str>,
pub extracted: ContactExtracted,
pub created_at: Timestamp,
pub updated_at: Timestamp,
}
#[derive(Debug, Clone)]
pub struct ContactSyncEntry {
pub uid: Box<str>,
pub etag: Box<str>,
pub deleted: bool,
pub updated_at: Timestamp,
}
#[derive(Debug, Default)]
pub struct ListContactOptions {
pub q: Option<String>,
pub cursor: Option<String>,
pub limit: Option<u32>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Calendar {
pub cal_id: u64,
pub name: Box<str>,
pub description: Option<Box<str>>,
pub color: Option<Box<str>>,
pub timezone: Option<Box<str>>,
pub components: Box<str>,
pub ctag: Box<str>,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub created_at: Timestamp,
#[serde(serialize_with = "serialize_timestamp_iso")]
pub updated_at: Timestamp,
}
#[derive(Debug, Default)]
pub struct CreateCalendarData {
pub name: String,
pub description: Option<String>,
pub color: Option<String>,
pub timezone: Option<String>,
pub components: Option<String>,
}
#[derive(Debug, Default)]
pub struct UpdateCalendarData {
pub name: Patch<String>,
pub description: Patch<String>,
pub color: Patch<String>,
pub timezone: Patch<String>,
pub components: Patch<String>,
}
#[derive(Debug, Clone, Default)]
pub struct CalendarObjectExtracted {
pub component: Box<str>,
pub summary: Option<Box<str>>,
pub location: Option<Box<str>>,
pub description: Option<Box<str>>,
pub dtstart: Option<Timestamp>,
pub dtend: Option<Timestamp>,
pub all_day: bool,
pub status: Option<Box<str>>,
pub priority: Option<u8>,
pub organizer: Option<Box<str>>,
pub rrule: Option<Box<str>>,
pub exdate: Vec<Timestamp>,
pub recurrence_id: Option<Timestamp>,
pub sequence: i64,
}
#[derive(Debug, Clone, Copy)]
pub struct CalendarObjectWrite<'a> {
pub uid: &'a str,
pub ical: &'a str,
pub etag: &'a str,
pub extracted: &'a CalendarObjectExtracted,
}
#[derive(Debug, Clone)]
pub struct CalendarObject {
pub co_id: u64,
pub cal_id: u64,
pub uid: Box<str>,
pub etag: Box<str>,
pub ical: Box<str>,
pub extracted: CalendarObjectExtracted,
pub created_at: Timestamp,
pub updated_at: Timestamp,
}
#[derive(Debug, Clone)]
pub struct CalendarObjectView {
pub co_id: u64,
pub cal_id: u64,
pub uid: Box<str>,
pub etag: Box<str>,
pub extracted: CalendarObjectExtracted,
pub created_at: Timestamp,
pub updated_at: Timestamp,
}
#[derive(Debug, Clone)]
pub struct CalendarObjectSyncEntry {
pub uid: Box<str>,
pub etag: Box<str>,
pub deleted: bool,
pub updated_at: Timestamp,
}
#[derive(Debug, Default)]
pub struct ListCalendarObjectOptions {
pub component: Option<String>,
pub q: Option<String>,
pub start: Option<Timestamp>,
pub end: Option<Timestamp>,
pub cursor: Option<String>,
pub limit: Option<u32>,
pub include_exceptions: bool,
}
#[async_trait]
pub trait MetaAdapter: Debug + Send + Sync {
async fn read_tenant(&self, tn_id: TnId) -> ClResult<Tenant<Box<str>>>;
async fn create_tenant(&self, tn_id: TnId, id_tag: &str) -> ClResult<TnId>;
async fn update_tenant(&self, tn_id: TnId, tenant: &UpdateTenantData) -> ClResult<()>;
async fn delete_tenant(&self, tn_id: TnId) -> ClResult<()>;
async fn list_tenants(&self, opts: &ListTenantsMetaOptions) -> ClResult<Vec<TenantListMeta>>;
async fn list_profiles(
&self,
tn_id: TnId,
opts: &ListProfileOptions,
) -> ClResult<Vec<Profile<Box<str>>>>;
async fn get_relationships(
&self,
tn_id: TnId,
target_id_tags: &[&str],
) -> ClResult<HashMap<String, (bool, bool)>>;
async fn read_profile(
&self,
tn_id: TnId,
id_tag: &str,
) -> ClResult<(Box<str>, Profile<Box<str>>)>;
async fn read_profile_roles(
&self,
tn_id: TnId,
id_tag: &str,
) -> ClResult<Option<Box<[Box<str>]>>>;
async fn upsert_profile(
&self,
tn_id: TnId,
id_tag: &str,
fields: &UpsertProfileFields,
) -> ClResult<UpsertResult>;
async fn read_profile_public_key(
&self,
id_tag: &str,
key_id: &str,
) -> ClResult<(Box<str>, Timestamp)>;
async fn add_profile_public_key(
&self,
id_tag: &str,
key_id: &str,
public_key: &str,
expires_at: Option<Timestamp>,
) -> ClResult<()>;
async fn list_stale_profiles(
&self,
max_age_secs: i64,
disable_after_secs: i64,
limit: u32,
) -> ClResult<Vec<(TnId, Box<str>, Option<Box<str>>)>>;
async fn get_action_id(&self, tn_id: TnId, a_id: u64) -> ClResult<Box<str>>;
async fn list_actions(
&self,
tn_id: TnId,
opts: &ListActionOptions,
) -> ClResult<Vec<ActionView>>;
async fn list_action_tokens(
&self,
tn_id: TnId,
opts: &ListActionOptions,
) -> ClResult<Box<[Box<str>]>>;
async fn create_action(
&self,
tn_id: TnId,
action: &Action<&str>,
key: Option<&str>,
) -> ClResult<ActionId<Box<str>>>;
async fn finalize_action(
&self,
tn_id: TnId,
a_id: u64,
action_id: &str,
options: FinalizeActionOptions<'_>,
) -> ClResult<()>;
async fn create_inbound_action(
&self,
tn_id: TnId,
action_id: &str,
token: &str,
ack_token: Option<&str>,
) -> ClResult<()>;
async fn get_action_root_id(&self, tn_id: TnId, action_id: &str) -> ClResult<Box<str>>;
async fn get_action_data(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionData>>;
async fn get_action_by_key(
&self,
tn_id: TnId,
action_key: &str,
) -> ClResult<Option<Action<Box<str>>>>;
async fn store_action_token(&self, tn_id: TnId, action_id: &str, token: &str) -> ClResult<()>;
async fn get_action_token(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<Box<str>>>;
async fn update_action_data(
&self,
tn_id: TnId,
action_id: &str,
opts: &UpdateActionDataOptions,
) -> ClResult<()>;
async fn update_inbound_action(
&self,
tn_id: TnId,
action_id: &str,
status: Option<char>,
) -> ClResult<()>;
async fn get_related_action_tokens(
&self,
tn_id: TnId,
aprv_action_id: &str,
) -> ClResult<Vec<(Box<str>, Box<str>)>>;
async fn get_file_id(&self, tn_id: TnId, f_id: u64) -> ClResult<Box<str>>;
async fn list_files(&self, tn_id: TnId, opts: &ListFileOptions) -> ClResult<Vec<FileView>>;
async fn list_file_variants(
&self,
tn_id: TnId,
file_id: FileId<&str>,
) -> ClResult<Vec<FileVariant<Box<str>>>>;
async fn list_available_variants(&self, tn_id: TnId, file_id: &str) -> ClResult<Vec<Box<str>>>;
async fn read_file_variant(
&self,
tn_id: TnId,
variant_id: &str,
) -> ClResult<FileVariant<Box<str>>>;
async fn read_file_id_by_variant(&self, tn_id: TnId, variant_id: &str) -> ClResult<Box<str>>;
async fn read_f_id_by_file_id(&self, tn_id: TnId, file_id: &str) -> ClResult<u64>;
async fn create_file(&self, tn_id: TnId, opts: CreateFile) -> ClResult<FileId<Box<str>>>;
async fn create_file_variant<'a>(
&'a self,
tn_id: TnId,
f_id: u64,
opts: FileVariant<&'a str>,
) -> ClResult<&'a str>;
async fn update_file_id(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
async fn finalize_file(&self, tn_id: TnId, f_id: u64, file_id: &str) -> ClResult<()>;
async fn list_tasks(&self, opts: ListTaskOptions) -> ClResult<Vec<Task>>;
async fn list_task_ids(&self, kind: &str, keys: &[Box<str>]) -> ClResult<Vec<u64>>;
async fn create_task(
&self,
kind: &'static str,
key: Option<&str>,
input: &str,
deps: &[u64],
) -> ClResult<u64>;
async fn update_task_finished(&self, task_id: u64, output: &str) -> ClResult<()>;
async fn update_task_error(
&self,
task_id: u64,
output: &str,
next_at: Option<Timestamp>,
) -> ClResult<()>;
async fn find_task_by_key(&self, key: &str) -> ClResult<Option<Task>>;
async fn update_task(&self, task_id: u64, patch: &TaskPatch) -> ClResult<()>;
async fn find_completed_deps(&self, deps: &[u64]) -> ClResult<Vec<u64>>;
async fn get_profile_info(&self, tn_id: TnId, id_tag: &str) -> ClResult<ProfileData>;
async fn get_action(&self, tn_id: TnId, action_id: &str) -> ClResult<Option<ActionView>>;
async fn update_action(
&self,
tn_id: TnId,
action_id: &str,
content: Option<&str>,
attachments: Option<&[&str]>,
) -> ClResult<()>;
async fn delete_action(&self, tn_id: TnId, action_id: &str) -> ClResult<()>;
async fn count_reactions(&self, tn_id: TnId, subject_id: &str) -> ClResult<String>;
async fn delete_file(&self, tn_id: TnId, file_id: &str) -> ClResult<()>;
async fn list_children_by_root(&self, tn_id: TnId, root_id: &str) -> ClResult<Vec<Box<str>>>;
async fn list_settings(
&self,
tn_id: TnId,
prefix: Option<&[String]>,
) -> ClResult<std::collections::HashMap<String, serde_json::Value>>;
async fn read_setting(&self, tn_id: TnId, name: &str) -> ClResult<Option<serde_json::Value>>;
async fn update_setting(
&self,
tn_id: TnId,
name: &str,
value: Option<serde_json::Value>,
) -> ClResult<()>;
async fn list_refs(&self, tn_id: TnId, opts: &ListRefsOptions) -> ClResult<Vec<RefData>>;
async fn get_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<Option<(Box<str>, Box<str>)>>;
async fn create_ref(
&self,
tn_id: TnId,
ref_id: &str,
opts: &CreateRefOptions,
) -> ClResult<RefData>;
async fn delete_ref(&self, tn_id: TnId, ref_id: &str) -> ClResult<()>;
async fn use_ref(
&self,
ref_id: &str,
expected_types: &[&str],
) -> ClResult<(TnId, Box<str>, RefData)>;
async fn validate_ref(
&self,
ref_id: &str,
expected_types: &[&str],
) -> ClResult<(TnId, Box<str>, RefData)>;
async fn list_tags(
&self,
tn_id: TnId,
prefix: Option<&str>,
with_counts: bool,
limit: Option<u32>,
) -> ClResult<Vec<TagInfo>>;
async fn add_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
async fn remove_tag(&self, tn_id: TnId, file_id: &str, tag: &str) -> ClResult<Vec<String>>;
async fn update_file_data(
&self,
tn_id: TnId,
file_id: &str,
opts: &UpdateFileOptions,
) -> ClResult<()>;
async fn read_file(&self, tn_id: TnId, file_id: &str) -> ClResult<Option<FileView>>;
async fn record_file_access(&self, tn_id: TnId, id_tag: &str, file_id: &str) -> ClResult<()>;
async fn record_file_modification(
&self,
tn_id: TnId,
id_tag: &str,
file_id: &str,
) -> ClResult<()>;
async fn update_file_user_data(
&self,
tn_id: TnId,
id_tag: &str,
file_id: &str,
pinned: Option<bool>,
starred: Option<bool>,
) -> ClResult<FileUserData>;
async fn get_file_user_data(
&self,
tn_id: TnId,
id_tag: &str,
file_id: &str,
) -> ClResult<Option<FileUserData>>;
async fn list_push_subscriptions(&self, tn_id: TnId) -> ClResult<Vec<PushSubscription>>;
async fn create_push_subscription(
&self,
tn_id: TnId,
subscription: &PushSubscriptionData,
) -> ClResult<u64>;
async fn delete_push_subscription(&self, tn_id: TnId, subscription_id: u64) -> ClResult<()>;
async fn create_share_entry(
&self,
tn_id: TnId,
resource_type: char,
resource_id: &str,
created_by: &str,
entry: &CreateShareEntry,
) -> ClResult<ShareEntry>;
async fn delete_share_entry(&self, tn_id: TnId, id: i64) -> ClResult<()>;
async fn list_share_entries(
&self,
tn_id: TnId,
resource_type: char,
resource_id: &str,
) -> ClResult<Vec<ShareEntry>>;
async fn list_share_entries_by_subject(
&self,
tn_id: TnId,
subject_type: Option<char>,
subject_id: &str,
) -> ClResult<Vec<ShareEntry>>;
async fn check_share_access(
&self,
tn_id: TnId,
resource_type: char,
resource_id: &str,
subject_type: char,
subject_id: &str,
) -> ClResult<Option<char>>;
async fn read_share_entry(&self, tn_id: TnId, id: i64) -> ClResult<Option<ShareEntry>>;
async fn install_app(&self, tn_id: TnId, install: &InstallApp) -> ClResult<()>;
async fn uninstall_app(&self, tn_id: TnId, app_name: &str, publisher_tag: &str)
-> ClResult<()>;
async fn list_installed_apps(
&self,
tn_id: TnId,
search: Option<&str>,
) -> ClResult<Vec<InstalledApp>>;
async fn get_installed_app(
&self,
tn_id: TnId,
app_name: &str,
publisher_tag: &str,
) -> ClResult<Option<InstalledApp>>;
async fn create_address_book(
&self,
tn_id: TnId,
name: &str,
description: Option<&str>,
) -> ClResult<AddressBook>;
async fn list_address_books(&self, tn_id: TnId) -> ClResult<Vec<AddressBook>>;
async fn get_address_book(&self, tn_id: TnId, ab_id: u64) -> ClResult<Option<AddressBook>>;
async fn get_address_book_by_name(
&self,
tn_id: TnId,
name: &str,
) -> ClResult<Option<AddressBook>>;
async fn update_address_book(
&self,
tn_id: TnId,
ab_id: u64,
patch: &UpdateAddressBookData,
) -> ClResult<()>;
async fn delete_address_book(&self, tn_id: TnId, ab_id: u64) -> ClResult<()>;
async fn list_contacts(
&self,
tn_id: TnId,
ab_id: Option<u64>,
opts: &ListContactOptions,
) -> ClResult<Vec<ContactView>>;
async fn get_contact(&self, tn_id: TnId, ab_id: u64, uid: &str) -> ClResult<Option<Contact>>;
async fn upsert_contact(
&self,
tn_id: TnId,
ab_id: u64,
uid: &str,
vcard: &str,
etag: &str,
extracted: &ContactExtracted,
) -> ClResult<Box<str>>;
async fn delete_contact(&self, tn_id: TnId, ab_id: u64, uid: &str) -> ClResult<()>;
async fn get_contacts_by_uids(
&self,
tn_id: TnId,
ab_id: u64,
uids: &[&str],
) -> ClResult<Vec<Contact>>;
async fn list_contacts_since(
&self,
tn_id: TnId,
ab_id: u64,
since: Option<Timestamp>,
limit: Option<u32>,
) -> ClResult<Vec<ContactSyncEntry>>;
async fn list_contacts_by_profile(
&self,
tn_id: TnId,
profile_id_tag: &str,
) -> ClResult<Vec<Contact>>;
async fn create_calendar(&self, tn_id: TnId, input: &CreateCalendarData) -> ClResult<Calendar>;
async fn list_calendars(&self, tn_id: TnId) -> ClResult<Vec<Calendar>>;
async fn get_calendar(&self, tn_id: TnId, cal_id: u64) -> ClResult<Option<Calendar>>;
async fn get_calendar_by_name(&self, tn_id: TnId, name: &str) -> ClResult<Option<Calendar>>;
async fn update_calendar(
&self,
tn_id: TnId,
cal_id: u64,
patch: &UpdateCalendarData,
) -> ClResult<()>;
async fn delete_calendar(&self, tn_id: TnId, cal_id: u64) -> ClResult<()>;
async fn list_calendar_objects(
&self,
tn_id: TnId,
cal_id: u64,
opts: &ListCalendarObjectOptions,
) -> ClResult<Vec<CalendarObjectView>>;
async fn get_calendar_object(
&self,
tn_id: TnId,
cal_id: u64,
uid: &str,
) -> ClResult<Option<CalendarObject>>;
async fn get_calendar_object_override(
&self,
tn_id: TnId,
cal_id: u64,
uid: &str,
recurrence_id: Timestamp,
) -> ClResult<Option<CalendarObject>>;
async fn list_calendar_object_overrides(
&self,
tn_id: TnId,
cal_id: u64,
uid: &str,
) -> ClResult<Vec<CalendarObject>>;
async fn delete_calendar_object_override(
&self,
tn_id: TnId,
cal_id: u64,
uid: &str,
recurrence_id: Timestamp,
) -> ClResult<()>;
async fn upsert_calendar_object(
&self,
tn_id: TnId,
cal_id: u64,
uid: &str,
ical: &str,
etag: &str,
extracted: &CalendarObjectExtracted,
) -> ClResult<Box<str>>;
async fn delete_calendar_object(&self, tn_id: TnId, cal_id: u64, uid: &str) -> ClResult<()>;
async fn split_calendar_object_series(
&self,
tn_id: TnId,
cal_id: u64,
master: CalendarObjectWrite<'_>,
tail: CalendarObjectWrite<'_>,
split_at: Timestamp,
) -> ClResult<(Box<str>, Box<str>)>;
async fn get_calendar_objects_by_uids(
&self,
tn_id: TnId,
cal_id: u64,
uids: &[&str],
) -> ClResult<Vec<CalendarObject>>;
async fn list_calendar_objects_since(
&self,
tn_id: TnId,
cal_id: u64,
since: Option<Timestamp>,
limit: Option<u32>,
) -> ClResult<Vec<CalendarObjectSyncEntry>>;
async fn query_calendar_objects_in_range(
&self,
tn_id: TnId,
cal_id: u64,
component: Option<&str>,
start: Option<Timestamp>,
end: Option<Timestamp>,
) -> ClResult<Vec<CalendarObject>>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_list_action_options_with_multiple_statuses() {
let query = "status=C,N&type=POST,REPLY";
let opts: ListActionOptions =
serde_urlencoded::from_str(query).expect("should deserialize");
assert!(opts.status.is_some());
let statuses = opts.status.expect("status should be Some");
assert_eq!(statuses.len(), 2);
assert_eq!(statuses[0].as_str(), "C");
assert_eq!(statuses[1].as_str(), "N");
assert!(opts.typ.is_some());
let types = opts.typ.expect("type should be Some");
assert_eq!(types.len(), 2);
assert_eq!(types[0].as_str(), "POST");
assert_eq!(types[1].as_str(), "REPLY");
}
#[test]
fn test_deserialize_list_action_options_without_status() {
let query = "issuer=alice";
let opts: ListActionOptions =
serde_urlencoded::from_str(query).expect("should deserialize");
assert!(opts.status.is_none());
assert!(opts.typ.is_none());
assert_eq!(opts.issuer.as_deref(), Some("alice"));
}
#[test]
fn test_deserialize_list_action_options_single_status() {
let query = "status=C";
let opts: ListActionOptions =
serde_urlencoded::from_str(query).expect("should deserialize");
assert!(opts.status.is_some());
let statuses = opts.status.expect("status should be Some");
assert_eq!(statuses.len(), 1);
assert_eq!(statuses[0].as_str(), "C");
}
#[test]
fn test_deserialize_list_action_options_audience_type() {
let opts: ListActionOptions = serde_urlencoded::from_str("audienceType=personal")
.expect("should deserialize personal");
assert!(matches!(opts.audience_type, Some(AudienceType::Personal)));
let opts: ListActionOptions = serde_urlencoded::from_str("audienceType=community")
.expect("should deserialize community");
assert!(matches!(opts.audience_type, Some(AudienceType::Community)));
let opts: ListActionOptions =
serde_urlencoded::from_str("issuer=alice").expect("should deserialize");
assert!(opts.audience_type.is_none());
let res: Result<ListActionOptions, _> = serde_urlencoded::from_str("audienceType=garbage");
assert!(res.is_err(), "garbage audienceType should error");
}
#[test]
fn test_deserialize_list_action_options_multi_visibility() {
let opts: ListActionOptions =
serde_urlencoded::from_str("visibility=F,C").expect("should deserialize");
let v = opts.visibility.expect("visibility should be Some");
assert_eq!(v.len(), 2);
assert_eq!(v[0].as_str(), "F");
assert_eq!(v[1].as_str(), "C");
let opts: ListActionOptions =
serde_urlencoded::from_str("visibility=P").expect("should deserialize");
let v = opts.visibility.expect("visibility should be Some");
assert_eq!(v.len(), 1);
assert_eq!(v[0].as_str(), "P");
let opts: ListActionOptions =
serde_urlencoded::from_str("issuer=alice").expect("should deserialize");
assert!(opts.visibility.is_none());
}
#[test]
fn test_deserialize_list_action_options_visibility_with_direct() {
let opts: ListActionOptions =
serde_urlencoded::from_str("visibility=D,F").expect("should deserialize");
let v = opts.visibility.expect("visibility should be Some");
assert_eq!(v.len(), 2);
assert_eq!(v[0].as_str(), "D");
assert_eq!(v[1].as_str(), "F");
}
}