1use async_trait::async_trait;
2use awaken_server_contract::contract::message::{
3 DeliveryBoundary, DeliveryMode, Message, MessageRecord, PendingMessageRecord,
4};
5use awaken_server_contract::contract::storage::{
6 RunRecord, StorageError, ThreadRunStore, message_append,
7};
8
9pub(crate) fn validate_pending_message_record(
10 record: &PendingMessageRecord,
11) -> Result<(), StorageError> {
12 if record.pending_id.trim().is_empty() {
13 return Err(StorageError::Validation(
14 "pending message id must not be empty".to_string(),
15 ));
16 }
17 if record.thread_id.trim().is_empty() {
18 return Err(StorageError::Validation(format!(
19 "pending message '{}' thread id must not be empty",
20 record.pending_id
21 )));
22 }
23 if record.position == 0 {
24 return Err(StorageError::Validation(format!(
25 "pending message '{}' position must be 1-based",
26 record.pending_id
27 )));
28 }
29 if record.revision == 0 {
30 return Err(StorageError::Validation(format!(
31 "pending message '{}' revision must be 1-based",
32 record.pending_id
33 )));
34 }
35 match record.message.id.as_deref() {
36 Some(message_id) if message_id == record.pending_id => {}
37 Some(message_id) => {
38 return Err(StorageError::Validation(format!(
39 "pending message '{}' cannot carry message id '{}'",
40 record.pending_id, message_id
41 )));
42 }
43 None => {
44 return Err(StorageError::Validation(format!(
45 "pending message '{}' must carry message id",
46 record.pending_id
47 )));
48 }
49 }
50 message_append::validate_message_shape(&record.message, "pending")
51}
52
53pub(crate) fn validate_pending_message_records(
54 records: &[PendingMessageRecord],
55) -> Result<(), StorageError> {
56 for record in records {
57 validate_pending_message_record(record)?;
58 }
59 Ok(())
60}
61
62#[async_trait]
64pub trait PendingMessageStore: Send + Sync {
65 async fn load_pending_message_records(
66 &self,
67 thread_id: &str,
68 ) -> Result<Vec<PendingMessageRecord>, StorageError>;
69
70 async fn list_threads_with_pending_messages(
77 &self,
78 limit: usize,
79 after: Option<&str>,
80 ) -> Result<Vec<String>, StorageError>;
81
82 async fn append_pending_message_records(
83 &self,
84 thread_id: &str,
85 messages: &[Message],
86 delivery_mode: DeliveryMode,
87 ) -> Result<Vec<PendingMessageRecord>, StorageError>;
88
89 async fn update_pending_message_record(
90 &self,
91 thread_id: &str,
92 pending_id: &str,
93 message: Message,
94 ) -> Result<PendingMessageRecord, StorageError> {
95 self.update_pending_message_record_checked(thread_id, pending_id, None, message)
96 .await
97 }
98
99 async fn update_pending_message_record_checked(
100 &self,
101 thread_id: &str,
102 pending_id: &str,
103 expected_revision: Option<u64>,
104 message: Message,
105 ) -> Result<PendingMessageRecord, StorageError>;
106
107 async fn retract_pending_message_record(
108 &self,
109 thread_id: &str,
110 pending_id: &str,
111 ) -> Result<PendingMessageRecord, StorageError> {
112 self.retract_pending_message_record_checked(thread_id, pending_id, None)
113 .await
114 }
115
116 async fn retract_pending_message_record_checked(
117 &self,
118 thread_id: &str,
119 pending_id: &str,
120 expected_revision: Option<u64>,
121 ) -> Result<PendingMessageRecord, StorageError>;
122
123 async fn reorder_pending_message_records(
124 &self,
125 thread_id: &str,
126 ordered_pending_ids: &[String],
127 ) -> Result<Vec<PendingMessageRecord>, StorageError> {
128 self.reorder_pending_message_records_checked(thread_id, None, ordered_pending_ids)
129 .await
130 }
131
132 async fn reorder_pending_message_records_checked(
133 &self,
134 thread_id: &str,
135 expected_queue_revision: Option<u64>,
136 ordered_pending_ids: &[String],
137 ) -> Result<Vec<PendingMessageRecord>, StorageError>;
138
139 async fn freeze_pending_message_records(
140 &self,
141 thread_id: &str,
142 boundary: DeliveryBoundary,
143 expected_message_version: Option<u64>,
144 ) -> Result<Vec<MessageRecord>, StorageError>;
145
146 async fn freeze_pending_message_records_with_run(
147 &self,
148 thread_id: &str,
149 boundary: DeliveryBoundary,
150 expected_message_version: Option<u64>,
151 expected_pending_ids: &[String],
152 run: &RunRecord,
153 ) -> Result<Vec<MessageRecord>, StorageError>;
154
155 #[allow(clippy::too_many_arguments)]
168 async fn append_and_freeze_pending_message_records_with_run(
169 &self,
170 thread_id: &str,
171 new_messages: &[Message],
172 append_delivery_mode: DeliveryMode,
173 boundary: DeliveryBoundary,
174 expected_message_version: Option<u64>,
175 expected_pending_ids: &[String],
176 run: &RunRecord,
177 ) -> Result<Vec<MessageRecord>, StorageError>;
178}
179
180pub trait PendingThreadRunStore: ThreadRunStore + PendingMessageStore {}
187
188impl<T> PendingThreadRunStore for T where T: ThreadRunStore + PendingMessageStore {}