use crate::commands::Command;
use anyhow::{Context as _, Result};
use super::ui_event::UIEvent;
use uuid::Uuid;
#[derive(
Debug,
Clone,
strum_macros::Display,
strum_macros::EnumIs,
strum_macros::AsRefStr,
strum_macros::EnumString,
strum_macros::EnumIter,
strum_macros::EnumMessage,
PartialEq,
)]
#[strum(serialize_all = "snake_case")]
pub enum UserInputCommand {
Quit,
ShowConfig,
IndexRepository,
NextChat,
NewChat,
DeleteChat,
Copy,
Diff(DiffVariant),
Retry,
Help,
#[strum(serialize = "github", serialize = "gh")]
Github(GithubVariant),
}
#[derive(
Debug,
Clone,
strum_macros::Display,
strum_macros::EnumIs,
strum_macros::AsRefStr,
strum_macros::EnumString,
strum_macros::EnumIter,
PartialEq,
)]
#[strum(serialize_all = "snake_case")]
pub enum GithubVariant {
Fix(u64),
}
impl Default for GithubVariant {
fn default() -> Self {
Self::Fix(0)
}
}
#[derive(
Default,
Debug,
Clone,
strum_macros::Display,
strum_macros::EnumIs,
strum_macros::AsRefStr,
strum_macros::EnumString,
strum_macros::EnumIter,
PartialEq,
)]
#[strum(serialize_all = "snake_case")]
pub enum DiffVariant {
#[default]
Show,
Pull,
}
impl UserInputCommand {
#[must_use]
pub fn to_command(&self) -> Option<Command> {
match self {
UserInputCommand::ShowConfig => Some(Command::ShowConfig),
UserInputCommand::IndexRepository => Some(Command::IndexRepository),
UserInputCommand::Retry => Some(Command::RetryChat),
_ => None,
}
}
#[must_use]
pub fn to_ui_event(&self, uuid: Uuid) -> Option<UIEvent> {
match self {
UserInputCommand::NextChat => Some(UIEvent::NextChat),
UserInputCommand::NewChat => Some(UIEvent::NewChat),
UserInputCommand::Copy => Some(UIEvent::CopyLastMessage),
UserInputCommand::DeleteChat => Some(UIEvent::DeleteChat),
UserInputCommand::Help => Some(UIEvent::Help),
UserInputCommand::Quit => Some(UIEvent::Quit),
UserInputCommand::Diff(diff_variant) => match diff_variant {
DiffVariant::Show => Some(UIEvent::DiffShow),
DiffVariant::Pull => Some(UIEvent::DiffPull),
},
UserInputCommand::Github(github_variable) => match github_variable {
GithubVariant::Fix(number) => Some(UIEvent::GithubFixIssue(uuid, *number)),
},
_ => None,
}
}
pub fn parse_from_input(input: &str) -> Result<UserInputCommand> {
debug_assert!(input.starts_with('/'));
let cmd_parts = input.split_whitespace().collect::<Vec<_>>();
let input = cmd_parts.first().unwrap();
let input_cmd = input[1..]
.parse::<UserInputCommand>()
.with_context(|| format!("failed to parse input command {input}"))?;
match input_cmd {
UserInputCommand::Diff(_) => {
let subcommand = cmd_parts.get(1);
let Some(subcommand) = subcommand else {
return Ok(UserInputCommand::Diff(DiffVariant::default()));
};
let diff_variant = subcommand
.parse()
.with_context(|| format!("failed to parse diff subcommand {subcommand}"))?;
Ok(UserInputCommand::Diff(diff_variant))
}
UserInputCommand::Github(_) => {
let subcommand = cmd_parts.get(1);
let Some(subcommand) = subcommand else {
return Err(anyhow::anyhow!("no subcommand provided for github command"));
};
let github_variant = subcommand
.parse()
.with_context(|| format!("failed to parse github subcommand {subcommand}"))?;
match github_variant {
GithubVariant::Fix(_) => {
let subsubcommand = cmd_parts.get(2);
let Some(subsubcommand) = subsubcommand else {
return Err(anyhow::anyhow!(
"no issue number provided for github fix command"
));
};
let issue_number = subsubcommand.parse::<u64>().with_context(|| {
format!("failed to parse issue number to fix {subsubcommand}")
})?;
Ok(UserInputCommand::Github(GithubVariant::Fix(issue_number)))
}
}
}
_ => Ok(input_cmd),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_from_input() {
let test_cases = vec![
("/quit", UserInputCommand::Quit),
("/show_config", UserInputCommand::ShowConfig),
("/index_repository", UserInputCommand::IndexRepository),
("/next_chat", UserInputCommand::NextChat),
("/new_chat", UserInputCommand::NewChat),
("/delete_chat", UserInputCommand::DeleteChat),
("/copy", UserInputCommand::Copy), ];
for (input, expected_command) in test_cases {
let parsed_command = UserInputCommand::parse_from_input(input).unwrap();
assert_eq!(parsed_command, expected_command);
}
}
#[test]
fn test_parse_diff_input() {
let test_cases = vec![
("/diff", UserInputCommand::Diff(DiffVariant::Show)),
("/diff show", UserInputCommand::Diff(DiffVariant::Show)),
("/diff pull", UserInputCommand::Diff(DiffVariant::Pull)),
];
for (input, expected_command) in test_cases {
let parsed_command = UserInputCommand::parse_from_input(input).unwrap();
assert_eq!(
parsed_command, expected_command,
"expected: {expected_command:?} for: {input:?}",
);
}
}
#[test]
fn test_parse_github_issue_input() {
let test_cases = vec![
(
"/github fix 123",
UserInputCommand::Github(GithubVariant::Fix(123)),
),
(
"/gh fix 456",
UserInputCommand::Github(GithubVariant::Fix(456)),
),
];
for (input, expected_command) in test_cases {
let parsed_command = UserInputCommand::parse_from_input(input).unwrap();
assert_eq!(
parsed_command, expected_command,
"expected: {expected_command:?} for: {input:?}",
);
}
}
}