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_separated_by, Fmt},
part::{Next, NextMethod},
paths::{ID, IN, META, OUT},
Part, Value,
};
use md5::{Digest, Md5};
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Display, Formatter};
use std::ops::Deref;
use std::str;
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Idiom";
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub struct Idioms(pub Vec<Idiom>);
impl Deref for Idioms {
type Target = Vec<Idiom>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl IntoIterator for Idioms {
type Item = Idiom;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl Display for Idioms {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&Fmt::comma_separated(&self.0), f)
}
}
impl InfoStructure for Idioms {
fn structure(self) -> Value {
self.to_string().into()
}
}
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[serde(rename = "$surrealdb::private::sql::Idiom")]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub struct Idiom(pub Vec<Part>);
impl Deref for Idiom {
type Target = [Part];
fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
impl From<String> for Idiom {
fn from(v: String) -> Self {
Self(vec![Part::from(v)])
}
}
impl From<&str> for Idiom {
fn from(v: &str) -> Self {
Self(vec![Part::from(v)])
}
}
impl From<Vec<Part>> for Idiom {
fn from(v: Vec<Part>) -> Self {
Self(v)
}
}
impl From<&[Part]> for Idiom {
fn from(v: &[Part]) -> Self {
Self(v.to_vec())
}
}
impl From<Part> for Idiom {
fn from(v: Part) -> Self {
Self(vec![v])
}
}
impl Idiom {
pub(crate) fn push(mut self, n: Part) -> Idiom {
self.0.push(n);
self
}
pub(crate) fn to_hash(&self) -> String {
let mut hasher = Md5::new();
hasher.update(self.to_string().as_str());
format!("{:x}", hasher.finalize())
}
pub(crate) fn to_path(&self) -> String {
format!("/{self}").replace(']', "").replace(&['.', '['][..], "/")
}
pub(crate) fn simplify(&self) -> Idiom {
self.0
.iter()
.filter(|&p| matches!(p, Part::Field(_) | Part::Start(_) | Part::Graph(_)))
.cloned()
.collect::<Vec<_>>()
.into()
}
pub(crate) fn is_id(&self) -> bool {
self.0.len() == 1 && self.0[0].eq(&ID[0])
}
pub(crate) fn is_in(&self) -> bool {
self.0.len() == 1 && self.0[0].eq(&IN[0])
}
pub(crate) fn is_out(&self) -> bool {
self.0.len() == 1 && self.0[0].eq(&OUT[0])
}
pub(crate) fn is_meta(&self) -> bool {
self.0.len() == 1 && self.0[0].eq(&META[0])
}
pub(crate) fn is_field(&self, other: &[Part]) -> bool {
self.as_ref().eq(other)
}
pub(crate) fn is_multi_yield(&self) -> bool {
self.iter().any(Self::split_multi_yield)
}
pub(crate) fn split_multi_yield(v: &Part) -> bool {
matches!(v, Part::Graph(g) if g.alias.is_some())
}
pub(crate) fn remove_trailing_all(&mut self) {
if self.ends_with(&[Part::All]) {
self.0.truncate(self.len() - 1);
}
}
pub(crate) fn starts_with(&self, other: &[Part]) -> bool {
self.0.starts_with(other)
}
}
impl Idiom {
pub(crate) fn writeable(&self) -> bool {
self.0.iter().any(|v| v.writeable())
}
pub(crate) async fn compute(
&self,
stk: &mut Stk,
ctx: &Context,
opt: &Options,
doc: Option<&CursorDoc>,
) -> Result<Value, Error> {
match self.first() {
Some(Part::Start(v)) => {
v.compute(stk, ctx, opt, doc)
.await?
.get(stk, ctx, opt, doc, self.as_ref().next())
.await?
.compute(stk, ctx, opt, doc)
.await
}
_ => match doc {
Some(v) => {
v.doc
.as_ref()
.get(stk, ctx, opt, doc, self)
.await?
.compute(stk, ctx, opt, doc)
.await
}
None => {
Value::None
.get(stk, ctx, opt, doc, self.next_method())
.await?
.compute(stk, ctx, opt, doc)
.await
}
},
}
}
}
impl Display for Idiom {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt(
&Fmt::new(
self.0.iter().enumerate().map(|args| {
Fmt::new(args, |(i, p), f| match (i, p) {
(0, Part::Field(v)) => Display::fmt(v, f),
_ => Display::fmt(p, f),
})
}),
fmt_separated_by(""),
),
f,
)
}
}
impl InfoStructure for Idiom {
fn structure(self) -> Value {
self.to_string().into()
}
}