Skip to main content

silicon_dm_client/
models.rs

1//! Public wire types, independent of the server implementation.
2use serde::{Deserialize, Serialize};
3use serde_json::{Map, Value};
4use std::collections::BTreeMap;
5use uuid::Uuid;
6
7/// JSON object that is preserved with every message and draft.
8pub type Metadata = Map<String, Value>;
9
10#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
11#[serde(rename_all = "snake_case")]
12pub enum ActorType {
13    Carbon,
14    Silicon,
15}
16
17#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
18pub struct Actor {
19    #[serde(rename = "type")]
20    pub actor_type: ActorType,
21    pub id: String,
22}
23
24#[derive(Clone, Debug, Serialize, Deserialize)]
25pub struct Attachment {
26    pub permanent_url: url::Url,
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub name: Option<String>,
29    #[serde(default, skip_serializing_if = "Option::is_none")]
30    pub content_type: Option<String>,
31    #[serde(default, skip_serializing_if = "Option::is_none")]
32    pub size: Option<u64>,
33}
34#[derive(Clone, Debug, Serialize, Deserialize)]
35pub struct VoiceAttachment {
36    #[serde(flatten)]
37    pub attachment: Attachment,
38    /// Required for writes; historical server records can contain null.
39    pub duration_milliseconds: Option<u64>,
40}
41#[derive(Clone, Debug, Serialize, Deserialize)]
42pub struct Gif {
43    pub provider_id: String,
44    pub url: url::Url,
45    #[serde(default, skip_serializing_if = "Option::is_none")]
46    pub preview_url: Option<url::Url>,
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub title: Option<String>,
49}
50#[derive(Clone, Debug, Default, Serialize, Deserialize)]
51pub struct MessageCreate {
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub sender_id: Option<String>,
54    /// Optional recipient address, for example deliberate@cos:tos.
55    #[serde(default, skip_serializing_if = "Option::is_none")]
56    pub recipient_id: Option<String>,
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    pub text: Option<String>,
59    #[serde(default)]
60    pub attachments: Vec<Attachment>,
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub voice: Option<VoiceAttachment>,
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub voice_transcript: Option<String>,
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub gif: Option<Gif>,
67    #[serde(default)]
68    pub metadata: Metadata,
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub reply_to_message_id: Option<Uuid>,
71}
72#[derive(Clone, Debug, Serialize, Deserialize)]
73#[serde(rename_all = "snake_case")]
74pub enum MessageStatus {
75    Waiting,
76    Sent,
77    Delivered,
78    Read,
79    Failed,
80}
81#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
82#[serde(rename_all = "snake_case")]
83pub enum ReceiptStatus {
84    Delivered,
85    Read,
86}
87#[derive(Clone, Debug, Serialize, Deserialize)]
88pub struct Message {
89    pub id: Uuid,
90    pub conversation_id: Uuid,
91    pub sender: Actor,
92    pub sequence: i64,
93    pub status: MessageStatus,
94    #[serde(flatten)]
95    pub content: MessageCreate,
96    pub created_at: String,
97    #[serde(default)]
98    pub delivered_at: Option<String>,
99    #[serde(default)]
100    pub read_at: Option<String>,
101    #[serde(default)]
102    pub failure_reason: Option<String>,
103    #[serde(default)]
104    pub bundle: Option<BundleRef>,
105    #[serde(default)]
106    pub version: Option<i64>,
107    #[serde(default)]
108    pub updated_at: Option<String>,
109    #[serde(default)]
110    pub deleted_at: Option<String>,
111}
112#[derive(Clone, Debug, Serialize, Deserialize)]
113pub struct BundleRef {
114    pub id: Uuid,
115    pub role: String,
116}
117#[derive(Clone, Debug, Serialize, Deserialize)]
118pub struct Conversation {
119    pub id: Uuid,
120    pub org_id: String,
121    pub participants: Vec<Actor>,
122    pub last_message: Option<Message>,
123    pub created_at: String,
124    pub updated_at: String,
125}
126#[derive(Clone, Debug, Default, Serialize, Deserialize)]
127pub struct PageRequest {
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub cursor: Option<String>,
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub limit: Option<u16>,
132}
133#[derive(Clone, Debug, Serialize, Deserialize)]
134pub struct Page<T> {
135    pub items: Vec<T>,
136    #[serde(default)]
137    pub next_cursor: Option<String>,
138}
139#[derive(Clone, Debug, Default, Serialize, Deserialize)]
140pub struct DraftInput {
141    #[serde(default, skip_serializing_if = "Option::is_none")]
142    pub message_content: Option<String>,
143    #[serde(default)]
144    pub attachments: Vec<Attachment>,
145    #[serde(default, skip_serializing_if = "Option::is_none")]
146    pub voice: Option<VoiceAttachment>,
147    #[serde(default, skip_serializing_if = "Option::is_none")]
148    pub voice_transcript: Option<String>,
149    #[serde(default, skip_serializing_if = "Option::is_none")]
150    pub gif: Option<Gif>,
151    #[serde(default)]
152    pub metadata: Metadata,
153    #[serde(default, skip_serializing_if = "Option::is_none")]
154    pub reply_to_message_id: Option<Uuid>,
155}
156#[derive(Clone, Debug, Serialize, Deserialize)]
157pub struct Draft {
158    pub conversation_id: Uuid,
159    pub actor_id: String,
160    pub version: i64,
161    #[serde(flatten)]
162    pub content: DraftInput,
163    pub updated_at: String,
164}
165#[derive(Clone, Debug, Serialize, Deserialize)]
166pub struct BundleCreate {
167    pub message_ids: Vec<Uuid>,
168    pub display_message: MessageCreate,
169}
170#[derive(Clone, Debug, Serialize, Deserialize)]
171pub struct Bundle {
172    pub id: Uuid,
173    pub conversation_id: Uuid,
174    pub original_message_ids: Vec<Uuid>,
175    pub display_message: Message,
176    pub created_by: Actor,
177    pub created_at: String,
178    #[serde(default)]
179    pub original_messages: Vec<Message>,
180}
181#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
182#[serde(rename_all = "snake_case")]
183pub enum Activity {
184    Typing,
185    RecordingVoice,
186    TranscribingVoice,
187    UploadingFile,
188    SearchingGifs,
189}
190#[derive(Clone, Debug, Serialize, Deserialize)]
191pub struct Presence {
192    pub actor_id: String,
193    pub availability: String,
194    pub activity: Option<Activity>,
195    pub last_seen_at: Option<String>,
196}
197/// Tokens are deliberately not Debug; callers explicitly own their persistence.
198#[derive(Clone, Serialize, Deserialize)]
199pub struct Tokens {
200    pub access_token: String,
201    pub refresh_token: String,
202    pub token_type: String,
203    pub expires_in: i64,
204    pub scope: String,
205    pub actor: Actor,
206    pub organization_id: String,
207}
208#[derive(Clone, Debug, Serialize, Deserialize)]
209pub struct Identity {
210    pub actor: Actor,
211    pub organization_id: String,
212    pub principal_id: String,
213    pub session_id: Option<String>,
214    pub org_role: Option<String>,
215    pub capabilities: Vec<String>,
216}
217/// Secrets are deliberately not Debug.
218#[derive(Clone, Serialize, Deserialize)]
219pub struct TestEnvironmentCreate {
220    pub name: String,
221    pub description: Option<String>,
222    pub iam_environment_id: Uuid,
223    pub iam_environment_key: String,
224    pub iam_app_id: String,
225    pub iam_app_secret: String,
226    #[serde(default, skip_serializing_if = "Option::is_none")]
227    pub iam_webhook_secret: Option<String>,
228    #[serde(default, skip_serializing_if = "Option::is_none")]
229    pub iam_webhook_key_version: Option<i64>,
230}
231#[derive(Clone, Debug, Default, Serialize, Deserialize)]
232pub struct TestEnvironmentUpdate {
233    #[serde(skip_serializing_if = "Option::is_none")]
234    pub name: Option<String>,
235    #[serde(skip_serializing_if = "Option::is_none")]
236    pub description: Option<String>,
237}
238#[derive(Clone, Serialize, Deserialize)]
239pub struct TestEnvironment {
240    pub environment_id: Uuid,
241    pub organization_id: String,
242    pub creator_actor_id: String,
243    pub creator_actor_kind: String,
244    pub iam_app_id: String,
245    pub version: i64,
246    pub name: String,
247    pub description: Option<String>,
248    pub status: String,
249    pub iam_environment_id: Uuid,
250    pub created_at: String,
251    pub last_activity_at: String,
252    pub deleted_at: Option<String>,
253    pub purge_after: Option<String>,
254    #[serde(default, skip_serializing_if = "Option::is_none")]
255    pub root_key: Option<String>,
256}
257#[derive(Clone, Serialize, Deserialize)]
258pub struct TestEnvironmentKey {
259    pub environment_id: Uuid,
260    pub root_key: String,
261}
262
263#[derive(Clone, Debug, Serialize, Deserialize)]
264#[serde(tag = "type", rename_all = "snake_case")]
265pub enum ClientFrame {
266    Pong {
267        ping_id: String,
268    },
269    Ack {
270        actor_id: String,
271        through_sequence: i64,
272    },
273    Resume {
274        actor_id: String,
275        after_sequence: i64,
276    },
277    Presence {
278        actor_id: String,
279        activity: Option<Activity>,
280    },
281    Receipt {
282        actor_id: String,
283        conversation_id: Uuid,
284        message_id: Uuid,
285        status: ReceiptStatus,
286        device_id: String,
287    },
288    SendMessage {
289        actor_id: String,
290        org_id: String,
291        conversation_id: Uuid,
292        idempotency_key: String,
293        message: Box<MessageCreate>,
294    },
295}
296#[derive(Clone, Debug, Serialize, Deserialize)]
297#[serde(tag = "type", rename_all = "snake_case")]
298pub enum ServerFrame {
299    Ready {
300        protocol_version: u16,
301        connection_id: Uuid,
302        actors: Vec<String>,
303        acknowledged_through: BTreeMap<String, i64>,
304        #[serde(default)]
305        testing_generation: Option<i64>,
306    },
307    Ping {
308        ping_id: String,
309    },
310    MessageAccepted {
311        idempotency_key: String,
312        message: Box<Message>,
313    },
314    ReceiptRecorded {
315        message_id: Uuid,
316        status: ReceiptStatus,
317    },
318    Message {
319        delivery_id: Uuid,
320        actor_id: String,
321        delivery_sequence: i64,
322        message: Box<Message>,
323    },
324    Receipt {
325        delivery_id: Uuid,
326        actor_id: String,
327        delivery_sequence: i64,
328        message_id: Uuid,
329        status: MessageStatus,
330    },
331    Error {
332        code: String,
333        message: String,
334        recoverable: bool,
335    },
336}
337impl ServerFrame {
338    pub fn delivery_position(&self) -> Option<(Uuid, &str, i64)> {
339        match self {
340            Self::Message {
341                delivery_id,
342                actor_id,
343                delivery_sequence,
344                ..
345            }
346            | Self::Receipt {
347                delivery_id,
348                actor_id,
349                delivery_sequence,
350                ..
351            } => Some((*delivery_id, actor_id, *delivery_sequence)),
352            _ => None,
353        }
354    }
355}
356
357/// Public application discovery. Application secrets are never returned.
358#[derive(Clone, Debug, Serialize, Deserialize)]
359pub struct IamInfo {
360    pub app_id: String,
361    pub iam_base_url: String,
362    pub api_base_url: String,
363}