diff --git a/doublets/src/data/handler.rs b/doublets/src/data/handler.rs index ef66d93..929d8c2 100644 --- a/doublets/src/data/handler.rs +++ b/doublets/src/data/handler.rs @@ -2,10 +2,34 @@ use crate::Link; use data::{Flow, LinkType}; use std::{marker::PhantomData, mem::MaybeUninit, ops::Try}; +/// Trait for handler return types that can be converted to `Try`. +/// This allows handlers to return `()` (implying `Flow::Continue`) or explicit `Flow` values. +pub trait HandlerResult { + type Try: Try; + + fn try_it(self) -> Self::Try; +} + +impl HandlerResult for () { + type Try = Flow; + + fn try_it(self) -> Self::Try { + Flow::Continue + } +} + +impl> HandlerResult for T { + type Try = T; + + fn try_it(self) -> Self::Try { + self + } +} + pub trait Handler: FnMut(Link, Link) -> R where T: LinkType, - R: Try, + R: HandlerResult, { fn fuse(self) -> Fuse where @@ -18,7 +42,7 @@ where impl Handler for All where T: LinkType, - R: Try, + R: HandlerResult, All: FnMut(Link, Link) -> R, { } @@ -27,7 +51,7 @@ pub struct Fuse where T: LinkType, H: Handler, - R: Try, + R: HandlerResult, { handler: H, done: bool, @@ -38,7 +62,7 @@ impl Fuse where T: LinkType, F: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, { pub fn new(handler: F) -> Self { Self { @@ -53,7 +77,7 @@ impl From for Fuse where T: LinkType, H: Handler, - R: Try, + R: HandlerResult, { fn from(handler: H) -> Self { Self::new(handler) @@ -63,13 +87,13 @@ where impl FnOnce<(Link, Link)> for Fuse where H: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, T: LinkType, { type Output = Flow; extern "rust-call" fn call_once(self, args: (Link, Link)) -> Self::Output { - self.handler.call_once(args).branch().into() + self.handler.call_once(args).try_it().branch().into() } } @@ -77,13 +101,13 @@ impl FnMut<(Link, Link)> for Fuse where T: LinkType, H: Handler, - R: Try, + R: HandlerResult, { extern "rust-call" fn call_mut(&mut self, args: (Link, Link)) -> Self::Output { if self.done { Flow::Break } else { - let result = self.handler.call_mut(args); + let result = self.handler.call_mut(args).try_it(); if result.branch().is_break() { self.done = false; Flow::Break diff --git a/doublets/src/data/mod.rs b/doublets/src/data/mod.rs index 6039909..b8b0313 100644 --- a/doublets/src/data/mod.rs +++ b/doublets/src/data/mod.rs @@ -6,7 +6,7 @@ mod traits; pub use doublet::Doublet; pub use error::Error; -pub use handler::{Fuse, Handler}; +pub use handler::{Fuse, Handler, HandlerResult}; pub use link::Link; pub use traits::{Doublets, DoubletsExt, Links, ReadHandler, WriteHandler}; diff --git a/doublets/src/data/traits.rs b/doublets/src/data/traits.rs index 2337fbe..2b1d573 100644 --- a/doublets/src/data/traits.rs +++ b/doublets/src/data/traits.rs @@ -6,7 +6,7 @@ use std::{ ops::{ControlFlow, Try}, }; -use crate::{Error, Fuse, Link}; +use crate::{Error, Fuse, HandlerResult, Link}; use data::{Flow, LinkType, LinksConstants, ToQuery}; pub type ReadHandler<'a, T> = &'a mut dyn FnMut(Link) -> Flow; @@ -53,21 +53,21 @@ pub trait Doublets: Links { &mut self, query: impl ToQuery, mut handler: F, - ) -> Result> + ) -> Result> where F: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { - let mut output = R::from_output(()); + let mut output = R::Try::from_output(()); let query = query.to_query(); self.create_links( &query[..], - &mut |before, after| match handler(before, after).branch() { + &mut |before, after| match handler(before, after).try_it().branch() { ControlFlow::Continue(_) => Flow::Continue, ControlFlow::Break(residual) => { - output = R::from_residual(residual); + output = R::Try::from_residual(residual); Flow::Break } }, @@ -87,10 +87,10 @@ pub trait Doublets: Links { .map(|_| index) } - fn create_with(&mut self, handler: F) -> Result> + fn create_with(&mut self, handler: F) -> Result> where F: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { self.create_by_with([], handler) @@ -103,19 +103,19 @@ pub trait Doublets: Links { self.create_by([]) } - fn each_by(&self, query: impl ToQuery, mut handler: F) -> R + fn each_by(&self, query: impl ToQuery, mut handler: F) -> R::Try where F: FnMut(Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { - let mut output = R::from_output(()); + let mut output = R::Try::from_output(()); let query = query.to_query(); - self.each_links(&query[..], &mut |link| match handler(link).branch() { + self.each_links(&query[..], &mut |link| match handler(link).try_it().branch() { ControlFlow::Continue(_) => Flow::Continue, ControlFlow::Break(residual) => { - output = R::from_residual(residual); + output = R::Try::from_residual(residual); Flow::Break } }); @@ -123,10 +123,10 @@ pub trait Doublets: Links { output } - fn each(&self, handler: F) -> R + fn each(&self, handler: F) -> R::Try where F: FnMut(Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { self.each_by([], handler) @@ -137,23 +137,23 @@ pub trait Doublets: Links { query: impl ToQuery, change: impl ToQuery, mut handler: H, - ) -> Result> + ) -> Result> where H: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { - let mut output = R::from_output(()); + let mut output = R::Try::from_output(()); let query = query.to_query(); let change = change.to_query(); self.update_links( &query[..], &change[..], - &mut |before, after| match handler(before, after).branch() { + &mut |before, after| match handler(before, after).try_it().branch() { ControlFlow::Continue(_) => Flow::Continue, ControlFlow::Break(residual) => { - output = R::from_residual(residual); + output = R::Try::from_residual(residual); Flow::Break } }, @@ -179,10 +179,10 @@ pub trait Doublets: Links { source: T, target: T, handler: F, - ) -> Result> + ) -> Result> where F: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { self.update_by_with([index], [index, source, target], handler) @@ -199,21 +199,21 @@ pub trait Doublets: Links { &mut self, query: impl ToQuery, mut handler: F, - ) -> Result> + ) -> Result> where F: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { - let mut output = R::from_output(()); + let mut output = R::Try::from_output(()); let query = query.to_query(); self.delete_links( &query[..], - &mut |before, after| match handler(before, after).branch() { + &mut |before, after| match handler(before, after).try_it().branch() { ControlFlow::Continue(_) => Flow::Continue, ControlFlow::Break(residual) => { - output = R::from_residual(residual); + output = R::Try::from_residual(residual); Flow::Break } }, @@ -233,10 +233,10 @@ pub trait Doublets: Links { .map(|_| result) } - fn delete_with(&mut self, index: T, handler: F) -> Result> + fn delete_with(&mut self, index: T, handler: F) -> Result> where F: FnMut(Link, Link) -> R, - R: Try, + R: HandlerResult, Self: Sized, { self.delete_by_with([index], handler) diff --git a/doublets/src/lib.rs b/doublets/src/lib.rs index beee7bf..f73dd39 100644 --- a/doublets/src/lib.rs +++ b/doublets/src/lib.rs @@ -62,5 +62,5 @@ pub mod mem; pub use self::mem::{parts, split, unit}; -pub use self::data::{Doublet, Doublets, DoubletsExt, Error, Fuse, Handler, Link, Links}; +pub use self::data::{Doublet, Doublets, DoubletsExt, Error, Fuse, Handler, HandlerResult, Link, Links}; pub(crate) use self::data::{Error as LinksError, ReadHandler, WriteHandler}; diff --git a/examples/handler_result_demo.rs b/examples/handler_result_demo.rs new file mode 100644 index 0000000..b4040a4 --- /dev/null +++ b/examples/handler_result_demo.rs @@ -0,0 +1,158 @@ +//! Demonstration of the HandlerResult trait solving issue #4 +//! +//! This example shows how the HandlerResult trait eliminates the need for +//! unnecessary trailing `Flow::Continue` returns in handlers. +//! +//! ## Before (Issue #4): +//! ```rust +//! links.each(|link| { +//! worker.work(link); +//! Flow::Continue // <- Unnecessary boilerplate :( +//! }); +//! ``` +//! +//! ## After (This solution): +//! ```rust +//! links.each(|link| { +//! worker.work(link); +//! // No trailing Flow::Continue needed! :) +//! }); +//! ``` + +#![feature(try_trait_v2)] + +use std::ops::{ControlFlow, Try, FromResidual}; + +// Mock types for demonstration +#[derive(Debug, PartialEq)] +pub enum Flow { + Continue, + Break, +} + +impl Try for Flow { + type Output = (); + type Residual = (); + + fn from_output(_: ()) -> Self { + Flow::Continue + } + + fn branch(self) -> ControlFlow { + match self { + Flow::Continue => ControlFlow::Continue(()), + Flow::Break => ControlFlow::Break(()), + } + } +} + +impl FromResidual<()> for Flow { + fn from_residual(_: ()) -> Self { + Flow::Break + } +} + +/// The new HandlerResult trait that solves issue #4 +pub trait HandlerResult { + type Try: Try; + + fn try_it(self) -> Self::Try; +} + +impl HandlerResult for () { + type Try = Flow; + + fn try_it(self) -> Self::Try { + Flow::Continue + } +} + +impl HandlerResult for Flow { + type Try = Flow; + + fn try_it(self) -> Self::Try { + self + } +} + +#[derive(Debug)] +pub struct Link { + pub index: u32, +} + +struct Worker; + +impl Worker { + fn work(&mut self, link: Link) { + println!("Processing link {}", link.index); + } +} + +struct MockLinks { + links: Vec, +} + +impl MockLinks { + fn new() -> Self { + Self { + links: vec![ + Link { index: 1 }, + Link { index: 2 }, + Link { index: 3 }, + ], + } + } + + /// New each method using HandlerResult trait + fn each(&self, mut handler: F) -> R::Try + where + F: FnMut(Link) -> R, + R: HandlerResult, + { + let mut output = R::Try::from_output(()); + + for link in self.links.iter() { + let result = handler(Link { index: link.index }).try_it(); + match result.branch() { + ControlFlow::Continue(_) => continue, + ControlFlow::Break(residual) => { + output = R::Try::from_residual(residual); + break; + } + } + } + + output + } +} + +fn main() { + println!("🚀 HandlerResult trait demo - solving issue #4\n"); + + let links = MockLinks::new(); + let mut worker = Worker; + + println!("=== Old way (what users had to write before): ==="); + println!("links.each(|link| {{"); + println!(" worker.work(link);"); + println!(" Flow::Continue // <- Annoying boilerplate! 😞"); + println!("}});"); + println!(); + + println!("=== New way (what users can write now): ==="); + println!("links.each(|link| {{"); + println!(" worker.work(link);"); + println!(" // No trailing Flow::Continue needed! 😍"); + println!("}});"); + println!(); + + println!("=== Running the new way: ==="); + links.each(|link| { + worker.work(link); + // No trailing Flow::Continue needed! This is the solution to issue #4! + }); + + println!(); + println!("✅ SUCCESS: HandlerResult trait eliminates unnecessary Flow::Continue!"); + println!("🎉 Issue #4 solved!"); +} \ No newline at end of file