use std::fmt;
use crate::{AnyError, StackError, StackErrorExt, add_meta, meta};
pub trait StackResultExt<T, E> {
    #[track_caller]
    fn context(self, context: impl fmt::Display) -> Result<T, AnyError>;
    #[track_caller]
    fn with_context<F, C>(self, context: F) -> Result<T, AnyError>
    where
        F: FnOnce(&E) -> C,
        C: fmt::Display;
}
impl<T, E: StackError + 'static> StackResultExt<T, E> for Result<T, E> {
    fn context(self, context: impl fmt::Display) -> Result<T, AnyError> {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_any().context(context)),
        }
    }
    fn with_context<F, C>(self, context: F) -> Result<T, AnyError>
    where
        F: FnOnce(&E) -> C,
        C: fmt::Display,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => {
                let context = context(&e);
                Err(e.into_any().context(context))
            }
        }
    }
}
pub trait StdResultExt<T, E> {
    #[track_caller]
    fn std_context(self, context: impl fmt::Display) -> Result<T, AnyError>;
    #[track_caller]
    fn with_std_context<F, C>(self, context: F) -> Result<T, AnyError>
    where
        F: FnOnce(&E) -> C,
        C: fmt::Display;
    #[track_caller]
    fn e(self) -> Result<T, AnyError>;
}
impl<T, E: std::error::Error + Send + Sync + 'static> StdResultExt<T, E> for Result<T, E> {
    fn std_context(self, context: impl fmt::Display) -> Result<T, AnyError> {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(AnyError::from_std(e).context(context)),
        }
    }
    fn with_std_context<F, C>(self, context: F) -> Result<T, AnyError>
    where
        F: FnOnce(&E) -> C,
        C: fmt::Display,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => {
                let context = context(&e);
                Err(AnyError::from_std(e).context(context))
            }
        }
    }
    fn e(self) -> Result<T, AnyError> {
        match self {
            Ok(v) => Ok(v),
            Err(err) => Err(AnyError::from_std(err)),
        }
    }
}
impl<T> StdResultExt<T, NoneError> for Option<T> {
    fn std_context(self, context: impl fmt::Display) -> Result<T, AnyError> {
        match self {
            Some(v) => Ok(v),
            None => Err(NoneError { meta: meta() }.into_any().context(context)),
        }
    }
    fn with_std_context<F, C>(self, context: F) -> Result<T, AnyError>
    where
        F: FnOnce(&NoneError) -> C,
        C: fmt::Display,
    {
        match self {
            Some(v) => Ok(v),
            None => {
                let err = NoneError { meta: meta() };
                let context = context(&err);
                Err(err.into_any().context(context))
            }
        }
    }
    fn e(self) -> Result<T, AnyError> {
        match self {
            Some(v) => Ok(v),
            None => Err(NoneError { meta: meta() }.into_any()),
        }
    }
}
impl<T> StackResultExt<T, NoneError> for Option<T> {
    fn context(self, context: impl fmt::Display) -> Result<T, AnyError> {
        match self {
            Some(v) => Ok(v),
            None => Err(NoneError { meta: meta() }.into_any().context(context)),
        }
    }
    fn with_context<F, C>(self, context: F) -> Result<T, AnyError>
    where
        F: FnOnce(&NoneError) -> C,
        C: fmt::Display,
    {
        match self {
            Some(v) => Ok(v),
            None => {
                let err = NoneError { meta: meta() };
                let context = context(&err);
                Err(err.into_any().context(context))
            }
        }
    }
}
#[add_meta]
#[derive(crate::Error)]
#[display("Expected some, found none")]
pub struct NoneError {}
#[add_meta]
#[derive(crate::Error)]
pub(crate) enum FromString {
    #[display("{message}")]
    WithSource { message: String, source: AnyError },
    #[display("{message}")]
    WithoutSource { message: String },
}