use core::error::Error;
use core::fmt;
use crate::separator::{NewLine, WithSep};
use super::{Format, Formatted, OneLine};
pub type MainResult<E, F = OneLine, T = ()> =
core::result::Result<T, DisplaySwapDebug<Formatted<E, F>>>;
pub type MainResultWithSuggestion<E, F = OneLine, T = ()> =
core::result::Result<T, DisplaySwapDebug<Formatted<E, WithSuggestion<F, NewLine>>>>;
pub type WithSuggestion<F = OneLine, Sep = NewLine> = WithSep<F, Sep, crate::Suggestion>;
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DisplaySwapDebug<T>(T);
impl<T> From<T> for DisplaySwapDebug<T> {
fn from(value: T) -> Self {
DisplaySwapDebug(value)
}
}
impl<T> DisplaySwapDebug<T> {
pub fn new(value: T) -> Self {
DisplaySwapDebug(value)
}
}
impl<D: fmt::Debug> fmt::Display for DisplaySwapDebug<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<D: fmt::Display> fmt::Debug for DisplaySwapDebug<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<E: Error, F: Format<E>> From<E> for DisplaySwapDebug<Formatted<E, F>> {
fn from(value: E) -> Self {
DisplaySwapDebug::new(Formatted::new(value))
}
}
#[cfg(test)]
mod tests {
use thiserror::Error as ThisError;
use super::*;
use crate::{Suggest, separator::Space, tests::Error};
#[derive(ThisError, Debug)]
enum SugError {
#[error("env file missing")]
NoEnv,
#[error("something else")]
Other,
}
impl Suggest for SugError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoEnv => f.write_str("Did you mean rename the .env.example file to .env?"),
Self::Other => Ok(()),
}
}
}
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Debug")
}
}
impl fmt::Display for Foo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Display")
}
}
fn test_return() -> MainResult<Error> {
Err(Error::One)?;
Ok(())
}
#[test]
fn test_swap() {
let result = DisplaySwapDebug::new(Foo);
assert_eq!(format!("{result:?}"), "Display");
assert_eq!(result.to_string(), "Debug");
}
#[test]
fn test_swap_with_formatted() {
let inner = Formatted::<_, OneLine>::new(Error::Two(crate::tests::ErrorInner::One));
let wrapped = DisplaySwapDebug::new(inner);
assert_eq!(format!("{wrapped:?}"), "Two: One");
assert_eq!(wrapped.to_string(), "Two(One)");
}
#[test]
fn test_main_result() {
assert_eq!(
DisplaySwapDebug::new(test_return().unwrap_err()).to_string(),
"One"
);
assert_eq!(
DisplaySwapDebug::new(&DisplaySwapDebug::new(Formatted::<_, OneLine>::new(
Error::One
)))
.to_string(),
"One"
);
}
#[test]
fn test_with_suggestion_renders_error_then_hint() {
let formatted = Formatted::<_, WithSuggestion>::new(SugError::NoEnv);
assert_eq!(
formatted.to_string(),
"env file missing\nDid you mean rename the .env.example file to .env?"
);
}
#[test]
fn test_with_suggestion_empty_hint_keeps_separator() {
let formatted = Formatted::<_, WithSuggestion>::new(SugError::Other);
assert_eq!(formatted.to_string(), "something else\n");
}
#[test]
fn test_with_suggestion_custom_separator() {
let formatted = Formatted::<_, WithSuggestion<OneLine, Space>>::new(SugError::NoEnv);
assert_eq!(
formatted.to_string(),
"env file missing Did you mean rename the .env.example file to .env?"
);
}
#[test]
fn test_main_result_with_suggestion_question_mark() {
fn run(err: bool) -> MainResultWithSuggestion<SugError> {
if err {
Err(SugError::NoEnv)?;
}
Ok(())
}
run(false).unwrap();
let wrapped = run(true).unwrap_err();
assert_eq!(
format!("{wrapped:?}"),
"env file missing\nDid you mean rename the .env.example file to .env?"
);
assert_eq!(wrapped.to_string(), "NoEnv");
}
#[test]
fn test_main_result_with_suggestion_exit_code() {
use std::process::ExitCode;
fn main_with_error(err: bool) -> MainResultWithSuggestion<SugError, OneLine, ExitCode> {
if err {
Err(SugError::NoEnv)?;
}
Ok(ExitCode::SUCCESS)
}
assert_eq!(main_with_error(false).unwrap(), ExitCode::SUCCESS);
let wrapped = main_with_error(true).unwrap_err();
assert_eq!(
wrapped.0.to_string(),
"env file missing\nDid you mean rename the .env.example file to .env?"
);
}
#[test]
fn test_main_result_with_exit_code() {
use std::process::ExitCode;
fn main_with_error(err: bool) -> MainResult<Error, OneLine, ExitCode> {
if err {
Err(Error::One)?;
}
Ok(ExitCode::SUCCESS)
}
assert_eq!(main_with_error(false).unwrap(), ExitCode::SUCCESS);
assert_eq!(main_with_error(true).unwrap_err().0.to_string(), "One");
}
}