use crate::runtime::values::Value;
#[derive(Debug, Clone)]
pub enum ControlFlow {
Continue,
Break(Option<Value>),
Next,
}
impl ControlFlow {
pub fn is_continue(&self) -> bool {
matches!(self, ControlFlow::Continue)
}
pub fn is_break(&self) -> bool {
matches!(self, ControlFlow::Break(_))
}
pub fn is_next(&self) -> bool {
matches!(self, ControlFlow::Next)
}
pub fn break_value(self) -> Option<Value> {
match self {
ControlFlow::Break(value) => value,
_ => None,
}
}
}
pub type StatementResult = Result<StatementOutcome, crate::runtime::functions::RuntimeError>;
#[derive(Debug, Clone)]
pub enum StatementOutcome {
Value(Value),
ControlFlow(ControlFlow),
}
impl StatementOutcome {
pub fn value(v: Value) -> Self {
StatementOutcome::Value(v)
}
pub fn break_with_value(v: Option<Value>) -> Self {
StatementOutcome::ControlFlow(ControlFlow::Break(v))
}
pub fn next() -> Self {
StatementOutcome::ControlFlow(ControlFlow::Next)
}
pub fn as_value(&self) -> Option<&Value> {
match self {
StatementOutcome::Value(v) => Some(v),
_ => None,
}
}
pub fn as_control_flow(&self) -> Option<&ControlFlow> {
match self {
StatementOutcome::ControlFlow(cf) => Some(cf),
_ => None,
}
}
pub fn is_control_flow(&self) -> bool {
matches!(self, StatementOutcome::ControlFlow(_))
}
}
impl From<Value> for StatementOutcome {
fn from(value: Value) -> Self {
StatementOutcome::Value(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn control_flow_predicates_and_break_value() {
let c = ControlFlow::Continue;
assert!(c.is_continue());
assert!(!c.is_break());
assert!(!c.is_next());
assert_eq!(c.break_value(), None);
let b0 = ControlFlow::Break(None);
assert!(!b0.is_continue());
assert!(b0.is_break());
assert!(!b0.is_next());
assert_eq!(b0.clone().break_value(), None);
let b1 = ControlFlow::Break(Some(Value::Int(7)));
assert!(!b1.is_continue());
assert!(b1.is_break());
assert!(!b1.is_next());
assert_eq!(b1.clone().break_value(), Some(Value::Int(7)));
let n = ControlFlow::Next;
assert!(!n.is_continue());
assert!(!n.is_break());
assert!(n.is_next());
assert_eq!(n.break_value(), None);
}
#[test]
fn statement_outcome_accessors() {
let v = StatementOutcome::value(Value::String("x".to_string()));
assert_eq!(v.as_value(), Some(&Value::String("x".to_string())));
assert!(v.as_control_flow().is_none());
assert!(!v.is_control_flow());
let cf = StatementOutcome::ControlFlow(ControlFlow::Continue);
assert_eq!(cf.as_value(), None);
assert!(matches!(cf.as_control_flow(), Some(ControlFlow::Continue)));
assert!(cf.is_control_flow());
let br = StatementOutcome::break_with_value(Some(Value::Null));
assert!(br.is_control_flow());
assert_eq!(
br.as_control_flow().unwrap().clone().break_value(),
Some(Value::Null)
);
let nx = StatementOutcome::next();
assert!(nx.is_control_flow());
assert!(matches!(nx.as_control_flow(), Some(ControlFlow::Next)));
}
}