Skip to main content

pr4xis_runtime/
archive.rs

1//! The `.prx` archive — the serialized ontology as a content-addressed set of
2//! definition-bearing nodes and the connections (morphisms) over them.
3//!
4//! The archive's **Merkle root** is the `.prx`'s identity: the content address
5//! over the sorted set of every node's and connection's definition-bearing
6//! address. Two archives are the same ontology iff their roots match — iff every
7//! node and connection agrees by address. Insertion order and duplicates do not
8//! affect it.
9//!
10//! This is the cycle-safe envelope: it roots over the already-computed *local*
11//! addresses, so the cyclic graphs (`opposition`, …) that complicate per-node
12//! *recursive* addressing never reach this layer. Promoting per-node addresses
13//! to the recursive Merkle-DAG form (where a node's address depends on its
14//! children's) is a separate step inside [`Definition`](crate::definition) /
15//! [`Connection`](crate::connection); the envelope is unchanged by it.
16
17use serde::{Deserialize, Serialize};
18
19use crate::address::ContentAddress;
20use crate::codec::{self, CodecError};
21use crate::connection::Connection;
22use crate::definition::Definition;
23
24/// A `.prx` archive: the nodes and connections of one serialized ontology.
25#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
26pub struct Archive {
27    /// The node definitions (concepts, axioms, lenses, …).
28    pub nodes: Vec<Definition>,
29    /// The connections (morphisms) — functors / adjunctions / lenses / nat-trans.
30    pub connections: Vec<Connection>,
31}
32
33impl Archive {
34    /// An empty archive.
35    pub fn new() -> Self {
36        Self::default()
37    }
38
39    /// The Merkle root — the content address over the sorted, deduplicated set
40    /// of every node's and connection's definition-bearing address.
41    pub fn root(&self) -> Result<ContentAddress, CodecError> {
42        let mut addrs: Vec<[u8; 32]> =
43            Vec::with_capacity(self.nodes.len() + self.connections.len());
44        for n in &self.nodes {
45            addrs.push(*n.address()?.as_bytes());
46        }
47        for c in &self.connections {
48            addrs.push(*c.address()?.as_bytes());
49        }
50        addrs.sort_unstable();
51        addrs.dedup();
52        codec::address_of(&addrs)
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use crate::connection::GeneratorAction;
60
61    fn node(name: &str) -> Definition {
62        Definition {
63            kind: "Concept".into(),
64            name: name.into(),
65            edges: vec![],
66            axioms: vec![],
67            lexical: None,
68        }
69    }
70
71    fn conn() -> Connection {
72        Connection {
73            kind: "Faithful".into(),
74            source: "A".into(),
75            target: "B".into(),
76            action: GeneratorAction::Functor {
77                map_object: vec![("A".into(), "B".into())],
78                map_morphism: vec![],
79            },
80            laws: vec![],
81        }
82    }
83
84    #[test]
85    fn empty_archive_has_a_stable_root() {
86        assert_eq!(
87            Archive::new().root().unwrap(),
88            Archive::new().root().unwrap()
89        );
90    }
91
92    #[test]
93    fn adding_a_node_changes_the_root() {
94        let mut a = Archive::new();
95        a.nodes.push(node("X"));
96        assert_ne!(Archive::new().root().unwrap(), a.root().unwrap());
97    }
98
99    #[test]
100    fn root_is_node_order_independent() {
101        let a = Archive {
102            nodes: vec![node("X"), node("Y")],
103            connections: vec![],
104        };
105        let b = Archive {
106            nodes: vec![node("Y"), node("X")],
107            connections: vec![],
108        };
109        assert_eq!(a.root().unwrap(), b.root().unwrap());
110    }
111
112    #[test]
113    fn duplicate_node_does_not_change_the_root() {
114        let a = Archive {
115            nodes: vec![node("X")],
116            connections: vec![],
117        };
118        let b = Archive {
119            nodes: vec![node("X"), node("X")],
120            connections: vec![],
121        };
122        assert_eq!(a.root().unwrap(), b.root().unwrap());
123    }
124
125    #[test]
126    fn a_connection_contributes_to_the_root() {
127        let a = Archive {
128            nodes: vec![node("A"), node("B")],
129            connections: vec![],
130        };
131        let mut b = a.clone();
132        b.connections.push(conn());
133        assert_ne!(a.root().unwrap(), b.root().unwrap());
134    }
135}