#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Format {
Plain,
Text,
Json,
Jsonl,
Markdown,
}
impl Format {
pub fn as_str(self) -> &'static str {
match self {
Self::Plain => "plain",
Self::Text => "text",
Self::Json => "json",
Self::Jsonl => "jsonl",
Self::Markdown => "markdown",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Self {
match s.to_lowercase().as_str() {
"plain" => Self::Plain,
"text" => Self::Text,
"json" => Self::Json,
"jsonl" => Self::Jsonl,
"markdown" => Self::Markdown,
_ => Self::Text,
}
}
pub fn is_structured(self) -> bool {
matches!(self, Self::Json | Self::Jsonl)
}
pub fn is_human(self) -> bool {
matches!(self, Self::Plain | Self::Text | Self::Markdown)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
Silent,
Quiet,
Normal,
Verbose,
Debug,
Trace,
}
impl Level {
#[allow(clippy::should_implement_trait)]
pub fn from_str(format: &str) -> Self {
match format.to_lowercase().as_str() {
"silent" => Self::Silent,
"quiet" => Self::Quiet,
"normal" => Self::Normal,
"verbose" => Self::Verbose,
"debug" => Self::Debug,
"trace" => Self::Trace,
_ => Self::Normal,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Silent => "silent",
Self::Quiet => "quiet",
Self::Normal => "normal",
Self::Verbose => "verbose",
Self::Debug => "debug",
Self::Trace => "trace",
}
}
pub fn from_quiet(quiet: u8) -> Self {
match quiet {
0 => Self::Normal,
1 => Self::Quiet,
_ => Self::Silent,
}
}
pub fn from_verbose(verbose: u8) -> Self {
match verbose {
0 => Self::Normal,
1 => Self::Verbose,
2 => Self::Debug,
_ => Self::Trace,
}
}
pub fn from_flags(verbose: u8, quiet: u8) -> Self {
if quiet >= 2 {
return Self::Silent;
}
if quiet == 1 {
return Self::Quiet;
}
match verbose {
0 => Self::Normal,
1 => Self::Verbose,
2 => Self::Debug,
_ => Self::Trace,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorMode {
Auto,
Always,
Never,
}
impl ColorMode {
#[allow(clippy::should_implement_trait)]
pub fn from_str(color: &str) -> Self {
match color.to_lowercase().as_str() {
"auto" => Self::Auto,
"always" => Self::Always,
"never" => Self::Never,
_ => Self::Auto,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Auto => "auto",
Self::Always => "always",
Self::Never => "never",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub interactive: bool,
pub auto_yes: bool,
pub color: ColorMode,
pub format: Format,
pub level: Level,
}
impl Default for Config {
fn default() -> Self {
Self {
interactive: false,
auto_yes: false,
color: ColorMode::Auto,
format: Format::Text,
level: Level::Normal,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_as_str_returns_correct_strings() {
assert_eq!(Format::Plain.as_str(), "plain");
assert_eq!(Format::Text.as_str(), "text");
assert_eq!(Format::Json.as_str(), "json");
assert_eq!(Format::Jsonl.as_str(), "jsonl");
assert_eq!(Format::Markdown.as_str(), "markdown");
}
#[test]
fn format_from_str_parses_correctly() {
assert_eq!(Format::from_str("plain"), Format::Plain);
assert_eq!(Format::from_str("text"), Format::Text);
assert_eq!(Format::from_str("json"), Format::Json);
assert_eq!(Format::from_str("jsonl"), Format::Jsonl);
assert_eq!(Format::from_str("markdown"), Format::Markdown);
}
#[test]
fn format_from_str_case_insensitive() {
assert_eq!(Format::from_str("PLAIN"), Format::Plain);
assert_eq!(Format::from_str("MaRkDoWn"), Format::Markdown);
assert_eq!(Format::from_str("JSON"), Format::Json);
}
#[test]
fn format_from_str_defaults_to_text_for_unknown() {
assert_eq!(Format::from_str("unknown"), Format::Text);
assert_eq!(Format::from_str("invalid"), Format::Text);
assert_eq!(Format::from_str(""), Format::Text);
}
#[test]
fn format_is_structured_returns_correct_values() {
assert!(!Format::Plain.is_structured());
assert!(!Format::Text.is_structured());
assert!(Format::Json.is_structured());
assert!(Format::Jsonl.is_structured());
assert!(!Format::Markdown.is_structured());
}
#[test]
fn format_is_human_returns_correct_values() {
assert!(Format::Plain.is_human());
assert!(Format::Text.is_human());
assert!(!Format::Json.is_human());
assert!(!Format::Jsonl.is_human());
assert!(Format::Markdown.is_human());
}
#[test]
fn level_as_str_returns_correct_strings() {
assert_eq!(Level::Silent.as_str(), "silent");
assert_eq!(Level::Quiet.as_str(), "quiet");
assert_eq!(Level::Normal.as_str(), "normal");
assert_eq!(Level::Verbose.as_str(), "verbose");
assert_eq!(Level::Debug.as_str(), "debug");
assert_eq!(Level::Trace.as_str(), "trace");
}
#[test]
fn level_from_str_parses_correctly() {
assert_eq!(Level::from_str("silent"), Level::Silent);
assert_eq!(Level::from_str("quiet"), Level::Quiet);
assert_eq!(Level::from_str("normal"), Level::Normal);
assert_eq!(Level::from_str("verbose"), Level::Verbose);
assert_eq!(Level::from_str("debug"), Level::Debug);
assert_eq!(Level::from_str("trace"), Level::Trace);
}
#[test]
fn level_from_str_case_insensitive() {
assert_eq!(Level::from_str("SILENT"), Level::Silent);
assert_eq!(Level::from_str("VeRbOsE"), Level::Verbose);
assert_eq!(Level::from_str("DEBUG"), Level::Debug);
}
#[test]
fn level_from_str_defaults_to_normal_for_unknown() {
assert_eq!(Level::from_str("unknown"), Level::Normal);
assert_eq!(Level::from_str("invalid"), Level::Normal);
assert_eq!(Level::from_str(""), Level::Normal);
}
#[test]
fn level_from_quiet_flag() {
assert_eq!(Level::from_quiet(0), Level::Normal);
assert_eq!(Level::from_quiet(1), Level::Quiet);
assert_eq!(Level::from_quiet(2), Level::Silent);
assert_eq!(Level::from_quiet(3), Level::Silent);
}
#[test]
fn level_from_verbose_flag() {
assert_eq!(Level::from_verbose(0), Level::Normal);
assert_eq!(Level::from_verbose(1), Level::Verbose);
assert_eq!(Level::from_verbose(2), Level::Debug);
assert_eq!(Level::from_verbose(3), Level::Trace);
assert_eq!(Level::from_verbose(4), Level::Trace);
}
#[test]
fn level_from_flags_quiet_takes_precedence() {
assert_eq!(Level::from_flags(0, 0), Level::Normal);
assert_eq!(Level::from_flags(3, 1), Level::Quiet);
assert_eq!(Level::from_flags(3, 2), Level::Silent);
}
#[test]
fn level_from_flags_verbose_when_no_quiet() {
assert_eq!(Level::from_flags(0, 0), Level::Normal);
assert_eq!(Level::from_flags(1, 0), Level::Verbose);
assert_eq!(Level::from_flags(2, 0), Level::Debug);
assert_eq!(Level::from_flags(3, 0), Level::Trace);
}
#[test]
fn level_ordering() {
assert!(Level::Silent < Level::Quiet);
assert!(Level::Quiet < Level::Normal);
assert!(Level::Normal < Level::Verbose);
assert!(Level::Verbose < Level::Debug);
assert!(Level::Debug < Level::Trace);
}
#[test]
fn color_mode_as_str_returns_correct_strings() {
assert_eq!(ColorMode::Auto.as_str(), "auto");
assert_eq!(ColorMode::Always.as_str(), "always");
assert_eq!(ColorMode::Never.as_str(), "never");
}
#[test]
fn color_mode_from_str_parses_correctly() {
assert_eq!(ColorMode::from_str("auto"), ColorMode::Auto);
assert_eq!(ColorMode::from_str("always"), ColorMode::Always);
assert_eq!(ColorMode::from_str("never"), ColorMode::Never);
}
#[test]
fn color_mode_from_str_case_insensitive() {
assert_eq!(ColorMode::from_str("AUTO"), ColorMode::Auto);
assert_eq!(ColorMode::from_str("AlWaYs"), ColorMode::Always);
assert_eq!(ColorMode::from_str("NEVER"), ColorMode::Never);
}
#[test]
fn color_mode_from_str_defaults_to_auto_for_unknown() {
assert_eq!(ColorMode::from_str("unknown"), ColorMode::Auto);
assert_eq!(ColorMode::from_str("invalid"), ColorMode::Auto);
assert_eq!(ColorMode::from_str(""), ColorMode::Auto);
}
#[test]
fn config_default_values() {
let config = Config::default();
assert!(!config.interactive);
assert!(!config.auto_yes);
assert_eq!(config.color, ColorMode::Auto);
assert_eq!(config.format, Format::Text);
assert_eq!(config.level, Level::Normal);
}
#[test]
fn config_can_be_created_manually() {
let config = Config {
interactive: true,
auto_yes: true,
color: ColorMode::Always,
format: Format::Markdown,
level: Level::Debug,
};
assert!(config.interactive);
assert!(config.auto_yes);
assert_eq!(config.color, ColorMode::Always);
assert_eq!(config.format, Format::Markdown);
assert_eq!(config.level, Level::Debug);
}
#[test]
fn config_is_copy() {
let config1 = Config::default();
let config2 = config1;
assert_eq!(config1.format, config2.format);
}
}