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