use crate::commands::Error::InvalidOption;
use crate::commands::{CommandOptions, LoopCondition, Result, ShellCommand};
use async_trait::async_trait;
use rsql_core::EchoMode;
use rust_i18n::t;
#[derive(Debug, Default)]
pub struct Command;
#[async_trait]
impl ShellCommand for Command {
fn name(&self, locale: &str) -> String {
t!("echo_command", locale = locale).to_string()
}
fn args(&self, locale: &str) -> String {
let on = t!("on", locale = locale).to_string();
let prompt = t!("echo_prompt", locale = locale).to_string();
let off = t!("off", locale = locale).to_string();
t!(
"on_prompt_off_argument",
locale = locale,
on = on,
prompt = prompt,
off = off
)
.to_string()
}
fn description(&self, locale: &str) -> String {
t!("echo_description", locale = locale).to_string()
}
async fn execute<'a>(&self, options: CommandOptions<'a>) -> Result<LoopCondition> {
let locale = options.configuration.locale.as_str();
let on = t!("on", locale = locale).to_string();
let prompt = t!("echo_prompt", locale = locale).to_string();
let off = t!("off", locale = locale).to_string();
if options.input.len() <= 1 {
let echo = match options.configuration.echo {
EchoMode::On => on,
EchoMode::Prompt => prompt,
EchoMode::Off => off,
};
let echo_setting = t!("echo_setting", locale = locale, echo = echo).to_string();
writeln!(options.output, "{echo_setting}")?;
return Ok(LoopCondition::Continue);
}
let argument = options
.input
.get(1)
.map_or_else(String::new, |argument| argument.to_lowercase());
let echo = if argument == on {
EchoMode::On
} else if argument == prompt {
EchoMode::Prompt
} else if argument == off {
EchoMode::Off
} else {
return Err(InvalidOption {
command_name: self.name(locale),
option: argument,
});
};
options.configuration.echo = echo;
Ok(LoopCondition::Continue)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::LoopCondition;
use crate::commands::{CommandManager, CommandOptions};
use crate::writers::Output;
use rsql_core::Configuration;
use rsql_drivers::MockConnection;
use rsql_formatters::FormatterManager;
use rustyline::history::DefaultHistory;
#[test]
fn test_name() {
let name = Command.name("en");
assert_eq!(name, "echo");
}
#[test]
fn test_args() {
let args = Command.args("en");
assert_eq!(args, "on|prompt|off");
}
#[test]
fn test_description() {
let description = Command.description("en");
assert_eq!(description, "Enable or disable echoing commands");
}
async fn test_execute_no_args(echo: EchoMode) -> anyhow::Result<()> {
let mut output = Output::default();
let configuration = &mut Configuration {
echo: echo.clone(),
..Default::default()
};
let options = CommandOptions {
configuration,
command_manager: &CommandManager::default(),
formatter_manager: &FormatterManager::default(),
connection: &mut MockConnection::new(),
history: &DefaultHistory::new(),
input: vec![".echo".to_string()],
output: &mut output,
};
let result = Command.execute(options).await?;
assert_eq!(result, LoopCondition::Continue);
let echo_output = output.to_string();
match echo {
EchoMode::On => assert_eq!(echo_output, "Echo: on\n"),
EchoMode::Prompt => assert_eq!(echo_output, "Echo: prompt\n"),
EchoMode::Off => assert_eq!(echo_output, "Echo: off\n"),
}
Ok(())
}
#[tokio::test]
async fn test_execute_no_args_on() -> anyhow::Result<()> {
test_execute_no_args(EchoMode::On).await
}
#[tokio::test]
async fn test_execute_no_args_prompt() -> anyhow::Result<()> {
test_execute_no_args(EchoMode::Prompt).await
}
#[tokio::test]
async fn test_execute_no_args_off() -> anyhow::Result<()> {
test_execute_no_args(EchoMode::Off).await
}
#[tokio::test]
async fn test_execute_set_on() -> anyhow::Result<()> {
let configuration = &mut Configuration {
echo: EchoMode::Off,
..Default::default()
};
let options = CommandOptions {
configuration,
command_manager: &CommandManager::default(),
formatter_manager: &FormatterManager::default(),
connection: &mut MockConnection::new(),
history: &DefaultHistory::new(),
input: vec![".echo".to_string(), "on".to_string()],
output: &mut Output::default(),
};
let result = Command.execute(options).await?;
assert_eq!(result, LoopCondition::Continue);
assert_eq!(configuration.echo, EchoMode::On);
Ok(())
}
#[tokio::test]
async fn test_execute_set_prompt() -> anyhow::Result<()> {
let configuration = &mut Configuration {
echo: EchoMode::Off,
..Default::default()
};
let options = CommandOptions {
configuration,
command_manager: &CommandManager::default(),
formatter_manager: &FormatterManager::default(),
connection: &mut MockConnection::new(),
history: &DefaultHistory::new(),
input: vec![".echo".to_string(), "prompt".to_string()],
output: &mut Output::default(),
};
let result = Command.execute(options).await?;
assert_eq!(result, LoopCondition::Continue);
assert_eq!(configuration.echo, EchoMode::Prompt);
Ok(())
}
#[tokio::test]
async fn test_execute_set_off() -> anyhow::Result<()> {
let configuration = &mut Configuration {
echo: EchoMode::On,
..Default::default()
};
let options = CommandOptions {
configuration,
command_manager: &CommandManager::default(),
formatter_manager: &FormatterManager::default(),
connection: &mut MockConnection::new(),
history: &DefaultHistory::new(),
input: vec![".echo".to_string(), "off".to_string()],
output: &mut Output::default(),
};
let result = Command.execute(options).await?;
assert_eq!(result, LoopCondition::Continue);
assert_eq!(configuration.echo, EchoMode::Off);
Ok(())
}
#[tokio::test]
async fn test_execute_invalid_option() {
let options = CommandOptions {
configuration: &mut Configuration::default(),
command_manager: &CommandManager::default(),
formatter_manager: &FormatterManager::default(),
connection: &mut MockConnection::new(),
history: &DefaultHistory::new(),
input: vec![".echo".to_string(), "foo".to_string()],
output: &mut Output::default(),
};
assert!(Command.execute(options).await.is_err());
}
}