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