From 4171443c4530d6daff0e1ceb8652bf581fd86a30 Mon Sep 17 00:00:00 2001 From: Alain Emilia Anna Zscheile Date: Fri, 4 Oct 2024 19:53:27 +0200 Subject: [PATCH] stepper: refactoring * put `Error` as an associated type into separate trait * make `Step::finish` generic over O instead of Step * introduce `PollStep` trait including `finish` method * use `PollStep` in GUI, fix infinitely repeating errors * replace {Activity,Invoker,Interaction}Status with Poll * `PollStep` provides `Step<_, Poll<_>>` Fixes #78. --- src/autorouter/autoroute.rs | 10 +-- src/autorouter/compare_detours.rs | 8 ++- src/autorouter/execution.rs | 52 ++++++++------- src/autorouter/invoker.rs | 45 +++---------- src/bin/topola-egui/activity.rs | 102 ++++++++++++----------------- src/bin/topola-egui/app.rs | 19 +++--- src/bin/topola-egui/interaction.rs | 31 +++------ src/bin/topola-egui/menu_bar.rs | 11 ++-- src/bin/topola-egui/status_bar.rs | 11 ++-- src/router/astar.rs | 15 ++++- src/router/route.rs | 14 ++-- src/stepper.rs | 51 +++++++++++++-- 12 files changed, 188 insertions(+), 181 deletions(-) diff --git a/src/autorouter/autoroute.rs b/src/autorouter/autoroute.rs index 74ad70e..982c7ce 100644 --- a/src/autorouter/autoroute.rs +++ b/src/autorouter/autoroute.rs @@ -9,7 +9,7 @@ use crate::{ drawing::{band::BandTermsegIndex, graph::PrimitiveIndex}, geometry::primitive::PrimitiveShape, router::{navmesh::Navmesh, route::RouteStepper, trace::TraceStepper, Router, RouterStatus}, - stepper::Step, + stepper::{Step, StepError}, }; use super::{ @@ -86,9 +86,11 @@ impl AutorouteExecutionStepper { } } -impl Step, AutorouteStatus, AutorouterError, ()> - for AutorouteExecutionStepper -{ +impl StepError for AutorouteExecutionStepper { + type Error = AutorouterError; +} + +impl Step, AutorouteStatus> for AutorouteExecutionStepper { fn step(&mut self, autorouter: &mut Autorouter) -> Result { let Some(curr_ratline) = self.curr_ratline else { return Ok(AutorouteStatus::Finished); diff --git a/src/autorouter/compare_detours.rs b/src/autorouter/compare_detours.rs index 122332c..9b705b0 100644 --- a/src/autorouter/compare_detours.rs +++ b/src/autorouter/compare_detours.rs @@ -11,7 +11,7 @@ use crate::{ geometry::{primitive::PrimitiveShape, shape::MeasureLength}, graph::MakeRef, router::{navmesh::Navmesh, trace::TraceStepper}, - stepper::Step, + stepper::{Step, StepError}, }; use super::{ @@ -66,9 +66,13 @@ impl CompareDetoursExecutionStepper { } } +impl StepError for CompareDetoursExecutionStepper { + type Error = AutorouterError; +} + // XXX: Do we really need this to be a stepper? We don't use at the moment, as sorting functions // aren't steppable either. It may be useful for debugging later on tho. -impl Step, CompareDetoursStatus, AutorouterError, (f64, f64)> +impl Step, CompareDetoursStatus> for CompareDetoursExecutionStepper { fn step( diff --git a/src/autorouter/execution.rs b/src/autorouter/execution.rs index 25f3b2f..6dd5d12 100644 --- a/src/autorouter/execution.rs +++ b/src/autorouter/execution.rs @@ -1,12 +1,17 @@ +use core::task::Poll; use enum_dispatch::enum_dispatch; use serde::{Deserialize, Serialize}; -use crate::{board::mesadata::AccessMesadata, layout::via::ViaWeight, stepper::Step}; +use crate::{ + board::mesadata::AccessMesadata, + layout::via::ViaWeight, + stepper::{PollStep, Step, StepError}, +}; use super::{ autoroute::{AutorouteExecutionStepper, AutorouteStatus}, compare_detours::{CompareDetoursExecutionStepper, CompareDetoursStatus}, - invoker::{Invoker, InvokerError, InvokerStatus}, + invoker::{Invoker, InvokerError}, measure_length::MeasureLengthExecutionStepper, place_via::PlaceViaExecutionStepper, remove_bands::RemoveBandsExecutionStepper, @@ -38,30 +43,28 @@ impl ExecutionStepper { fn step_catch_err( &mut self, invoker: &mut Invoker, - ) -> Result { - Ok(match self { + ) -> Poll> { + match self { ExecutionStepper::Autoroute(autoroute) => { match autoroute.step(&mut invoker.autorouter)? { - AutorouteStatus::Running => InvokerStatus::Running, - AutorouteStatus::Routed(..) => InvokerStatus::Running, - AutorouteStatus::Finished => { - InvokerStatus::Finished("finished autorouting".to_string()) - } + AutorouteStatus::Running => Poll::Pending, + AutorouteStatus::Routed(..) => Poll::Pending, + AutorouteStatus::Finished => Poll::Ready("finished autorouting".to_string()), } } ExecutionStepper::PlaceVia(place_via) => { place_via.doit(&mut invoker.autorouter)?; - InvokerStatus::Finished("finished placing via".to_string()) + Poll::Ready("finished placing via".to_string()) } ExecutionStepper::RemoveBands(remove_bands) => { remove_bands.doit(&mut invoker.autorouter)?; - InvokerStatus::Finished("finished removing bands".to_string()) + Poll::Ready("finished removing bands".to_string()) } ExecutionStepper::CompareDetours(compare_detours) => { match compare_detours.step(&mut invoker.autorouter)? { - CompareDetoursStatus::Running => InvokerStatus::Running, + CompareDetoursStatus::Running => Poll::Pending, CompareDetoursStatus::Finished(total_length1, total_length2) => { - InvokerStatus::Finished(format!( + Poll::Ready(format!( "total detour lengths are {} and {}", total_length1, total_length2 )) @@ -70,27 +73,30 @@ impl ExecutionStepper { } ExecutionStepper::MeasureLength(measure_length) => { let length = measure_length.doit(&mut invoker.autorouter)?; - InvokerStatus::Finished(format!("Total length of selected bands: {}", length)) + Poll::Ready(format!("Total length of selected bands: {}", length)) } - }) + } + .map(Ok) } } -impl Step, InvokerStatus, InvokerError, ()> for ExecutionStepper { - fn step(&mut self, invoker: &mut Invoker) -> Result { - match self.step_catch_err(invoker) { - Ok(InvokerStatus::Running) => Ok(InvokerStatus::Running), - Ok(InvokerStatus::Finished(msg)) => { +impl StepError for ExecutionStepper { + type Error = InvokerError; +} + +impl PollStep, String> for ExecutionStepper { + fn poll_step(&mut self, invoker: &mut Invoker) -> Poll> { + self.step_catch_err(invoker).map(|x| match x { + Ok(msg) => { if let Some(command) = invoker.ongoing_command.take() { invoker.history.do_(command); } - - Ok(InvokerStatus::Finished(msg)) + Ok(msg) } Err(err) => { invoker.ongoing_command = None; Err(err) } - } + }) } } diff --git a/src/autorouter/invoker.rs b/src/autorouter/invoker.rs index e09da3b..74aaa74 100644 --- a/src/autorouter/invoker.rs +++ b/src/autorouter/invoker.rs @@ -1,8 +1,7 @@ //! Manages the execution of routing commands within the autorouting system. -use std::cmp::Ordering; - use contracts_try::debug_requires; +use core::cmp::Ordering; use enum_dispatch::enum_dispatch; use thiserror::Error; @@ -11,7 +10,7 @@ use crate::{ drawing::graph::PrimitiveIndex, geometry::primitive::PrimitiveShape, router::{navmesh::Navmesh, trace::TraceStepper}, - stepper::Step, + stepper::{PollStep, Step}, }; use super::{ @@ -59,16 +58,6 @@ pub enum InvokerError { Autorouter(#[from] AutorouterError), } -impl TryInto<()> for InvokerStatus { - type Error = (); - fn try_into(self) -> Result<(), ()> { - match self { - InvokerStatus::Running => Err(()), - InvokerStatus::Finished(..) => Ok(()), - } - } -} - pub struct Invoker { pub(super) autorouter: Autorouter, pub(super) history: History, @@ -93,20 +82,11 @@ impl Invoker { } //#[debug_requires(self.ongoing_command.is_none())] - pub fn execute(&mut self, command: Command) -> Result<(), InvokerError> { + pub fn execute(&mut self, command: Command) -> Result { let mut execute = self.execute_stepper(command)?; - - loop { - let status = match execute.step(self) { - Ok(status) => status, - Err(err) => return Err(err), - }; - - if let InvokerStatus::Finished(..) = status { - self.history.set_undone(std::iter::empty()); - return Ok(()); - } - } + let res = PollStep::finish(&mut execute, self)?; + self.history.set_undone(std::iter::empty()); + Ok(res) } #[debug_requires(self.ongoing_command.is_none())] @@ -178,17 +158,8 @@ impl Invoker { pub fn redo(&mut self) -> Result<(), InvokerError> { let command = self.history.last_undone()?.clone(); let mut execute = self.execute_stepper(command)?; - - loop { - let status = match execute.step(self) { - Ok(status) => status, - Err(err) => return Err(err), - }; - - if let InvokerStatus::Finished(..) = status { - return Ok(self.history.redo()?); - } - } + PollStep::finish(&mut execute, self)?; + Ok(self.history.redo()?) } #[debug_requires(self.ongoing_command.is_none())] diff --git a/src/bin/topola-egui/activity.rs b/src/bin/topola-egui/activity.rs index 073056a..60df549 100644 --- a/src/bin/topola-egui/activity.rs +++ b/src/bin/topola-egui/activity.rs @@ -1,63 +1,25 @@ +use core::task::Poll; use thiserror::Error; use topola::{ autorouter::{ execution::ExecutionStepper, - invoker::{ - GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles, Invoker, InvokerError, - InvokerStatus, - }, + invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles, Invoker, InvokerError}, }, board::mesadata::AccessMesadata, drawing::graph::PrimitiveIndex, geometry::primitive::PrimitiveShape, router::{navmesh::Navmesh, trace::TraceStepper}, specctra::mesadata::SpecctraMesadata, - stepper::{Abort, Step}, + stepper::{Abort, PollStep, StepError}, }; -use crate::interaction::{ - InteractionContext, InteractionError, InteractionStatus, InteractionStepper, -}; +use crate::interaction::{InteractionContext, InteractionError, InteractionStepper}; pub struct ActivityContext<'a> { pub interaction: InteractionContext, pub invoker: &'a mut Invoker, } -#[derive(Debug, Clone)] -pub enum ActivityStatus { - Running, - Finished(String), -} - -impl From for ActivityStatus { - fn from(status: InteractionStatus) -> Self { - match status { - InteractionStatus::Running => ActivityStatus::Running, - InteractionStatus::Finished(msg) => ActivityStatus::Finished(msg), - } - } -} - -impl From for ActivityStatus { - fn from(status: InvokerStatus) -> Self { - match status { - InvokerStatus::Running => ActivityStatus::Running, - InvokerStatus::Finished(msg) => ActivityStatus::Finished(msg), - } - } -} - -impl TryInto<()> for ActivityStatus { - type Error = (); - fn try_into(self) -> Result<(), ()> { - match self { - ActivityStatus::Running => Err(()), - ActivityStatus::Finished(..) => Ok(()), - } - } -} - #[derive(Error, Debug, Clone)] pub enum ActivityError { #[error(transparent)] @@ -71,13 +33,22 @@ pub enum ActivityStepper { Execution(ExecutionStepper), } -impl Step, ActivityStatus, ActivityError, ()> for ActivityStepper { - fn step(&mut self, context: &mut ActivityContext) -> Result { +impl StepError for ActivityStepper { + type Error = ActivityError; +} + +impl PollStep, String> for ActivityStepper { + fn poll_step( + &mut self, + context: &mut ActivityContext<'_>, + ) -> Poll> { match self { - ActivityStepper::Interaction(interaction) => { - Ok(interaction.step(&mut context.interaction)?.into()) - } - ActivityStepper::Execution(execution) => Ok(execution.step(context.invoker)?.into()), + ActivityStepper::Interaction(interaction) => interaction + .poll_step(&mut context.interaction) + .map(|x| x.map_err(Into::into)), + ActivityStepper::Execution(execution) => execution + .poll_step(context.invoker) + .map(|x| x.map_err(Into::into)), } } } @@ -86,9 +57,11 @@ impl Abort> for ActivityStepper { fn abort(&mut self, context: &mut ActivityContext) { match self { ActivityStepper::Interaction(interaction) => { - Ok(interaction.abort(&mut context.interaction)) + interaction.abort(&mut context.interaction); + } + ActivityStepper::Execution(execution) => { + PollStep::finish(execution, context.invoker); // TODO. } - ActivityStepper::Execution(execution) => execution.finish(context.invoker), // TODO. }; } } @@ -135,7 +108,7 @@ impl GetObstacles for ActivityStepper { pub struct ActivityStepperWithStatus { activity: ActivityStepper, - maybe_status: Option, + maybe_status: Option>, } impl ActivityStepperWithStatus { @@ -146,22 +119,33 @@ impl ActivityStepperWithStatus { } } - pub fn maybe_status(&self) -> Option { + pub fn maybe_status(&self) -> Option> { self.maybe_status.clone() } } -impl Step, ActivityStatus, ActivityError, ()> for ActivityStepperWithStatus { - fn step(&mut self, context: &mut ActivityContext) -> Result { - let status = self.activity.step(context)?; - self.maybe_status = Some(status.clone()); - Ok(status.into()) +impl StepError for ActivityStepperWithStatus { + type Error = ActivityError; +} + +impl PollStep, String> for ActivityStepperWithStatus { + fn poll_step( + &mut self, + context: &mut ActivityContext<'_>, + ) -> Poll> { + let res = self.activity.poll_step(context); + self.maybe_status = match &res { + Poll::Pending => Some(Poll::Pending), + Poll::Ready(Ok(msg)) => Some(Poll::Ready(msg.clone())), + Poll::Ready(Err(_)) => None, + }; + res } } impl Abort> for ActivityStepperWithStatus { - fn abort(&mut self, context: &mut ActivityContext) { - self.maybe_status = Some(ActivityStatus::Finished(String::from("aborted"))); + fn abort(&mut self, context: &mut ActivityContext<'_>) { + self.maybe_status = Some(Poll::Ready("aborted".to_string())); self.activity.abort(context); } } diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 740c6e7..b1eaa65 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -6,6 +6,7 @@ use std::{ mpsc::{channel, Receiver, Sender}, Arc, Mutex, }, + task::Poll, }; use unic_langid::{langid, LanguageIdentifier}; @@ -15,11 +16,11 @@ use topola::{ design::{LoadingError as SpecctraLoadingError, SpecctraDesign}, mesadata::SpecctraMesadata, }, - stepper::Step, + stepper::PollStep, }; use crate::{ - activity::{ActivityContext, ActivityStatus, ActivityStepperWithStatus}, + activity::{ActivityContext, ActivityStepperWithStatus}, config::Config, error_dialog::ErrorDialog, interaction::InteractionContext, @@ -171,15 +172,17 @@ impl App { } if let Some(ref mut activity) = self.maybe_activity { - return match activity.step(&mut ActivityContext { + return match activity.poll_step(&mut ActivityContext { interaction: InteractionContext {}, invoker, }) { - Ok(ActivityStatus::Running) => true, - Ok(ActivityStatus::Finished(..)) => false, - Err(err) => { - self.error_dialog - .push_error("tr-module-invoker", format!("{}", err)); + Poll::Pending => true, + Poll::Ready(res) => { + if let Err(err) = res { + self.error_dialog + .push_error("tr-module-invoker", format!("{}", err)); + } + self.maybe_activity = None; false } }; diff --git a/src/bin/topola-egui/interaction.rs b/src/bin/topola-egui/interaction.rs index 3dfa5e8..8de0d7d 100644 --- a/src/bin/topola-egui/interaction.rs +++ b/src/bin/topola-egui/interaction.rs @@ -1,10 +1,11 @@ +use core::task::Poll; use thiserror::Error; use topola::{ autorouter::invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles}, drawing::graph::PrimitiveIndex, geometry::primitive::PrimitiveShape, router::{navmesh::Navmesh, trace::TraceStepper}, - stepper::{Abort, Step}, + stepper::{Abort, PollStep, StepError}, }; use crate::activity::ActivityStepperWithStatus; @@ -15,22 +16,6 @@ pub struct InteractionContext { // (we will need an additional struct to hold a reference to a `Board<...>`) } -#[derive(Debug, Clone)] -pub enum InteractionStatus { - Running, - Finished(String), -} - -impl TryInto<()> for InteractionStatus { - type Error = (); - fn try_into(self) -> Result<(), ()> { - match self { - InteractionStatus::Running => Err(()), - InteractionStatus::Finished(..) => Ok(()), - } - } -} - #[derive(Error, Debug, Clone)] pub enum InteractionError { #[error("nothing to interact with")] @@ -44,12 +29,16 @@ pub enum InteractionStepper { // - interactively moving a footprint. } -impl Step for InteractionStepper { - fn step( +impl StepError for InteractionStepper { + type Error = InteractionError; +} + +impl PollStep for InteractionStepper { + fn poll_step( &mut self, invoker: &mut InteractionContext, - ) -> Result { - Ok(InteractionStatus::Finished(String::from(""))) + ) -> Poll> { + Poll::Ready(Ok(String::new())) } } diff --git a/src/bin/topola-egui/menu_bar.rs b/src/bin/topola-egui/menu_bar.rs index cd7830e..5010d31 100644 --- a/src/bin/topola-egui/menu_bar.rs +++ b/src/bin/topola-egui/menu_bar.rs @@ -1,6 +1,7 @@ use std::{ path::Path, sync::{mpsc::Sender, Arc, Mutex}, + task::Poll, }; use topola::{ @@ -20,7 +21,7 @@ use topola::{ use crate::{ action::{Action, Switch, Trigger}, - activity::{ActivityContext, ActivityStatus, ActivityStepperWithStatus}, + activity::{ActivityContext, ActivityStepperWithStatus}, app::{execute, handle_file}, interaction::InteractionContext, overlay::Overlay, @@ -340,7 +341,7 @@ impl MenuBar { } } else if remove_bands.consume_key_triggered(ctx, ui) { if maybe_activity.as_mut().map_or(true, |activity| { - matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) + matches!(activity.maybe_status(), Some(Poll::Ready(..))) }) { if let (Some(invoker), Some(ref mut overlay)) = ( arc_mutex_maybe_invoker.lock().unwrap().as_mut(), @@ -357,7 +358,7 @@ impl MenuBar { } else if place_via.consume_key_enabled(ctx, ui, &mut self.is_placing_via) { } else if autoroute.consume_key_triggered(ctx, ui) { if maybe_activity.as_mut().map_or(true, |activity| { - matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) + matches!(activity.maybe_status(), Some(Poll::Ready(..))) }) { if let (Some(invoker), Some(ref mut overlay)) = ( arc_mutex_maybe_invoker.lock().unwrap().as_mut(), @@ -374,7 +375,7 @@ impl MenuBar { } } else if compare_detours.consume_key_triggered(ctx, ui) { if maybe_activity.as_mut().map_or(true, |activity| { - matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) + matches!(activity.maybe_status(), Some(Poll::Ready(..))) }) { if let (Some(invoker), Some(ref mut overlay)) = ( arc_mutex_maybe_invoker.lock().unwrap().as_mut(), @@ -391,7 +392,7 @@ impl MenuBar { } } else if measure_length.consume_key_triggered(ctx, ui) { if maybe_activity.as_mut().map_or(true, |activity| { - matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) + matches!(activity.maybe_status(), Some(Poll::Ready(..))) }) { if let (Some(invoker), Some(ref mut overlay)) = ( arc_mutex_maybe_invoker.lock().unwrap().as_mut(), diff --git a/src/bin/topola-egui/status_bar.rs b/src/bin/topola-egui/status_bar.rs index fd9a1ea..f6000bc 100644 --- a/src/bin/topola-egui/status_bar.rs +++ b/src/bin/topola-egui/status_bar.rs @@ -1,8 +1,5 @@ -use crate::{ - activity::{ActivityStatus, ActivityStepperWithStatus}, - translator::Translator, - viewport::Viewport, -}; +use crate::{activity::ActivityStepperWithStatus, translator::Translator, viewport::Viewport}; +use core::task::Poll; pub struct StatusBar {} @@ -22,10 +19,10 @@ impl StatusBar { let latest_pos = viewport.transform.inverse() * ctx.input(|i| i.pointer.latest_pos().unwrap_or_default()); - let mut message = String::from(""); + let mut message = String::new(); if let Some(activity) = maybe_activity { - if let Some(ActivityStatus::Finished(msg)) = activity.maybe_status() { + if let Some(Poll::Ready(msg)) = activity.maybe_status() { message = msg; } } diff --git a/src/router/astar.rs b/src/router/astar.rs index 3c49fbe..ce43637 100644 --- a/src/router/astar.rs +++ b/src/router/astar.rs @@ -15,7 +15,7 @@ use thiserror::Error; use std::cmp::Ordering; -use crate::stepper::Step; +use crate::stepper::{Step, StepError}; #[derive(Copy, Clone, Debug)] pub struct MinScored(pub K, pub T); @@ -203,8 +203,17 @@ where } } -impl> - Step, AstarError, (K, Vec, R)> for Astar +impl StepError for Astar +where + G: GraphBase, + G::NodeId: Eq + Hash, + for<'a> &'a G: IntoEdges + MakeEdgeRef, + K: Measure + Copy, +{ + type Error = AstarError; +} + +impl> Step> for Astar where G: GraphBase, G::NodeId: Eq + Hash, diff --git a/src/router/route.rs b/src/router/route.rs index 63285fb..04a7353 100644 --- a/src/router/route.rs +++ b/src/router/route.rs @@ -1,7 +1,5 @@ use crate::{ - drawing::{ - band::BandTermsegIndex, dot::FixedDotIndex, graph::PrimitiveIndex, rules::AccessRules, - }, + drawing::{dot::FixedDotIndex, graph::PrimitiveIndex, rules::AccessRules}, geometry::primitive::PrimitiveShape, router::{ astar::{Astar, AstarError, AstarStatus}, @@ -10,7 +8,7 @@ use crate::{ tracer::Tracer, Router, RouterAstarStrategy, RouterStatus, }, - stepper::Step, + stepper::{Step, StepError}, }; pub struct RouteStepper { @@ -73,9 +71,11 @@ impl RouteStepper { } } -impl<'a, R: AccessRules> Step, RouterStatus, AstarError, BandTermsegIndex> - for RouteStepper -{ +impl StepError for RouteStepper { + type Error = AstarError; +} + +impl<'a, R: AccessRules> Step, RouterStatus> for RouteStepper { fn step(&mut self, router: &mut Router) -> Result { let tracer = Tracer::new(router.layout_mut()); let target = self.astar.graph.destination(); diff --git a/src/stepper.rs b/src/stepper.rs index 385219d..0c4909a 100644 --- a/src/stepper.rs +++ b/src/stepper.rs @@ -1,7 +1,16 @@ -pub trait Step, E, O> { - fn step(&mut self, context: &mut C) -> Result; +use core::task::Poll; - fn finish(&mut self, context: &mut C) -> Result { +pub trait StepError { + type Error; +} + +pub trait Step: StepError { + fn step(&mut self, context: &mut C) -> Result; + + fn finish(&mut self, context: &mut C) -> Result + where + S: TryInto, + { loop { if let Ok(outcome) = self.step(context)?.try_into() { return Ok(outcome); @@ -10,8 +19,40 @@ pub trait Step, E, O> { } } -pub trait StepBack { - fn step_back(&mut self, context: &mut C) -> Result; +// Note that PollStep's `S` is usually not the same as Step's `S`. +pub trait PollStep: StepError { + fn poll_step(&mut self, context: &mut C) -> Poll>; + + fn finish(&mut self, context: &mut C) -> Result { + loop { + if let Poll::Ready(outcome) = self.poll_step(context) { + return outcome; + } + } + } +} + +/* +impl>> PollStep for Stepper { + #[inline] + fn poll_step(&mut self, context: &mut C) -> Poll> { + self.step(context)?.map(Ok) + } +} +*/ + +impl> Step> for Stepper { + #[inline] + fn step(&mut self, context: &mut C) -> Result, Self::Error> { + Ok(match self.poll_step(context) { + Poll::Pending => Poll::Pending, + Poll::Ready(x) => Poll::Ready(x?), + }) + } +} + +pub trait StepBack: StepError { + fn step_back(&mut self, context: &mut C) -> Result; } pub trait Abort {