dynamodb_expression/path/
element.rs1use core::{
4 fmt::{self, Write},
5 mem,
6 str::FromStr,
7};
8
9use super::{Name, PathParseError};
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub enum Element {
20 Name(Name),
21 IndexedField(IndexedField),
22}
23
24impl Element {
25 pub fn new_name<T>(name: T) -> Self
32 where
33 T: Into<Name>,
34 {
35 Self::Name(name.into())
36 }
37
38 pub fn new_indexed_field<N, I>(name: N, indexes: I) -> Self
68 where
69 N: Into<Name>,
70 I: Indexes,
71 {
72 let indexes = indexes.into_indexes();
73 if indexes.is_empty() {
74 Self::new_name(name)
75 } else {
76 Self::IndexedField(IndexedField {
77 name: name.into(),
78 indexes,
79 })
80 }
81 }
82}
83
84impl fmt::Display for Element {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 match self {
87 Element::Name(name) => name.fmt(f),
88 Element::IndexedField(field_index) => field_index.fmt(f),
89 }
90 }
91}
92
93impl From<Element> for String {
94 fn from(element: Element) -> Self {
95 match element {
96 Element::Name(name) => name.into(),
97 Element::IndexedField(new_indexed_field) => new_indexed_field.to_string(),
98 }
99 }
100}
101
102impl From<IndexedField> for Element {
103 fn from(value: IndexedField) -> Self {
104 if value.indexes.is_empty() {
105 Self::Name(value.name)
106 } else {
107 Self::IndexedField(value)
108 }
109 }
110}
111
112impl<N, I> From<(N, I)> for Element
113where
114 N: Into<Name>,
115 I: Indexes,
116{
117 fn from((name, indexes): (N, I)) -> Self {
118 Self::new_indexed_field(name, indexes)
119 }
120}
121
122impl From<Name> for Element {
123 fn from(name: Name) -> Self {
124 Self::Name(name)
125 }
126}
127
128impl FromStr for Element {
133 type Err = PathParseError;
134
135 fn from_str(input: &str) -> Result<Self, Self::Err> {
136 let mut remaining = input;
137 let mut name = None;
138 let mut indexes = Vec::new();
139 while !remaining.is_empty() {
140 let open = remaining.find('[');
141 let close = remaining.find(']');
142
143 match (open, close) {
144 (None, None) => {
145 if name.is_some() {
146 return Err(PathParseError);
148 }
149
150 name = Some(mem::take(&mut remaining));
152 break;
153 }
154 (None, Some(_close)) => return Err(PathParseError),
155 (Some(_open), None) => return Err(PathParseError),
156 (Some(open), Some(close)) => {
157 if open >= close {
158 return Err(PathParseError);
160 }
161
162 if name.is_none() {
163 if open > 0 {
164 name = Some(&remaining[..open]);
165 } else {
166 return Err(PathParseError);
169 }
170 } else if open > 0 {
171 return Err(PathParseError);
174 }
175
176 let index: usize = remaining[open + 1..close]
178 .parse()
179 .map_err(|_| PathParseError)?;
180 indexes.push(index);
181
182 remaining = &remaining[close + 1..];
183 }
184 }
185 }
186
187 Ok(if indexes.is_empty() {
188 Self::Name(input.into())
189 } else {
190 if !remaining.is_empty() {
191 return Err(PathParseError);
194 }
195
196 let name = name.ok_or(PathParseError)?;
197
198 indexes.shrink_to_fit();
199
200 Self::IndexedField(IndexedField {
201 name: name.into(),
202 indexes,
203 })
204 })
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
219pub struct IndexedField {
220 pub(crate) name: Name,
221 indexes: Vec<usize>,
222}
223
224impl fmt::Display for IndexedField {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 self.name.fmt(f)?;
227 self.indexes.iter().try_for_each(|index| {
228 f.write_char('[')?;
229 index.fmt(f)?;
230 f.write_char(']')
231 })
232 }
233}
234
235pub trait Indexes {
241 fn into_indexes(self) -> Vec<usize>;
242}
243
244impl Indexes for usize {
245 fn into_indexes(self) -> Vec<usize> {
246 vec![self]
247 }
248}
249
250impl Indexes for Vec<usize> {
251 fn into_indexes(self) -> Vec<usize> {
252 self
253 }
254}
255
256impl Indexes for &[usize] {
257 fn into_indexes(self) -> Vec<usize> {
258 self.to_vec()
259 }
260}
261
262impl<const N: usize> Indexes for [usize; N] {
263 fn into_indexes(self) -> Vec<usize> {
264 self.to_vec()
265 }
266}
267
268impl<const N: usize> Indexes for &[usize; N] {
269 fn into_indexes(self) -> Vec<usize> {
270 self.to_vec()
271 }
272}
273
274#[cfg(test)]
275mod test {
276 use pretty_assertions::assert_eq;
277
278 use crate::{Num, Path};
279
280 use super::{Element, Name};
281
282 #[test]
283 fn display_name() {
284 let path = Element::new_name("foo");
285 assert_eq!("foo", path.to_string());
286 }
287
288 #[test]
289 fn display_indexed() {
290 let path = Element::new_indexed_field("foo", 42);
294 assert_eq!("foo[42]", path.to_string());
295
296 let path = Element::new_indexed_field("foo", [42]);
298 assert_eq!("foo[42]", path.to_string());
299
300 let path = Element::new_indexed_field("foo", &([42, 37, 9])[..]);
302 assert_eq!("foo[42][37][9]", path.to_string());
303 }
304
305 #[test]
306 fn display_path() {
307 let path: Path = ["foo", "bar"].into_iter().map(Name::from).collect();
308 assert_eq!("foo.bar", path.to_string());
309
310 let path = Path::from_iter([
311 Element::new_name("foo"),
312 Element::new_indexed_field("bar", 42),
313 ]);
314 assert_eq!("foo.bar[42]", path.to_string());
315
316 let path = Path::from_iter([
317 Element::new_indexed_field("foo", 42),
318 Element::new_name("bar"),
319 ]);
320 assert_eq!("foo[42].bar", path.to_string());
321 }
322
323 #[test]
324 fn size() {
325 assert_eq!(
326 "size(a) = 0",
327 "a".parse::<Path>()
328 .unwrap()
329 .size()
330 .equal(Num::new(0))
331 .to_string()
332 );
333 }
334}