1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#[cfg(feature = "cypher")]
use super::cypher_graph::CypherGraph;
use super::in_memory_graph::InMemoryGraph;
use super::invalid_graph::InvalidGraph;
use super::{Graph, KBValue};
use crate::concepts::attributes::{Attribute, Inherits, Owner, Value};
use crate::concepts::{Archetype, ArchetypeTrait, Tao};
use std::cell::RefCell;
use std::rc::Rc;

thread_local! {
    static GRAPH: RefCell<Box<dyn Graph>> = RefCell::new(Box::new(InvalidGraph{}));
}

/// Add the given Concept type to the KB.

///

/// # Examples

///

/// Note: do not actually run this on existing types, since they are automatically added when the

/// KB is initialized.

///

/// ```rust

/// # use zamm_yin::graph::bind_in_memory_graph;

/// # bind_in_memory_graph();

/// use zamm_yin::initialize_type;

/// use zamm_yin::concepts::ArchetypeTrait;

/// use zamm_yin::concepts::attributes::Inherits;

/// use zamm_yin::concepts::{Archetype, Tao}; // import your own types instead

/// use zamm_yin::graph::{Graph, InjectionGraph};

///

/// let mut ig = InjectionGraph::new();

/// initialize_type!(ig, (Archetype, Tao));

/// ```

#[macro_export]
macro_rules! initialize_type {
    ($g:expr, ($($t:ty),*)) => {
        $(
            $g.add_node();
            $g.set_node_name(<$t>::TYPE_ID, <$t>::TYPE_NAME.to_string());
        )*
        // set edges later, since edges contain references to node names, and that will be

        // impossible if the nodes themselves don't exist yet

        $($g.add_edge(<$t>::TYPE_ID, Inherits::TYPE_ID, <$t>::PARENT_TYPE_ID);)*
    };
}

/// Bind GRAPH to a new graph that sits entirely in memory.

pub fn bind_in_memory_graph() {
    GRAPH.with(|g| {
        let mut img = InMemoryGraph::new();
        initialize_type!(img, (Tao, Archetype, Attribute, Owner, Value, Inherits));
        *g.borrow_mut() = Box::new(img);
    });
}

/// Bind GRAPH to an external Neo4j database.

///

/// Current limitations:

///

///  * This requries Neo4j version 3, because rusted_cypher currently does not appear to support

///    version 4. See the [deprecation notice](https://neo4j.com/docs/rest-docs/3.5/) on the

///    version 3 API.

///  * Only string values can be attached to nodes.

#[cfg(feature = "cypher")]
pub fn bind_cypher_graph(uri: &str) {
    GRAPH.with(|g| {
        let mut cg = CypherGraph::new(uri);
        initialize_type!(cg, (Tao, Archetype, Attribute, Owner, Value, Inherits));
        *g.borrow_mut() = Box::new(cg);
    });
}

/// Graph usable with dependency injection.

#[derive(Copy, Clone)]
pub struct InjectionGraph {}

impl InjectionGraph {
    /// Creates a new reference to

    pub fn new() -> Self {
        Self {}
    }
}

impl Graph for InjectionGraph {
    fn size(&self) -> usize {
        GRAPH.with(|g| g.borrow().size())
    }

    fn add_node(&mut self) -> usize {
        GRAPH.with(|g| g.borrow_mut().add_node().clone())
    }

    fn set_node_value(&mut self, id: usize, value: Box<dyn KBValue>) {
        GRAPH.with(|g| g.borrow_mut().set_node_value(id, value))
    }

    fn set_node_name(&mut self, id: usize, name: String) {
        GRAPH.with(|g| g.borrow_mut().set_node_name(id, name))
    }

    fn node_name(&self, id: usize) -> Option<Rc<String>> {
        GRAPH.with(|g| g.borrow().node_name(id))
    }

    fn node_value(&self, id: usize) -> Option<Rc<Box<dyn KBValue>>> {
        GRAPH.with(|g| g.borrow().node_value(id).map(|r| r.clone()))
    }

    fn lookup(&self, name: &str) -> Vec<usize> {
        GRAPH.with(|g| g.borrow().lookup(name))
    }

    fn add_edge(&mut self, from: usize, edge_type: usize, to: usize) {
        GRAPH.with(|g| g.borrow_mut().add_edge(from, edge_type, to))
    }

    fn has_edge(&self, from: usize, edge_type: usize, to: usize) -> bool {
        GRAPH.with(|g| g.borrow().has_edge(from, edge_type, to))
    }

    fn outgoing_nodes(&self, from: usize, edge_type: usize) -> Vec<usize> {
        GRAPH.with(|g| g.borrow().outgoing_nodes(from, edge_type))
    }

    fn incoming_nodes(&self, to: usize, edge_type: usize) -> Vec<usize> {
        GRAPH.with(|g| g.borrow().incoming_nodes(to, edge_type))
    }

    fn all_outgoing_nodes(&self, from: usize) -> Vec<usize> {
        GRAPH.with(|g| g.borrow().all_outgoing_nodes(from))
    }

    fn all_incoming_nodes(&self, to: usize) -> Vec<usize> {
        GRAPH.with(|g| g.borrow().all_incoming_nodes(to))
    }

    fn into_dot(&self) -> String {
        GRAPH.with(|g| g.borrow().into_dot())
    }
}

/// Print graph to stdout for debugging purposes.

pub fn print_graph_debug() {
    println!("{}", InjectionGraph::new().into_dot());
}