use std::sync::Arc;
use surrealdb_types::{SqlFormat, ToSql, write_sql};
use crate::err::Error;
use crate::exec::physical_expr::{EvalContext, PhysicalExpr};
use crate::exec::{AccessMode, BoxFut, ContextLevel};
use crate::expr::{ControlFlow, FlowResult};
use crate::val::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ControlFlowKind {
Return,
Throw,
Break,
Continue,
}
impl std::fmt::Display for ControlFlowKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ControlFlowKind::Return => write!(f, "RETURN"),
ControlFlowKind::Throw => write!(f, "THROW"),
ControlFlowKind::Break => write!(f, "BREAK"),
ControlFlowKind::Continue => write!(f, "CONTINUE"),
}
}
}
#[derive(Debug, Clone)]
pub struct ControlFlowExpr {
pub(crate) kind: ControlFlowKind,
pub(crate) inner: Option<Arc<dyn PhysicalExpr>>,
}
impl PhysicalExpr for ControlFlowExpr {
fn name(&self) -> &'static str {
"ControlFlow"
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn required_context(&self) -> ContextLevel {
self.inner.as_ref().map_or(ContextLevel::Root, |e| e.required_context())
}
fn evaluate<'a>(&'a self, ctx: EvalContext<'a>) -> BoxFut<'a, FlowResult<Value>> {
Box::pin(async move {
match self.kind {
ControlFlowKind::Break => Err(ControlFlow::Break),
ControlFlowKind::Continue => Err(ControlFlow::Continue),
ControlFlowKind::Throw => {
let inner = self.inner.as_ref().expect("THROW must have inner expression");
let value = inner.evaluate(ctx).await?;
Err(ControlFlow::Err(anyhow::Error::new(Error::Thrown(value.to_raw_string()))))
}
ControlFlowKind::Return => {
let inner = self.inner.as_ref().expect("RETURN must have inner expression");
let value = inner.evaluate(ctx).await?;
Err(ControlFlow::Return(value))
}
}
})
}
fn access_mode(&self) -> AccessMode {
self.inner.as_ref().map_or(AccessMode::ReadOnly, |e| e.access_mode())
}
}
impl ToSql for ControlFlowExpr {
fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
match self.kind {
ControlFlowKind::Break => f.push_str("BREAK"),
ControlFlowKind::Continue => f.push_str("CONTINUE"),
ControlFlowKind::Throw => {
f.push_str("THROW");
if let Some(inner) = &self.inner {
write_sql!(f, fmt, " {}", inner);
}
}
ControlFlowKind::Return => {
f.push_str("RETURN");
if let Some(inner) = &self.inner {
write_sql!(f, fmt, " {}", inner);
}
}
}
}
}