pub struct Pure<T>(pub T);
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as the rest of an `Option` block in `hdo!`",
label = "expected `Option` or `pure`",
note = "every statement after an `Option` bind must produce `Option` or end in `pure`"
)]
pub trait OptionContinuation {
type Value;
fn into_option(self) -> Option<Self::Value>;
}
impl<T> OptionContinuation for Pure<T> {
type Value = T;
fn into_option(self) -> Option<T> {
Some(self.0)
}
}
impl<T> OptionContinuation for Option<T> {
type Value = T;
fn into_option(self) -> Option<T> {
self
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as the rest of a `Result` block in `hdo!`",
label = "expected `Result<_, {E}>` or `pure`",
note = "`guard condition;` produces `Option`; inside a `Result` block write \
`guard condition throw MyError;`",
note = "to bind a `Result` with a different error type, use `<-?`, which \
converts through `From`"
)]
pub trait ResultContinuation<E> {
type Value;
fn into_result(self) -> Result<Self::Value, E>;
}
impl<T, E> ResultContinuation<E> for Pure<T> {
type Value = T;
fn into_result(self) -> Result<T, E> {
Ok(self.0)
}
}
impl<T, E> ResultContinuation<E> for Result<T, E> {
type Value = T;
fn into_result(self) -> Result<T, E> {
self
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as the rest of a list block in `hdo!`",
label = "expected `Vec`, `Option` or `pure`"
)]
pub trait ListContinuation {
type Value;
fn into_list(self) -> Vec<Self::Value>;
}
impl<T> ListContinuation for Pure<T> {
type Value = T;
fn into_list(self) -> Vec<T> {
::std::vec![self.0]
}
}
impl<T> ListContinuation for Vec<T> {
type Value = T;
fn into_list(self) -> Vec<T> {
self
}
}
impl<T> ListContinuation for Option<T> {
type Value = T;
fn into_list(self) -> Vec<T> {
self.into_iter().collect()
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a monad supported by `hdo!`",
label = "cannot bind this with `<-`",
note = "`hdo!` binds `Option`, `Result` and `Vec`"
)]
pub trait HdoBind<F> {
type Output;
fn hdo_bind(self, next: F) -> Self::Output;
}
impl<T, F, C> HdoBind<F> for Option<T>
where
F: FnOnce(T) -> C,
C: OptionContinuation,
{
type Output = Option<C::Value>;
fn hdo_bind(self, next: F) -> Self::Output {
self.and_then(|value| next(value).into_option())
}
}
impl<T, E, F, C> HdoBind<F> for Result<T, E>
where
F: FnOnce(T) -> C,
C: ResultContinuation<E>,
{
type Output = Result<C::Value, E>;
fn hdo_bind(self, next: F) -> Self::Output {
self.and_then(|value| next(value).into_result())
}
}
impl<T, F, C> HdoBind<F> for Vec<T>
where
F: FnMut(T) -> C,
C: ListContinuation,
{
type Output = Vec<C::Value>;
fn hdo_bind(self, mut next: F) -> Self::Output {
self.into_iter()
.flat_map(|value| next(value).into_list())
.collect()
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a statement inside `hdo!`",
label = "this action is neither `()` nor a supported monad",
note = "bind it with `<-` instead, or discard it with `let _ = ...;`"
)]
pub trait HdoThen<F> {
type Output;
fn hdo_then(self, next: F) -> Self::Output;
}
impl<F, C> HdoThen<F> for ()
where
F: FnOnce() -> C,
{
type Output = C;
fn hdo_then(self, next: F) -> Self::Output {
next()
}
}
impl<T, F, C> HdoThen<F> for Option<T>
where
F: FnOnce() -> C,
C: OptionContinuation,
{
type Output = Option<C::Value>;
fn hdo_then(self, next: F) -> Self::Output {
self.and_then(|_| next().into_option())
}
}
impl<T, E, F, C> HdoThen<F> for Result<T, E>
where
F: FnOnce() -> C,
C: ResultContinuation<E>,
{
type Output = Result<C::Value, E>;
fn hdo_then(self, next: F) -> Self::Output {
self.and_then(|_| next().into_result())
}
}
impl<T, F, C> HdoThen<F> for Vec<T>
where
F: FnMut() -> C,
C: ListContinuation,
{
type Output = Vec<C::Value>;
fn hdo_then(self, mut next: F) -> Self::Output {
self.into_iter().flat_map(|_| next().into_list()).collect()
}
}
pub fn guard_option<C>(condition: bool, next: impl FnOnce() -> C) -> Option<C::Value>
where
C: OptionContinuation,
{
if condition {
next().into_option()
} else {
None
}
}
pub fn guard_result<C, E>(
condition: bool,
error: E,
next: impl FnOnce() -> C,
) -> Result<C::Value, E>
where
C: ResultContinuation<E>,
{
if condition {
next().into_result()
} else {
Err(error)
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a valid result for an `hdo!` block",
label = "this block does not produce `Option`, `Result` or `Vec`"
)]
pub trait HdoFinish {
type Output;
fn hdo_finish(self) -> Self::Output;
}
impl<T> HdoFinish for Pure<T> {
type Output = T;
fn hdo_finish(self) -> T {
self.0
}
}
impl<T> HdoFinish for Option<T> {
type Output = Option<T>;
fn hdo_finish(self) -> Self::Output {
self
}
}
impl<T, E> HdoFinish for Result<T, E> {
type Output = Result<T, E>;
fn hdo_finish(self) -> Self::Output {
self
}
}
impl<T> HdoFinish for Vec<T> {
type Output = Vec<T>;
fn hdo_finish(self) -> Self::Output {
self
}
}
#[diagnostic::on_unimplemented(
message = "refutable pattern without `throw` is only supported for `Option` in `hdo!`",
label = "this bind needs an explicit failure value",
note = "for `Result`, use `pattern <- expression throw YourError;`"
)]
pub trait HdoPatternBind<F> {
type Output;
fn hdo_pattern_bind(self, next: F) -> Self::Output;
}
#[diagnostic::do_not_recommend]
impl<T, F, U> HdoPatternBind<F> for Option<T>
where
F: FnOnce(T) -> Option<U>,
{
type Output = Option<U>;
fn hdo_pattern_bind(self, next: F) -> Self::Output {
self.and_then(next)
}
}
pub struct NoneResidual;
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot short-circuit on `{Residual}`",
label = "the block's type and the bound value's failure do not match",
note = "an `Option` bind needs an `Option` block; a `Result<_, E>` bind needs \
a `Result<_, E>` block, or `<-?` to convert the error"
)]
pub trait HdoShortCircuit<Residual> {
fn hdo_short_circuit(residual: Residual) -> Self;
}
impl<T> HdoShortCircuit<NoneResidual> for Option<T> {
fn hdo_short_circuit(_: NoneResidual) -> Self {
None
}
}
impl<T, E> HdoShortCircuit<E> for Result<T, E> {
fn hdo_short_circuit(residual: E) -> Self {
Err(residual)
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is not a monad that `pure` can produce",
label = "expected `Option` or `Result`",
note = "annotate the awaited block, for example `let x: Result<_, MyError> = block.await;`"
)]
pub trait HdoPure<T> {
fn hdo_pure(value: T) -> Self;
}
impl<T> HdoPure<T> for Option<T> {
fn hdo_pure(value: T) -> Self {
Some(value)
}
}
impl<T, E> HdoPure<T> for Result<T, E> {
fn hdo_pure(value: T) -> Self {
Ok(value)
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be bound with `<-` inside an `hdo!` async block",
label = "expected `Option` or `Result`",
note = "an `hdo!` async block binds `Option` and `Result`; lists have no short-circuiting form",
note = "to await a future, write the `.await` yourself: `x <- fetch().await;`"
)]
pub trait HdoAsyncBind<B> {
type Value;
fn hdo_async_bind(self) -> ::core::ops::ControlFlow<B, Self::Value>;
}
impl<T, B> HdoAsyncBind<B> for Option<T>
where
B: HdoShortCircuit<NoneResidual>,
{
type Value = T;
fn hdo_async_bind(self) -> ::core::ops::ControlFlow<B, T> {
match self {
Some(value) => ::core::ops::ControlFlow::Continue(value),
None => ::core::ops::ControlFlow::Break(B::hdo_short_circuit(NoneResidual)),
}
}
}
impl<T, E, B> HdoAsyncBind<B> for Result<T, E>
where
B: HdoShortCircuit<E>,
{
type Value = T;
fn hdo_async_bind(self) -> ::core::ops::ControlFlow<B, T> {
match self {
Ok(value) => ::core::ops::ControlFlow::Continue(value),
Err(error) => ::core::ops::ControlFlow::Break(B::hdo_short_circuit(error)),
}
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as a statement inside an `hdo!` async block",
label = "this action is neither `()` nor `Option` nor `Result`",
note = "bind it with `<-` instead, or discard it with `let _ = ...;`"
)]
pub trait HdoAsyncAction<B> {
fn hdo_async_action(self) -> ::core::ops::ControlFlow<B, ()>;
}
impl<B> HdoAsyncAction<B> for () {
fn hdo_async_action(self) -> ::core::ops::ControlFlow<B, ()> {
::core::ops::ControlFlow::Continue(())
}
}
impl<T, B> HdoAsyncAction<B> for Option<T>
where
B: HdoShortCircuit<NoneResidual>,
{
fn hdo_async_action(self) -> ::core::ops::ControlFlow<B, ()> {
match self {
Some(_) => ::core::ops::ControlFlow::Continue(()),
None => ::core::ops::ControlFlow::Break(B::hdo_short_circuit(NoneResidual)),
}
}
}
impl<T, E, B> HdoAsyncAction<B> for Result<T, E>
where
B: HdoShortCircuit<E>,
{
fn hdo_async_action(self) -> ::core::ops::ControlFlow<B, ()> {
match self {
Ok(_) => ::core::ops::ControlFlow::Continue(()),
Err(error) => ::core::ops::ControlFlow::Break(B::hdo_short_circuit(error)),
}
}
}