use std::sync::Arc;
use surrealdb_types::{SqlFormat, ToSql, write_sql};
use crate::exec::physical_expr::{EvalContext, PhysicalExpr};
use crate::exec::{AccessMode, BoxFut, CombineAccessModes};
use crate::expr::FlowResult;
use crate::val::Value;
#[derive(Debug, Clone)]
pub struct IfElseExpr {
pub(crate) branches: Vec<(Arc<dyn PhysicalExpr>, Arc<dyn PhysicalExpr>)>,
pub(crate) otherwise: Option<Arc<dyn PhysicalExpr>>,
}
impl PhysicalExpr for IfElseExpr {
fn name(&self) -> &'static str {
"IfElse"
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn required_context(&self) -> crate::exec::ContextLevel {
use crate::exec::ContextLevel;
let branches_ctx = self
.branches
.iter()
.flat_map(|(cond, val)| [cond.required_context(), val.required_context()])
.max()
.unwrap_or(ContextLevel::Root);
let otherwise_ctx =
self.otherwise.as_ref().map_or(ContextLevel::Root, |e| e.required_context());
branches_ctx.max(otherwise_ctx)
}
fn evaluate<'a>(&'a self, ctx: EvalContext<'a>) -> BoxFut<'a, FlowResult<Value>> {
Box::pin(async move {
for (condition, value) in &self.branches {
let cond_result = condition.evaluate(ctx.clone()).await?;
if cond_result.is_truthy() {
return value.evaluate(ctx).await;
}
}
if let Some(otherwise) = &self.otherwise {
otherwise.evaluate(ctx).await
} else {
Ok(Value::None)
}
})
}
fn access_mode(&self) -> AccessMode {
let branches_mode = self
.branches
.iter()
.flat_map(|(cond, val)| [cond.access_mode(), val.access_mode()])
.combine_all();
let otherwise_mode =
self.otherwise.as_ref().map_or(AccessMode::ReadOnly, |e| e.access_mode());
branches_mode.combine(otherwise_mode)
}
}
impl ToSql for IfElseExpr {
fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
for (i, (condition, value)) in self.branches.iter().enumerate() {
if i == 0 {
write_sql!(f, fmt, "IF {} THEN {}", condition, value);
} else {
write_sql!(f, fmt, " ELSE IF {} THEN {}", condition, value);
}
}
if let Some(otherwise) = &self.otherwise {
write_sql!(f, fmt, " ELSE {}", otherwise);
}
f.push_str(" END");
}
}