use crate::{Result, TypedValue, TypedValueMap, TypedValueVec};
pub use Subtypes as OutcomeSubtypes;
pub use Subtypes as OutcomeSubTypes;
type AsNoun = String;
type AsVerb = String;
#[derive(Debug, Clone)]
pub enum OutcomeState {
Success(AsNoun, AsVerb),
Fail(AsNoun, AsVerb),
Error(AsNoun, AsVerb),
}
impl OutcomeState {
pub fn success() -> Self {
Self::Success("Success".to_string(), "Succeeded".to_string())
}
pub fn pass() -> Self {
Self::Success("Pass".to_string(), "Passed".to_string())
}
pub fn fail() -> Self {
Self::Fail("Fail".to_string(), "Failed".to_string())
}
pub fn error() -> Self {
Self::Error("Error".to_string(), "Errored".to_string())
}
pub fn as_verb(&self) -> String {
match self {
Self::Success(_, verb) => verb.to_string(),
Self::Fail(_, verb) => verb.to_string(),
Self::Error(_, verb) => verb.to_string(),
}
}
pub fn as_noun(&self) -> String {
match self {
Self::Success(noun, _) => noun.to_string(),
Self::Fail(noun, _) => noun.to_string(),
Self::Error(noun, _) => noun.to_string(),
}
}
}
#[derive(Debug, Clone)]
pub enum Subtypes {
UnitTestStatus,
BuildResult,
UploadResult,
ExecResult,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct Outcome {
pub state: OutcomeState,
pub message: Option<String>,
pub positional_results: Option<TypedValueVec>,
pub keyword_results: Option<TypedValueMap>,
pub metadata: Option<TypedValueMap>,
pub inferred: Option<bool>,
pub subtype: Option<Subtypes>,
}
impl std::fmt::Display for Outcome {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_verb())
}
}
impl Outcome {
pub fn new(state: OutcomeState) -> Self {
Self {
state,
message: None,
positional_results: None,
keyword_results: None,
metadata: None,
inferred: None,
subtype: None,
}
}
pub fn new_success() -> Self {
Self::new(OutcomeState::success())
}
pub fn new_success_with_msg(message: impl std::fmt::Display) -> Self {
let mut s = Self::new_success();
s.set_msg(message);
s
}
pub fn new_succeeded() -> Self {
Self::new(OutcomeState::success())
}
pub fn new_succeeded_with_msg(message: impl std::fmt::Display) -> Self {
let mut s = Self::new_succeeded();
s.set_msg(message);
s
}
pub fn new_pass() -> Self {
Self::new(OutcomeState::pass())
}
pub fn new_passed() -> Self {
Self::new(OutcomeState::pass())
}
pub fn new_fail() -> Self {
Self::new(OutcomeState::fail())
}
pub fn new_failed() -> Self {
Self::new(OutcomeState::fail())
}
pub fn new_err() -> Self {
Self::new(OutcomeState::error())
}
pub fn new_error() -> Self {
Self::new(OutcomeState::error())
}
pub fn new_errored() -> Self {
Self::new(OutcomeState::error())
}
pub fn new_success_or_fail(success: bool) -> Self {
if success {
Self::new_success()
} else {
Self::new_fail()
}
}
pub fn new_pass_or_fail(pass: bool) -> Self {
if pass {
Self::new_pass()
} else {
Self::new_fail()
}
}
pub fn succeeded(&self) -> bool {
match self.state {
OutcomeState::Success(_, _) => true,
_ => false,
}
}
pub fn passed(&self) -> bool {
self.succeeded()
}
pub fn failed(&self) -> bool {
match self.state {
OutcomeState::Fail(_, _) => true,
_ => false,
}
}
pub fn errored(&self) -> bool {
match self.state {
OutcomeState::Error(_, _) => true,
_ => false,
}
}
pub fn set_msg(&mut self, message: impl std::fmt::Display) -> &mut Self {
self.message = Some(message.to_string());
self
}
pub fn message(&self) -> &Option<String> {
&self.message
}
pub fn msg(&self) -> &Option<String> {
self.message()
}
pub fn msg_or_default(&self) -> &str {
match &self.message {
Some(m) => m,
None => "No message was provided!",
}
}
pub fn add_metadata(&mut self, key: &str, m: impl Into<TypedValue>) -> &mut Self {
if self.metadata.is_none() {
self.metadata = Some(TypedValueMap::new());
}
self.metadata.as_mut().unwrap().insert(key, m);
self
}
pub fn insert_keyword_result(
&mut self,
kw: impl AsRef<str>,
r: impl Into<TypedValue>,
) -> &mut Self {
if self.keyword_results.is_none() {
self.keyword_results = Some(TypedValueMap::new());
}
self.keyword_results
.as_mut()
.unwrap()
.insert(kw.as_ref(), r);
self
}
pub fn require_keyword_result(&self, kw: impl AsRef<str>) -> Result<&TypedValue> {
if let Some(kw_results) = self.keyword_results.as_ref() {
if let Some(r) = kw_results.get(kw.as_ref()) {
return Ok(r);
}
}
bail!(
"Required that outcome has keyword result '{}' but no such result was provided",
kw.as_ref()
)
}
pub fn gist(&self) {
match &self.state {
OutcomeState::Success(_, _) => {
display_greenln!("{}", self.as_verb());
}
OutcomeState::Fail(_, _) => {
display_redln!("{}", self.as_verb());
}
OutcomeState::Error(_, _) => {
display_redln!("{}", self.as_verb());
}
}
}
pub fn summarize_and_exit(&self) {
match &self.state {
OutcomeState::Success(n, _) => {
display_greenln!("{}", self.as_verb());
if n == "Pass" {
exit_pass!();
} else {
exit_success!();
}
}
OutcomeState::Fail(_, _) => {
display_redln!("{}", self.as_verb());
exit_fail!();
}
OutcomeState::Error(_, _) => {
display_redln!("{}", self.as_verb());
exit_error!();
}
}
}
pub fn as_verb(&self) -> String {
if let Some(m) = self.message.as_ref() {
format!("{} with message: {}", self.state.as_verb(), m)
} else {
format!("{}", self.state.as_verb())
}
}
}