rafia_stpa/
trudag.rs

1use std::path::{Path, PathBuf, StripPrefixError};
2
3use pyo3::prelude::*;
4use pyo3::types::IntoPyDict;
5use thiserror::Error;
6
7use crate::stpa::constraints::Constraint;
8
9#[derive(Error, Debug)]
10pub enum RootError {
11    #[error("Could not find the root")]
12    NoRootFound,
13}
14
15pub fn find_root(location: &Path) -> Result<PathBuf, RootError> {
16    if location.is_dir() && location.join(".git").exists() {
17        return Ok(location.to_owned());
18    }
19    if let Some(parent) = location.parent() {
20        find_root(parent)
21    } else {
22        Err(RootError::NoRootFound)
23    }
24}
25
26#[derive(Error, Debug)]
27pub enum RootRelativeError {
28    #[error("Root and to relative did not match")]
29    StripPrefixError(#[from] StripPrefixError),
30    #[error("Root could not be made canonical")]
31    NotPathRootError(std::io::Error),
32    #[error("Target could not be made canonical")]
33    NotPathTargetError(std::io::Error),
34}
35
36pub fn rooted_relative_path(root: &Path, to_relitive: &Path) -> Result<PathBuf, RootRelativeError> {
37    let root = root
38        .canonicalize()
39        .map_err(RootRelativeError::NotPathRootError)?;
40    let to_relitive = to_relitive
41        .canonicalize()
42        .map_err(RootRelativeError::NotPathTargetError)?;
43    Ok(to_relitive.strip_prefix(root)?.to_owned())
44}
45
46#[derive(Error, Debug)]
47pub enum TrudagError {
48    #[error("Python trudag error")]
49    ErrorPython(#[from] PyErr),
50}
51
52// Be careful using this in the GUI as it might block for a while as trudag can be slow
53pub fn create_node(
54    root: &Path,
55    trudag_graph: &Py<PyAny>,
56    group: &str,
57    parent: Option<&str>,
58    directory: &Path,
59    constraint: &Constraint,
60) -> Result<(), TrudagError> {
61    Ok(Python::with_gil(|py| {
62        let graph = trudag_graph.bind(py);
63
64        let trudag_manage = py.import("trudag.manage")?;
65        trudag_manage.call_method1(
66            "create_new_item",
67            (
68                group,
69                directory,
70                parent.unwrap_or(""),
71                constraint.trudag_id(),
72                root.join(".dotstop.dot"),
73                graph,
74            ),
75        )?;
76        let save = graph.getattr("to_file")?;
77        save.call((root.join(".dotstop.dot"),), None)?;
78        PyResult::Ok(())
79    })?)
80}
81
82// Be careful using this in the GUI as it might block for a while as trudag can be slow
83pub fn remove_node(
84    root: &Path,
85    trudag_graph: &Py<PyAny>,
86    item_name: &str,
87) -> Result<(), TrudagError> {
88    Ok(Python::with_gil(|py| {
89        let graph = trudag_graph.bind(py);
90        graph.call_method1("remove_item", (item_name,))?;
91        graph.call_method1("to_file", (root.join(".dotstop.dot"),))?;
92        PyResult::Ok(())
93    })?)
94}
95
96// Be careful using this in the GUI as it might block for a while as trudag can be slow
97pub fn set_node(
98    root: &Path,
99    trudag_graph: &Py<PyAny>,
100    group: &str,
101    constraint: &Constraint,
102) -> Result<(), TrudagError> {
103    Ok(Python::with_gil(|py| {
104        let graph = trudag_graph.bind(py);
105        graph.call_method(
106            "set_review_status",
107            (constraint.trudag_name(group),),
108            Some(&[("status", true)].into_py_dict(py)?),
109        )?;
110        graph.call_method1("to_file", (root.join(".dotstop.dot"),))?;
111        PyResult::Ok(())
112    })?)
113}
114
115// Be careful using this in the GUI as it might block for a while as trudag can be slow
116pub fn set_node_links(
117    root: &Path,
118    trudag_graph: &Py<PyAny>,
119    group: &str,
120    constraint: &Constraint,
121) -> Result<(), TrudagError> {
122    Ok(Python::with_gil(|py| {
123        let graph = trudag_graph.bind(py);
124
125        let trudag_manage = py.import("trudag.manage")?;
126        let trustable_graph = py.import("trudag.dotstop.core.graph.trustable_graph")?;
127        let link_status = trustable_graph.getattr("LinkStatus")?;
128        let linked = link_status.getattr("LINKED")?;
129        trudag_manage.call_method(
130            "set_all_link_status",
131            (trudag_graph, constraint.trudag_name(group), linked),
132            None,
133        )?;
134
135        graph.call_method1("to_file", (root.join(".dotstop.dot"),))?;
136        PyResult::Ok(())
137    })?)
138}
139
140// Be careful using this in the GUI as it might block for a while as trudag can be slow
141pub fn create_link(
142    root: &Path,
143    trudag_graph: &Py<PyAny>,
144    group: &str,
145    constraint: &Constraint,
146    link: &str,
147) -> Result<(), TrudagError> {
148    Ok(Python::with_gil(|py| {
149        let module = PyModule::import(py, "trudag.dotstop.core.graph")?;
150        let link_status = module.getattr("LinkStatus")?;
151        let linked = link_status.getattr("LINKED")?;
152
153        let graph = trudag_graph.bind(py);
154
155        graph.call_method1(
156            "set_link_status",
157            (link, constraint.trudag_name(group), linked),
158        )?;
159        graph.call_method1("to_file", (root.join(".dotstop.dot"),))?;
160        PyResult::Ok(())
161    })?)
162}