use std::sync::Arc;
use surrealdb_types::{SqlFormat, ToSql};
use crate::exec::physical_expr::{EvalContext, PhysicalExpr};
use crate::exec::{AccessMode, BoxFut, ContextLevel};
use crate::expr::FlowResult;
use crate::val::Value;
#[derive(Debug, Clone)]
pub struct IndexPart {
pub expr: Arc<dyn PhysicalExpr>,
}
impl PhysicalExpr for IndexPart {
fn name(&self) -> &'static str {
"Index"
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn required_context(&self) -> ContextLevel {
self.expr.required_context()
}
fn evaluate<'a>(&'a self, ctx: EvalContext<'a>) -> BoxFut<'a, FlowResult<Value>> {
Box::pin(async move {
let value = ctx.current_value.unwrap_or(&Value::NONE);
let index_ctx = if let Some(doc) = ctx.document_root {
ctx.with_value(doc)
} else {
ctx
};
let index = self.expr.evaluate(index_ctx).await?;
Ok(evaluate_index(value, &index)?)
})
}
fn access_mode(&self) -> AccessMode {
self.expr.access_mode()
}
}
impl ToSql for IndexPart {
fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
f.push('[');
self.expr.fmt_sql(f, fmt);
f.push(']');
}
}
pub(crate) fn evaluate_index(value: &Value, index: &Value) -> anyhow::Result<Value> {
use crate::val::record_id::RecordIdKey;
match (value, index) {
(Value::Array(arr), Value::Number(n)) => {
Ok(n.as_array_index().and_then(|idx| arr.get(idx).cloned()).unwrap_or(Value::None))
}
(Value::Set(set), Value::Number(n)) => {
Ok(n.as_array_index().and_then(|idx| set.nth(idx).cloned()).unwrap_or(Value::None))
}
(Value::Array(arr), Value::Range(range)) => {
let slice = range
.as_ref()
.clone()
.coerce_to_typed::<i64>()
.map_err(|e| anyhow::anyhow!("Invalid range: {}", e))?
.slice(arr.as_slice())
.map(|s| Value::Array(s.to_vec().into()))
.unwrap_or(Value::None);
Ok(slice)
}
(Value::Object(obj), Value::String(key)) => {
Ok(obj.get(key.as_str()).cloned().unwrap_or(Value::None))
}
(Value::Object(obj), Value::Number(n)) => {
let key = n.to_string();
Ok(obj.get(&key).cloned().unwrap_or(Value::None))
}
(Value::RecordId(rid), Value::Number(n)) => match &rid.key {
RecordIdKey::Array(arr) => {
Ok(n.as_array_index().and_then(|idx| arr.get(idx).cloned()).unwrap_or(Value::None))
}
_ => Ok(Value::None),
},
_ => Ok(Value::None),
}
}