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 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        // Find the last '/' separator from the end of the string
37        match self.0.rfind('/') {
38            // Case 1: Separator found (e.g., "a/b/c" -> parent: "a/b", field: "c")
39            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            // Case 2: No separator found, the string is a single field (e.g., "a" -> parent: "", field: "a")
48            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        // Skip the first empty part from the leading '/'
58        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}