ad4m_client/
subject_proxy.rs

1use anyhow::{anyhow, Result};
2use maplit::btreemap;
3use serde_json::Value;
4use std::collections::BTreeMap;
5
6use crate::perspective_proxy::PerspectiveProxy;
7
8fn prolog_list_to_array(list: &Value) -> Vec<String> {
9    if let Some(Value::String(head)) = list.get("head") {
10        let mut values = vec![head.clone()];
11        if let Some(tail) = list.get("tail") {
12            values.extend(prolog_list_to_array(tail));
13        }
14        values
15    } else {
16        vec![]
17    }
18}
19
20pub struct SubjectProxy<'a> {
21    perspective: &'a PerspectiveProxy,
22    subject_class: String,
23    base: String,
24}
25
26impl<'a> SubjectProxy<'a> {
27    pub fn new(perspective: &'a PerspectiveProxy, subject_class: String, base: String) -> Self {
28        Self {
29            perspective,
30            subject_class,
31            base,
32        }
33    }
34
35    async fn query_to_array(&self, query: String) -> Result<Vec<String>> {
36        let result = self.perspective.infer(query.clone()).await?;
37        let mut values = Vec::new();
38        if let Some(result_array) = result.as_array() {
39            for p in result_array {
40                if let Some(Value::String(p)) = p.get("Value") {
41                    values.push(p.clone());
42                }
43            }
44            Ok(values)
45        } else {
46            Err(anyhow!("No results found running query: {}", query))
47        }
48    }
49
50    pub async fn property_names(&self) -> Result<Vec<String>> {
51        self.query_to_array(format!(
52            r#"subject_class("{}", C), property(C, Value)"#,
53            self.subject_class
54        ))
55        .await
56        .or_else(|_| Ok(Vec::new()))
57    }
58
59    pub async fn get_property_values(&self) -> Result<BTreeMap<String, String>> {
60        let mut values = BTreeMap::new();
61        let properties = self.property_names().await?;
62        for p in properties {
63            let query = format!(
64                r#"subject_class("{}", C), property_getter(C, "{}", "{}", Value)"#,
65                self.subject_class, self.base, p
66            );
67            let result = self.perspective.infer(query).await?;
68
69            if let Some(result_array) = result.as_array() {
70                match result_array[0].get("Value") {
71                    Some(Value::String(value)) => values.insert(p.clone(), value.clone()),
72                    Some(Value::Number(value)) => values.insert(p.clone(), value.to_string()),
73                    _ => None,
74                };
75            }
76        }
77        Ok(values)
78    }
79
80    pub async fn collection_names(&self) -> Result<Vec<String>> {
81        self.query_to_array(format!(
82            r#"subject_class("{}", C), collection(C, Value)"#,
83            self.subject_class
84        ))
85        .await
86        .or_else(|_| Ok(Vec::new()))
87    }
88
89    pub async fn get_collection_values(&self) -> Result<BTreeMap<String, Vec<String>>> {
90        let mut values = BTreeMap::new();
91        let collections = self.collection_names().await?;
92        for c in collections {
93            let query = format!(
94                r#"subject_class("{}", C), collection_getter(C, "{}", "{}", Value)"#,
95                self.subject_class, self.base, c
96            );
97            let result = self.perspective.infer(query).await?;
98
99            if let Some(result_array) = result.as_array() {
100                let mut collection_values = Vec::new();
101                for p in result_array {
102                    println!("{:?}", p.get("Value"));
103                    let value = p.get("Value");
104                    match value {
105                        Some(Value::String(value)) => {
106                            collection_values.push(value.clone());
107                        }
108                        Some(Value::Object(_)) => {
109                            collection_values.extend(prolog_list_to_array(value.as_ref().unwrap()));
110                        }
111                        Some(Value::Array(value)) => {
112                            collection_values.extend(
113                                value
114                                    .iter()
115                                    .map(|v| v.as_str().unwrap().to_string())
116                                    .collect::<Vec<String>>(),
117                            );
118                        }
119                        _ => {}
120                    }
121                }
122                values.insert(c, collection_values);
123            }
124        }
125        Ok(values)
126    }
127
128    pub async fn set_property(&self, property: &String, value: &String) -> Result<()> {
129        let query = format!(
130            r#"subject_class("{}", C), property_setter(C, "{}", Action)"#,
131            self.subject_class, property,
132        );
133        let result = self.perspective.infer(query).await?;
134        if let Some(result_array) = result.as_array() {
135            if let Some(Value::String(action)) = result_array[0].get("Action") {
136                self.perspective
137                    .execute_action(action, &self.base, Some(btreemap! {"value" => value}))
138                    .await?;
139            }
140        } else {
141            return Err(anyhow!(
142                r#"No property_setter found for property "{}" on class "{}""#,
143                property,
144                self.subject_class
145            ));
146        }
147        Ok(())
148    }
149
150    pub async fn add_collection(&self, collection: &String, new_element: &String) -> Result<()> {
151        let query = format!(
152            r#"subject_class("{}", C), collection_adder(C, "{}", Action)"#,
153            self.subject_class, collection,
154        );
155        let result = self.perspective.infer(query).await?;
156        if let Some(result_array) = result.as_array() {
157            if let Some(Value::String(action)) = result_array[0].get("Action") {
158                self.perspective
159                    .execute_action(action, &self.base, Some(btreemap! {"value" => new_element}))
160                    .await?;
161            }
162        } else {
163            return Err(anyhow!(
164                r#"No collection_adder found for collection "{}" on class "{}""#,
165                collection,
166                self.subject_class
167            ));
168        }
169        Ok(())
170    }
171}