Skip to main content

sim_lib_control/
restart.rs

1use sim_kernel::{
2    Cx, Error, Ref, Result, Symbol,
3    control::{ControlResume, default_control_result_shape, resume},
4};
5
6use crate::{ContinuationValue, ControlResultValue};
7
8/// A named recovery point: a symbol bound to a captured continuation.
9///
10/// The condition system's restart: invoking it resumes the underlying
11/// [`ContinuationValue`] with a supplied value, returning control to the point
12/// where the restart was established.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Restart {
15    name: Symbol,
16    continuation: ContinuationValue,
17}
18
19impl Restart {
20    /// Binds `name` to the recovery `continuation`.
21    pub fn new(name: Symbol, continuation: ContinuationValue) -> Self {
22        Self { name, continuation }
23    }
24
25    /// Returns the restart name.
26    pub fn name(&self) -> &Symbol {
27        &self.name
28    }
29
30    /// Returns the continuation this restart resumes.
31    pub fn continuation(&self) -> &ContinuationValue {
32        &self.continuation
33    }
34
35    /// Invokes the restart, resuming its continuation with `value` and
36    /// returning the resulting [`ControlResultValue`].
37    pub fn invoke(&self, cx: &mut Cx, value: Ref) -> Result<ControlResultValue> {
38        let result = resume(
39            cx,
40            ControlResume::new(
41                self.continuation.continuation().clone(),
42                value,
43                default_control_result_shape(),
44            ),
45        )?;
46        Ok(ControlResultValue::new(result))
47    }
48}
49
50/// A stack of [`Restart`]s, searched innermost-first by name on invocation.
51///
52/// Models the dynamic-extent restart chain of the condition system.
53#[derive(Clone, Debug, Default, PartialEq, Eq)]
54pub struct RestartStack {
55    restarts: Vec<Restart>,
56}
57
58impl RestartStack {
59    /// Builds an empty restart stack.
60    pub fn new() -> Self {
61        Self::default()
62    }
63
64    /// Installs `restart` as the new innermost restart.
65    pub fn push(&mut self, restart: Restart) {
66        self.restarts.push(restart);
67    }
68
69    /// Removes and returns the innermost restart, if any.
70    pub fn pop(&mut self) -> Option<Restart> {
71        self.restarts.pop()
72    }
73
74    /// Invokes the nearest restart named `name` with `value`.
75    ///
76    /// Fails with [`Error::Eval`](sim_kernel::Error::Eval) when no restart with
77    /// that name is installed.
78    pub fn invoke(&self, cx: &mut Cx, name: &Symbol, value: Ref) -> Result<ControlResultValue> {
79        self.nearest_restart(name)?.invoke(cx, value)
80    }
81
82    /// Returns the innermost restart named `name`, or
83    /// [`Error::Eval`](sim_kernel::Error::Eval) when none matches.
84    pub fn nearest_restart(&self, name: &Symbol) -> Result<&Restart> {
85        self.restarts
86            .iter()
87            .rev()
88            .find(|restart| restart.name() == name)
89            .ok_or_else(|| Error::Eval(format!("no restart named {name}")))
90    }
91}
92
93/// Invokes the restart named `name` on `stack` with `value`; free-function
94/// form of [`RestartStack::invoke`].
95pub fn invoke_restart(
96    cx: &mut Cx,
97    stack: &RestartStack,
98    name: &Symbol,
99    value: Ref,
100) -> Result<ControlResultValue> {
101    stack.invoke(cx, name, value)
102}