1use haste_reflect::MetaValue;
2use std::{fmt::Display, sync::Arc};
3
4mod escape;
5
6#[cfg(test)]
7mod proptests;
8
9#[derive(Debug, Clone)]
10pub struct Path(String);
11
12impl Default for Path {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl Display for Path {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 write!(f, "{}", self.0)
21 }
22}
23
24impl Path {
25 #[must_use]
26 pub fn new() -> Self {
27 Self(String::new())
28 }
29 #[must_use]
30 pub fn descend(&self, field: &str) -> Self {
31 Self(format!("{}/{}", self.0, escape::escape_field(field)))
32 }
33 #[must_use]
34 pub fn ascend(&self) -> Option<(Self, Key)> {
35 if self.0.is_empty() {
36 return None;
37 }
38
39 match self.0.rfind('/') {
41 Some(idx) => {
43 let parent_path = &self.0[..idx];
44 let field = &self.0[idx + 1..];
45 Some((
46 Path(parent_path.to_string()),
47 Key::parse(&escape::unescape_field(field)),
48 ))
49 }
50 None => Some((
52 Path(String::new()),
53 Key::parse(&escape::unescape_field(&self.0)),
54 )),
55 }
56 }
57
58 pub fn get<'a>(&self, value: &'a dyn MetaValue) -> Option<&'a dyn MetaValue> {
59 let mut current = value;
60 for part in self.0.split('/').skip(1) {
62 let k = Key::parse(&escape::unescape_field(part));
63
64 match k {
65 Key::Field(field) => {
66 current = current.get_field(&field)?;
67 }
68 Key::Index(index) => {
69 current = current.get_index(index)?;
70 }
71 }
72 }
73
74 Some(current)
75 }
76
77 pub fn get_typed<'a, Type: MetaValue>(&self, value: &'a dyn MetaValue) -> Option<&'a Type> {
78 let current = self.get(value)?;
79 current.as_any().downcast_ref::<Type>()
80 }
81}
82
83#[derive(Debug)]
84pub enum Key {
85 Field(String),
86 Index(usize),
87}
88
89impl Key {
90 #[must_use]
91 pub fn parse(field: &str) -> Self {
92 if let Ok(index) = field.parse::<usize>() {
93 Key::Index(index)
94 } else {
95 Key::Field(field.to_string())
96 }
97 }
98}
99
100#[derive(Clone)]
101struct ChildPointer<U>(*const U);
102
103unsafe impl<U> Send for ChildPointer<U> {}
104unsafe impl<U> Sync for ChildPointer<U> {}
105
106#[derive(Clone)]
107pub struct TypedPointer<T: MetaValue, U: MetaValue> {
108 root: Arc<T>,
109 value: ChildPointer<U>,
110 path: Path,
111}
112
113impl<Root: MetaValue, U: MetaValue> TypedPointer<Root, U> {
114 pub fn new(value: Arc<Root>) -> TypedPointer<Root, Root> {
115 TypedPointer {
116 value: ChildPointer(&raw const *value.as_ref()),
117 root: value,
118 path: Path::new(),
119 }
120 }
121
122 #[must_use]
123 pub fn root(&self) -> TypedPointer<Root, Root> {
124 TypedPointer {
125 value: ChildPointer(&raw const *self.root.as_ref()),
126 root: self.root.clone(),
127 path: Path::new(),
128 }
129 }
130
131 #[must_use]
132 pub fn path(&self) -> &str {
133 self.path.0.as_str()
134 }
135
136 #[must_use]
137 pub fn value(&self) -> Option<&U> {
138 unsafe { (*self.value.0).as_any().downcast_ref::<U>() }
139 }
140
141 #[must_use]
142 pub fn descend<Child: MetaValue>(&self, field: &Key) -> Option<TypedPointer<Root, Child>> {
143 match field {
144 Key::Field(field) => self.value().and_then(|v| {
145 v.get_field(field)
146 .and_then(|v| v.as_any().downcast_ref::<Child>())
147 .map(|child| TypedPointer {
148 root: self.root.clone(),
149 value: ChildPointer(&raw const *child),
150 path: self.path.descend(field),
151 })
152 }),
153 Key::Index(index) => self.value().and_then(|v| {
154 v.get_index(*index)
155 .and_then(|v| v.as_any().downcast_ref::<Child>())
156 .map(|child| TypedPointer {
157 root: self.root.clone(),
158 value: ChildPointer(&raw const *child),
159 path: self.path.descend(&index.to_string()),
160 })
161 }),
162 }
163 }
164
165 #[must_use]
166 pub fn ascend(&self) -> Option<(Path, Key)> {
167 self.path.ascend()
168 }
169}
170
171#[cfg(test)]
172mod test {
173 use super::*;
174 use haste_fhir_model::r4::generated::{
175 resources::Patient, types::FHIRString, types::HumanName,
176 };
177
178 #[test]
179 fn test_pointer_descend() {
180 let patient = Arc::new(Patient {
181 id: Some("patient-1".to_string()),
182 name: Some(vec![HumanName {
183 family: Some(Box::new(FHIRString {
184 value: Some("Doe".to_string()),
185 ..Default::default()
186 })),
187 ..Default::default()
188 }]),
189 ..Default::default()
190 });
191
192 let pointer = TypedPointer::<Patient, Patient>::new(patient);
193 let pointer = pointer
194 .descend::<Vec<HumanName>>(&Key::Field("name".to_string()))
195 .unwrap();
196 assert_eq!(pointer.path(), "/name");
197 let pointer = pointer.descend::<HumanName>(&Key::Index(0)).unwrap();
198 assert_eq!(pointer.path(), "/name/0");
199 let pointer = pointer
200 .descend::<Box<FHIRString>>(&Key::Field("family".to_string()))
201 .unwrap();
202 let pointer = pointer
203 .descend::<String>(&Key::Field("value".to_string()))
204 .unwrap();
205
206 assert_eq!(pointer.path(), "/name/0/family/value");
207 assert_eq!(pointer.value(), Some(&"Doe".to_string()));
208 }
209
210 #[test]
211 fn test_path() {
212 let patient = Arc::new(Patient {
213 id: Some("patient-1".to_string()),
214 name: Some(vec![HumanName {
215 family: Some(Box::new(FHIRString {
216 value: Some("Doe".to_string()),
217 ..Default::default()
218 })),
219 ..Default::default()
220 }]),
221 ..Default::default()
222 });
223
224 let path = Path::new()
225 .descend("name")
226 .descend("0")
227 .descend("family")
228 .descend("value");
229
230 assert_eq!(path.0, "/name/0/family/value");
231 let k = path.get_typed::<String>(patient.as_ref());
232
233 assert_eq!(k, Some(&"Doe".to_string()));
234 }
235}