Skip to content

Instantly share code, notes, and snippets.

@barrowsys
Last active July 9, 2022 04:08
Show Gist options
  • Save barrowsys/ea0cd9e230475c906e41b6c90c8392c6 to your computer and use it in GitHub Desktop.
Save barrowsys/ea0cd9e230475c906e41b6c90c8392c6 to your computer and use it in GitHub Desktop.
my ~~cursed~~ neat control flow type
/*
* --------------------
* THIS FILE IS LICENSED UNDER THE FOLLOWING TERMS
*
* this code may not be used for any purpose. be gay, do crime
*
* THE FOLLOWING MESSAGE IS NOT A LICENSE
*
* <barrow@tilde.team> wrote this file.
* by reading this text, you are reading "TRANS RIGHTS".
* this file and the content within it is the gay agenda.
* if we meet some day, and you think this stuff is worth it,
* you can buy me a beer, tea, or something stronger.
* -Ezra Barrow
* --------------------
*/
#![allow(
clippy::use_self // theres a lot of From impls here that are harder to read when using Self
)]
use std::{
borrow::Cow,
convert::Infallible,
ops::{ControlFlow, FromResidual},
};
type Error = anyhow::Error;
type StaticStr = Cow<'static, str>;
pub type CrResultOk = ControlFlow<(), StaticStr>;
pub type CrResultErr = ControlFlow<anyhow::Error, anyhow::Error>;
pub type CrResult = Result<CrResultOk, CrResultErr>;
pub type CrFlowBreak = Result<(), anyhow::Error>;
pub type CrFlowContinue = Result<StaticStr, anyhow::Error>;
pub type CrFlow = ControlFlow<CrFlowBreak, CrFlowContinue>;
#[derive(Debug)]
pub enum CommandResult {
Info(StaticStr),
Warning(Error),
Fatal(Error),
Exit,
}
impl CommandResult {
pub const OK: Self = Self::Info(Cow::Borrowed(""));
pub fn into_flow(self) -> CrFlow {
self.into()
}
pub fn into_result(self) -> CrResult {
self.into()
}
pub fn without_err_flow(self) -> Result<CrResultOk, anyhow::Error> {
self.into_result().map_err(|cf| match cf {
ControlFlow::Continue(e) | ControlFlow::Break(e) => e,
})
}
}
impl From<CrResult> for CommandResult {
fn from(f: CrResult) -> Self {
f.map(Self::from).map_err(Self::from).into_ok_or_err()
}
}
impl From<CrResultOk> for CommandResult {
fn from(f: CrResultOk) -> Self {
match f {
ControlFlow::Continue(s) => CommandResult::Info(s),
ControlFlow::Break(()) => CommandResult::Exit,
}
}
}
impl From<CrResultErr> for CommandResult {
fn from(f: CrResultErr) -> Self {
match f {
ControlFlow::Continue(e) => CommandResult::Warning(e),
ControlFlow::Break(e) => CommandResult::Fatal(e),
}
}
}
impl From<CommandResult> for CrResult {
fn from(f: CommandResult) -> CrResult {
match f {
CommandResult::Info(msg) => Ok(ControlFlow::Continue(msg)),
CommandResult::Warning(err) => Err(ControlFlow::Continue(err)),
CommandResult::Fatal(error) => Err(ControlFlow::Break(error)),
CommandResult::Exit => Ok(ControlFlow::Break(())),
}
}
}
impl From<CrFlow> for CommandResult {
fn from(f: CrFlow) -> Self {
match f {
ControlFlow::Continue(Ok(s)) => CommandResult::Info(s),
ControlFlow::Break(Ok(())) => CommandResult::Exit,
ControlFlow::Continue(Err(e)) => CommandResult::Warning(e),
ControlFlow::Break(Err(e)) => CommandResult::Fatal(e),
}
}
}
impl From<CrFlowContinue> for CommandResult {
fn from(f: CrFlowContinue) -> Self {
f.map(CommandResult::Info)
.map_err(CommandResult::Warning)
.into_ok_or_err()
}
}
impl From<CrFlowBreak> for CommandResult {
fn from(f: CrFlowBreak) -> Self {
f.map(|_| CommandResult::Exit)
.map_err(CommandResult::Fatal)
.into_ok_or_err()
}
}
impl From<CommandResult> for CrFlow {
fn from(f: CommandResult) -> Self {
match f {
CommandResult::Info(msg) => ControlFlow::Continue(Ok(msg)),
CommandResult::Warning(err) => ControlFlow::Continue(Err(err)),
CommandResult::Fatal(error) => ControlFlow::Break(Err(error)),
CommandResult::Exit => ControlFlow::Break(Ok(())),
}
}
}
impl FromResidual<Result<Infallible, CrResultErr>> for CommandResult {
fn from_residual(residual: Result<Infallible, CrResultErr>) -> Self {
unsafe { residual.unwrap_err_unchecked().into() }
}
}
pub trait ErrorFlow {
type T;
fn warn(self) -> Result<Self::T, ControlFlow<anyhow::Error, anyhow::Error>>;
fn fatal(self) -> Result<Self::T, ControlFlow<anyhow::Error, anyhow::Error>>;
}
impl<T> ErrorFlow for Result<T, anyhow::Error> {
type T = T;
fn warn(self) -> Result<T, ControlFlow<anyhow::Error, anyhow::Error>> {
self.map_err(ControlFlow::Continue)
}
fn fatal(self) -> Result<T, ControlFlow<anyhow::Error, anyhow::Error>> {
self.map_err(ControlFlow::Break)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment