1use haste_reflect::MetaValue;
2use std::sync::Arc;
3
4#[derive(Clone)]
5struct ChildPointer<U>(*const U);
6
7unsafe impl<U> Send for ChildPointer<U> {}
8unsafe impl<U> Sync for ChildPointer<U> {}
9
10#[derive(Clone)]
11pub struct Pointer<T: MetaValue, U: MetaValue> {
12 root: Arc<T>,
13 value: ChildPointer<U>,
14 path: String,
15}
16
17pub enum Key {
18 Field(String),
19 Index(usize),
20}
21
22fn path_descend(path: &str, key: &str) -> String {
23 format!("{}/{}", path, key)
24}
25
26impl<'a, Root: MetaValue, U: MetaValue> Pointer<Root, U> {
27 pub fn new(value: Arc<Root>) -> Pointer<Root, Root> {
28 Pointer {
29 value: ChildPointer(&*value.as_ref() as *const Root),
30 root: value,
31 path: "".to_string(),
32 }
33 }
34
35 pub fn root(&self) -> Pointer<Root, Root> {
36 Pointer {
37 value: ChildPointer(&*self.root.as_ref() as *const Root),
38 root: self.root.clone(),
39 path: "".to_string(),
40 }
41 }
42
43 pub fn path(&self) -> &str {
44 self.path.as_str()
45 }
46
47 pub fn value(&self) -> Option<&U> {
48 let p = unsafe { (*self.value.0).as_any().downcast_ref::<U>() };
49
50 p
51 }
52
53 pub fn descend<Child: MetaValue>(&'a self, key: &Key) -> Option<Pointer<Root, Child>> {
54 match key {
55 Key::Field(field) => self.value().and_then(|v| {
56 v.get_field(field)
57 .and_then(|v| v.as_any().downcast_ref::<Child>())
58 .map(|child| Pointer {
59 root: self.root.clone(),
60 value: ChildPointer(&*child as *const Child),
61 path: path_descend(self.path.as_str(), field.as_str()),
62 })
63 }),
64 Key::Index(index) => self.value().and_then(|v| {
65 v.get_index(*index)
66 .and_then(|v| v.as_any().downcast_ref::<Child>())
67 .map(|child| Pointer {
68 root: self.root.clone(),
69 value: ChildPointer(&*child as *const Child),
70 path: path_descend(self.path.as_str(), index.to_string().as_str()),
71 })
72 }),
73 }
74 }
75}
76
77#[cfg(test)]
78mod test {
79 use super::*;
80 use haste_fhir_model::r4::generated::{
81 resources::Patient, types::FHIRString, types::HumanName,
82 };
83
84 #[test]
85 fn test_pointer_descend() {
86 let patient = Arc::new(Patient {
87 id: Some("patient-1".to_string()),
88 name: Some(vec![Box::new(HumanName {
89 family: Some(Box::new(FHIRString {
90 value: Some("Doe".to_string()),
91 ..Default::default()
92 })),
93 ..Default::default()
94 })]),
95 ..Default::default()
96 });
97
98 let pointer = Pointer::<Patient, Patient>::new(patient);
99 let pointer = pointer
100 .descend::<Vec<Box<HumanName>>>(&Key::Field("name".to_string()))
101 .unwrap();
102 assert_eq!(pointer.path(), "/name");
103 let pointer = pointer.descend::<Box<HumanName>>(&Key::Index(0)).unwrap();
104 assert_eq!(pointer.path(), "/name/0");
105 let pointer = pointer
106 .descend::<Box<FHIRString>>(&Key::Field("family".to_string()))
107 .unwrap();
108 let pointer = pointer
109 .descend::<String>(&Key::Field("value".to_string()))
110 .unwrap();
111
112 assert_eq!(pointer.path(), "/name/0/family/value");
113 assert_eq!(pointer.value(), Some(&"Doe".to_string()));
114 }
115}