use crate::errors::CargoMessageFormatError;
use clap::ValueEnum;
use nextest_metadata::BuildPlatform;
use nextest_runner::reporter::{FinalStatusLevel, ShowProgress, StatusLevel, TestOutputDisplay};
use std::collections::HashSet;
#[derive(Copy, Clone, Debug, ValueEnum, Default)]
pub(crate) enum PlatformFilterOpts {
Target,
Host,
#[default]
Any,
}
impl From<PlatformFilterOpts> for Option<BuildPlatform> {
fn from(opt: PlatformFilterOpts) -> Self {
match opt {
PlatformFilterOpts::Target => Some(BuildPlatform::Target),
PlatformFilterOpts::Host => Some(BuildPlatform::Host),
PlatformFilterOpts::Any => None,
}
}
}
#[derive(Copy, Clone, Debug, ValueEnum, Default)]
pub(crate) enum ListType {
#[default]
Full,
BinariesOnly,
}
#[derive(Copy, Clone, Debug, ValueEnum, Default)]
pub(crate) enum MessageFormatOpts {
#[default]
Auto,
Human,
Oneline,
Json,
JsonPretty,
}
impl MessageFormatOpts {
pub(crate) fn to_output_format(
self,
verbose: bool,
is_terminal: bool,
) -> nextest_runner::list::OutputFormat {
use nextest_runner::list::{OutputFormat, SerializableFormat};
match self {
Self::Auto => {
if is_terminal {
OutputFormat::Human { verbose }
} else {
OutputFormat::Oneline { verbose }
}
}
Self::Human => OutputFormat::Human { verbose },
Self::Oneline => OutputFormat::Oneline { verbose },
Self::Json => OutputFormat::Serializable(SerializableFormat::Json),
Self::JsonPretty => OutputFormat::Serializable(SerializableFormat::JsonPretty),
}
}
pub(crate) fn is_human_readable(self) -> bool {
match self {
Self::Auto | Self::Human | Self::Oneline => true,
Self::Json | Self::JsonPretty => false,
}
}
}
#[derive(Copy, Clone, Debug, ValueEnum)]
pub(crate) enum RunIgnoredOpt {
Default,
#[clap(alias = "ignored-only")]
Only,
All,
}
impl From<RunIgnoredOpt> for nextest_runner::test_filter::RunIgnored {
fn from(opt: RunIgnoredOpt) -> Self {
match opt {
RunIgnoredOpt::Default => Self::Default,
RunIgnoredOpt::Only => Self::Only,
RunIgnoredOpt::All => Self::All,
}
}
}
#[derive(Clone, Copy, Debug, Default, ValueEnum)]
pub(crate) enum NoTestsBehaviorOpt {
#[default]
Auto,
Pass,
Warn,
#[clap(alias = "error")]
Fail,
}
#[derive(Clone, Copy, Debug, ValueEnum)]
pub(crate) enum TestOutputDisplayOpt {
Immediate,
ImmediateFinal,
Final,
Never,
}
impl From<TestOutputDisplayOpt> for TestOutputDisplay {
fn from(opt: TestOutputDisplayOpt) -> Self {
match opt {
TestOutputDisplayOpt::Immediate => TestOutputDisplay::Immediate,
TestOutputDisplayOpt::ImmediateFinal => TestOutputDisplay::ImmediateFinal,
TestOutputDisplayOpt::Final => TestOutputDisplay::Final,
TestOutputDisplayOpt::Never => TestOutputDisplay::Never,
}
}
}
#[derive(Clone, Copy, Debug, ValueEnum)]
pub(crate) enum StatusLevelOpt {
None,
Fail,
Retry,
Slow,
Leak,
Pass,
Skip,
All,
}
impl From<StatusLevelOpt> for StatusLevel {
fn from(opt: StatusLevelOpt) -> Self {
match opt {
StatusLevelOpt::None => StatusLevel::None,
StatusLevelOpt::Fail => StatusLevel::Fail,
StatusLevelOpt::Retry => StatusLevel::Retry,
StatusLevelOpt::Slow => StatusLevel::Slow,
StatusLevelOpt::Leak => StatusLevel::Leak,
StatusLevelOpt::Pass => StatusLevel::Pass,
StatusLevelOpt::Skip => StatusLevel::Skip,
StatusLevelOpt::All => StatusLevel::All,
}
}
}
#[derive(Clone, Copy, Debug, ValueEnum)]
pub(crate) enum FinalStatusLevelOpt {
None,
Fail,
#[clap(alias = "retry")]
Flaky,
Slow,
Skip,
Pass,
All,
}
impl From<FinalStatusLevelOpt> for FinalStatusLevel {
fn from(opt: FinalStatusLevelOpt) -> Self {
match opt {
FinalStatusLevelOpt::None => FinalStatusLevel::None,
FinalStatusLevelOpt::Fail => FinalStatusLevel::Fail,
FinalStatusLevelOpt::Flaky => FinalStatusLevel::Flaky,
FinalStatusLevelOpt::Slow => FinalStatusLevel::Slow,
FinalStatusLevelOpt::Skip => FinalStatusLevel::Skip,
FinalStatusLevelOpt::Pass => FinalStatusLevel::Pass,
FinalStatusLevelOpt::All => FinalStatusLevel::All,
}
}
}
#[derive(Default, Clone, Copy, Debug, ValueEnum)]
pub(crate) enum ShowProgressOpt {
#[default]
Auto,
None,
#[clap(alias = "running")]
Bar,
Counter,
Only,
}
impl From<ShowProgressOpt> for ShowProgress {
fn from(opt: ShowProgressOpt) -> Self {
match opt {
ShowProgressOpt::Auto => ShowProgress::Auto {
suppress_success: false,
},
ShowProgressOpt::None => ShowProgress::None,
ShowProgressOpt::Bar => ShowProgress::Running,
ShowProgressOpt::Counter => ShowProgress::Counter,
ShowProgressOpt::Only => ShowProgress::Auto {
suppress_success: true,
},
}
}
}
#[derive(Clone, Copy, Debug, ValueEnum)]
pub(crate) enum IgnoreOverridesOpt {
Retries,
All,
}
#[derive(Clone, Copy, Debug, ValueEnum, Default)]
pub(crate) enum MessageFormat {
#[default]
Human,
LibtestJson,
LibtestJsonPlus,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) enum CargoMessageFormat {
#[default]
Human,
Json {
render_diagnostics: bool,
short: bool,
ansi: bool,
},
}
impl CargoMessageFormat {
pub(crate) fn cargo_arg(&self) -> &'static str {
match self {
Self::Human => "json-render-diagnostics",
Self::Json {
render_diagnostics: false,
short: false,
ansi: false,
} => "json",
Self::Json {
render_diagnostics: false,
short: true,
ansi: false,
} => "json-diagnostic-short",
Self::Json {
render_diagnostics: false,
short: false,
ansi: true,
} => "json-diagnostic-rendered-ansi",
Self::Json {
render_diagnostics: false,
short: true,
ansi: true,
} => "json-diagnostic-short,json-diagnostic-rendered-ansi",
Self::Json {
render_diagnostics: true,
short: false,
ansi: false,
} => "json-render-diagnostics",
Self::Json {
render_diagnostics: true,
short: true,
ansi: false,
} => "json-render-diagnostics,json-diagnostic-short",
Self::Json {
render_diagnostics: true,
short: false,
ansi: true,
} => "json-render-diagnostics,json-diagnostic-rendered-ansi",
Self::Json {
render_diagnostics: true,
short: true,
ansi: true,
} => "json-render-diagnostics,json-diagnostic-short,json-diagnostic-rendered-ansi",
}
}
pub(crate) fn forward_json(&self) -> bool {
matches!(self, Self::Json { .. })
}
}
#[derive(Clone, Copy, Debug, ValueEnum, Default, PartialEq, Eq, Hash)]
pub(crate) enum CargoMessageFormatOpt {
#[default]
Human,
Short,
Json,
JsonDiagnosticShort,
JsonDiagnosticRenderedAnsi,
JsonRenderDiagnostics,
}
impl CargoMessageFormatOpt {
fn as_str(self) -> &'static str {
match self {
Self::Human => "human",
Self::Short => "short",
Self::Json => "json",
Self::JsonDiagnosticShort => "json-diagnostic-short",
Self::JsonDiagnosticRenderedAnsi => "json-diagnostic-rendered-ansi",
Self::JsonRenderDiagnostics => "json-render-diagnostics",
}
}
pub(crate) fn combine(opts: &[Self]) -> Result<CargoMessageFormat, CargoMessageFormatError> {
let mut base_format: Option<Self> = None;
let mut short = false;
let mut ansi = false;
let mut render_diagnostics = false;
let mut seen = HashSet::new();
for &opt in opts {
if !seen.insert(opt) {
return Err(CargoMessageFormatError::Duplicate {
option: opt.as_str(),
});
}
match opt {
Self::Human | Self::Short | Self::Json => {
if let Some(existing) = base_format {
return Err(CargoMessageFormatError::ConflictingBaseFormats {
first: existing.as_str(),
second: opt.as_str(),
});
}
base_format = Some(opt);
}
Self::JsonDiagnosticShort => short = true,
Self::JsonDiagnosticRenderedAnsi => ansi = true,
Self::JsonRenderDiagnostics => render_diagnostics = true,
}
}
let has_json_modifiers = short || ansi || render_diagnostics;
match base_format {
None if has_json_modifiers => {
Ok(CargoMessageFormat::Json {
render_diagnostics,
short,
ansi,
})
}
None => {
Ok(CargoMessageFormat::Human)
}
Some(fmt @ Self::Human) | Some(fmt @ Self::Short) => {
if has_json_modifiers {
return Err(CargoMessageFormatError::JsonModifierWithNonJson {
modifiers: json_modifiers_str(short, ansi, render_diagnostics),
base: fmt.as_str(),
});
}
Ok(CargoMessageFormat::Human)
}
Some(Self::Json) => Ok(CargoMessageFormat::Json {
render_diagnostics,
short,
ansi,
}),
Some(_) => unreachable!(),
}
}
}
fn json_modifiers_str(short: bool, ansi: bool, render_diagnostics: bool) -> &'static str {
match (short, ansi, render_diagnostics) {
(true, false, false) => "`json-diagnostic-short`",
(false, true, false) => "`json-diagnostic-rendered-ansi`",
(false, false, true) => "`json-render-diagnostics`",
(true, true, false) => "`json-diagnostic-short` and `json-diagnostic-rendered-ansi`",
(true, false, true) => "`json-diagnostic-short` and `json-render-diagnostics`",
(false, true, true) => "`json-diagnostic-rendered-ansi` and `json-render-diagnostics`",
(true, true, true) => {
"`json-diagnostic-short`, `json-diagnostic-rendered-ansi`, \
and `json-render-diagnostics`"
}
(false, false, false) => unreachable!("at least one modifier must be set"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use CargoMessageFormatOpt::*;
fn assert_combine(opts: &[CargoMessageFormatOpt], expected: CargoMessageFormat) {
let result = CargoMessageFormatOpt::combine(opts);
assert_eq!(result, Ok(expected), "opts={opts:?}");
}
fn assert_conflicting(
opts: &[CargoMessageFormatOpt],
first: &'static str,
second: &'static str,
) {
let result = CargoMessageFormatOpt::combine(opts);
assert_eq!(
result,
Err(CargoMessageFormatError::ConflictingBaseFormats { first, second }),
"opts={opts:?}"
);
}
fn assert_modifier_with_non_json(
opts: &[CargoMessageFormatOpt],
modifiers: &'static str,
base: &'static str,
) {
let result = CargoMessageFormatOpt::combine(opts);
assert_eq!(
result,
Err(CargoMessageFormatError::JsonModifierWithNonJson { modifiers, base }),
"opts={opts:?}"
);
}
fn assert_duplicate(opts: &[CargoMessageFormatOpt], option: &'static str) {
let result = CargoMessageFormatOpt::combine(opts);
assert_eq!(
result,
Err(CargoMessageFormatError::Duplicate { option }),
"opts={opts:?}"
);
}
#[test]
fn test_cargo_message_format_opt_combine() {
assert_combine(&[], CargoMessageFormat::Human);
assert_combine(&[Human], CargoMessageFormat::Human);
assert_combine(&[Short], CargoMessageFormat::Human);
assert_combine(
&[Json],
CargoMessageFormat::Json {
render_diagnostics: false,
short: false,
ansi: false,
},
);
assert_combine(
&[JsonDiagnosticShort],
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: false,
},
);
assert_combine(
&[JsonDiagnosticRenderedAnsi],
CargoMessageFormat::Json {
render_diagnostics: false,
short: false,
ansi: true,
},
);
assert_combine(
&[JsonRenderDiagnostics],
CargoMessageFormat::Json {
render_diagnostics: true,
short: false,
ansi: false,
},
);
assert_combine(
&[Json, JsonDiagnosticShort],
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: false,
},
);
assert_combine(
&[Json, JsonDiagnosticRenderedAnsi],
CargoMessageFormat::Json {
render_diagnostics: false,
short: false,
ansi: true,
},
);
assert_combine(
&[Json, JsonRenderDiagnostics],
CargoMessageFormat::Json {
render_diagnostics: true,
short: false,
ansi: false,
},
);
assert_combine(
&[Json, JsonDiagnosticShort, JsonDiagnosticRenderedAnsi],
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: true,
},
);
assert_combine(
&[Json, JsonDiagnosticShort, JsonRenderDiagnostics],
CargoMessageFormat::Json {
render_diagnostics: true,
short: true,
ansi: false,
},
);
assert_combine(
&[Json, JsonDiagnosticRenderedAnsi, JsonRenderDiagnostics],
CargoMessageFormat::Json {
render_diagnostics: true,
short: false,
ansi: true,
},
);
assert_combine(
&[
Json,
JsonDiagnosticShort,
JsonDiagnosticRenderedAnsi,
JsonRenderDiagnostics,
],
CargoMessageFormat::Json {
render_diagnostics: true,
short: true,
ansi: true,
},
);
assert_combine(
&[JsonDiagnosticShort, JsonDiagnosticRenderedAnsi],
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: true,
},
);
assert_combine(
&[JsonDiagnosticShort, JsonRenderDiagnostics],
CargoMessageFormat::Json {
render_diagnostics: true,
short: true,
ansi: false,
},
);
assert_combine(
&[JsonDiagnosticRenderedAnsi, JsonRenderDiagnostics],
CargoMessageFormat::Json {
render_diagnostics: true,
short: false,
ansi: true,
},
);
assert_combine(
&[
JsonDiagnosticShort,
JsonDiagnosticRenderedAnsi,
JsonRenderDiagnostics,
],
CargoMessageFormat::Json {
render_diagnostics: true,
short: true,
ansi: true,
},
);
assert_combine(
&[JsonDiagnosticRenderedAnsi, JsonDiagnosticShort],
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: true,
},
);
assert_combine(
&[JsonDiagnosticShort, Json],
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: false,
},
);
assert_combine(
&[
JsonRenderDiagnostics,
JsonDiagnosticShort,
JsonDiagnosticRenderedAnsi,
Json,
],
CargoMessageFormat::Json {
render_diagnostics: true,
short: true,
ansi: true,
},
);
assert_conflicting(&[Human, Json], "human", "json");
assert_conflicting(&[Human, Short], "human", "short");
assert_conflicting(&[Short, Json], "short", "json");
assert_conflicting(&[Json, Human], "json", "human");
assert_conflicting(&[Json, Short], "json", "short");
assert_conflicting(&[Short, Human], "short", "human");
assert_modifier_with_non_json(
&[Human, JsonDiagnosticShort],
"`json-diagnostic-short`",
"human",
);
assert_modifier_with_non_json(
&[Human, JsonDiagnosticRenderedAnsi],
"`json-diagnostic-rendered-ansi`",
"human",
);
assert_modifier_with_non_json(
&[Human, JsonRenderDiagnostics],
"`json-render-diagnostics`",
"human",
);
assert_modifier_with_non_json(
&[Human, JsonDiagnosticShort, JsonDiagnosticRenderedAnsi],
"`json-diagnostic-short` and `json-diagnostic-rendered-ansi`",
"human",
);
assert_modifier_with_non_json(
&[Human, JsonDiagnosticShort, JsonRenderDiagnostics],
"`json-diagnostic-short` and `json-render-diagnostics`",
"human",
);
assert_modifier_with_non_json(
&[Human, JsonDiagnosticRenderedAnsi, JsonRenderDiagnostics],
"`json-diagnostic-rendered-ansi` and `json-render-diagnostics`",
"human",
);
assert_modifier_with_non_json(
&[
Human,
JsonDiagnosticShort,
JsonDiagnosticRenderedAnsi,
JsonRenderDiagnostics,
],
"`json-diagnostic-short`, `json-diagnostic-rendered-ansi`, \
and `json-render-diagnostics`",
"human",
);
assert_modifier_with_non_json(
&[Short, JsonDiagnosticShort],
"`json-diagnostic-short`",
"short",
);
assert_modifier_with_non_json(
&[Short, JsonDiagnosticRenderedAnsi],
"`json-diagnostic-rendered-ansi`",
"short",
);
assert_modifier_with_non_json(
&[Short, JsonRenderDiagnostics],
"`json-render-diagnostics`",
"short",
);
assert_modifier_with_non_json(
&[Short, JsonDiagnosticShort, JsonDiagnosticRenderedAnsi],
"`json-diagnostic-short` and `json-diagnostic-rendered-ansi`",
"short",
);
assert_modifier_with_non_json(
&[Short, JsonDiagnosticShort, JsonRenderDiagnostics],
"`json-diagnostic-short` and `json-render-diagnostics`",
"short",
);
assert_modifier_with_non_json(
&[Short, JsonDiagnosticRenderedAnsi, JsonRenderDiagnostics],
"`json-diagnostic-rendered-ansi` and `json-render-diagnostics`",
"short",
);
assert_modifier_with_non_json(
&[
Short,
JsonDiagnosticShort,
JsonDiagnosticRenderedAnsi,
JsonRenderDiagnostics,
],
"`json-diagnostic-short`, `json-diagnostic-rendered-ansi`, \
and `json-render-diagnostics`",
"short",
);
assert_duplicate(&[Human, Human], "human");
assert_duplicate(&[Short, Short], "short");
assert_duplicate(&[Json, Json], "json");
assert_duplicate(
&[JsonDiagnosticShort, JsonDiagnosticShort],
"json-diagnostic-short",
);
assert_duplicate(
&[JsonDiagnosticRenderedAnsi, JsonDiagnosticRenderedAnsi],
"json-diagnostic-rendered-ansi",
);
assert_duplicate(
&[JsonRenderDiagnostics, JsonRenderDiagnostics],
"json-render-diagnostics",
);
assert_duplicate(&[Json, JsonDiagnosticShort, Json], "json");
assert_duplicate(
&[
JsonDiagnosticShort,
JsonDiagnosticRenderedAnsi,
JsonDiagnosticShort,
],
"json-diagnostic-short",
);
}
#[test]
fn test_cargo_message_format_to_cargo_arg() {
assert_eq!(
CargoMessageFormat::Human.cargo_arg(),
"json-render-diagnostics"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: false,
short: false,
ansi: false
}
.cargo_arg(),
"json"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: false
}
.cargo_arg(),
"json-diagnostic-short"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: false,
short: false,
ansi: true
}
.cargo_arg(),
"json-diagnostic-rendered-ansi"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: false,
short: true,
ansi: true
}
.cargo_arg(),
"json-diagnostic-short,json-diagnostic-rendered-ansi"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: true,
short: false,
ansi: false
}
.cargo_arg(),
"json-render-diagnostics"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: true,
short: true,
ansi: false
}
.cargo_arg(),
"json-render-diagnostics,json-diagnostic-short"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: true,
short: false,
ansi: true
}
.cargo_arg(),
"json-render-diagnostics,json-diagnostic-rendered-ansi"
);
assert_eq!(
CargoMessageFormat::Json {
render_diagnostics: true,
short: true,
ansi: true
}
.cargo_arg(),
"json-render-diagnostics,json-diagnostic-short,json-diagnostic-rendered-ansi"
);
}
#[test]
fn test_cargo_message_format_predicates() {
assert!(!CargoMessageFormat::Human.forward_json());
assert!(
CargoMessageFormat::Json {
render_diagnostics: false,
short: false,
ansi: false
}
.forward_json()
);
}
}