use alloc::string::{String, ToString};
use std::io::{Write, stderr};
use crate::ansi::*;
pub trait ActionResult {
fn bind_action(self, action: Action) -> Self;
fn error_action(self, action: &mut Action) -> Self;
}
impl<T, E> ActionResult for Result<T, E> {
fn bind_action(self, mut action: Action) -> Self {
match &self {
Ok(_) => action.report_success(),
Err(_) => action.report_fail(),
}
self
}
fn error_action(self, action: &mut Action) -> Self {
if self.is_err() {
action.report_fail();
}
self
}
}
impl<T> ActionResult for Option<T> {
fn bind_action(self, mut action: Action) -> Self {
match &self {
Some(_) => action.report_success(),
None => action.report_fail(),
}
self
}
fn error_action(self, action: &mut Action) -> Self {
if self.is_none() {
action.report_fail();
}
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
enum ActionState {
InProgress,
Success,
Fail,
}
#[derive(Debug, Clone)]
pub struct Action {
state: ActionState,
actioning_verb: String,
actioned_verb: String,
detail: String,
should_erase: bool,
}
impl Action {
pub fn new<S1: ToString, S2: ToString, S3: ToString>(
actioning_verb: S1,
actioned_verb: S2,
detail: S3,
) -> Self {
let mut progress = Self {
state: ActionState::InProgress,
actioning_verb: actioning_verb.to_string(),
actioned_verb: actioned_verb.to_string(),
detail: detail.to_string(),
should_erase: false,
};
progress.print();
progress
}
pub fn report_fail(&mut self) {
self.state = ActionState::Fail;
self.print();
}
pub fn report_success(&mut self) {
self.state = ActionState::Success;
self.print();
}
pub fn print(&mut self) {
#![expect(
unused_must_use,
reason = "displaying output is a non-critical part of the program, so this should not
panic, additionally, I don't want to have to think about the errors when calling this"
)]
let mut stderr = stderr().lock();
if self.should_erase {
stderr.write_all(ERASE_LINE_UP.as_bytes());
}
let actioning = &self.actioning_verb;
let actioned = &self.actioned_verb;
let detail = &self.detail;
match self.state {
ActionState::InProgress => {
writeln!(stderr, "{CYAN}{BOLD}{actioning}{RESET} {detail}");
}
ActionState::Success => {
writeln!(stderr, "{GREEN}{BOLD}{actioned}{RESET} {detail}");
}
ActionState::Fail => {
writeln!(
stderr,
"{RED}{BOLD}{actioning}{RESET} {detail} {RED}{BOLD}failed{RESET}"
);
}
};
stderr.flush();
self.should_erase = true;
}
pub fn dont_erase(&mut self) {
self.should_erase = false;
}
}