use crate::ctx::Context;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::statements::info::InfoStructure;
use crate::sql::{fmt::Fmt, Idiom, Part, Value};
use crate::syn;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::{self, Display, Formatter, Write};
use std::ops::Deref;
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub struct Fields(pub Vec<Field>, pub bool);
impl Fields {
pub fn all() -> Self {
Self(vec![Field::All], false)
}
pub fn is_all(&self) -> bool {
self.0.iter().any(|v| matches!(v, Field::All))
}
pub fn other(&self) -> impl Iterator<Item = &Field> {
self.0.iter().filter(|v| !matches!(v, Field::All))
}
pub fn single(&self) -> Option<&Field> {
match (self.0.len(), self.1) {
(1, true) => match self.0.first() {
Some(Field::All) => None,
Some(v) => Some(v),
_ => None,
},
_ => None,
}
}
pub(crate) fn is_count_all_only(&self) -> bool {
let mut is_count_only = false;
for field in &self.0 {
if let Field::Single {
expr: Value::Function(func),
..
} = field
{
if func.is_count_all() {
is_count_only = true;
continue;
}
}
return false;
}
is_count_only
}
}
impl Deref for Fields {
type Target = Vec<Field>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl IntoIterator for Fields {
type Item = Field;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Display for Fields {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.single() {
Some(v) => write!(f, "VALUE {}", &v),
None => Display::fmt(&Fmt::comma_separated(&self.0), f),
}
}
}
impl InfoStructure for Fields {
fn structure(self) -> Value {
self.to_string().into()
}
}
impl Fields {
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context,
opt: &Options,
doc: Option<&CursorDoc>,
group: bool,
) -> Result<Value, Error> {
if let Some(doc) = doc {
self.compute_value(stk, ctx, opt, doc, group).await
} else {
let doc = Value::None.into();
self.compute_value(stk, ctx, opt, &doc, group).await
}
}
async fn compute_value(
&self,
stk: &mut Stk,
ctx: &Context,
opt: &Options,
doc: &CursorDoc,
group: bool,
) -> Result<Value, Error> {
let opt = &opt.new_with_futures(true);
let mut out = match self.is_all() {
true => doc.doc.as_ref().compute(stk, ctx, opt, Some(doc)).await?,
false => Value::base(),
};
for v in self.other() {
match v {
Field::All => (),
Field::Single {
expr,
alias,
} => {
let name = alias
.as_ref()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
match expr {
Value::Function(f) if group && f.is_aggregate() => {
let x = match f.args().len() {
0 => f.compute(stk, ctx, opt, Some(doc)).await?,
_ => f.args()[0].compute(stk, ctx, opt, Some(doc)).await?,
};
match self.single().is_some() {
false => out.set(stk, ctx, opt, name.as_ref(), x).await?,
true => out = x,
}
}
Value::Idiom(v) if v.is_multi_yield() => {
let mut res: Vec<(&[Part], Value)> = Vec::new();
for v in v.split_inclusive(Idiom::split_multi_yield) {
let x = match res.last() {
Some((_, r)) => r,
None => doc.doc.as_ref(),
};
let x = x
.get(stk, ctx, opt, Some(doc), v)
.await?
.compute(stk, ctx, opt, Some(doc))
.await?
.flatten();
res.push((v, x));
}
for (p, x) in res {
match p.last().unwrap().alias() {
Some(a) => {
if let Some(i) = alias {
out.set(stk, ctx, opt, i, x.clone()).await?;
}
out.set(stk, ctx, opt, a, x).await?;
}
None => {
out.set(stk, ctx, opt, alias.as_ref().unwrap_or(v), x)
.await?
}
}
}
}
Value::Function(f) if f.name() == Some("type::fields") => {
let expr = expr.compute(stk, ctx, opt, Some(doc)).await?;
match self.single().is_some() {
false => {
let args = match f.args().first().unwrap() {
Value::Param(v) => {
v.compute(stk, ctx, opt, Some(doc)).await?
}
v => v.to_owned(),
};
let expr: Vec<Value> = expr.try_into()?;
let args: Vec<Value> = args.try_into()?;
for (name, expr) in args.into_iter().zip(expr) {
let name = syn::idiom(&name.to_raw_string())?;
out.set(stk, ctx, opt, name.as_ref(), expr).await?
}
}
true => out = expr,
}
}
Value::Function(f) if f.name() == Some("type::field") => {
let expr = expr.compute(stk, ctx, opt, Some(doc)).await?;
match self.single().is_some() {
false => {
let name = match f.args().first().unwrap() {
Value::Param(v) => {
v.compute(stk, ctx, opt, Some(doc)).await?
}
v => v.to_owned(),
};
let name = if let Some(x) = alias.as_ref().map(Cow::Borrowed) {
x
} else {
Cow::Owned(syn::idiom(&name.to_raw_string())?)
};
out.set(stk, ctx, opt, name.as_ref(), expr).await?
}
true => out = expr,
}
}
_ => {
let expr = expr.compute(stk, ctx, opt, Some(doc)).await?;
if self.single().is_some() {
out = expr;
} else {
out.set(stk, ctx, opt, name.as_ref(), expr).await?;
}
}
}
}
}
}
Ok(out)
}
}
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub enum Field {
#[default]
All,
Single {
expr: Value,
alias: Option<Idiom>,
},
}
impl Display for Field {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::All => f.write_char('*'),
Self::Single {
expr,
alias,
} => {
Display::fmt(expr, f)?;
if let Some(alias) = alias {
f.write_str(" AS ")?;
Display::fmt(alias, f)
} else {
Ok(())
}
}
}
}
}