- Added SessionSummary struct for session metadata. - Updated ws_handler to create and manage CLI sessions more robustly. - Implemented session creation, loading, renaming, archiving, and deletion via WebSocket messages. - Introduced SessionStore for persistent session storage using SQLite. - Enhanced error handling and logging for session operations. - Updated protocol definitions for new session-related WebSocket messages. - Refactored tests to cover new session functionalities and ensure proper serialization.
130 lines
4.0 KiB
Rust
130 lines
4.0 KiB
Rust
use crate::bus::ChatMessage;
|
|
|
|
use super::channel::CliChannel;
|
|
|
|
pub enum InputEvent {
|
|
Message(ChatMessage),
|
|
Command(InputCommand),
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum InputCommand {
|
|
Exit,
|
|
Clear,
|
|
New(Option<String>),
|
|
Sessions,
|
|
Use(String),
|
|
Rename(String),
|
|
Archive,
|
|
Delete,
|
|
}
|
|
|
|
pub struct InputHandler {
|
|
channel: CliChannel,
|
|
}
|
|
|
|
impl InputHandler {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
channel: CliChannel::new(),
|
|
}
|
|
}
|
|
|
|
pub async fn read_input(&mut self, prompt: &str) -> Result<Option<InputEvent>, InputError> {
|
|
match self.channel.read_line(prompt).await {
|
|
Ok(Some(line)) => {
|
|
if line.trim().is_empty() {
|
|
return Ok(None);
|
|
}
|
|
|
|
if let Some(cmd) = self.handle_special_commands(&line) {
|
|
return Ok(Some(InputEvent::Command(cmd)));
|
|
}
|
|
|
|
Ok(Some(InputEvent::Message(ChatMessage::user(line))))
|
|
}
|
|
Ok(None) => Ok(None),
|
|
Err(e) => Err(InputError::IoError(e)),
|
|
}
|
|
}
|
|
|
|
pub async fn write_output(&mut self, content: &str) -> Result<(), InputError> {
|
|
self.channel.write_line(content).await.map_err(InputError::IoError)
|
|
}
|
|
|
|
pub async fn write_response(&mut self, content: &str) -> Result<(), InputError> {
|
|
self.channel.write_response(content).await.map_err(InputError::IoError)
|
|
}
|
|
|
|
fn handle_special_commands(&self, line: &str) -> Option<InputCommand> {
|
|
let trimmed = line.trim();
|
|
let mut parts = trimmed.splitn(2, char::is_whitespace);
|
|
let command = parts.next()?;
|
|
let arg = parts.next().map(str::trim).filter(|value| !value.is_empty());
|
|
|
|
match command {
|
|
"/quit" | "/exit" | "/q" => Some(InputCommand::Exit),
|
|
"/clear" => Some(InputCommand::Clear),
|
|
"/new" => Some(InputCommand::New(arg.map(ToOwned::to_owned))),
|
|
"/sessions" => Some(InputCommand::Sessions),
|
|
"/use" => arg.map(|value| InputCommand::Use(value.to_string())),
|
|
"/rename" => arg.map(|value| InputCommand::Rename(value.to_string())),
|
|
"/archive" => Some(InputCommand::Archive),
|
|
"/delete" => Some(InputCommand::Delete),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for InputHandler {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum InputError {
|
|
IoError(std::io::Error),
|
|
}
|
|
|
|
impl std::fmt::Display for InputError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
InputError::IoError(e) => write!(f, "IO error: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for InputError {}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_special_command_parsing() {
|
|
let handler = InputHandler::new();
|
|
|
|
assert_eq!(handler.handle_special_commands("/quit"), Some(InputCommand::Exit));
|
|
assert_eq!(handler.handle_special_commands("/clear"), Some(InputCommand::Clear));
|
|
assert_eq!(handler.handle_special_commands("/new"), Some(InputCommand::New(None)));
|
|
assert_eq!(
|
|
handler.handle_special_commands("/new planning"),
|
|
Some(InputCommand::New(Some("planning".to_string())))
|
|
);
|
|
assert_eq!(handler.handle_special_commands("/sessions"), Some(InputCommand::Sessions));
|
|
assert_eq!(
|
|
handler.handle_special_commands("/use abc123"),
|
|
Some(InputCommand::Use("abc123".to_string()))
|
|
);
|
|
assert_eq!(
|
|
handler.handle_special_commands("/rename project alpha"),
|
|
Some(InputCommand::Rename("project alpha".to_string()))
|
|
);
|
|
assert_eq!(handler.handle_special_commands("/archive"), Some(InputCommand::Archive));
|
|
assert_eq!(handler.handle_special_commands("/delete"), Some(InputCommand::Delete));
|
|
assert_eq!(handler.handle_special_commands("/unknown"), None);
|
|
assert_eq!(handler.handle_special_commands("/use"), None);
|
|
}
|
|
}
|