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    #[serde(rename = "message", alias = "text")]
59    pub text: Option<String>,
60    #[serde(default)]
61    pub attachments: Vec<Attachment>,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub voice: Option<VoiceAttachment>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub voice_transcript: Option<String>,
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub gif: Option<Gif>,
68    #[serde(default)]
69    pub metadata: Metadata,
70    #[serde(default, skip_serializing_if = "Option::is_none")]
71    pub reply_to_message_id: Option<Uuid>,
72}
73#[derive(Clone, Debug, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75pub enum MessageStatus {
76    Waiting,
77    Sent,
78    Delivered,
79    Read,
80    Failed,
81}
82#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
83#[serde(rename_all = "snake_case")]
84pub enum ReceiptStatus {
85    Delivered,
86    Read,
87}
88#[derive(Clone, Debug, Serialize, Deserialize)]
89pub struct Message {
90    pub id: Uuid,
91    pub conversation_id: Uuid,
92    pub sender: Actor,
93    pub sequence: i64,
94    pub status: MessageStatus,
95    #[serde(flatten)]
96    pub content: MessageCreate,
97    pub created_at: String,
98    #[serde(default)]
99    pub delivered_at: Option<String>,
100    #[serde(default)]
101    pub read_at: Option<String>,
102    #[serde(default)]
103    pub failure_reason: Option<String>,
104    #[serde(default)]
105    pub bundle: Option<BundleRef>,
106    #[serde(default)]
107    pub version: Option<i64>,
108    #[serde(default)]
109    pub updated_at: Option<String>,
110    #[serde(default)]
111    pub deleted_at: Option<String>,
112}
113#[derive(Clone, Debug, Serialize, Deserialize)]
114pub struct BundleRef {
115    pub id: Uuid,
116    pub role: String,
117}
118#[derive(Clone, Debug, Serialize, Deserialize)]
119pub struct Conversation {
120    pub id: Uuid,
121    pub org_id: String,
122    pub participants: Vec<Actor>,
123    pub last_message: Option<Message>,
124    pub created_at: String,
125    pub updated_at: String,
126}
127#[derive(Clone, Debug, Default, Serialize, Deserialize)]
128pub struct PageRequest {
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub cursor: Option<String>,
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub limit: Option<u16>,
133}
134#[derive(Clone, Debug, Serialize, Deserialize)]
135pub struct Page<T> {
136    pub items: Vec<T>,
137    #[serde(default)]
138    pub next_cursor: Option<String>,
139}
140#[derive(Clone, Debug, Default, Serialize, Deserialize)]
141pub struct DraftInput {
142    #[serde(default, skip_serializing_if = "Option::is_none")]
143    pub message_content: Option<String>,
144    #[serde(default)]
145    pub attachments: Vec<Attachment>,
146    #[serde(default, skip_serializing_if = "Option::is_none")]
147    pub voice: Option<VoiceAttachment>,
148    #[serde(default, skip_serializing_if = "Option::is_none")]
149    pub voice_transcript: Option<String>,
150    #[serde(default, skip_serializing_if = "Option::is_none")]
151    pub gif: Option<Gif>,
152    #[serde(default)]
153    pub metadata: Metadata,
154    #[serde(default, skip_serializing_if = "Option::is_none")]
155    pub reply_to_message_id: Option<Uuid>,
156}
157#[derive(Clone, Debug, Serialize, Deserialize)]
158pub struct Draft {
159    pub conversation_id: Uuid,
160    pub actor_id: String,
161    pub version: i64,
162    #[serde(flatten)]
163    pub content: DraftInput,
164    pub updated_at: String,
165}
166#[derive(Clone, Debug, Serialize, Deserialize)]
167pub struct BundleCreate {
168    pub message_ids: Vec<Uuid>,
169    pub display_message: MessageCreate,
170}
171#[derive(Clone, Debug, Serialize, Deserialize)]
172pub struct Bundle {
173    pub id: Uuid,
174    pub conversation_id: Uuid,
175    pub original_message_ids: Vec<Uuid>,
176    pub display_message: Message,
177    pub created_by: Actor,
178    pub created_at: String,
179    #[serde(default)]
180    pub original_messages: Vec<Message>,
181}
182#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
183#[serde(rename_all = "snake_case")]
184pub enum Activity {
185    Typing,
186    RecordingVoice,
187    TranscribingVoice,
188    UploadingFile,
189    SearchingGifs,
190}
191#[derive(Clone, Debug, Serialize, Deserialize)]
192pub struct Presence {
193    pub actor_id: String,
194    pub availability: String,
195    pub activity: Option<Activity>,
196    pub last_seen_at: Option<String>,
197}
198/// Tokens are deliberately not Debug; callers explicitly own their persistence.
199#[derive(Clone, Serialize, Deserialize)]
200pub struct Tokens {
201    pub access_token: String,
202    pub refresh_token: String,
203    pub token_type: String,
204    pub expires_in: i64,
205    pub scope: String,
206    pub actor: Actor,
207    pub organization_id: String,
208}
209#[derive(Clone, Debug, Serialize, Deserialize)]
210pub struct Identity {
211    pub actor: Actor,
212    pub organization_id: String,
213    pub principal_id: String,
214    pub session_id: Option<String>,
215    pub org_role: Option<String>,
216    pub capabilities: Vec<String>,
217}
218/// Secrets are deliberately not Debug.
219#[derive(Clone, Serialize, Deserialize)]
220pub struct TestEnvironmentCreate {
221    pub name: String,
222    pub description: Option<String>,
223    pub iam_environment_id: Uuid,
224    pub iam_environment_key: String,
225    pub iam_app_id: String,
226    pub iam_app_secret: String,
227    #[serde(default, skip_serializing_if = "Option::is_none")]
228    pub iam_webhook_secret: Option<String>,
229    #[serde(default, skip_serializing_if = "Option::is_none")]
230    pub iam_webhook_key_version: Option<i64>,
231}
232#[derive(Clone, Debug, Default, Serialize, Deserialize)]
233pub struct TestEnvironmentUpdate {
234    #[serde(skip_serializing_if = "Option::is_none")]
235    pub name: Option<String>,
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub description: Option<String>,
238}
239#[derive(Clone, Serialize, Deserialize)]
240pub struct TestEnvironment {
241    pub environment_id: Uuid,
242    pub organization_id: String,
243    pub creator_actor_id: String,
244    pub creator_actor_kind: String,
245    pub iam_app_id: String,
246    pub version: i64,
247    pub name: String,
248    pub description: Option<String>,
249    pub status: String,
250    pub iam_environment_id: Uuid,
251    pub created_at: String,
252    pub last_activity_at: String,
253    pub deleted_at: Option<String>,
254    pub purge_after: Option<String>,
255    #[serde(default, skip_serializing_if = "Option::is_none")]
256    pub root_key: Option<String>,
257}
258#[derive(Clone, Serialize, Deserialize)]
259pub struct TestEnvironmentKey {
260    pub environment_id: Uuid,
261    pub root_key: String,
262}
263
264#[derive(Clone, Debug, Serialize, Deserialize)]
265#[serde(
266    tag = "type",
267    content = "data",
268    rename_all = "snake_case",
269    deny_unknown_fields
270)]
271pub enum ClientFrame {
272    Pong {
273        ping_id: String,
274    },
275    Ack {
276        actor_id: String,
277        through_sequence: i64,
278    },
279    Resume {
280        actor_id: String,
281        after_sequence: i64,
282    },
283    Presence {
284        actor_id: String,
285        activity: Option<Activity>,
286    },
287    Receipt {
288        actor_id: String,
289        conversation_id: Uuid,
290        message_id: Uuid,
291        status: ReceiptStatus,
292        device_id: String,
293    },
294    #[serde(rename = "new_message")]
295    SendMessage {
296        actor_id: String,
297        org_id: String,
298        conversation_id: Uuid,
299        idempotency_key: String,
300        #[serde(flatten)]
301        message: Box<MessageCreate>,
302    },
303}
304#[derive(Clone, Debug, Serialize, Deserialize)]
305#[serde(tag = "type", content = "data", rename_all = "snake_case")]
306pub enum ServerFrame {
307    Ready {
308        protocol_version: u16,
309        connection_id: Uuid,
310        actors: Vec<String>,
311        acknowledged_through: BTreeMap<String, i64>,
312        #[serde(default)]
313        testing_generation: Option<i64>,
314    },
315    Ping {
316        ping_id: String,
317    },
318    MessageAccepted {
319        idempotency_key: String,
320        #[serde(flatten)]
321        message: Box<Message>,
322    },
323    ReceiptRecorded {
324        message_id: Uuid,
325        status: ReceiptStatus,
326    },
327    #[serde(rename = "new_message")]
328    Message {
329        delivery_id: Uuid,
330        actor_id: String,
331        delivery_sequence: i64,
332        #[serde(flatten)]
333        message: Box<Message>,
334    },
335    Receipt {
336        delivery_id: Uuid,
337        actor_id: String,
338        delivery_sequence: i64,
339        message_id: Uuid,
340        status: MessageStatus,
341    },
342    Error {
343        code: String,
344        message: String,
345        recoverable: bool,
346    },
347}
348impl ServerFrame {
349    pub fn delivery_position(&self) -> Option<(Uuid, &str, i64)> {
350        match self {
351            Self::Message {
352                delivery_id,
353                actor_id,
354                delivery_sequence,
355                ..
356            }
357            | Self::Receipt {
358                delivery_id,
359                actor_id,
360                delivery_sequence,
361                ..
362            } => Some((*delivery_id, actor_id, *delivery_sequence)),
363            _ => None,
364        }
365    }
366}
367
368/// Public application discovery. Application secrets are never returned.
369#[derive(Clone, Debug, Serialize, Deserialize)]
370pub struct IamInfo {
371    pub app_id: String,
372    pub iam_base_url: String,
373    pub api_base_url: String,
374}