Real-time index of opencode sessions
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add SessionLoader with session/message/part loading

SessionLoader provides:
- load_session: Load session info with optional diff
- load_messages/parts: Load all entities for a session/message
- load_message_with_parts: Message with its parts
- load_session_tree: Complete session with all messages and parts
- list_* methods for discovering available entities

Also exports LoadedSession, MessageWithParts, SessionTree structs.

rektide 782f1bb3 ffcae75e

+134
+2
src/lib.rs
··· 1 1 pub mod error; 2 2 pub mod id; 3 + pub mod loader; 3 4 pub mod storage; 4 5 pub mod types; 5 6 ··· 14 15 15 16 pub use error::{Error, Result}; 16 17 pub use id::{MessageId, PartId, SessionId}; 18 + pub use loader::SessionLoader; 17 19 pub use types::{ 18 20 message::{AssistantMessage, Message, UserMessage}, 19 21 part::Part,
+132
src/loader.rs
··· 1 + use crate::id::{MessageId, PartId, SessionId}; 2 + use crate::storage::FileReader; 3 + use crate::types::message::FileDiff; 4 + use crate::types::{Message, Part, SessionInfo}; 5 + use crate::Result; 6 + 7 + pub struct SessionLoader { 8 + reader: FileReader, 9 + } 10 + 11 + impl 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 + let mut messages = Vec::with_capacity(message_ids.len()); 42 + for msg_id in &message_ids { 43 + messages.push(self.load_message(session_id, msg_id)?); 44 + } 45 + Ok(messages) 46 + } 47 + 48 + pub fn load_parts(&self, message_id: &MessageId) -> Result<Vec<Part>> { 49 + let part_ids = self.reader.list_parts(message_id)?; 50 + let mut parts = Vec::with_capacity(part_ids.len()); 51 + for part_id in &part_ids { 52 + parts.push(self.load_part(message_id, part_id)?); 53 + } 54 + Ok(parts) 55 + } 56 + 57 + pub fn load_message_with_parts( 58 + &self, 59 + session_id: &SessionId, 60 + message_id: &MessageId, 61 + ) -> Result<MessageWithParts> { 62 + let message = self.load_message(session_id, message_id)?; 63 + let parts = self.load_parts(message_id)?; 64 + Ok(MessageWithParts { message, parts }) 65 + } 66 + 67 + pub fn load_session_tree( 68 + &self, 69 + project_id: &str, 70 + session_id: &SessionId, 71 + ) -> Result<SessionTree> { 72 + let session = self.load_session(project_id, session_id)?; 73 + let message_ids = self.reader.list_messages(session_id)?; 74 + 75 + let mut messages = Vec::with_capacity(message_ids.len()); 76 + for msg_id in &message_ids { 77 + messages.push(self.load_message_with_parts(session_id, msg_id)?); 78 + } 79 + 80 + Ok(SessionTree { session, messages }) 81 + } 82 + 83 + pub fn list_sessions(&self, project_id: &str) -> Result<Vec<SessionId>> { 84 + self.reader.list_sessions(project_id) 85 + } 86 + 87 + pub fn list_messages(&self, session_id: &SessionId) -> Result<Vec<MessageId>> { 88 + self.reader.list_messages(session_id) 89 + } 90 + 91 + pub fn list_parts(&self, message_id: &MessageId) -> Result<Vec<PartId>> { 92 + self.reader.list_parts(message_id) 93 + } 94 + 95 + pub fn list_projects(&self) -> Result<Vec<String>> { 96 + self.reader.paths().project_dirs() 97 + } 98 + } 99 + 100 + #[derive(Debug, Clone)] 101 + pub struct LoadedSession { 102 + pub info: SessionInfo, 103 + pub diff: Option<Vec<FileDiff>>, 104 + } 105 + 106 + #[derive(Debug, Clone)] 107 + pub struct MessageWithParts { 108 + pub message: Message, 109 + pub parts: Vec<Part>, 110 + } 111 + 112 + #[derive(Debug, Clone)] 113 + pub struct SessionTree { 114 + pub session: LoadedSession, 115 + pub messages: Vec<MessageWithParts>, 116 + } 117 + 118 + #[cfg(test)] 119 + mod tests { 120 + use super::*; 121 + use crate::Error; 122 + 123 + #[test] 124 + fn test_loader_creation() { 125 + let result = SessionLoader::new(); 126 + match result { 127 + Ok(_) => {} 128 + Err(Error::StorageRootNotFound) => {} 129 + Err(e) => panic!("unexpected error: {}", e), 130 + } 131 + } 132 + }