ad4m_client/
perspective_proxy.rs

1use std::collections::BTreeMap;
2
3use crate::{
4    literal::{Literal, LiteralValue},
5    perspectives::{
6        add_link::AddLinkPerspectiveAddLink, query_links::QueryLinksPerspectiveQueryLinks,
7        PerspectivesClient,
8    },
9    subject_proxy::SubjectProxy,
10    types::LinkExpression,
11};
12use anyhow::{anyhow, Result};
13use chrono::naive::NaiveDateTime;
14use regex::Regex;
15use serde_json::Value;
16type DateTime = NaiveDateTime;
17
18pub struct PerspectiveProxy {
19    client: PerspectivesClient,
20    perspective_uuid: String,
21}
22
23impl PerspectiveProxy {
24    pub fn new(client: PerspectivesClient, perspective_uuid: String) -> Self {
25        Self {
26            client,
27            perspective_uuid,
28        }
29    }
30
31    pub async fn add_link(
32        &self,
33        source: String,
34        target: String,
35        predicate: Option<String>,
36        status: Option<String>,
37    ) -> Result<AddLinkPerspectiveAddLink> {
38        self.client
39            .add_link(
40                self.perspective_uuid.clone(),
41                source,
42                target,
43                predicate,
44                status,
45            )
46            .await
47    }
48
49    pub async fn get(
50        &self,
51        source: Option<String>,
52        target: Option<String>,
53        predicate: Option<String>,
54        from_date: Option<DateTime>,
55        until_date: Option<DateTime>,
56        limit: Option<f64>,
57    ) -> Result<Vec<QueryLinksPerspectiveQueryLinks>> {
58        self.client
59            .query_links(
60                self.perspective_uuid.clone(),
61                source,
62                target,
63                predicate,
64                from_date,
65                until_date,
66                limit,
67            )
68            .await
69    }
70
71    pub async fn infer(&self, prolog_query: String) -> Result<Value> {
72        self.client
73            .infer(self.perspective_uuid.clone(), prolog_query)
74            .await
75    }
76
77    pub async fn add_dna(&self, name: String, dna: String, dna_type: String) -> Result<()> {
78        let mut predicate = "ad4m://has_custom_dna";
79
80        if dna_type == "subject_class" {
81            predicate = "ad4m://has_subject_class"
82        } else if dna_type == "flow" {
83            predicate = "ad4m://has_flow"
84        }
85
86        let literal_name = Literal::from_string(name);
87
88        let links = self
89            .get(
90                Some("ad4m://self".into()),
91                Some(literal_name.to_url().unwrap()),
92                Some(predicate.into()),
93                None,
94                None,
95                None,
96            )
97            .await?;
98
99        if links.is_empty() {
100            self.set_single_target(
101                "ad4m://self".into(),
102                predicate.into(),
103                literal_name.to_url().unwrap(),
104            )
105            .await?;
106        }
107
108        let literal = Literal::from_string(dna);
109
110        self.add_link(
111            literal_name.to_url().unwrap(),
112            literal.to_url().unwrap(),
113            Some("ad4m://sdna".into()),
114            Some("shared".to_string()),
115        )
116        .await?;
117        Ok(())
118    }
119
120    pub async fn get_dna(&self) -> Result<Vec<String>> {
121        self.get(Some("ad4m://sdna".into()), None, None, None, None, None)
122            .await?
123            .into_iter()
124            .map(|link| {
125                let literal = Literal::from_url(link.data.target)?;
126                match literal.get() {
127                    Ok(LiteralValue::String(string)) => Ok(string),
128                    _ => Err(anyhow::anyhow!("Not a string literal")),
129                }
130            })
131            .collect()
132    }
133
134    pub async fn get_single_target(&self, source: String, predicate: String) -> Result<String> {
135        let links = self
136            .client
137            .query_links(
138                self.perspective_uuid.clone(),
139                Some(source),
140                None,
141                Some(predicate),
142                None,
143                None,
144                None,
145            )
146            .await?;
147        if links.is_empty() {
148            return Err(anyhow::anyhow!("No links found"));
149        }
150        if links.len() > 1 {
151            return Err(anyhow::anyhow!("Multiple links found"));
152        }
153        Ok(links[0].data.target.clone())
154    }
155
156    pub async fn set_single_target(
157        &self,
158        source: String,
159        predicate: String,
160        target: String,
161    ) -> Result<()> {
162        let links: Vec<LinkExpression> = self
163            .client
164            .query_links(
165                self.perspective_uuid.clone(),
166                Some(source.clone()),
167                None,
168                Some(predicate.clone()),
169                None,
170                None,
171                None,
172            )
173            .await?
174            .into_iter()
175            .map(LinkExpression::from)
176            .collect();
177
178        for link in links {
179            self.client
180                .remove_link(self.perspective_uuid.clone(), link)
181                .await?;
182        }
183
184        self.client
185            .add_link(
186                self.perspective_uuid.clone(),
187                source,
188                target,
189                Some(predicate),
190                Some("shared".to_string()),
191            )
192            .await?;
193        Ok(())
194    }
195
196    pub async fn subject_classes(&self) -> Result<Vec<String>> {
197        let mut classes = Vec::new();
198        if let Value::Array(classes_results) = self.infer("subject_class(X, _)".into()).await? {
199            for class in classes_results {
200                if let Some(Value::String(class_name)) = class.get("X") {
201                    classes.push(class_name.clone());
202                }
203            }
204        }
205        Ok(classes)
206    }
207
208    pub async fn subject_class_properties(&self, class: &String) -> Result<Vec<String>> {
209        let mut propertys = Vec::new();
210        if let Value::Array(propertys_results) = self
211            .infer(format!("subject_class(\"{}\", C), property(C, X)", class))
212            .await?
213        {
214            for property in propertys_results {
215                if let Some(Value::String(property_name)) = property.get("X") {
216                    propertys.push(property_name.clone());
217                }
218            }
219        }
220        Ok(propertys)
221    }
222
223    pub async fn subject_class_collections(&self, class: &String) -> Result<Vec<String>> {
224        let mut collections = Vec::new();
225        if let Value::Array(collection_results) = self
226            .infer(format!("subject_class(\"{}\", C), collection(C, X)", class))
227            .await?
228        {
229            for colletion in collection_results {
230                if let Some(Value::String(collection_name)) = colletion.get("X") {
231                    collections.push(collection_name.clone());
232                }
233            }
234        }
235        Ok(collections)
236    }
237
238    pub async fn create_subject(&self, class: &String, base: &str) -> Result<()> {
239        if let Ok(Value::Array(results)) = self
240            .infer(format!(
241                "subject_class(\"{}\", C), constructor(C, Action)",
242                class
243            ))
244            .await
245        {
246            //println!("{:?}", results);
247            if let Some(Value::Object(action)) = results.first() {
248                //println!("{:?}", action);
249                if let Value::String(action_string) = action
250                    .get("Action")
251                    .ok_or(anyhow::anyhow!("Unbound variable Action is not set"))?
252                {
253                    //println!("{}", action_string);
254                    self.execute_action(action_string, base, None).await?;
255                    return Ok(());
256                }
257            }
258        }
259        Err(anyhow::anyhow!("No constructor found for class: {}", class))
260    }
261
262    pub async fn is_subject_instance(&self, class: &String, base: &String) -> Result<bool> {
263        match self
264            .infer(format!(
265                r#"subject_class("{}", C), instance(C, "{}")"#,
266                class, base
267            ))
268            .await?
269        {
270            Value::Array(results) => {
271                if results.is_empty() {
272                    Ok(false)
273                } else {
274                    Ok(true)
275                }
276            }
277            Value::Bool(b) => Ok(b),
278            _ => Ok(false),
279        }
280    }
281
282    pub async fn get_subject(&self, class: &String, base: &String) -> Result<SubjectProxy> {
283        if self.is_subject_instance(class, base).await? {
284            Ok(SubjectProxy::new(self, class.clone(), base.clone()))
285        } else {
286            Err(anyhow!(
287                "Expression {} is not a subject instance of class: {}",
288                base,
289                class
290            ))
291        }
292    }
293
294    pub async fn get_subject_classes(&self, base: &String) -> Result<Vec<String>> {
295        let mut classes = Vec::new();
296        if let Value::Array(classes_results) = self
297            .infer(format!(
298                r#"instance(C, "{}"), subject_class(Classname, C)"#,
299                base
300            ))
301            .await?
302        {
303            for class in classes_results {
304                if let Some(Value::String(class_name)) = class.get("Classname") {
305                    classes.push(class_name.clone());
306                }
307            }
308        }
309        Ok(classes)
310    }
311
312    pub async fn execute_action(
313        &self,
314        action: &str,
315        base: &str,
316        params: Option<BTreeMap<&str, &String>>,
317    ) -> Result<()> {
318        let commands = parse_action(action)?;
319        for command in commands {
320            let mut command = command.replace("this", base);
321            if let Some(ref params) = params {
322                for (key, value) in params.iter() {
323                    command = command.replace(key, value);
324                }
325            }
326            match command.action.as_str() {
327                "addLink" => {
328                    //println!("addLink: {:?}", command);
329                    self.add_link(
330                        command.source,
331                        command.target,
332                        command.predicate,
333                        command.status,
334                    )
335                    .await?;
336                }
337                "removeLink" => {
338                    unimplemented!();
339                    //let links = self.get(Some(source), Some(target), predicate, None, None, None).await?;
340                    //elf.remove_link(source, target.into(), predicate.into()).await?;
341                }
342                "setSingleTarget" => {
343                    self.set_single_target(
344                        command.source,
345                        command
346                            .predicate
347                            .ok_or(anyhow::anyhow!("No predicate in set_single_target action"))?,
348                        command.target,
349                    )
350                    .await?;
351                }
352                _ => {
353                    return Err(anyhow::anyhow!("Unknown action: {}", command.action));
354                }
355            }
356        }
357        Ok(())
358    }
359}
360
361#[derive(Debug)]
362struct Command {
363    pub action: String,
364    pub source: String,
365    pub predicate: Option<String>,
366    pub target: String,
367    pub status: Option<String>,
368}
369
370impl Command {
371    pub fn replace(&self, pattern: &str, replacement: &str) -> Command {
372        Command {
373            action: self.action.replace(pattern, replacement),
374            source: self.source.replace(pattern, replacement),
375            predicate: self
376                .predicate
377                .as_ref()
378                .map(|f| f.replace(pattern, replacement)),
379            target: self.target.replace(pattern, replacement),
380            status: self
381                .status
382                .as_ref()
383                .map(|f| f.replace(pattern, replacement)),
384        }
385    }
386}
387
388fn parse_action(action: &str) -> Result<Vec<Command>> {
389    let action_regex = Regex::new(r"\[(?P<command>\{.*})*\]")?;
390
391    // This parses strings like:
392    // {action: "<action>", source: "<source>", predicate: "<predicate>", target: "<target>", status: "<status>"}
393    let command_regex = Regex::new(
394        r#"\{(action:\s*"(?P<action>[\S--,]+)",?\s*)|(source:\s*"(?P<source>[\S--,]+)",?\s*)|(predicate:\s*"(?P<predicate>[\S--,]+)",?\s*)|(target:\s*"(?P<target>[\S--,]+)",?\s*)|(status:\s*"(?P<status>[\S--,]+)",?\s*)\}"#,
395    )?;
396
397    let mut commands = Vec::new();
398    for capture in action_regex.captures_iter(action) {
399        let mut action = None;
400        let mut source = None;
401        let mut predicate = None;
402        let mut target = None;
403        let mut status = None;
404        command_regex
405            .captures_iter(capture.name("command").unwrap().as_str())
406            .for_each(|capture| {
407                action = action.or(capture.name("action").map(|e| e.as_str()));
408                source = source.or(capture.name("source").map(|e| e.as_str()));
409                predicate = predicate.or(capture.name("predicate").map(|e| e.as_str()));
410                target = target.or(capture.name("target").map(|e| e.as_str()));
411                status = status.or(capture.name("status").map(|e| e.as_str()));
412            });
413
414        commands.push(Command {
415            action: action.ok_or(anyhow!("Comman without action"))?.into(),
416            source: source.ok_or(anyhow!("Comman without source"))?.into(),
417            predicate: predicate.map(|e| e.into()),
418            target: target.ok_or(anyhow!("Comman without target"))?.into(),
419            status: status.map(|e| e.into()),
420        });
421        //println!("{:?}", commands);
422    }
423
424    Ok(commands)
425}