1use crate::thread::Thread;
2use crate::thread::Version;
3use crate::Message;
4use crate::Visibility;
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "lowercase")]
11pub enum SortOrder {
12 #[default]
13 Asc,
14 Desc,
15}
16
17#[derive(Debug, Clone)]
19pub struct MessageQuery {
20 pub after: Option<i64>,
22 pub before: Option<i64>,
24 pub limit: usize,
26 pub order: SortOrder,
28 pub visibility: Option<Visibility>,
30 pub run_id: Option<String>,
32}
33
34impl Default for MessageQuery {
35 fn default() -> Self {
36 Self {
37 after: None,
38 before: None,
39 limit: 50,
40 order: SortOrder::Asc,
41 visibility: Some(Visibility::All),
42 run_id: None,
43 }
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct MessageWithCursor {
50 pub cursor: i64,
51 #[serde(flatten)]
52 pub message: Message,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct MessagePage {
58 pub messages: Vec<MessageWithCursor>,
59 pub has_more: bool,
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub next_cursor: Option<i64>,
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub prev_cursor: Option<i64>,
66}
67
68#[derive(Debug, Clone)]
70pub struct ThreadListQuery {
71 pub offset: usize,
73 pub limit: usize,
75 pub resource_id: Option<String>,
77 pub parent_thread_id: Option<String>,
79}
80
81impl Default for ThreadListQuery {
82 fn default() -> Self {
83 Self {
84 offset: 0,
85 limit: 50,
86 resource_id: None,
87 parent_thread_id: None,
88 }
89 }
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct ThreadListPage {
95 pub items: Vec<String>,
96 pub total: usize,
97 pub has_more: bool,
98}
99
100pub fn paginate_in_memory(
102 messages: &[std::sync::Arc<Message>],
103 query: &MessageQuery,
104) -> MessagePage {
105 let limit = query.limit.clamp(1, 200);
106 let total = messages.len();
107 if total == 0 {
108 return MessagePage {
109 messages: Vec::new(),
110 has_more: false,
111 next_cursor: None,
112 prev_cursor: None,
113 };
114 }
115
116 let start = query.after.map(|c| (c + 1).max(0) as usize).unwrap_or(0);
117 let end = query
118 .before
119 .map(|c| (c.max(0) as usize).min(total))
120 .unwrap_or(total);
121
122 if start >= total || start >= end {
123 return MessagePage {
124 messages: Vec::new(),
125 has_more: false,
126 next_cursor: None,
127 prev_cursor: None,
128 };
129 }
130
131 let mut items: Vec<(i64, &std::sync::Arc<Message>)> = messages[start..end]
132 .iter()
133 .enumerate()
134 .filter(|(_, m)| match query.visibility {
135 Some(vis) => m.visibility == vis,
136 None => true,
137 })
138 .filter(|(_, m)| match &query.run_id {
139 Some(rid) => {
140 m.metadata.as_ref().and_then(|meta| meta.run_id.as_deref()) == Some(rid.as_str())
141 }
142 None => true,
143 })
144 .map(|(i, m)| ((start + i) as i64, m))
145 .collect();
146
147 if query.order == SortOrder::Desc {
148 items.reverse();
149 }
150
151 let has_more = items.len() > limit;
152 let limited: Vec<_> = items.into_iter().take(limit).collect();
153
154 MessagePage {
155 next_cursor: limited.last().map(|(c, _)| *c),
156 prev_cursor: limited.first().map(|(c, _)| *c),
157 messages: limited
158 .into_iter()
159 .map(|(c, m)| MessageWithCursor {
160 cursor: c,
161 message: (**m).clone(),
162 })
163 .collect(),
164 has_more,
165 }
166}
167
168#[derive(Debug, Error)]
170pub enum ThreadStoreError {
171 #[error("Thread not found: {0}")]
173 NotFound(String),
174
175 #[error("IO error: {0}")]
177 Io(#[from] std::io::Error),
178
179 #[error("Serialization error: {0}")]
181 Serialization(String),
182
183 #[error("Invalid thread id: {0}")]
185 InvalidId(String),
186
187 #[error("Thread already exists")]
189 AlreadyExists,
190
191 #[error("Version conflict: expected {expected}, actual {actual}")]
193 VersionConflict { expected: Version, actual: Version },
194}
195
196#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
198#[serde(rename_all = "snake_case")]
199pub enum VersionPrecondition {
200 #[default]
202 Any,
203 Exact(Version),
205}
206
207#[derive(Debug, Clone, Copy)]
209pub struct Committed {
210 pub version: Version,
211}
212
213#[derive(Debug, Clone)]
215pub struct ThreadHead {
216 pub thread: Thread,
217 pub version: Version,
218}