147 lines
4.0 KiB
Rust
147 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,
|
|
New(Option<String>),
|
|
Save(Option<String>),
|
|
Sessions,
|
|
Use(String),
|
|
}
|
|
|
|
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),
|
|
"/new" => Some(InputCommand::New(arg.map(ToOwned::to_owned))),
|
|
"/save" => Some(InputCommand::Save(arg.map(ToOwned::to_owned))),
|
|
"/sessions" | "/list" => Some(InputCommand::Sessions),
|
|
"/use" => arg.map(|value| InputCommand::Use(value.to_string())),
|
|
_ => 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("/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("/save"),
|
|
Some(InputCommand::Save(None))
|
|
);
|
|
assert_eq!(
|
|
handler.handle_special_commands("/save ./debug/session.md"),
|
|
Some(InputCommand::Save(Some("./debug/session.md".to_string())))
|
|
);
|
|
assert_eq!(
|
|
handler.handle_special_commands("/list"),
|
|
Some(InputCommand::Sessions)
|
|
);
|
|
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("/unknown"), None);
|
|
assert_eq!(handler.handle_special_commands("/use"), None);
|
|
}
|
|
}
|