rune/runtime/
control_flow.rs

1use core::ops;
2
3use crate as rune;
4use crate::alloc::clone::TryClone;
5use crate::alloc::fmt::TryWrite;
6use crate::Any;
7
8use super::{
9    EnvProtocolCaller, Formatter, FromValue, ProtocolCaller, RuntimeError, ToValue, Value, VmResult,
10};
11
12/// Used to tell an operation whether it should exit early or go on as usual.
13///
14/// This acts as the basis of the [`TRY`] protocol in Rune.
15///
16/// [`TRY`]: crate::runtime::Protocol::TRY
17///
18/// # Examples
19///
20/// ```rune
21/// use std::ops::ControlFlow;
22///
23/// let c = ControlFlow::Continue(42);
24/// assert_eq!(c.0, 42);
25/// assert_eq!(c, ControlFlow::Continue(42));
26/// ```
27#[derive(Debug, Clone, TryClone, Any)]
28#[try_clone(crate)]
29#[rune(item = ::std::ops)]
30pub enum ControlFlow {
31    /// Move on to the next phase of the operation as normal.
32    #[rune(constructor)]
33    Continue(#[rune(get, set)] Value),
34    /// Exit the operation without running subsequent phases.
35    #[rune(constructor)]
36    Break(#[rune(get, set)] Value),
37}
38
39impl ControlFlow {
40    /// Test two control flows for partial equality.
41    ///
42    /// # Examples
43    ///
44    /// ```rune
45    /// use std::ops::{partial_eq, ControlFlow};
46    ///
47    /// assert_eq! {
48    ///     partial_eq(ControlFlow::Continue(true), ControlFlow::Continue(true)),
49    ///     true
50    /// };
51    /// assert_eq! {
52    ///     partial_eq(ControlFlow::Continue(true), ControlFlow::Break(false)),
53    ///     false
54    /// };
55    /// assert_eq! {
56    ///     partial_eq(ControlFlow::Break(false), ControlFlow::Continue(true)),
57    ///     false
58    /// };
59    /// ```
60    #[rune::function(keep, protocol = PARTIAL_EQ)]
61    pub(crate) fn partial_eq(&self, other: &Self) -> VmResult<bool> {
62        Self::partial_eq_with(self, other, &mut EnvProtocolCaller)
63    }
64
65    pub(crate) fn partial_eq_with(
66        &self,
67        other: &Self,
68        caller: &mut dyn ProtocolCaller,
69    ) -> VmResult<bool> {
70        match (self, other) {
71            (ControlFlow::Continue(a), ControlFlow::Continue(b)) => {
72                Value::partial_eq_with(a, b, caller)
73            }
74            (ControlFlow::Break(a), ControlFlow::Break(b)) => Value::partial_eq_with(a, b, caller),
75            _ => VmResult::Ok(false),
76        }
77    }
78
79    /// Test two control flows for total equality.
80    ///
81    /// # Examples
82    ///
83    /// ```rune
84    /// use std::ops::{eq, ControlFlow};
85    ///
86    /// assert_eq! {
87    ///     eq(ControlFlow::Continue(true), ControlFlow::Continue(true)),
88    ///     true
89    /// };
90    /// assert_eq! {
91    ///     eq(ControlFlow::Continue(true), ControlFlow::Break(false)),
92    ///     false
93    /// };
94    /// assert_eq! {
95    ///     eq(ControlFlow::Break(false), ControlFlow::Continue(true)),
96    ///     false
97    /// };
98    /// ```
99    #[rune::function(keep, protocol = EQ)]
100    pub(crate) fn eq(&self, other: &ControlFlow) -> VmResult<bool> {
101        self.eq_with(other, &mut EnvProtocolCaller)
102    }
103
104    pub(crate) fn eq_with(
105        &self,
106        other: &ControlFlow,
107        caller: &mut dyn ProtocolCaller,
108    ) -> VmResult<bool> {
109        match (self, other) {
110            (ControlFlow::Continue(a), ControlFlow::Continue(b)) => Value::eq_with(a, b, caller),
111            (ControlFlow::Break(a), ControlFlow::Break(b)) => Value::eq_with(a, b, caller),
112            _ => VmResult::Ok(false),
113        }
114    }
115
116    /// Debug print the control flow.
117    ///
118    /// # Examples
119    ///
120    /// ```rune
121    /// use std::ops::ControlFlow;
122    ///
123    /// let string = format!("{:?}", ControlFlow::Continue(true));
124    /// ```
125    #[rune::function(keep, protocol = DEBUG_FMT)]
126    pub(crate) fn debug_fmt(&self, f: &mut Formatter) -> VmResult<()> {
127        Self::debug_fmt_with(self, f, &mut EnvProtocolCaller)
128    }
129
130    pub(crate) fn debug_fmt_with(
131        &self,
132        f: &mut Formatter,
133        caller: &mut dyn ProtocolCaller,
134    ) -> VmResult<()> {
135        match self {
136            ControlFlow::Continue(value) => {
137                vm_try!(vm_write!(f, "Continue("));
138                vm_try!(Value::debug_fmt_with(value, f, caller));
139                vm_try!(vm_write!(f, ")"));
140            }
141            ControlFlow::Break(value) => {
142                vm_try!(vm_write!(f, "Break("));
143                vm_try!(Value::debug_fmt_with(value, f, caller));
144                vm_try!(vm_write!(f, ")"));
145            }
146        }
147
148        VmResult::Ok(())
149    }
150
151    /// Clone the control flow.
152    ///
153    /// # Examples
154    ///
155    /// ```rune
156    /// use std::ops::ControlFlow;
157    ///
158    /// let flow = ControlFlow::Continue("Hello World");
159    /// let flow2 = flow.clone();
160    ///
161    /// assert_eq!(flow, flow2);
162    /// ```
163    #[rune::function(keep, protocol = CLONE)]
164    pub(crate) fn clone(&self) -> VmResult<Self> {
165        VmResult::Ok(vm_try!(self.try_clone()))
166    }
167}
168
169impl<B, C> ToValue for ops::ControlFlow<B, C>
170where
171    B: ToValue,
172    C: ToValue,
173{
174    #[inline]
175    fn to_value(self) -> Result<Value, RuntimeError> {
176        let value = match self {
177            ops::ControlFlow::Continue(value) => ControlFlow::Continue(C::to_value(value)?),
178            ops::ControlFlow::Break(value) => ControlFlow::Break(B::to_value(value)?),
179        };
180
181        Ok(Value::try_from(value)?)
182    }
183}
184
185impl<B, C> FromValue for ops::ControlFlow<B, C>
186where
187    B: FromValue,
188    C: FromValue,
189{
190    #[inline]
191    fn from_value(value: Value) -> Result<Self, RuntimeError> {
192        Ok(match &*value.borrow_ref::<ControlFlow>()? {
193            ControlFlow::Continue(value) => {
194                ops::ControlFlow::Continue(C::from_value(value.clone())?)
195            }
196            ControlFlow::Break(value) => ops::ControlFlow::Break(B::from_value(value.clone())?),
197        })
198    }
199}