1use crate::ctx::Context;
2use crate::dbs::{Options, Transaction};
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::fmt::{fmt_separated_by, Fmt};
6use crate::part::Next;
7use crate::part::Part;
8use crate::paths::{ID, IN, META, OUT};
9use crate::value::Value;
10use md5::Digest;
11use md5::Md5;
12use revision::revisioned;
13use serde::{Deserialize, Serialize};
14use std::fmt::{self, Display, Formatter};
15use std::ops::Deref;
16use std::str;
17
18pub(crate) const TOKEN: &str = "$surrealdb::private::crate::Idiom";
19
20#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
21#[revisioned(revision = 1)]
22pub struct Idioms(pub Vec<Idiom>);
23
24impl Deref for Idioms {
25 type Target = Vec<Idiom>;
26 fn deref(&self) -> &Self::Target {
27 &self.0
28 }
29}
30
31impl IntoIterator for Idioms {
32 type Item = Idiom;
33 type IntoIter = std::vec::IntoIter<Self::Item>;
34 fn into_iter(self) -> Self::IntoIter {
35 self.0.into_iter()
36 }
37}
38
39impl Display for Idioms {
40 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
41 Display::fmt(&Fmt::comma_separated(&self.0), f)
42 }
43}
44
45#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
46#[serde(rename = "$surrealdb::private::crate::Idiom")]
47#[revisioned(revision = 1)]
48pub struct Idiom(pub Vec<Part>);
49
50impl Deref for Idiom {
51 type Target = [Part];
52 fn deref(&self) -> &Self::Target {
53 self.0.as_slice()
54 }
55}
56
57impl From<String> for Idiom {
58 fn from(v: String) -> Self {
59 Self(vec![Part::from(v)])
60 }
61}
62
63impl From<Vec<Part>> for Idiom {
64 fn from(v: Vec<Part>) -> Self {
65 Self(v)
66 }
67}
68
69impl From<&[Part]> for Idiom {
70 fn from(v: &[Part]) -> Self {
71 Self(v.to_vec())
72 }
73}
74
75impl Idiom {
76 pub(crate) fn push(mut self, n: Part) -> Idiom {
78 self.0.push(n);
79 self
80 }
81 pub(crate) fn to_hash(&self) -> String {
83 let mut hasher = Md5::new();
84 hasher.update(self.to_string().as_str());
85 format!("{:x}", hasher.finalize())
86 }
87 pub(crate) fn to_path(&self) -> String {
89 format!("/{self}").replace(']', "").replace(&['.', '['][..], "/")
90 }
91 pub(crate) fn simplify(&self) -> Idiom {
93 self.0
94 .iter()
95 .filter(|&p| {
96 matches!(p, Part::Field(_) | Part::Start(_) | Part::Value(_) | Part::Graph(_))
97 })
98 .cloned()
99 .collect::<Vec<_>>()
100 .into()
101 }
102 pub(crate) fn is_id(&self) -> bool {
104 self.0.len() == 1 && self.0[0].eq(&ID[0])
105 }
106 pub(crate) fn is_in(&self) -> bool {
108 self.0.len() == 1 && self.0[0].eq(&IN[0])
109 }
110 pub(crate) fn is_out(&self) -> bool {
112 self.0.len() == 1 && self.0[0].eq(&OUT[0])
113 }
114 pub(crate) fn is_meta(&self) -> bool {
116 self.0.len() == 1 && self.0[0].eq(&META[0])
117 }
118 pub(crate) fn is_multi_yield(&self) -> bool {
120 self.iter().any(Self::split_multi_yield)
121 }
122 pub(crate) fn split_multi_yield(v: &Part) -> bool {
124 matches!(v, Part::Graph(g) if g.alias.is_some())
125 }
126}
127
128impl Idiom {
129 pub(crate) fn writeable(&self) -> bool {
131 self.0.iter().any(|v| v.writeable())
132 }
133 pub(crate) async fn compute(
135 &self,
136 ctx: &Context<'_>,
137 opt: &Options,
138 txn: &Transaction,
139 doc: Option<&CursorDoc<'_>>,
140 ) -> Result<Value, Error> {
141 match self.first() {
142 Some(Part::Start(v)) => {
144 v.compute(ctx, opt, txn, doc)
145 .await?
146 .get(ctx, opt, txn, doc, self.as_ref().next())
147 .await?
148 .compute(ctx, opt, txn, doc)
149 .await
150 }
151 _ => match doc {
153 Some(v) => {
155 v.doc.get(ctx, opt, txn, doc, self).await?.compute(ctx, opt, txn, doc).await
156 }
157 None => Ok(Value::None),
159 },
160 }
161 }
162}
163
164impl Display for Idiom {
165 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166 Display::fmt(
167 &Fmt::new(
168 self.0.iter().enumerate().map(|args| {
169 Fmt::new(args, |(i, p), f| match (i, p) {
170 (0, Part::Field(v)) => Display::fmt(v, f),
171 _ => Display::fmt(p, f),
172 })
173 }),
174 fmt_separated_by(""),
175 ),
176 f,
177 )
178 }
179}