refactor: make `Step::step(...)` return `ControlFlow<...>`

This is an iteration on what was suggested in
https://codeberg.org/topola/topola/pulls/79 .

This removes the need to create a new status type for steppers that
have only one intermediate state, and removes unidiomatic `TryInto<...>`
usage to determine if a stepper has finished.
This commit is contained in:
Mikolaj Wielgus 2024-10-12 05:28:13 +02:00
parent a9e42eef17
commit 23d1eaa722
14 changed files with 129 additions and 258 deletions

View File

@ -1,15 +1,15 @@
//! Manages autorouting of ratlines in a layout, tracking status and processed //! Manages autorouting of ratlines in a layout, tracking status and processed
//! routing steps. //! routing steps.
use std::ops::ControlFlow;
use petgraph::graph::EdgeIndex; use petgraph::graph::EdgeIndex;
use crate::{ use crate::{
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
drawing::{band::BandTermsegIndex, graph::PrimitiveIndex}, drawing::{band::BandTermsegIndex, graph::PrimitiveIndex},
geometry::primitive::PrimitiveShape, geometry::primitive::PrimitiveShape,
router::{ router::{navcord::NavcordStepper, navmesh::Navmesh, route::RouteStepper, Router},
navcord::NavcordStepper, navmesh::Navmesh, route::RouteStepper, Router, RouterStatus,
},
stepper::Step, stepper::Step,
}; };
@ -19,29 +19,11 @@ use super::{
}; };
/// Represents the current status of the autoroute operation. /// Represents the current status of the autoroute operation.
pub enum AutorouteStatus { pub enum AutorouteContinueStatus {
/// The autoroute is currently running and in progress. /// The autoroute is currently running and in progress.
Running, Running,
/// A specific segment has been successfully routed. /// A specific segment has been successfully routed.
Routed(BandTermsegIndex), Routed(BandTermsegIndex),
/// The autoroute process has completed successfully.
Finished,
}
impl TryInto<()> for AutorouteStatus {
type Error = ();
/// Attempts to get the [`Result`] from the [`AutorouteStatus`].
///
/// This implementation allows transitioning from [`AutorouteStatus`] to a
/// [`Result`]. It returns success for the [`AutorouteStatus::Finished`] state
/// or an error for [`AutorouteStatus::Running`] or [`AutorouteStatus::Routed`] states.
fn try_into(self) -> Result<(), ()> {
match self {
AutorouteStatus::Running => Err(()),
AutorouteStatus::Routed(..) => Err(()),
AutorouteStatus::Finished => Ok(()),
}
}
} }
/// Manages the autorouting process across multiple ratlines. /// Manages the autorouting process across multiple ratlines.
@ -87,17 +69,22 @@ impl AutorouteExecutionStepper {
} }
} }
impl<M: AccessMesadata> Step<Autorouter<M>, AutorouteStatus, ()> for AutorouteExecutionStepper { impl<M: AccessMesadata> Step<Autorouter<M>, (), AutorouteContinueStatus>
for AutorouteExecutionStepper
{
type Error = AutorouterError; type Error = AutorouterError;
fn step(&mut self, autorouter: &mut Autorouter<M>) -> Result<AutorouteStatus, AutorouterError> { fn step(
&mut self,
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<(), AutorouteContinueStatus>, AutorouterError> {
let Some(curr_ratline) = self.curr_ratline else { let Some(curr_ratline) = self.curr_ratline else {
return Ok(AutorouteStatus::Finished); return Ok(ControlFlow::Break(()));
}; };
let Some(ref mut route) = self.route else { let Some(ref mut route) = self.route else {
// Shouldn't happen. // Shouldn't happen.
return Ok(AutorouteStatus::Finished); return Ok(ControlFlow::Break(()));
}; };
let (source, target) = autorouter.ratline_endpoints(curr_ratline); let (source, target) = autorouter.ratline_endpoints(curr_ratline);
@ -106,8 +93,8 @@ impl<M: AccessMesadata> Step<Autorouter<M>, AutorouteStatus, ()> for AutorouteEx
let mut router = let mut router =
Router::new(autorouter.board.layout_mut(), self.options.router_options); Router::new(autorouter.board.layout_mut(), self.options.router_options);
let RouterStatus::Finished(band_termseg) = route.step(&mut router)? else { let ControlFlow::Break(band_termseg) = route.step(&mut router)? else {
return Ok(AutorouteStatus::Running); return Ok(ControlFlow::Continue(AutorouteContinueStatus::Running));
}; };
band_termseg band_termseg
}; };
@ -139,7 +126,9 @@ impl<M: AccessMesadata> Step<Autorouter<M>, AutorouteStatus, ()> for AutorouteEx
//return Ok(AutorouteStatus::Finished); //return Ok(AutorouteStatus::Finished);
} }
Ok(AutorouteStatus::Routed(band_termseg)) Ok(ControlFlow::Continue(AutorouteContinueStatus::Routed(
band_termseg,
)))
} }
} }

View File

@ -1,6 +1,8 @@
//! Manages the comparison of detours between two ratlines, tracking their //! Manages the comparison of detours between two ratlines, tracking their
//! routing statuses and recording their lengths. //! routing statuses and recording their lengths.
use std::ops::ControlFlow;
use petgraph::graph::EdgeIndex; use petgraph::graph::EdgeIndex;
use crate::{ use crate::{
@ -13,28 +15,11 @@ use crate::{
}; };
use super::{ use super::{
autoroute::{AutorouteExecutionStepper, AutorouteStatus}, autoroute::{AutorouteContinueStatus, AutorouteExecutionStepper},
invoker::{GetGhosts, GetMaybeNavcord, GetMaybeNavmesh, GetObstacles}, invoker::{GetGhosts, GetMaybeNavcord, GetMaybeNavmesh, GetObstacles},
Autorouter, AutorouterError, AutorouterOptions, Autorouter, AutorouterError, AutorouterOptions,
}; };
pub enum CompareDetoursStatus {
Running,
Finished(f64, f64),
}
impl TryInto<(f64, f64)> for CompareDetoursStatus {
type Error = ();
fn try_into(self) -> Result<(f64, f64), ()> {
match self {
CompareDetoursStatus::Running => Err(()),
CompareDetoursStatus::Finished(total_length1, total_length2) => {
Ok((total_length1, total_length2))
}
}
}
}
pub struct CompareDetoursExecutionStepper { pub struct CompareDetoursExecutionStepper {
autoroute: AutorouteExecutionStepper, autoroute: AutorouteExecutionStepper,
next_autoroute: Option<AutorouteExecutionStepper>, next_autoroute: Option<AutorouteExecutionStepper>,
@ -66,25 +51,22 @@ impl CompareDetoursExecutionStepper {
// 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, (f64, f64)> impl<M: AccessMesadata> Step<Autorouter<M>, (f64, f64)> for CompareDetoursExecutionStepper {
for CompareDetoursExecutionStepper
{
type Error = AutorouterError; type Error = AutorouterError;
fn step( fn step(
&mut self, &mut self,
autorouter: &mut Autorouter<M>, autorouter: &mut Autorouter<M>,
) -> Result<CompareDetoursStatus, AutorouterError> { ) -> Result<ControlFlow<(f64, f64)>, AutorouterError> {
if self.done { if self.done {
return Ok(CompareDetoursStatus::Finished( return Ok(ControlFlow::Break((self.total_length1, self.total_length2)));
self.total_length1,
self.total_length2,
));
} }
match self.autoroute.step(autorouter)? { match self.autoroute.step(autorouter)? {
AutorouteStatus::Running => Ok(CompareDetoursStatus::Running), ControlFlow::Continue(AutorouteContinueStatus::Running) => {
AutorouteStatus::Routed(band_termseg) => { Ok(ControlFlow::Continue(()))
}
ControlFlow::Continue(AutorouteContinueStatus::Routed(band_termseg)) => {
let length = band_termseg let length = band_termseg
.ref_(autorouter.board.layout().drawing()) .ref_(autorouter.board.layout().drawing())
.length(); .length();
@ -95,22 +77,19 @@ impl<M: AccessMesadata> Step<Autorouter<M>, CompareDetoursStatus, (f64, f64)>
self.total_length2 += length; self.total_length2 += length;
} }
Ok(CompareDetoursStatus::Running) Ok(ControlFlow::Continue(()))
} }
AutorouteStatus::Finished => { ControlFlow::Break(()) => {
if let Some(next_autoroute) = self.next_autoroute.take() { if let Some(next_autoroute) = self.next_autoroute.take() {
autorouter.undo_autoroute_ratlines(vec![self.ratline1, self.ratline2])?; autorouter.undo_autoroute_ratlines(vec![self.ratline1, self.ratline2])?;
self.autoroute = next_autoroute; self.autoroute = next_autoroute;
Ok(CompareDetoursStatus::Running) Ok(ControlFlow::Continue(()))
} else { } else {
self.done = true; self.done = true;
autorouter.undo_autoroute_ratlines(vec![self.ratline2, self.ratline1])?; autorouter.undo_autoroute_ratlines(vec![self.ratline2, self.ratline1])?;
Ok(CompareDetoursStatus::Finished( Ok(ControlFlow::Break((self.total_length1, self.total_length2)))
self.total_length1,
self.total_length2,
))
} }
} }
} }

View File

@ -1,12 +1,14 @@
use std::ops::ControlFlow;
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::Step};
use super::{ use super::{
autoroute::{AutorouteExecutionStepper, AutorouteStatus}, autoroute::AutorouteExecutionStepper,
compare_detours::{CompareDetoursExecutionStepper, CompareDetoursStatus}, compare_detours::CompareDetoursExecutionStepper,
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,28 +40,25 @@ impl ExecutionStepper {
fn step_catch_err<M: AccessMesadata>( fn step_catch_err<M: AccessMesadata>(
&mut self, &mut self,
autorouter: &mut Autorouter<M>, autorouter: &mut Autorouter<M>,
) -> Result<InvokerStatus, InvokerError> { ) -> Result<ControlFlow<String>, InvokerError> {
Ok(match self { Ok(match self {
ExecutionStepper::Autoroute(autoroute) => match autoroute.step(autorouter)? { ExecutionStepper::Autoroute(autoroute) => match autoroute.step(autorouter)? {
AutorouteStatus::Running => InvokerStatus::Running, ControlFlow::Continue(..) => ControlFlow::Continue(()),
AutorouteStatus::Routed(..) => InvokerStatus::Running, ControlFlow::Break(..) => ControlFlow::Break("finished autorouting".to_string()),
AutorouteStatus::Finished => {
InvokerStatus::Finished("finished autorouting".to_string())
}
}, },
ExecutionStepper::PlaceVia(place_via) => { ExecutionStepper::PlaceVia(place_via) => {
place_via.doit(autorouter)?; place_via.doit(autorouter)?;
InvokerStatus::Finished("finished placing via".to_string()) ControlFlow::Break("finished placing via".to_string())
} }
ExecutionStepper::RemoveBands(remove_bands) => { ExecutionStepper::RemoveBands(remove_bands) => {
remove_bands.doit(autorouter)?; remove_bands.doit(autorouter)?;
InvokerStatus::Finished("finished removing bands".to_string()) ControlFlow::Break("finished removing bands".to_string())
} }
ExecutionStepper::CompareDetours(compare_detours) => { ExecutionStepper::CompareDetours(compare_detours) => {
match compare_detours.step(autorouter)? { match compare_detours.step(autorouter)? {
CompareDetoursStatus::Running => InvokerStatus::Running, ControlFlow::Continue(()) => ControlFlow::Continue(()),
CompareDetoursStatus::Finished(total_length1, total_length2) => { ControlFlow::Break((total_length1, total_length2)) => {
InvokerStatus::Finished(format!( ControlFlow::Break(format!(
"total detour lengths are {} and {}", "total detour lengths are {} and {}",
total_length1, total_length2 total_length1, total_length2
)) ))
@ -68,24 +67,24 @@ impl ExecutionStepper {
} }
ExecutionStepper::MeasureLength(measure_length) => { ExecutionStepper::MeasureLength(measure_length) => {
let length = measure_length.doit(autorouter)?; let length = measure_length.doit(autorouter)?;
InvokerStatus::Finished(format!("Total length of selected bands: {}", length)) ControlFlow::Break(format!("Total length of selected bands: {}", length))
} }
}) })
} }
} }
impl<M: AccessMesadata> Step<Invoker<M>, InvokerStatus, ()> for ExecutionStepper { impl<M: AccessMesadata> Step<Invoker<M>, String> for ExecutionStepper {
type Error = InvokerError; type Error = InvokerError;
fn step(&mut self, invoker: &mut Invoker<M>) -> Result<InvokerStatus, InvokerError> { fn step(&mut self, invoker: &mut Invoker<M>) -> Result<ControlFlow<String>, InvokerError> {
match self.step_catch_err(&mut invoker.autorouter) { match self.step_catch_err(&mut invoker.autorouter) {
Ok(InvokerStatus::Running) => Ok(InvokerStatus::Running), Ok(ControlFlow::Continue(())) => Ok(ControlFlow::Continue(())),
Ok(InvokerStatus::Finished(msg)) => { Ok(ControlFlow::Break(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(InvokerStatus::Finished(msg)) Ok(ControlFlow::Break(msg))
} }
Err(err) => { Err(err) => {
invoker.ongoing_command = None; invoker.ongoing_command = None;

View File

@ -1,6 +1,6 @@
//! 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 std::{cmp::Ordering, ops::ControlFlow};
use contracts_try::debug_requires; use contracts_try::debug_requires;
use derive_getters::{Dissolve, Getters}; use derive_getters::{Dissolve, Getters};
@ -46,12 +46,6 @@ pub trait GetObstacles {
fn obstacles(&self) -> &[PrimitiveIndex]; fn obstacles(&self) -> &[PrimitiveIndex];
} }
#[derive(Debug, Clone)]
pub enum InvokerStatus {
Running,
Finished(String),
}
#[derive(Error, Debug, Clone)] #[derive(Error, Debug, Clone)]
pub enum InvokerError { pub enum InvokerError {
#[error(transparent)] #[error(transparent)]
@ -60,16 +54,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(()),
}
}
}
#[derive(Getters, Dissolve)] #[derive(Getters, Dissolve)]
pub struct Invoker<M: AccessMesadata> { pub struct Invoker<M: AccessMesadata> {
pub(super) autorouter: Autorouter<M>, pub(super) autorouter: Autorouter<M>,
@ -95,12 +79,9 @@ impl<M: AccessMesadata> Invoker<M> {
let mut execute = self.execute_stepper(command)?; let mut execute = self.execute_stepper(command)?;
loop { loop {
let status = match execute.step(self) { let status = execute.step(self)?;
Ok(status) => status,
Err(err) => return Err(err),
};
if let InvokerStatus::Finished(..) = status { if let ControlFlow::Break(..) = status {
self.history.set_undone(std::iter::empty()); self.history.set_undone(std::iter::empty());
return Ok(()); return Ok(());
} }
@ -183,7 +164,7 @@ impl<M: AccessMesadata> Invoker<M> {
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
if let InvokerStatus::Finished(..) = status { if let ControlFlow::Break(..) = status {
return Ok(self.history.redo()?); return Ok(self.history.redo()?);
} }
} }

View File

@ -1,10 +1,11 @@
use std::ops::ControlFlow;
use thiserror::Error; use thiserror::Error;
use topola::{ use topola::{
autorouter::{ autorouter::{
execution::ExecutionStepper, execution::ExecutionStepper,
invoker::{ invoker::{
GetGhosts, GetMaybeNavcord, GetMaybeNavmesh, GetObstacles, Invoker, InvokerError, GetGhosts, GetMaybeNavcord, GetMaybeNavmesh, GetObstacles, Invoker, InvokerError,
InvokerStatus,
}, },
}, },
board::mesadata::AccessMesadata, board::mesadata::AccessMesadata,
@ -14,49 +15,13 @@ use topola::{
stepper::{Abort, Step}, stepper::{Abort, Step},
}; };
use crate::interaction::{ use crate::interaction::{InteractionContext, InteractionError, InteractionStepper};
InteractionContext, InteractionError, InteractionStatus, InteractionStepper,
};
pub struct ActivityContext<'a, M: AccessMesadata> { pub struct ActivityContext<'a, M: AccessMesadata> {
pub interaction: InteractionContext, pub interaction: InteractionContext,
pub invoker: &'a mut Invoker<M>, pub invoker: &'a mut Invoker<M>,
} }
#[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)]
@ -70,15 +35,18 @@ pub enum ActivityStepper {
Execution(ExecutionStepper), Execution(ExecutionStepper),
} }
impl<M: AccessMesadata> Step<ActivityContext<'_, M>, ActivityStatus, ()> for ActivityStepper { impl<M: AccessMesadata> Step<ActivityContext<'_, M>, String> for ActivityStepper {
type Error = ActivityError; type Error = ActivityError;
fn step(&mut self, context: &mut ActivityContext<M>) -> Result<ActivityStatus, ActivityError> { fn step(
&mut self,
context: &mut ActivityContext<M>,
) -> Result<ControlFlow<String>, ActivityError> {
match self { match self {
ActivityStepper::Interaction(interaction) => { ActivityStepper::Interaction(interaction) => {
Ok(interaction.step(&mut context.interaction)?.into()) Ok(interaction.step(&mut context.interaction)?)
} }
ActivityStepper::Execution(execution) => Ok(execution.step(context.invoker)?.into()), ActivityStepper::Execution(execution) => Ok(execution.step(context.invoker)?),
} }
} }
} }
@ -87,9 +55,11 @@ impl<M: AccessMesadata> Abort<ActivityContext<'_, M>> for ActivityStepper {
fn abort(&mut self, context: &mut ActivityContext<M>) { fn abort(&mut self, context: &mut ActivityContext<M>) {
match self { match self {
ActivityStepper::Interaction(interaction) => { ActivityStepper::Interaction(interaction) => {
Ok(interaction.abort(&mut context.interaction)) interaction.abort(&mut context.interaction)
} }
ActivityStepper::Execution(execution) => execution.finish(context.invoker), // TODO. ActivityStepper::Execution(execution) => {
execution.finish(context.invoker);
} // TODO.
}; };
} }
} }
@ -136,7 +106,7 @@ impl GetObstacles for ActivityStepper {
pub struct ActivityStepperWithStatus { pub struct ActivityStepperWithStatus {
activity: ActivityStepper, activity: ActivityStepper,
maybe_status: Option<ActivityStatus>, maybe_status: Option<ControlFlow<String>>,
} }
impl ActivityStepperWithStatus { impl ActivityStepperWithStatus {
@ -147,17 +117,18 @@ impl ActivityStepperWithStatus {
} }
} }
pub fn maybe_status(&self) -> Option<ActivityStatus> { pub fn maybe_status(&self) -> Option<ControlFlow<String>> {
self.maybe_status.clone() self.maybe_status.clone()
} }
} }
impl<M: AccessMesadata> Step<ActivityContext<'_, M>, ActivityStatus, ()> impl<M: AccessMesadata> Step<ActivityContext<'_, M>, String> for ActivityStepperWithStatus {
for ActivityStepperWithStatus
{
type Error = ActivityError; type Error = ActivityError;
fn step(&mut self, context: &mut ActivityContext<M>) -> Result<ActivityStatus, ActivityError> { fn step(
&mut self,
context: &mut ActivityContext<M>,
) -> Result<ControlFlow<String>, ActivityError> {
let status = self.activity.step(context)?; let status = self.activity.step(context)?;
self.maybe_status = Some(status.clone()); self.maybe_status = Some(status.clone());
Ok(status.into()) Ok(status.into())
@ -166,7 +137,7 @@ impl<M: AccessMesadata> Step<ActivityContext<'_, M>, ActivityStatus, ()>
impl<M: AccessMesadata> Abort<ActivityContext<'_, M>> for ActivityStepperWithStatus { impl<M: AccessMesadata> Abort<ActivityContext<'_, M>> for ActivityStepperWithStatus {
fn abort(&mut self, context: &mut ActivityContext<M>) { fn abort(&mut self, context: &mut ActivityContext<M>) {
self.maybe_status = Some(ActivityStatus::Finished(String::from("aborted"))); self.maybe_status = Some(ControlFlow::Break(String::from("aborted")));
self.activity.abort(context); self.activity.abort(context);
} }
} }

View File

@ -1,3 +1,5 @@
use std::ops::ControlFlow;
use thiserror::Error; use thiserror::Error;
use topola::{ use topola::{
autorouter::invoker::{GetGhosts, GetMaybeNavcord, GetMaybeNavmesh, GetObstacles}, autorouter::invoker::{GetGhosts, GetMaybeNavcord, GetMaybeNavmesh, GetObstacles},
@ -13,22 +15,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")]
@ -42,14 +28,14 @@ pub enum InteractionStepper {
// - interactively moving a footprint. // - interactively moving a footprint.
} }
impl Step<InteractionContext, InteractionStatus, ()> for InteractionStepper { impl Step<InteractionContext, String> for InteractionStepper {
type Error = InteractionError; type Error = InteractionError;
fn step( fn step(
&mut self, &mut self,
context: &mut InteractionContext, context: &mut InteractionContext,
) -> Result<InteractionStatus, InteractionError> { ) -> Result<ControlFlow<String>, InteractionError> {
Ok(InteractionStatus::Finished(String::from(""))) Ok(ControlFlow::Break(String::from("")))
} }
} }

View File

@ -13,7 +13,7 @@ use topola::{
}; };
use crate::{ use crate::{
activity::{ActivityContext, ActivityError, ActivityStatus, ActivityStepperWithStatus}, activity::{ActivityContext, ActivityError, ActivityStepperWithStatus},
interaction::InteractionContext, interaction::InteractionContext,
}; };
@ -68,8 +68,8 @@ impl<M: AccessMesadata> Interactor<M> {
interaction: InteractionContext {}, interaction: InteractionContext {},
invoker: &mut self.invoker, invoker: &mut self.invoker,
}) { }) {
Ok(ActivityStatus::Running) => ControlFlow::Continue(()), Ok(ControlFlow::Continue(())) => ControlFlow::Continue(()),
Ok(ActivityStatus::Finished(..)) => ControlFlow::Break(Ok(())), Ok(ControlFlow::Break(msg)) => ControlFlow::Break(Ok(())),
Err(err) => { Err(err) => {
self.activity = None; self.activity = None;
ControlFlow::Break(Err(err)) ControlFlow::Break(Err(err))

View File

@ -1,4 +1,4 @@
use std::{path::Path, sync::mpsc::Sender}; use std::{ops::ControlFlow, path::Path, sync::mpsc::Sender};
use topola::{ use topola::{
autorouter::{ autorouter::{
@ -11,7 +11,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,
translator::Translator, translator::Translator,
@ -130,7 +130,7 @@ impl MenuBar {
.maybe_activity() .maybe_activity()
.as_ref() .as_ref()
.map_or(true, |activity| { .map_or(true, |activity| {
matches!(activity.maybe_status(), Some(ActivityStatus::Finished(..))) matches!(activity.maybe_status(), Some(ControlFlow::Break(..)))
}), }),
None => false, None => false,
}; };

View File

@ -1,8 +1,6 @@
use crate::{ use std::ops::ControlFlow;
activity::{ActivityStatus, ActivityStepperWithStatus},
translator::Translator, use crate::{activity::ActivityStepperWithStatus, translator::Translator, viewport::Viewport};
viewport::Viewport,
};
pub struct StatusBar {} pub struct StatusBar {}
@ -25,7 +23,7 @@ impl StatusBar {
let mut message = String::from(""); let mut message = String::from("");
if let Some(activity) = maybe_activity { if let Some(activity) = maybe_activity {
if let Some(ActivityStatus::Finished(msg)) = activity.maybe_status() { if let Some(ControlFlow::Break(msg)) = activity.maybe_status() {
message = msg; message = msg;
} }
} }

View File

@ -10,7 +10,7 @@ use topola::{
}; };
use crate::{ use crate::{
activity::{ActivityContext, ActivityStatus, ActivityStepperWithStatus}, activity::{ActivityContext, ActivityStepperWithStatus},
error_dialog::ErrorDialog, error_dialog::ErrorDialog,
interaction::InteractionContext, interaction::InteractionContext,
interactor::Interactor, interactor::Interactor,

View File

@ -8,6 +8,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::{BinaryHeap, HashMap, VecDeque}; use std::collections::{BinaryHeap, HashMap, VecDeque};
use std::hash::Hash; use std::hash::Hash;
use std::ops::ControlFlow;
use petgraph::algo::Measure; use petgraph::algo::Measure;
use petgraph::visit::{EdgeRef, GraphBase, IntoEdgeReferences, IntoEdges}; use petgraph::visit::{EdgeRef, GraphBase, IntoEdgeReferences, IntoEdges};
@ -138,44 +139,19 @@ where
pub is_probing: bool, pub is_probing: bool,
} }
#[derive(Debug)]
pub enum AstarContinueStatus {
Probing,
Probed,
Visited,
}
#[derive(Error, Debug, Clone)] #[derive(Error, Debug, Clone)]
pub enum AstarError { pub enum AstarError {
#[error("A* search found no path")] #[error("A* search found no path")]
NotFound, NotFound,
} }
#[derive(Debug)]
pub enum AstarStatus<G, K, R>
where
G: GraphBase,
G::NodeId: Eq + Hash,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy,
{
Probing,
Probed,
Visited,
Finished(K, Vec<G::NodeId>, R),
}
impl<G, K, R> TryInto<(K, Vec<G::NodeId>, R)> for AstarStatus<G, K, R>
where
G: GraphBase,
G::NodeId: Eq + Hash,
for<'a> &'a G: IntoEdges<NodeId = G::NodeId, EdgeId = G::EdgeId> + MakeEdgeRef,
K: Measure + Copy,
{
type Error = ();
fn try_into(self) -> Result<(K, Vec<G::NodeId>, R), ()> {
match self {
AstarStatus::Probing => Err(()),
AstarStatus::Probed => Err(()),
AstarStatus::Visited => Err(()),
AstarStatus::Finished(cost, path, result) => Ok((cost, path, result)),
}
}
}
impl<G, K> Astar<G, K> impl<G, K> Astar<G, K>
where where
G: GraphBase, G: GraphBase,
@ -203,7 +179,7 @@ where
} }
} }
impl<G, K, R, S: AstarStrategy<G, K, R>> Step<S, AstarStatus<G, K, R>, (K, Vec<G::NodeId>, R)> impl<G, K, R, S: AstarStrategy<G, K, R>> Step<S, (K, Vec<G::NodeId>, R), AstarContinueStatus>
for Astar<G, K> for Astar<G, K>
where where
G: GraphBase, G: GraphBase,
@ -213,7 +189,10 @@ where
{ {
type Error = AstarError; type Error = AstarError;
fn step(&mut self, strategy: &mut S) -> Result<AstarStatus<G, K, R>, AstarError> { fn step(
&mut self,
strategy: &mut S,
) -> Result<ControlFlow<(K, Vec<G::NodeId>, R), AstarContinueStatus>, AstarError> {
if let Some(curr_node) = self.maybe_curr_node { if let Some(curr_node) = self.maybe_curr_node {
if self.is_probing { if self.is_probing {
strategy.remove_probe(&self.graph); strategy.remove_probe(&self.graph);
@ -235,7 +214,7 @@ where
// No need to add neighbors that we have already reached through a // No need to add neighbors that we have already reached through a
// shorter path than now. // shorter path than now.
if *entry.get() <= next_score { if *entry.get() <= next_score {
return Ok(AstarStatus::Probed); return Ok(ControlFlow::Continue(AstarContinueStatus::Probed));
} }
entry.insert(next_score); entry.insert(next_score);
} }
@ -250,10 +229,10 @@ where
self.visit_next.push(MinScored(next_estimate_score, next)); self.visit_next.push(MinScored(next_estimate_score, next));
self.is_probing = true; self.is_probing = true;
return Ok(AstarStatus::Probing); return Ok(ControlFlow::Continue(AstarContinueStatus::Probing));
} }
return Ok(AstarStatus::Probed); return Ok(ControlFlow::Continue(AstarContinueStatus::Probed));
} }
self.maybe_curr_node = None; self.maybe_curr_node = None;
@ -266,7 +245,7 @@ where
if let Some(result) = strategy.is_goal(&self.graph, node, &self.path_tracker) { if let Some(result) = strategy.is_goal(&self.graph, node, &self.path_tracker) {
let path = self.path_tracker.reconstruct_path_to(node); let path = self.path_tracker.reconstruct_path_to(node);
let cost = self.scores[&node]; let cost = self.scores[&node];
return Ok(AstarStatus::Finished(cost, path, result)); return Ok(ControlFlow::Break((cost, path, result)));
} }
match self.estimate_scores.entry(node) { match self.estimate_scores.entry(node) {
@ -274,7 +253,7 @@ where
// If the node has already been visited with an equal or lower score than // If the node has already been visited with an equal or lower score than
// now, then we do not need to re-visit it. // now, then we do not need to re-visit it.
if *entry.get() <= estimate_score { if *entry.get() <= estimate_score {
return Ok(AstarStatus::Visited); return Ok(ControlFlow::Continue(AstarContinueStatus::Visited));
} }
entry.insert(estimate_score); entry.insert(estimate_score);
} }
@ -286,6 +265,6 @@ where
self.maybe_curr_node = Some(node); self.maybe_curr_node = Some(node);
self.edge_ids = self.graph.edges(node).map(|edge| edge.id()).collect(); self.edge_ids = self.graph.edges(node).map(|edge| edge.id()).collect();
Ok(AstarStatus::Visited) Ok(ControlFlow::Continue(AstarContinueStatus::Visited))
} }
} }

View File

@ -1,14 +1,16 @@
use std::ops::ControlFlow;
use crate::{ use crate::{
drawing::{ drawing::{
band::BandTermsegIndex, 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},
navcord::NavcordStepper, navcord::NavcordStepper,
navcorder::Navcorder, navcorder::Navcorder,
navmesh::{Navmesh, NavmeshError}, navmesh::{Navmesh, NavmeshError},
Router, RouterAstarStrategy, RouterStatus, Router, RouterAstarStrategy,
}, },
stepper::Step, stepper::Step,
}; };
@ -73,19 +75,20 @@ impl RouteStepper {
} }
} }
impl<'a, R: AccessRules> Step<Router<'a, R>, RouterStatus, BandTermsegIndex> for RouteStepper { impl<'a, R: AccessRules> Step<Router<'a, R>, BandTermsegIndex> for RouteStepper {
type Error = AstarError; type Error = AstarError;
fn step(&mut self, router: &mut Router<R>) -> Result<RouterStatus, AstarError> { fn step(
&mut self,
router: &mut Router<R>,
) -> Result<ControlFlow<BandTermsegIndex>, AstarError> {
let navcorder = Navcorder::new(router.layout_mut()); let navcorder = Navcorder::new(router.layout_mut());
let target = self.astar.graph.destination(); let target = self.astar.graph.destination();
let mut strategy = RouterAstarStrategy::new(navcorder, &mut self.navcord, target); let mut strategy = RouterAstarStrategy::new(navcorder, &mut self.navcord, target);
let result = match self.astar.step(&mut strategy)? { let result = match self.astar.step(&mut strategy)? {
AstarStatus::Probing | AstarStatus::Probed | AstarStatus::Visited => { ControlFlow::Continue(..) => Ok(ControlFlow::Continue(())),
Ok(RouterStatus::Running) ControlFlow::Break((_cost, _path, band)) => Ok(ControlFlow::Break(band)),
}
AstarStatus::Finished(_cost, _path, band) => Ok(RouterStatus::Finished(band)),
}; };
self.ghosts = strategy.probe_ghosts; self.ghosts = strategy.probe_ghosts;

View File

@ -36,22 +36,6 @@ pub struct RouterOptions {
pub squeeze_through_under_bands: bool, pub squeeze_through_under_bands: bool,
} }
#[derive(Debug)]
pub enum RouterStatus {
Running,
Finished(BandTermsegIndex),
}
impl TryInto<BandTermsegIndex> for RouterStatus {
type Error = ();
fn try_into(self) -> Result<BandTermsegIndex, ()> {
match self {
RouterStatus::Running => Err(()),
RouterStatus::Finished(band_termseg) => Ok(band_termseg),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct RouterAstarStrategy<'a, R: AccessRules> { pub struct RouterAstarStrategy<'a, R: AccessRules> {
pub navcorder: Navcorder<'a, R>, pub navcorder: Navcorder<'a, R>,

View File

@ -1,11 +1,13 @@
pub trait Step<C, S: TryInto<O>, O> { use std::ops::ControlFlow;
pub trait Step<Ctx, B, C = ()> {
type Error; type Error;
fn step(&mut self, context: &mut C) -> Result<S, Self::Error>; fn step(&mut self, context: &mut Ctx) -> Result<ControlFlow<B, C>, Self::Error>;
fn finish(&mut self, context: &mut C) -> Result<O, Self::Error> { fn finish(&mut self, context: &mut Ctx) -> Result<B, Self::Error> {
loop { loop {
if let Ok(outcome) = self.step(context)?.try_into() { if let ControlFlow::Break(outcome) = self.step(context)? {
return Ok(outcome); return Ok(outcome);
} }
} }