surrealdb_core/api/
path.rs1use std::fmt::{self, Display, Formatter};
2use std::ops::Deref;
3use std::str::FromStr;
4
5use revision::revisioned;
6use serde::{Deserialize, Serialize};
7
8use crate::err::Error;
9use crate::expr::Kind;
10use crate::expr::fmt::{Fmt, fmt_separated_by};
11use crate::syn;
12use crate::val::{Array, Object, Strand, Value};
13
14#[revisioned(revision = 1)]
15#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, Hash)]
16pub struct Path(pub Vec<Segment>);
17
18impl<'a> Path {
19 pub fn fit(&'a self, segments: &'a [&'a str]) -> Option<Object> {
29 let mut obj = Object::default();
30 for (i, segment) in self.iter().enumerate() {
31 if let Some(res) = segment.fit(&segments[i..]) {
32 if let Some((k, v)) = res {
33 obj.insert(k, v);
34 }
35 } else {
36 return None;
37 }
38 }
39
40 if segments.len() == self.len() || matches!(self.last(), Some(Segment::Rest(_))) {
41 Some(obj)
42 } else {
43 None
44 }
45 }
46
47 pub fn specificity(&self) -> u8 {
48 self.iter().map(|s| s.specificity()).sum()
49 }
50}
51
52impl From<Vec<Segment>> for Path {
53 fn from(segments: Vec<Segment>) -> Self {
54 Path(segments)
55 }
56}
57
58impl Deref for Path {
59 type Target = Vec<Segment>;
60 fn deref(&self) -> &Self::Target {
61 &self.0
62 }
63}
64
65impl IntoIterator for Path {
66 type Item = Segment;
67 type IntoIter = std::vec::IntoIter<Self::Item>;
68 fn into_iter(self) -> Self::IntoIter {
69 self.0.into_iter()
70 }
71}
72
73impl Display for Path {
74 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
75 write!(f, "/")?;
76 Display::fmt(&Fmt::new(self.iter(), fmt_separated_by("/")), f)
77 }
78}
79
80impl FromStr for Path {
81 type Err = Error;
82 fn from_str(s: &str) -> Result<Self, Self::Err> {
83 if s.is_empty() {
84 return Err(Error::InvalidPath("Path cannot be empty".into()));
85 }
86
87 let mut chars = s.chars().peekable();
88 let mut segments: Vec<Segment> = Vec::new();
89
90 while let Some(c) = chars.next() {
91 if c != '/' {
92 return Err(Error::InvalidPath("Segment should start with /".into()));
93 }
94
95 let mut scratch = String::new();
96 let mut kind: Option<Kind> = None;
97
98 'segment: while let Some(c) = chars.peek() {
99 match c {
100 '/' if scratch.is_empty() => {
101 chars.next();
102 continue 'segment;
103 }
104
105 '\\' if scratch.is_empty() => {
108 chars.next();
109 if let Some(x @ ':' | x @ '*') = chars.next() {
110 scratch.push('\\');
111 scratch.push(x);
112 continue 'segment;
113 } else {
114 return Err(Error::InvalidPath("Expected an instruction symbol `:` or `*` to follow after an escape character".into()));
115 }
116 }
117
118 x if x.is_ascii_alphanumeric() => (),
120 '.' | '-' | '_' | '~' | '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+'
121 | ',' | ';' | '=' | ':' | '@' => (),
122
123 '<' if scratch.starts_with(':') => {
125 if scratch.len() == 1 {
126 return Err(Error::InvalidPath(
127 "Encountered a type, but expected a name or content for this segment first".into(),
128 ));
129 }
130
131 chars.next();
133
134 let mut balance = 0;
135 let mut inner = String::new();
136
137 'kind: loop {
138 let Some(c) = chars.next() else {
139 return Err(Error::InvalidPath(
140 "Kind segment did not close".into(),
141 ));
142 };
143
144 if c == '<' {
146 balance += 1;
147 } else if c == '>' {
148 if balance == 0 {
149 break 'kind;
150 } else {
151 balance -= 1;
152 }
153 }
154
155 inner.push(c);
156 }
157
158 kind = Some(
159 syn::kind(&inner)
160 .map_err(|e| Error::InvalidPath(e.to_string()))?
161 .into(),
162 );
163
164 break 'segment;
165 }
166
167 _ => {
169 break 'segment;
170 }
171 }
172
173 if let Some(c) = chars.next() {
174 scratch.push(c);
175 } else {
176 return Err(Error::Unreachable(
177 "Expected to find a character as we peeked it before".into(),
178 ));
179 }
180 }
181
182 let (segment, done) = if scratch.is_empty() {
183 break;
184 } else if (scratch.starts_with(':')
185 || scratch.starts_with('*')
186 || scratch.starts_with('\\'))
187 && scratch[1..].is_empty()
188 {
189 return Err(Error::InvalidPath(
192 "Expected a name or content for this segment".into(),
193 ));
194 } else if let Some(name) = scratch.strip_prefix(':') {
195 let segment = Segment::Dynamic(name.to_string(), kind);
196 (segment, false)
197 } else if let Some(name) = scratch.strip_prefix('*') {
198 let segment = Segment::Rest(name.to_string());
199 (segment, true)
200 } else if let Some(name) = scratch.strip_prefix('\\') {
201 let segment = Segment::Fixed(name.to_string());
202 (segment, false)
203 } else {
204 let segment = Segment::Fixed(scratch.to_string());
205 (segment, false)
206 };
207
208 segments.push(segment);
209
210 if done {
211 break;
212 }
213 }
214
215 if chars.peek().is_some() {
216 return Err(Error::InvalidPath("Path not finished".into()));
217 }
218
219 if segments.len() > MAX_PATH_SEGMENTS as usize {
220 return Err(Error::InvalidPath(format!(
221 "Path cannot have more than {MAX_PATH_SEGMENTS} segments"
222 )));
223 }
224
225 Ok(Self(segments))
226 }
227}
228
229#[revisioned(revision = 1)]
230#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
231pub enum Segment {
232 Fixed(String),
233 Dynamic(String, Option<Kind>),
234 Rest(String),
235}
236
237pub const MAX_PATH_SPECIFICITY: u8 = 255;
238pub const MAX_PATH_SEGMENTS: u8 = MAX_PATH_SPECIFICITY / 3; impl Segment {
241 fn fit(&self, segments: &[&str]) -> Option<Option<(String, Value)>> {
242 if let Some(current) = segments.first() {
243 match self {
244 Self::Fixed(x) if x == current => Some(None),
245 Self::Dynamic(x, k) => {
246 let val: Value = current.to_owned().into();
247 let val: Option<Value> = match k {
248 None => Some(val),
249 Some(k) => val.cast_to_kind(k).ok(),
250 };
251
252 val.map(|val| Some((x.to_owned(), val)))
253 }
254 Self::Rest(x) => {
255 let values = segments
257 .iter()
258 .copied()
259 .map(|x| Value::Strand(Strand::new(x.to_owned()).unwrap()))
260 .collect::<Vec<_>>();
261
262 Some(Some((x.to_owned(), Value::Array(Array(values)))))
263 }
264 _ => None,
265 }
266 } else {
267 None
268 }
269 }
270
271 fn specificity(&self) -> u8 {
272 match self {
273 Self::Fixed(_) => 3,
274 Self::Dynamic(_, _) => 2,
275 Self::Rest(_) => 1,
276 }
277 }
278}
279
280impl Display for Segment {
281 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
282 match self {
283 Self::Fixed(v) => write!(f, "{v}"),
284 Self::Dynamic(v, k) => {
285 write!(f, ":{v}")?;
286 if let Some(k) = k {
287 write!(f, "<{k}>")?;
288 }
289
290 Ok(())
291 }
292 Self::Rest(v) => write!(f, "*{v}"),
293 }
294 }
295}