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