Real-time index of opencode sessions
1use crate::id::{MessageId, PartId, SessionId};
2use crate::storage::FileReader;
3use crate::types::message::FileDiff;
4use crate::types::{Message, Part, SessionInfo};
5use crate::Result;
6
7pub struct SessionLoader {
8 reader: FileReader,
9}
10
11impl SessionLoader {
12 pub fn new() -> Result<Self> {
13 let reader = FileReader::new()?;
14 Ok(Self { reader })
15 }
16
17 pub fn with_reader(reader: FileReader) -> Self {
18 Self { reader }
19 }
20
21 pub fn reader(&self) -> &FileReader {
22 &self.reader
23 }
24
25 pub fn load_session(&self, project_id: &str, session_id: &SessionId) -> Result<LoadedSession> {
26 let info = self.reader.read_session(project_id, session_id)?;
27 let diff = self.reader.read_diff(session_id).ok();
28 Ok(LoadedSession { info, diff })
29 }
30
31 pub fn load_message(&self, session_id: &SessionId, message_id: &MessageId) -> Result<Message> {
32 self.reader.read_message(session_id, message_id)
33 }
34
35 pub fn load_part(&self, message_id: &MessageId, part_id: &PartId) -> Result<Part> {
36 self.reader.read_part(message_id, part_id)
37 }
38
39 pub fn load_messages(&self, session_id: &SessionId) -> Result<Vec<Message>> {
40 let message_ids = self.reader.list_messages(session_id)?;
41 self.load_messages_by_ids(session_id, &message_ids)
42 }
43
44 pub fn load_messages_by_ids(
45 &self,
46 session_id: &SessionId,
47 message_ids: &[MessageId],
48 ) -> Result<Vec<Message>> {
49 let mut messages = Vec::with_capacity(message_ids.len());
50 for msg_id in message_ids {
51 messages.push(self.load_message(session_id, msg_id)?);
52 }
53 Ok(messages)
54 }
55
56 pub fn load_parts(&self, message_id: &MessageId) -> Result<Vec<Part>> {
57 let part_ids = self.reader.list_parts(message_id)?;
58 self.load_parts_by_ids(message_id, &part_ids)
59 }
60
61 pub fn load_parts_by_ids(
62 &self,
63 message_id: &MessageId,
64 part_ids: &[PartId],
65 ) -> Result<Vec<Part>> {
66 let mut parts = Vec::with_capacity(part_ids.len());
67 for part_id in part_ids {
68 parts.push(self.load_part(message_id, part_id)?);
69 }
70 Ok(parts)
71 }
72
73 pub fn load_message_with_parts(
74 &self,
75 session_id: &SessionId,
76 message_id: &MessageId,
77 ) -> Result<MessageWithParts> {
78 let message = self.load_message(session_id, message_id)?;
79 let part_ids = self.reader.list_parts(message_id)?;
80 let parts = self.load_parts_by_ids(message_id, &part_ids)?;
81 Ok(MessageWithParts { message, parts })
82 }
83
84 pub fn load_messages_with_parts(
85 &self,
86 session_id: &SessionId,
87 message_ids: &[MessageId],
88 ) -> Result<Vec<MessageWithParts>> {
89 let mut messages = Vec::with_capacity(message_ids.len());
90 for message_id in message_ids {
91 messages.push(self.load_message_with_parts(session_id, message_id)?);
92 }
93 Ok(messages)
94 }
95
96 pub fn load_session_tree(
97 &self,
98 project_id: &str,
99 session_id: &SessionId,
100 ) -> Result<SessionTree> {
101 let session = self.load_session(project_id, session_id)?;
102 let message_ids = self.reader.list_messages(session_id)?;
103 let messages = self.load_messages_with_parts(session_id, &message_ids)?;
104
105 Ok(SessionTree { session, messages })
106 }
107
108 pub fn list_sessions(&self, project_id: &str) -> Result<Vec<SessionId>> {
109 self.reader.list_sessions(project_id)
110 }
111
112 pub fn list_messages(&self, session_id: &SessionId) -> Result<Vec<MessageId>> {
113 self.reader.list_messages(session_id)
114 }
115
116 pub fn list_parts(&self, message_id: &MessageId) -> Result<Vec<PartId>> {
117 self.reader.list_parts(message_id)
118 }
119
120 pub fn list_projects(&self) -> Result<Vec<String>> {
121 self.reader.paths().project_dirs()
122 }
123}
124
125#[derive(Debug, Clone)]
126pub struct LoadedSession {
127 pub info: SessionInfo,
128 pub diff: Option<Vec<FileDiff>>,
129}
130
131#[derive(Debug, Clone)]
132pub struct MessageWithParts {
133 pub message: Message,
134 pub parts: Vec<Part>,
135}
136
137#[derive(Debug, Clone)]
138pub struct SessionTree {
139 pub session: LoadedSession,
140 pub messages: Vec<MessageWithParts>,
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::Error;
147
148 #[test]
149 fn test_loader_creation() {
150 let result = SessionLoader::new();
151 match result {
152 Ok(_) => {}
153 Err(Error::StorageRootNotFound) => {}
154 Err(e) => panic!("unexpected error: {}", e),
155 }
156 }
157}