Skip to main content

haste_pointer/
lib.rs

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        // Skip the first empty part from the leading '/'
40        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}