1use crate::ctx::Context;
2use crate::dbs::Options;
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::sql::statements::info::InfoStructure;
6use crate::sql::{
7 fmt::{fmt_separated_by, Fmt},
8 part::{Next, NextMethod},
9 paths::{ID, IN, META, OUT},
10 Part, Value,
11};
12use md5::{Digest, Md5};
13use reblessive::tree::Stk;
14use revision::revisioned;
15use serde::{Deserialize, Serialize};
16use std::fmt::{self, Display, Formatter};
17use std::ops::Deref;
18use std::str;
19
20pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Idiom";
21
22#[revisioned(revision = 1)]
23#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[non_exhaustive]
26pub struct Idioms(pub Vec<Idiom>);
27
28impl Deref for Idioms {
29 type Target = Vec<Idiom>;
30 fn deref(&self) -> &Self::Target {
31 &self.0
32 }
33}
34
35impl IntoIterator for Idioms {
36 type Item = Idiom;
37 type IntoIter = std::vec::IntoIter<Self::Item>;
38 fn into_iter(self) -> Self::IntoIter {
39 self.0.into_iter()
40 }
41}
42
43impl Display for Idioms {
44 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
45 Display::fmt(&Fmt::comma_separated(&self.0), f)
46 }
47}
48
49impl InfoStructure for Idioms {
50 fn structure(self) -> Value {
51 self.to_string().into()
52 }
53}
54
55#[revisioned(revision = 1)]
56#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
57#[serde(rename = "$surrealdb::private::sql::Idiom")]
58#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
59#[non_exhaustive]
60pub struct Idiom(pub Vec<Part>);
61
62impl Deref for Idiom {
63 type Target = [Part];
64 fn deref(&self) -> &Self::Target {
65 self.0.as_slice()
66 }
67}
68
69impl From<String> for Idiom {
70 fn from(v: String) -> Self {
71 Self(vec![Part::from(v)])
72 }
73}
74
75impl From<&str> for Idiom {
76 fn from(v: &str) -> Self {
77 Self(vec![Part::from(v)])
78 }
79}
80
81impl From<Vec<Part>> for Idiom {
82 fn from(v: Vec<Part>) -> Self {
83 Self(v)
84 }
85}
86
87impl From<&[Part]> for Idiom {
88 fn from(v: &[Part]) -> Self {
89 Self(v.to_vec())
90 }
91}
92
93impl From<Part> for Idiom {
94 fn from(v: Part) -> Self {
95 Self(vec![v])
96 }
97}
98
99impl Idiom {
100 pub(crate) fn push(mut self, n: Part) -> Idiom {
102 self.0.push(n);
103 self
104 }
105 pub(crate) fn to_hash(&self) -> String {
107 let mut hasher = Md5::new();
108 hasher.update(self.to_string().as_str());
109 format!("{:x}", hasher.finalize())
110 }
111 pub(crate) fn to_path(&self) -> String {
113 format!("/{self}").replace(']', "").replace(&['.', '['][..], "/")
114 }
115 pub(crate) fn simplify(&self) -> Idiom {
117 self.0
118 .iter()
119 .filter(|&p| matches!(p, Part::Field(_) | Part::Start(_) | Part::Graph(_)))
120 .cloned()
121 .collect::<Vec<_>>()
122 .into()
123 }
124 pub(crate) fn is_id(&self) -> bool {
126 self.0.len() == 1 && self.0[0].eq(&ID[0])
127 }
128 pub(crate) fn is_special(&self) -> bool {
130 self.0.len() == 1 && [&ID[0], &IN[0], &OUT[0], &META[0]].iter().any(|f| self.0[0].eq(f))
131 }
132 pub(crate) fn is_field(&self, other: &[Part]) -> bool {
134 self.as_ref().eq(other)
135 }
136 pub(crate) fn is_multi_yield(&self) -> bool {
138 self.iter().any(Self::split_multi_yield)
139 }
140 pub(crate) fn split_multi_yield(v: &Part) -> bool {
142 matches!(v, Part::Graph(g) if g.alias.is_some())
143 }
144 pub(crate) fn remove_trailing_all(&mut self) {
146 if self.ends_with(&[Part::All]) {
147 self.0.truncate(self.len() - 1);
148 }
149 }
150 pub(crate) fn starts_with(&self, other: &[Part]) -> bool {
152 self.0.starts_with(other)
153 }
154}
155
156impl Idiom {
157 pub(crate) fn writeable(&self) -> bool {
159 self.0.iter().any(|v| v.writeable())
160 }
161 pub(crate) async fn compute(
163 &self,
164 stk: &mut Stk,
165 ctx: &Context,
166 opt: &Options,
167 doc: Option<&CursorDoc>,
168 ) -> Result<Value, Error> {
169 match self.first() {
170 Some(Part::Start(v)) => {
172 v.compute(stk, ctx, opt, doc)
173 .await?
174 .get(stk, ctx, opt, doc, self.as_ref().next())
175 .await?
176 .compute(stk, ctx, opt, doc)
177 .await
178 }
179 _ => match doc {
181 Some(v) => {
183 v.doc
184 .as_ref()
185 .get(stk, ctx, opt, doc, self)
186 .await?
187 .compute(stk, ctx, opt, doc)
188 .await
189 }
190 None => {
192 Value::None
193 .get(stk, ctx, opt, doc, self.next_method())
194 .await?
195 .compute(stk, ctx, opt, doc)
196 .await
197 }
198 },
199 }
200 }
201}
202
203impl Display for Idiom {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 Display::fmt(
206 &Fmt::new(
207 self.0.iter().enumerate().map(|args| {
208 Fmt::new(args, |(i, p), f| match (i, p) {
209 (0, Part::Field(v)) => Display::fmt(v, f),
210 _ => Display::fmt(p, f),
211 })
212 }),
213 fmt_separated_by(""),
214 ),
215 f,
216 )
217 }
218}
219
220impl InfoStructure for Idiom {
221 fn structure(self) -> Value {
222 self.to_string().into()
223 }
224}