datalogic_rs/value/
access.rs1use super::data_value::DataValue;
7use crate::arena::DataArena;
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum PathSegment<'a> {
12 Key(&'a str),
14
15 Index(usize),
17}
18
19impl<'a> PathSegment<'a> {
20 pub fn key(arena: &'a DataArena, key: &str) -> Self {
22 PathSegment::Key(arena.intern_str(key))
23 }
24
25 pub fn index(index: usize) -> Self {
27 PathSegment::Index(index)
28 }
29
30 pub fn parse(arena: &'a DataArena, segment: &str) -> Self {
32 if let Ok(index) = segment.parse::<usize>() {
33 PathSegment::Index(index)
34 } else {
35 PathSegment::Key(arena.intern_str(segment))
36 }
37 }
38}
39
40pub trait ValueAccess<'a> {
42 fn get_path(&self, path: &[PathSegment<'a>]) -> Option<&DataValue<'a>>;
44
45 fn get_path_str(&self, arena: &'a DataArena, path: &str) -> Option<&DataValue<'a>>;
47}
48
49impl<'a> ValueAccess<'a> for DataValue<'a> {
50 fn get_path(&self, path: &[PathSegment<'a>]) -> Option<&DataValue<'a>> {
51 if path.is_empty() {
52 return Some(self);
53 }
54
55 let (segment, rest) = path.split_first().unwrap();
56
57 match segment {
58 PathSegment::Key(key) => match self {
59 DataValue::Object(entries) => {
60 for (k, v) in *entries {
61 if *k == *key {
62 if rest.is_empty() {
63 return Some(v);
64 } else {
65 return v.get_path(rest);
66 }
67 }
68 }
69 None
70 }
71 _ => None,
72 },
73 PathSegment::Index(index) => match self {
74 DataValue::Array(elements) => {
75 if let Some(value) = elements.get(*index) {
76 if rest.is_empty() {
77 Some(value)
78 } else {
79 value.get_path(rest)
80 }
81 } else {
82 None
83 }
84 }
85 _ => None,
86 },
87 }
88 }
89
90 fn get_path_str(&self, arena: &'a DataArena, path: &str) -> Option<&DataValue<'a>> {
91 if path.is_empty() {
92 return Some(self);
93 }
94
95 let segments = parse_path(arena, path);
97 self.get_path(segments)
98 }
99}
100
101pub fn parse_path<'a>(arena: &'a DataArena, path: &str) -> &'a [PathSegment<'a>] {
103 if path.is_empty() {
105 return &[];
106 }
107
108 if !path.contains('.') {
110 let segment = PathSegment::parse(arena, path);
111 return arena.vec_into_slice(vec![segment]);
112 }
113
114 let segment_count = path.chars().filter(|&c| c == '.').count() + 1;
116
117 let mut segments = Vec::with_capacity(segment_count);
119
120 for segment in path.split('.') {
122 segments.push(PathSegment::parse(arena, segment));
123 }
124
125 arena.vec_into_slice(segments)
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::arena::DataArena;
133
134 #[test]
135 fn test_path_segment_parsing() {
136 let arena = DataArena::new();
137
138 let key = PathSegment::parse(&arena, "name");
139 let index = PathSegment::parse(&arena, "42");
140
141 assert_eq!(key, PathSegment::Key(arena.intern_str("name")));
142 assert_eq!(index, PathSegment::Index(42));
143 }
144
145 #[test]
146 fn test_value_access() {
147 let arena = DataArena::new();
148
149 let user = DataValue::object(
151 &arena,
152 &[
153 (arena.intern_str("name"), DataValue::string(&arena, "John")),
154 (arena.intern_str("age"), DataValue::integer(30)),
155 (
156 arena.intern_str("address"),
157 DataValue::object(
158 &arena,
159 &[
160 (
161 arena.intern_str("city"),
162 DataValue::string(&arena, "New York"),
163 ),
164 (arena.intern_str("zip"), DataValue::string(&arena, "10001")),
165 ],
166 ),
167 ),
168 (
169 arena.intern_str("scores"),
170 DataValue::array(
171 &arena,
172 &[
173 DataValue::integer(85),
174 DataValue::integer(90),
175 DataValue::integer(95),
176 ],
177 ),
178 ),
179 ],
180 );
181
182 assert_eq!(
184 user.get_path_str(&arena, "name").unwrap().as_str(),
185 Some("John")
186 );
187 assert_eq!(user.get_path_str(&arena, "age").unwrap().as_i64(), Some(30));
188 assert_eq!(
189 user.get_path_str(&arena, "address.city").unwrap().as_str(),
190 Some("New York")
191 );
192 assert_eq!(
193 user.get_path_str(&arena, "scores.1").unwrap().as_i64(),
194 Some(90)
195 );
196
197 let path = vec![
199 PathSegment::key(&arena, "address"),
200 PathSegment::key(&arena, "zip"),
201 ];
202 assert_eq!(user.get_path(&path).unwrap().as_str(), Some("10001"));
203
204 assert_eq!(user.get_path_str(&arena, "email"), None);
206 assert_eq!(user.get_path_str(&arena, "address.country"), None);
207 assert_eq!(user.get_path_str(&arena, "scores.5"), None);
208 }
209}