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<String>
* `PollStep` provides `Step<_, Poll<_>>`

Fixes #78.
This commit is contained in:
Alain Emilia Anna Zscheile 2024-10-04 19:53:27 +02:00
parent f02b7be878
commit 4171443c45
12 changed files with 188 additions and 181 deletions

View File

@ -9,7 +9,7 @@ use crate::{
drawing::{band::BandTermsegIndex, graph::PrimitiveIndex}, drawing::{band::BandTermsegIndex, graph::PrimitiveIndex},
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{navmesh::Navmesh, route::RouteStepper, trace::TraceStepper, Router, RouterStatus}, router::{navmesh::Navmesh, route::RouteStepper, trace::TraceStepper, Router, RouterStatus},
stepper::Step, stepper::{Step, StepError},
}; };
use super::{ use super::{
@ -86,9 +86,11 @@ impl AutorouteExecutionStepper {
} }
} }
impl<M: AccessMesadata> Step<Autorouter<M>, AutorouteStatus, AutorouterError, ()> impl StepError for AutorouteExecutionStepper {
for AutorouteExecutionStepper type Error = AutorouterError;
{ }
impl<M: AccessMesadata> Step<Autorouter<M>, AutorouteStatus> for AutorouteExecutionStepper {
fn step(&mut self, autorouter: &mut Autorouter<M>) -> Result<AutorouteStatus, AutorouterError> { fn step(&mut self, autorouter: &mut Autorouter<M>) -> Result<AutorouteStatus, AutorouterError> {
let Some(curr_ratline) = self.curr_ratline else { let Some(curr_ratline) = self.curr_ratline else {
return Ok(AutorouteStatus::Finished); return Ok(AutorouteStatus::Finished);

View File

@ -11,7 +11,7 @@ use crate::{
geometry::{primitive::PrimitiveShape, shape::MeasureLength}, geometry::{primitive::PrimitiveShape, shape::MeasureLength},
graph::MakeRef, graph::MakeRef,
router::{navmesh::Navmesh, trace::TraceStepper}, router::{navmesh::Navmesh, trace::TraceStepper},
stepper::Step, stepper::{Step, StepError},
}; };
use super::{ 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 // 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. // aren't steppable either. It may be useful for debugging later on tho.
impl<M: AccessMesadata> Step<Autorouter<M>, CompareDetoursStatus, AutorouterError, (f64, f64)> impl<M: AccessMesadata> Step<Autorouter<M>, CompareDetoursStatus>
for CompareDetoursExecutionStepper for CompareDetoursExecutionStepper
{ {
fn step( fn step(

View File

@ -1,12 +1,17 @@
use core::task::Poll;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use serde::{Deserialize, Serialize}; 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::{ use super::{
autoroute::{AutorouteExecutionStepper, AutorouteStatus}, autoroute::{AutorouteExecutionStepper, AutorouteStatus},
compare_detours::{CompareDetoursExecutionStepper, CompareDetoursStatus}, compare_detours::{CompareDetoursExecutionStepper, CompareDetoursStatus},
invoker::{Invoker, InvokerError, InvokerStatus}, invoker::{Invoker, InvokerError},
measure_length::MeasureLengthExecutionStepper, measure_length::MeasureLengthExecutionStepper,
place_via::PlaceViaExecutionStepper, place_via::PlaceViaExecutionStepper,
remove_bands::RemoveBandsExecutionStepper, remove_bands::RemoveBandsExecutionStepper,
@ -38,30 +43,28 @@ impl ExecutionStepper {
fn step_catch_err<M: AccessMesadata>( fn step_catch_err<M: AccessMesadata>(
&mut self, &mut self,
invoker: &mut Invoker<M>, invoker: &mut Invoker<M>,
) -> Result<InvokerStatus, InvokerError> { ) -> Poll<Result<String, InvokerError>> {
Ok(match self { match self {
ExecutionStepper::Autoroute(autoroute) => { ExecutionStepper::Autoroute(autoroute) => {
match autoroute.step(&mut invoker.autorouter)? { match autoroute.step(&mut invoker.autorouter)? {
AutorouteStatus::Running => InvokerStatus::Running, AutorouteStatus::Running => Poll::Pending,
AutorouteStatus::Routed(..) => InvokerStatus::Running, AutorouteStatus::Routed(..) => Poll::Pending,
AutorouteStatus::Finished => { AutorouteStatus::Finished => Poll::Ready("finished autorouting".to_string()),
InvokerStatus::Finished("finished autorouting".to_string())
}
} }
} }
ExecutionStepper::PlaceVia(place_via) => { ExecutionStepper::PlaceVia(place_via) => {
place_via.doit(&mut invoker.autorouter)?; place_via.doit(&mut invoker.autorouter)?;
InvokerStatus::Finished("finished placing via".to_string()) Poll::Ready("finished placing via".to_string())
} }
ExecutionStepper::RemoveBands(remove_bands) => { ExecutionStepper::RemoveBands(remove_bands) => {
remove_bands.doit(&mut invoker.autorouter)?; remove_bands.doit(&mut invoker.autorouter)?;
InvokerStatus::Finished("finished removing bands".to_string()) Poll::Ready("finished removing bands".to_string())
} }
ExecutionStepper::CompareDetours(compare_detours) => { ExecutionStepper::CompareDetours(compare_detours) => {
match compare_detours.step(&mut invoker.autorouter)? { match compare_detours.step(&mut invoker.autorouter)? {
CompareDetoursStatus::Running => InvokerStatus::Running, CompareDetoursStatus::Running => Poll::Pending,
CompareDetoursStatus::Finished(total_length1, total_length2) => { CompareDetoursStatus::Finished(total_length1, total_length2) => {
InvokerStatus::Finished(format!( Poll::Ready(format!(
"total detour lengths are {} and {}", "total detour lengths are {} and {}",
total_length1, total_length2 total_length1, total_length2
)) ))
@ -70,27 +73,30 @@ impl ExecutionStepper {
} }
ExecutionStepper::MeasureLength(measure_length) => { ExecutionStepper::MeasureLength(measure_length) => {
let length = measure_length.doit(&mut invoker.autorouter)?; 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<M: AccessMesadata> Step<Invoker<M>, InvokerStatus, InvokerError, ()> for ExecutionStepper { impl StepError for ExecutionStepper {
fn step(&mut self, invoker: &mut Invoker<M>) -> Result<InvokerStatus, InvokerError> { type Error = InvokerError;
match self.step_catch_err(invoker) { }
Ok(InvokerStatus::Running) => Ok(InvokerStatus::Running),
Ok(InvokerStatus::Finished(msg)) => { impl<M: AccessMesadata> PollStep<Invoker<M>, String> for ExecutionStepper {
fn poll_step(&mut self, invoker: &mut Invoker<M>) -> Poll<Result<String, InvokerError>> {
self.step_catch_err(invoker).map(|x| match x {
Ok(msg) => {
if let Some(command) = invoker.ongoing_command.take() { if let Some(command) = invoker.ongoing_command.take() {
invoker.history.do_(command); invoker.history.do_(command);
} }
Ok(msg)
Ok(InvokerStatus::Finished(msg))
} }
Err(err) => { Err(err) => {
invoker.ongoing_command = None; invoker.ongoing_command = None;
Err(err) Err(err)
} }
} })
} }
} }

View File

@ -1,8 +1,7 @@
//! Manages the execution of routing commands within the autorouting system. //! Manages the execution of routing commands within the autorouting system.
use std::cmp::Ordering;
use contracts_try::debug_requires; use contracts_try::debug_requires;
use core::cmp::Ordering;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use thiserror::Error; use thiserror::Error;
@ -11,7 +10,7 @@ use crate::{
drawing::graph::PrimitiveIndex, drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{navmesh::Navmesh, trace::TraceStepper}, router::{navmesh::Navmesh, trace::TraceStepper},
stepper::Step, stepper::{PollStep, Step},
}; };
use super::{ use super::{
@ -59,16 +58,6 @@ pub enum InvokerError {
Autorouter(#[from] AutorouterError), 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<M: AccessMesadata> { pub struct Invoker<M: AccessMesadata> {
pub(super) autorouter: Autorouter<M>, pub(super) autorouter: Autorouter<M>,
pub(super) history: History, pub(super) history: History,
@ -93,20 +82,11 @@ impl<M: AccessMesadata> Invoker<M> {
} }
//#[debug_requires(self.ongoing_command.is_none())] //#[debug_requires(self.ongoing_command.is_none())]
pub fn execute(&mut self, command: Command) -> Result<(), InvokerError> { pub fn execute(&mut self, command: Command) -> Result<String, InvokerError> {
let mut execute = self.execute_stepper(command)?; let mut execute = self.execute_stepper(command)?;
let res = PollStep::finish(&mut execute, self)?;
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()); self.history.set_undone(std::iter::empty());
return Ok(()); Ok(res)
}
}
} }
#[debug_requires(self.ongoing_command.is_none())] #[debug_requires(self.ongoing_command.is_none())]
@ -178,17 +158,8 @@ impl<M: AccessMesadata> Invoker<M> {
pub fn redo(&mut self) -> Result<(), InvokerError> { pub fn redo(&mut self) -> Result<(), InvokerError> {
let command = self.history.last_undone()?.clone(); let command = self.history.last_undone()?.clone();
let mut execute = self.execute_stepper(command)?; let mut execute = self.execute_stepper(command)?;
PollStep::finish(&mut execute, self)?;
loop { Ok(self.history.redo()?)
let status = match execute.step(self) {
Ok(status) => status,
Err(err) => return Err(err),
};
if let InvokerStatus::Finished(..) = status {
return Ok(self.history.redo()?);
}
}
} }
#[debug_requires(self.ongoing_command.is_none())] #[debug_requires(self.ongoing_command.is_none())]

View File

@ -1,63 +1,25 @@
use core::task::Poll;
use thiserror::Error; use thiserror::Error;
use topola::{ use topola::{
autorouter::{ autorouter::{
execution::ExecutionStepper, execution::ExecutionStepper,
invoker::{ invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles, Invoker, InvokerError},
GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles, Invoker, InvokerError,
InvokerStatus,
},
}, },
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
drawing::graph::PrimitiveIndex, drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{navmesh::Navmesh, trace::TraceStepper}, router::{navmesh::Navmesh, trace::TraceStepper},
specctra::mesadata::SpecctraMesadata, specctra::mesadata::SpecctraMesadata,
stepper::{Abort, Step}, stepper::{Abort, PollStep, StepError},
}; };
use crate::interaction::{ use crate::interaction::{InteractionContext, InteractionError, InteractionStepper};
InteractionContext, InteractionError, InteractionStatus, InteractionStepper,
};
pub struct ActivityContext<'a> { pub struct ActivityContext<'a> {
pub interaction: InteractionContext, pub interaction: InteractionContext,
pub invoker: &'a mut Invoker<SpecctraMesadata>, pub invoker: &'a mut Invoker<SpecctraMesadata>,
} }
#[derive(Debug, Clone)]
pub enum ActivityStatus {
Running,
Finished(String),
}
impl From<InteractionStatus> for ActivityStatus {
fn from(status: InteractionStatus) -> Self {
match status {
InteractionStatus::Running => ActivityStatus::Running,
InteractionStatus::Finished(msg) => ActivityStatus::Finished(msg),
}
}
}
impl From<InvokerStatus> 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)] #[derive(Error, Debug, Clone)]
pub enum ActivityError { pub enum ActivityError {
#[error(transparent)] #[error(transparent)]
@ -71,13 +33,22 @@ pub enum ActivityStepper {
Execution(ExecutionStepper), Execution(ExecutionStepper),
} }
impl Step<ActivityContext<'_>, ActivityStatus, ActivityError, ()> for ActivityStepper { impl StepError for ActivityStepper {
fn step(&mut self, context: &mut ActivityContext) -> Result<ActivityStatus, ActivityError> { type Error = ActivityError;
match self {
ActivityStepper::Interaction(interaction) => {
Ok(interaction.step(&mut context.interaction)?.into())
} }
ActivityStepper::Execution(execution) => Ok(execution.step(context.invoker)?.into()),
impl PollStep<ActivityContext<'_>, String> for ActivityStepper {
fn poll_step(
&mut self,
context: &mut ActivityContext<'_>,
) -> Poll<Result<String, ActivityError>> {
match self {
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<ActivityContext<'_>> for ActivityStepper {
fn abort(&mut self, context: &mut ActivityContext) { fn abort(&mut self, context: &mut ActivityContext) {
match self { match self {
ActivityStepper::Interaction(interaction) => { 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 { pub struct ActivityStepperWithStatus {
activity: ActivityStepper, activity: ActivityStepper,
maybe_status: Option<ActivityStatus>, maybe_status: Option<Poll<String>>,
} }
impl ActivityStepperWithStatus { impl ActivityStepperWithStatus {
@ -146,22 +119,33 @@ impl ActivityStepperWithStatus {
} }
} }
pub fn maybe_status(&self) -> Option<ActivityStatus> { pub fn maybe_status(&self) -> Option<Poll<String>> {
self.maybe_status.clone() self.maybe_status.clone()
} }
} }
impl Step<ActivityContext<'_>, ActivityStatus, ActivityError, ()> for ActivityStepperWithStatus { impl StepError for ActivityStepperWithStatus {
fn step(&mut self, context: &mut ActivityContext) -> Result<ActivityStatus, ActivityError> { type Error = ActivityError;
let status = self.activity.step(context)?; }
self.maybe_status = Some(status.clone());
Ok(status.into()) impl PollStep<ActivityContext<'_>, String> for ActivityStepperWithStatus {
fn poll_step(
&mut self,
context: &mut ActivityContext<'_>,
) -> Poll<Result<String, ActivityError>> {
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<ActivityContext<'_>> for ActivityStepperWithStatus { impl Abort<ActivityContext<'_>> for ActivityStepperWithStatus {
fn abort(&mut self, context: &mut ActivityContext) { fn abort(&mut self, context: &mut ActivityContext<'_>) {
self.maybe_status = Some(ActivityStatus::Finished(String::from("aborted"))); self.maybe_status = Some(Poll::Ready("aborted".to_string()));
self.activity.abort(context); self.activity.abort(context);
} }
} }

View File

@ -6,6 +6,7 @@ use std::{
mpsc::{channel, Receiver, Sender}, mpsc::{channel, Receiver, Sender},
Arc, Mutex, Arc, Mutex,
}, },
task::Poll,
}; };
use unic_langid::{langid, LanguageIdentifier}; use unic_langid::{langid, LanguageIdentifier};
@ -15,11 +16,11 @@ use topola::{
design::{LoadingError as SpecctraLoadingError, SpecctraDesign}, design::{LoadingError as SpecctraLoadingError, SpecctraDesign},
mesadata::SpecctraMesadata, mesadata::SpecctraMesadata,
}, },
stepper::Step, stepper::PollStep,
}; };
use crate::{ use crate::{
activity::{ActivityContext, ActivityStatus, ActivityStepperWithStatus}, activity::{ActivityContext, ActivityStepperWithStatus},
config::Config, config::Config,
error_dialog::ErrorDialog, error_dialog::ErrorDialog,
interaction::InteractionContext, interaction::InteractionContext,
@ -171,15 +172,17 @@ impl App {
} }
if let Some(ref mut activity) = self.maybe_activity { if let Some(ref mut activity) = self.maybe_activity {
return match activity.step(&mut ActivityContext { return match activity.poll_step(&mut ActivityContext {
interaction: InteractionContext {}, interaction: InteractionContext {},
invoker, invoker,
}) { }) {
Ok(ActivityStatus::Running) => true, Poll::Pending => true,
Ok(ActivityStatus::Finished(..)) => false, Poll::Ready(res) => {
Err(err) => { if let Err(err) = res {
self.error_dialog self.error_dialog
.push_error("tr-module-invoker", format!("{}", err)); .push_error("tr-module-invoker", format!("{}", err));
}
self.maybe_activity = None;
false false
} }
}; };

View File

@ -1,10 +1,11 @@
use core::task::Poll;
use thiserror::Error; use thiserror::Error;
use topola::{ use topola::{
autorouter::invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles}, autorouter::invoker::{GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles},
drawing::graph::PrimitiveIndex, drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{navmesh::Navmesh, trace::TraceStepper}, router::{navmesh::Navmesh, trace::TraceStepper},
stepper::{Abort, Step}, stepper::{Abort, PollStep, StepError},
}; };
use crate::activity::ActivityStepperWithStatus; use crate::activity::ActivityStepperWithStatus;
@ -15,22 +16,6 @@ pub struct InteractionContext {
// (we will need an additional struct to hold a reference to a `Board<...>`) // (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)] #[derive(Error, Debug, Clone)]
pub enum InteractionError { pub enum InteractionError {
#[error("nothing to interact with")] #[error("nothing to interact with")]
@ -44,12 +29,16 @@ pub enum InteractionStepper {
// - interactively moving a footprint. // - interactively moving a footprint.
} }
impl Step<InteractionContext, InteractionStatus, InteractionError, ()> for InteractionStepper { impl StepError for InteractionStepper {
fn step( type Error = InteractionError;
}
impl PollStep<InteractionContext, String> for InteractionStepper {
fn poll_step(
&mut self, &mut self,
invoker: &mut InteractionContext, invoker: &mut InteractionContext,
) -> Result<InteractionStatus, InteractionError> { ) -> Poll<Result<String, InteractionError>> {
Ok(InteractionStatus::Finished(String::from(""))) Poll::Ready(Ok(String::new()))
} }
} }

View File

@ -1,6 +1,7 @@
use std::{ use std::{
path::Path, path::Path,
sync::{mpsc::Sender, Arc, Mutex}, sync::{mpsc::Sender, Arc, Mutex},
task::Poll,
}; };
use topola::{ use topola::{
@ -20,7 +21,7 @@ use topola::{
use crate::{ use crate::{
action::{Action, Switch, Trigger}, action::{Action, Switch, Trigger},
activity::{ActivityContext, ActivityStatus, ActivityStepperWithStatus}, activity::{ActivityContext, ActivityStepperWithStatus},
app::{execute, handle_file}, app::{execute, handle_file},
interaction::InteractionContext, interaction::InteractionContext,
overlay::Overlay, overlay::Overlay,
@ -340,7 +341,7 @@ impl MenuBar {
} }
} else if remove_bands.consume_key_triggered(ctx, ui) { } else if remove_bands.consume_key_triggered(ctx, ui) {
if maybe_activity.as_mut().map_or(true, |activity| { 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)) = ( if let (Some(invoker), Some(ref mut overlay)) = (
arc_mutex_maybe_invoker.lock().unwrap().as_mut(), 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 place_via.consume_key_enabled(ctx, ui, &mut self.is_placing_via) {
} else if autoroute.consume_key_triggered(ctx, ui) { } else if autoroute.consume_key_triggered(ctx, ui) {
if maybe_activity.as_mut().map_or(true, |activity| { 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)) = ( if let (Some(invoker), Some(ref mut overlay)) = (
arc_mutex_maybe_invoker.lock().unwrap().as_mut(), arc_mutex_maybe_invoker.lock().unwrap().as_mut(),
@ -374,7 +375,7 @@ impl MenuBar {
} }
} else if compare_detours.consume_key_triggered(ctx, ui) { } else if compare_detours.consume_key_triggered(ctx, ui) {
if maybe_activity.as_mut().map_or(true, |activity| { 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)) = ( if let (Some(invoker), Some(ref mut overlay)) = (
arc_mutex_maybe_invoker.lock().unwrap().as_mut(), arc_mutex_maybe_invoker.lock().unwrap().as_mut(),
@ -391,7 +392,7 @@ impl MenuBar {
} }
} else if measure_length.consume_key_triggered(ctx, ui) { } else if measure_length.consume_key_triggered(ctx, ui) {
if maybe_activity.as_mut().map_or(true, |activity| { 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)) = ( if let (Some(invoker), Some(ref mut overlay)) = (
arc_mutex_maybe_invoker.lock().unwrap().as_mut(), arc_mutex_maybe_invoker.lock().unwrap().as_mut(),

View File

@ -1,8 +1,5 @@
use crate::{ use crate::{activity::ActivityStepperWithStatus, translator::Translator, viewport::Viewport};
activity::{ActivityStatus, ActivityStepperWithStatus}, use core::task::Poll;
translator::Translator,
viewport::Viewport,
};
pub struct StatusBar {} pub struct StatusBar {}
@ -22,10 +19,10 @@ impl StatusBar {
let latest_pos = viewport.transform.inverse() let latest_pos = viewport.transform.inverse()
* ctx.input(|i| i.pointer.latest_pos().unwrap_or_default()); * 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(activity) = maybe_activity {
if let Some(ActivityStatus::Finished(msg)) = activity.maybe_status() { if let Some(Poll::Ready(msg)) = activity.maybe_status() {
message = msg; message = msg;
} }
} }

View File

@ -15,7 +15,7 @@ use thiserror::Error;
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::stepper::Step; use crate::stepper::{Step, StepError};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct MinScored<K, T>(pub K, pub T); pub struct MinScored<K, T>(pub K, pub T);
@ -203,8 +203,17 @@ where
} }
} }
impl<G, K, R, S: AstarStrategy<G, K, R>> impl<G, K> StepError for Astar<G, K>
Step<S, AstarStatus<G, K, R>, AstarError, (K, Vec<G::NodeId>, R)> for Astar<G, K> where
G: GraphBase,
G::NodeId: Eq + Hash,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy,
{
type Error = AstarError;
}
impl<G, K, R, S: AstarStrategy<G, K, R>> Step<S, AstarStatus<G, K, R>> for Astar<G, K>
where where
G: GraphBase, G: GraphBase,
G::NodeId: Eq + Hash, G::NodeId: Eq + Hash,

View File

@ -1,7 +1,5 @@
use crate::{ use crate::{
drawing::{ drawing::{dot::FixedDotIndex, graph::PrimitiveIndex, rules::AccessRules},
band::BandTermsegIndex, dot::FixedDotIndex, graph::PrimitiveIndex, rules::AccessRules,
},
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{ router::{
astar::{Astar, AstarError, AstarStatus}, astar::{Astar, AstarError, AstarStatus},
@ -10,7 +8,7 @@ use crate::{
tracer::Tracer, tracer::Tracer,
Router, RouterAstarStrategy, RouterStatus, Router, RouterAstarStrategy, RouterStatus,
}, },
stepper::Step, stepper::{Step, StepError},
}; };
pub struct RouteStepper { pub struct RouteStepper {
@ -73,9 +71,11 @@ impl RouteStepper {
} }
} }
impl<'a, R: AccessRules> Step<Router<'a, R>, RouterStatus, AstarError, BandTermsegIndex> impl StepError for RouteStepper {
for RouteStepper type Error = AstarError;
{ }
impl<'a, R: AccessRules> Step<Router<'a, R>, RouterStatus> for RouteStepper {
fn step(&mut self, router: &mut Router<R>) -> Result<RouterStatus, AstarError> { fn step(&mut self, router: &mut Router<R>) -> Result<RouterStatus, AstarError> {
let tracer = Tracer::new(router.layout_mut()); let tracer = Tracer::new(router.layout_mut());
let target = self.astar.graph.destination(); let target = self.astar.graph.destination();

View File

@ -1,7 +1,16 @@
pub trait Step<C, S: TryInto<O>, E, O> { use core::task::Poll;
fn step(&mut self, context: &mut C) -> Result<S, E>;
fn finish(&mut self, context: &mut C) -> Result<O, E> { pub trait StepError {
type Error;
}
pub trait Step<C, S>: StepError {
fn step(&mut self, context: &mut C) -> Result<S, Self::Error>;
fn finish<O>(&mut self, context: &mut C) -> Result<O, Self::Error>
where
S: TryInto<O>,
{
loop { loop {
if let Ok(outcome) = self.step(context)?.try_into() { if let Ok(outcome) = self.step(context)?.try_into() {
return Ok(outcome); return Ok(outcome);
@ -10,8 +19,40 @@ pub trait Step<C, S: TryInto<O>, E, O> {
} }
} }
pub trait StepBack<C, S, E> { // Note that PollStep's `S` is usually not the same as Step's `S`.
fn step_back(&mut self, context: &mut C) -> Result<S, E>; pub trait PollStep<C, S>: StepError {
fn poll_step(&mut self, context: &mut C) -> Poll<Result<S, Self::Error>>;
fn finish(&mut self, context: &mut C) -> Result<S, Self::Error> {
loop {
if let Poll::Ready(outcome) = self.poll_step(context) {
return outcome;
}
}
}
}
/*
impl<C, S, Stepper: Step<C, Poll<S>>> PollStep<C, S> for Stepper {
#[inline]
fn poll_step(&mut self, context: &mut C) -> Poll<Result<S, Self::Error>> {
self.step(context)?.map(Ok)
}
}
*/
impl<C, S, Stepper: PollStep<C, S>> Step<C, Poll<S>> for Stepper {
#[inline]
fn step(&mut self, context: &mut C) -> Result<Poll<S>, Self::Error> {
Ok(match self.poll_step(context) {
Poll::Pending => Poll::Pending,
Poll::Ready(x) => Poll::Ready(x?),
})
}
}
pub trait StepBack<C, S>: StepError {
fn step_back(&mut self, context: &mut C) -> Result<S, Self::Error>;
} }
pub trait Abort<C> { pub trait Abort<C> {