surrealdb_core/sql/
part.rs

1use crate::sql::{fmt::Fmt, strand::no_nul_bytes, Graph, Ident, Idiom, Number, Value};
2use revision::revisioned;
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::fmt::Write;
6use std::str;
7
8use super::fmt::{is_pretty, pretty_indent};
9
10#[revisioned(revision = 2)]
11#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
12#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
13#[non_exhaustive]
14pub enum Part {
15	All,
16	Flatten,
17	Last,
18	First,
19	Field(Ident),
20	Index(Number),
21	Where(Value),
22	Graph(Graph),
23	Value(Value),
24	Start(Value),
25	Method(#[serde(with = "no_nul_bytes")] String, Vec<Value>),
26	#[revision(start = 2)]
27	Destructure(Vec<DestructurePart>),
28	Optional,
29}
30
31impl From<i32> for Part {
32	fn from(v: i32) -> Self {
33		Self::Index(v.into())
34	}
35}
36
37impl From<isize> for Part {
38	fn from(v: isize) -> Self {
39		Self::Index(v.into())
40	}
41}
42
43impl From<usize> for Part {
44	fn from(v: usize) -> Self {
45		Self::Index(v.into())
46	}
47}
48
49impl From<String> for Part {
50	fn from(v: String) -> Self {
51		Self::Field(v.into())
52	}
53}
54
55impl From<Number> for Part {
56	fn from(v: Number) -> Self {
57		Self::Index(v)
58	}
59}
60
61impl From<Ident> for Part {
62	fn from(v: Ident) -> Self {
63		Self::Field(v)
64	}
65}
66
67impl From<Graph> for Part {
68	fn from(v: Graph) -> Self {
69		Self::Graph(v)
70	}
71}
72
73impl From<&str> for Part {
74	fn from(v: &str) -> Self {
75		match v.parse::<isize>() {
76			Ok(v) => Self::from(v),
77			_ => Self::from(v.to_owned()),
78		}
79	}
80}
81
82impl Part {
83	/// Check if we require a writeable transaction
84	pub(crate) fn writeable(&self) -> bool {
85		match self {
86			Part::Start(v) => v.writeable(),
87			Part::Where(v) => v.writeable(),
88			Part::Value(v) => v.writeable(),
89			Part::Method(_, v) => v.iter().any(Value::writeable),
90			_ => false,
91		}
92	}
93	/// Returns a yield if an alias is specified
94	pub(crate) fn alias(&self) -> Option<&Idiom> {
95		match self {
96			Part::Graph(v) => v.alias.as_ref(),
97			_ => None,
98		}
99	}
100}
101
102impl fmt::Display for Part {
103	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104		match self {
105			Part::All => f.write_str("[*]"),
106			Part::Last => f.write_str("[$]"),
107			Part::First => f.write_str("[0]"),
108			Part::Start(v) => write!(f, "{v}"),
109			Part::Field(v) => write!(f, ".{v}"),
110			Part::Flatten => f.write_str("…"),
111			Part::Index(v) => write!(f, "[{v}]"),
112			Part::Where(v) => write!(f, "[WHERE {v}]"),
113			Part::Graph(v) => write!(f, "{v}"),
114			Part::Value(v) => write!(f, "[{v}]"),
115			Part::Method(v, a) => write!(f, ".{v}({})", Fmt::comma_separated(a)),
116			Part::Destructure(v) => {
117				f.write_str(".{")?;
118				if !is_pretty() {
119					f.write_char(' ')?;
120				}
121				if !v.is_empty() {
122					let indent = pretty_indent();
123					write!(f, "{}", Fmt::pretty_comma_separated(v))?;
124					drop(indent);
125				}
126				if is_pretty() {
127					f.write_char('}')
128				} else {
129					f.write_str(" }")
130				}
131			}
132			Part::Optional => write!(f, "?"),
133		}
134	}
135}
136
137// ------------------------------
138
139pub trait Next<'a> {
140	fn next(&'a self) -> &[Part];
141}
142
143impl<'a> Next<'a> for &'a [Part] {
144	fn next(&'a self) -> &'a [Part] {
145		match self.len() {
146			0 => &[],
147			_ => &self[1..],
148		}
149	}
150}
151
152// ------------------------------
153
154pub trait NextMethod<'a> {
155	fn next_method(&'a self) -> &[Part];
156}
157
158impl<'a> NextMethod<'a> for &'a [Part] {
159	fn next_method(&'a self) -> &'a [Part] {
160		match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
161			None => &[],
162			Some(i) => &self[i..],
163		}
164	}
165}
166
167impl<'a> NextMethod<'a> for &'a Idiom {
168	fn next_method(&'a self) -> &'a [Part] {
169		match self.iter().position(|p| matches!(p, Part::Method(_, _))) {
170			None => &[],
171			Some(i) => &self[i..],
172		}
173	}
174}
175
176// ------------------------------
177
178#[revisioned(revision = 1)]
179#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
180#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
181#[non_exhaustive]
182pub enum DestructurePart {
183	All(Ident),
184	Field(Ident),
185	Aliased(Ident, Idiom),
186	Destructure(Ident, Vec<DestructurePart>),
187}
188
189impl DestructurePart {
190	pub fn field(&self) -> &Ident {
191		match self {
192			DestructurePart::All(v) => v,
193			DestructurePart::Field(v) => v,
194			DestructurePart::Aliased(v, _) => v,
195			DestructurePart::Destructure(v, _) => v,
196		}
197	}
198
199	pub fn path(&self) -> Vec<Part> {
200		match self {
201			DestructurePart::All(v) => vec![Part::Field(v.clone()), Part::All],
202			DestructurePart::Field(v) => vec![Part::Field(v.clone())],
203			DestructurePart::Aliased(_, v) => v.0.clone(),
204			DestructurePart::Destructure(f, d) => {
205				vec![Part::Field(f.clone()), Part::Destructure(d.clone())]
206			}
207		}
208	}
209}
210
211impl fmt::Display for DestructurePart {
212	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213		match self {
214			DestructurePart::All(fd) => write!(f, "{fd}.*"),
215			DestructurePart::Field(fd) => write!(f, "{fd}"),
216			DestructurePart::Aliased(fd, v) => write!(f, "{fd}: {v}"),
217			DestructurePart::Destructure(fd, d) => {
218				write!(f, "{fd}{}", Part::Destructure(d.clone()))
219			}
220		}
221	}
222}