conversation_api/
control.rs1use crate::{ActiveRun, ConversationQueue, ConversationSurface, PendingInteraction, ThreadSummary};
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize)]
7#[serde(transparent)]
8pub struct ControlVersion(String);
9impl ControlVersion {
10 pub fn new(catalog: u64, state: u64) -> Self {
11 Self(format!("{catalog:020}:{state:020}"))
12 }
13 pub fn as_str(&self) -> &str {
14 &self.0
15 }
16}
17impl<'de> Deserialize<'de> for ControlVersion {
18 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
19 let value = String::deserialize(deserializer)?;
20 let bytes = value.as_bytes();
21 if bytes.len() != 41
22 || bytes[20] != b':'
23 || !bytes[..20]
24 .iter()
25 .chain(&bytes[21..])
26 .all(u8::is_ascii_digit)
27 || value[..20].parse::<u64>().is_err()
28 || value[21..].parse::<u64>().is_err()
29 {
30 return Err(serde::de::Error::custom("invalid control version"));
31 }
32 Ok(Self(value))
33 }
34}
35#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(deny_unknown_fields)]
37pub struct ControlGetRequest {}
38
39#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase", deny_unknown_fields)]
41pub struct ConversationControl {
42 pub surface_id: ConversationSurface,
43 pub version: ControlVersion,
44 pub active_thread: Option<ThreadSummary>,
45 pub active_run: Option<ActiveRun>,
46 pub pending_interaction: Option<PendingInteraction>,
47 pub queue: ConversationQueue,
48 pub history_version: u64,
50}
51
52#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase", deny_unknown_fields)]
54pub struct ControlledResponse<T> {
55 pub receipt: T,
56 pub control: Option<ConversationControl>,
58}
59
60#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
61pub enum ControlUpdatedEventType {
62 #[default]
63 #[serde(rename = "control.updated")]
64 ControlUpdated,
65}
66#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
67#[serde(rename_all = "camelCase", deny_unknown_fields)]
68pub struct ControlUpdatedEvent {
69 #[serde(rename = "type")]
70 pub event_type: ControlUpdatedEventType,
71 pub surface_id: ConversationSurface,
72 pub data: ConversationControl,
73}
74#[cfg(test)]
75mod tests {
76 use super::*;
77 #[test]
78 fn catalog_epoch_dominates_thread_revision() {
79 assert!(ControlVersion::new(2, 0) > ControlVersion::new(1, u64::MAX));
80 assert!(ControlVersion::new(2, 10) > ControlVersion::new(2, 9));
81 }
82 #[test]
83 fn invalid_versions_never_enter_ordering() {
84 for value in [
85 "1:2",
86 "00000000000000000000:18446744073709551616",
87 "0000000000000000000é:00000000000000000000",
88 ] {
89 assert!(serde_json::from_value::<ControlVersion>(serde_json::json!(value)).is_err());
90 }
91 let max = ControlVersion::new(u64::MAX, u64::MAX);
92 assert_eq!(
93 serde_json::from_str::<ControlVersion>(&serde_json::to_string(&max).unwrap()).unwrap(),
94 max
95 );
96 }
97}