1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use core::ops;

use crate as rune;
use crate::alloc::fmt::TryWrite;
use crate::runtime::{Formatter, FromValue, ProtocolCaller, ToValue, Value, VmResult};
use crate::Any;

/// Used to tell an operation whether it should exit early or go on as usual.
///
/// This acts as the basis of the [`TRY`] protocol in Rune.
///
/// [`TRY`]: crate::runtime::Protocol::TRY
///
/// # Examples
///
/// ```rune
/// use std::ops::ControlFlow;
///
/// let c = ControlFlow::Continue(42);
/// assert_eq!(c.0, 42);
/// assert_eq!(c, ControlFlow::Continue(42));
/// ```
#[derive(Debug, Clone, Any)]
#[rune(builtin, static_type = CONTROL_FLOW_TYPE)]
pub enum ControlFlow {
    /// Move on to the next phase of the operation as normal.
    #[rune(constructor)]
    Continue(#[rune(get, set)] Value),
    /// Exit the operation without running subsequent phases.
    #[rune(constructor)]
    Break(#[rune(get, set)] Value),
}

impl ControlFlow {
    pub(crate) fn string_debug_with(
        &self,
        f: &mut Formatter,
        caller: &mut impl ProtocolCaller,
    ) -> VmResult<()> {
        match self {
            ControlFlow::Continue(value) => {
                vm_write!(f, "Continue(");
                vm_try!(Value::string_debug_with(value, f, caller));
                vm_write!(f, ")");
            }
            ControlFlow::Break(value) => {
                vm_write!(f, "Break(");
                vm_try!(Value::string_debug_with(value, f, caller));
                vm_write!(f, ")");
            }
        }

        VmResult::Ok(())
    }

    pub(crate) fn partial_eq_with(
        &self,
        other: &Self,
        caller: &mut impl ProtocolCaller,
    ) -> VmResult<bool> {
        match (self, other) {
            (ControlFlow::Continue(a), ControlFlow::Continue(b)) => {
                Value::partial_eq_with(a, b, caller)
            }
            (ControlFlow::Break(a), ControlFlow::Break(b)) => Value::partial_eq_with(a, b, caller),
            _ => VmResult::Ok(false),
        }
    }

    pub(crate) fn eq_with(
        &self,
        other: &ControlFlow,
        caller: &mut impl ProtocolCaller,
    ) -> VmResult<bool> {
        match (self, other) {
            (ControlFlow::Continue(a), ControlFlow::Continue(b)) => Value::eq_with(a, b, caller),
            (ControlFlow::Break(a), ControlFlow::Break(b)) => Value::eq_with(a, b, caller),
            _ => VmResult::Ok(false),
        }
    }
}

from_value!(ControlFlow, into_control_flow);

impl<B, C> ToValue for ops::ControlFlow<B, C>
where
    B: ToValue,
    C: ToValue,
{
    #[inline]
    fn to_value(self) -> VmResult<Value> {
        let value = match self {
            ops::ControlFlow::Continue(value) => {
                ControlFlow::Continue(vm_try!(ToValue::to_value(value)))
            }
            ops::ControlFlow::Break(value) => ControlFlow::Break(vm_try!(ToValue::to_value(value))),
        };

        VmResult::Ok(vm_try!(Value::try_from(value)))
    }
}

impl<B, C> FromValue for ops::ControlFlow<B, C>
where
    B: FromValue,
    C: FromValue,
{
    #[inline]
    fn from_value(value: Value) -> VmResult<Self> {
        let value = vm_try!(value.into_control_flow());

        VmResult::Ok(match vm_try!(value.take()) {
            ControlFlow::Continue(value) => {
                ops::ControlFlow::Continue(vm_try!(C::from_value(value)))
            }
            ControlFlow::Break(value) => ops::ControlFlow::Break(vm_try!(B::from_value(value))),
        })
    }
}