tirea_contract/storage/
traits.rs

1use crate::thread::Thread;
2use crate::thread::ThreadChangeSet;
3use crate::thread::Version;
4use async_trait::async_trait;
5
6use super::{
7    paginate_in_memory, Committed, MessagePage, MessageQuery, RunPage, RunQuery, RunRecord,
8    ThreadHead, ThreadListPage, ThreadListQuery, ThreadStoreError, VersionPrecondition,
9};
10
11#[async_trait]
12pub trait ThreadReader: Send + Sync {
13    /// Load an Thread and its current version.
14    async fn load(&self, thread_id: &str) -> Result<Option<ThreadHead>, ThreadStoreError>;
15
16    /// Load an Thread without version info. Convenience wrapper.
17    async fn load_thread(&self, thread_id: &str) -> Result<Option<Thread>, ThreadStoreError> {
18        Ok(self.load(thread_id).await?.map(|h| h.thread))
19    }
20
21    /// Load paginated messages for an Thread.
22    async fn load_messages(
23        &self,
24        thread_id: &str,
25        query: &MessageQuery,
26    ) -> Result<MessagePage, ThreadStoreError> {
27        let head = self
28            .load(thread_id)
29            .await?
30            .ok_or_else(|| ThreadStoreError::NotFound(thread_id.to_string()))?;
31        Ok(paginate_in_memory(&head.thread.messages, query))
32    }
33
34    /// List Thread ids.
35    async fn list_threads(
36        &self,
37        query: &ThreadListQuery,
38    ) -> Result<ThreadListPage, ThreadStoreError>;
39
40    /// List all Thread ids with default paging.
41    async fn list(&self) -> Result<Vec<String>, ThreadStoreError> {
42        let page = self
43            .list_threads(&ThreadListQuery {
44                offset: 0,
45                limit: 200,
46                resource_id: None,
47                parent_thread_id: None,
48            })
49            .await?;
50        Ok(page.items)
51    }
52
53    /// List Thread ids with explicit query.
54    async fn list_paginated(
55        &self,
56        query: &ThreadListQuery,
57    ) -> Result<ThreadListPage, ThreadStoreError> {
58        self.list_threads(query).await
59    }
60
61    /// Return total message count.
62    async fn message_count(&self, thread_id: &str) -> Result<usize, ThreadStoreError> {
63        let head = self
64            .load(thread_id)
65            .await?
66            .ok_or_else(|| ThreadStoreError::NotFound(thread_id.to_string()))?;
67        Ok(head.thread.messages.len())
68    }
69
70    // ----- run index (populated from `RunMeta` in changesets) -----
71
72    /// Load one run record by run id.
73    async fn load_run(&self, _run_id: &str) -> Result<Option<RunRecord>, ThreadStoreError> {
74        Ok(None)
75    }
76
77    /// List runs with optional filtering and pagination.
78    async fn list_runs(&self, _query: &RunQuery) -> Result<RunPage, ThreadStoreError> {
79        Ok(RunPage {
80            items: vec![],
81            total: 0,
82            has_more: false,
83        })
84    }
85
86    /// Load the most recent non-terminal run for a thread, if any.
87    async fn active_run_for_thread(
88        &self,
89        _thread_id: &str,
90    ) -> Result<Option<RunRecord>, ThreadStoreError> {
91        Ok(None)
92    }
93}
94
95#[async_trait]
96pub trait ThreadWriter: ThreadReader {
97    /// Create a new Thread.
98    async fn create(&self, thread: &Thread) -> Result<Committed, ThreadStoreError>;
99
100    /// Append an ThreadChangeSet to an existing Thread.
101    async fn append(
102        &self,
103        thread_id: &str,
104        delta: &ThreadChangeSet,
105        precondition: VersionPrecondition,
106    ) -> Result<Committed, ThreadStoreError>;
107
108    /// Delete an Thread.
109    async fn delete(&self, thread_id: &str) -> Result<(), ThreadStoreError>;
110
111    /// Upsert or replace the current persisted Thread.
112    ///
113    /// Implementations must provide atomic semantics suitable for their backend.
114    async fn save(&self, thread: &Thread) -> Result<(), ThreadStoreError>;
115}
116
117#[async_trait]
118pub trait ThreadSync: ThreadWriter {
119    /// Load delta list appended after a specific version.
120    async fn load_deltas(
121        &self,
122        thread_id: &str,
123        after_version: Version,
124    ) -> Result<Vec<ThreadChangeSet>, ThreadStoreError>;
125}
126
127/// Full storage trait.
128pub trait ThreadStore: ThreadWriter {}
129
130impl<T: ThreadWriter + ?Sized> ThreadStore for T {}