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