use std::borrow::Cow;
use anyhow::Result;
use reblessive::tree::Stk;
use crate::ctx::FrozenContext;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::exe::FlowResultExt as _;
use crate::expr::field::Fields;
use crate::expr::{Expr, Function, Idiom, Part};
use crate::fnc::args::FromArgs;
use crate::syn;
use crate::val::{Array, Value};
pub(crate) async fn fields_compute(
this: &Fields,
stk: &mut Stk,
ctx: &FrozenContext,
opt: &Options,
doc: Option<&CursorDoc>,
) -> Result<Value> {
if let Some(doc) = doc {
crate::legacy::fields_compute_value(this, stk, ctx, opt, doc).await
} else {
let doc = Value::None.into();
crate::legacy::fields_compute_value(this, stk, ctx, opt, &doc).await
}
}
pub(crate) async fn fields_compute_value(
this: &Fields,
stk: &mut Stk,
ctx: &FrozenContext,
opt: &Options,
doc: &CursorDoc,
) -> Result<Value> {
let mut out = if this.has_all_selection() {
doc.doc.as_ref().clone()
} else {
Value::empty_object()
};
for v in this.iter_non_all_fields() {
let name =
v.alias.as_ref().map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(v.expr.to_idiom()));
match &v.expr {
Expr::Idiom(i) if i.is_multi_yield() => {
let mut res: Vec<(&[Part], Value)> = Vec::new();
for v in i.split_inclusive(Idiom::part_is_multi_yield) {
let x = match res.last() {
Some((_, r)) => r,
None => doc.doc.as_ref(),
};
let x = crate::legacy::value_get(x, stk, ctx, opt, Some(doc), v)
.await
.catch_return()?
.flatten();
res.push((v, x));
}
for (p, x) in res {
match p.last().expect("idiom is non-empty").alias() {
Some(a) => {
if let Some(i) = &v.alias {
crate::legacy::value_set(&mut out, stk, ctx, opt, &i.0, x.clone())
.await?;
}
crate::legacy::value_set(&mut out, stk, ctx, opt, a, x).await?;
}
None => {
crate::legacy::value_set(
&mut out,
stk,
ctx,
opt,
v.alias.as_ref().unwrap_or(i),
x,
)
.await?
}
}
}
}
Expr::FunctionCall(f) => {
match f.receiver {
Function::Normal(ref x) if x == "type::fields" => {
let mut arguments = Vec::new();
for arg in f.arguments.iter() {
arguments.push(
stk.run(|stk| {
crate::legacy::expr_compute(arg, stk, ctx, opt, Some(doc))
})
.await
.catch_return()?,
);
}
let (args,) = <(Vec<String>,)>::from_args("type::fields", arguments)?;
let mut idioms = Vec::<Idiom>::new();
for arg in args {
idioms.push(syn::idiom(&arg)?.into())
}
let mut idiom_results = Vec::new();
for idiom in idioms.iter() {
let res = crate::legacy::idiom_compute(idiom, stk, ctx, opt, Some(doc))
.await
.catch_return()?;
idiom_results.push(res);
}
if this.is_single() {
out = Value::Array(Array(idiom_results));
} else {
for (idiom, idiom_res) in idioms.iter().zip(idiom_results) {
crate::legacy::value_set(
&mut out, stk, ctx, opt, &idiom.0, idiom_res,
)
.await?;
}
}
}
Function::Normal(ref x) if x == "type::field" => {
let mut arguments = Vec::new();
for arg in f.arguments.iter() {
arguments.push(
stk.run(|stk| {
crate::legacy::expr_compute(arg, stk, ctx, opt, Some(doc))
})
.await
.catch_return()?,
);
}
let (arg,) = <(String,)>::from_args("type::field", arguments)?;
let idiom: Idiom = syn::idiom(&arg)?.into();
let res = crate::legacy::idiom_compute(&idiom, stk, ctx, opt, Some(doc))
.await
.catch_return()?;
if let Some(alias) = &v.alias {
crate::legacy::value_set(&mut out, stk, ctx, opt, alias, res).await?;
} else if this.is_single() {
out = res
} else {
crate::legacy::value_set(&mut out, stk, ctx, opt, &idiom.0, res)
.await?;
}
}
_ => {
let expr = stk
.run(|stk| {
crate::legacy::expr_compute(&v.expr, stk, ctx, opt, Some(doc))
})
.await
.catch_return()?;
if this.is_single() {
out = expr;
} else {
crate::legacy::value_set(&mut out, stk, ctx, opt, name.as_ref(), expr)
.await?;
}
}
}
}
_ => {
let expr = stk
.run(|stk| crate::legacy::expr_compute(&v.expr, stk, ctx, opt, Some(doc)))
.await
.catch_return()?;
if this.is_single() {
out = expr;
} else {
crate::legacy::value_set(&mut out, stk, ctx, opt, name.as_ref(), expr).await?;
}
}
}
}
Ok(out)
}