1#![deny(
7 clippy::unwrap_used,
8 clippy::expect_used,
9 clippy::panic,
10 clippy::todo,
11 clippy::unimplemented
12)]
13
14pub mod delta;
15pub mod diagnostics;
16pub mod dialect;
17pub mod doctor;
18pub mod graph;
19pub mod interchangeable;
20pub mod ocel;
21pub mod prelude;
22pub mod receipt;
23pub mod shacl;
24pub mod sparql;
25pub mod vocab;
26
27pub use graph::quad::parse_nquad;
28pub use graph::{
29 extract_prefixes, iri_terms, parse_nquads_located, parse_ntriples_located,
30 parse_turtle_located, DeterministicGraph, IriTerms, KnowledgeHook, LocatedParse,
31 ParseDiagnostic, RdfDelta, TransitionReceipt,
32};
33pub use interchangeable::{AdapterLayer, GenesisCore, OuterMembrane, ProjectionLayer};
34pub use ocel::{check_guard, check_lifecycle_order, discover_dfg, DfgEdge};
35pub use shacl::{validate_shacl, ShaclSeverity, ShaclViolation};
36pub use sparql::{check_sparql_syntax, sparql_kind, SparqlKind};
37
38#[derive(Debug, thiserror::Error)]
40pub enum GraphError {
41 #[error("Oxigraph storage error: {0}")]
43 Oxigraph(#[from] oxigraph::store::StorageError),
44
45 #[error("SPARQL evaluation error: {0}")]
47 Sparql(#[from] oxigraph::sparql::QueryEvaluationError),
48
49 #[error("Serialization or parsing error: {0}")]
51 Serialization(String),
52
53 #[error("Receipt verification failed: {0}")]
55 VerificationFailed(String),
56
57 #[error("Knowledge hook validation failed: {0}")]
59 HookFailed(String),
60
61 #[error("Other error: {0}")]
63 Other(String),
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn test_graph_and_receipt_flow() -> Result<(), Box<dyn std::error::Error>> {
72 let graph = DeterministicGraph::new()?;
73 let q1_str = "<http://example.org/alice> <http://example.org/name> \"Alice\" <http://example.org/graph> .";
74 let q2_str = "<http://example.org/bob> <http://example.org/name> \"Bob\" <http://example.org/graph> .";
75 let q1 = parse_nquad(q1_str)?;
76 let q2 = parse_nquad(q2_str)?;
77
78 graph.insert_quad(&q1)?;
80 assert!(graph.contains_quad(&q1)?);
81 assert!(!graph.contains_quad(&q2)?);
82
83 let hash1 = graph.state_hash()?;
85
86 let target = DeterministicGraph::new()?;
88 target.insert_quad(&q1)?;
89 target.insert_quad(&q2)?;
90 let hash2 = target.state_hash()?;
91
92 let delta = RdfDelta::compute(&graph, &target)?;
93 assert_eq!(delta.additions.len(), 1);
94 assert_eq!(delta.deletions.len(), 0);
95 assert_eq!(delta.additions[0], q2.to_string());
96
97 let receipt = graph.apply_delta(&delta, &[])?;
99 assert_eq!(receipt.pre_state_hash, hash1);
100 assert_eq!(receipt.post_state_hash, hash2);
101 assert_eq!(receipt.delta_hash, delta.hash());
102 receipt.verify()?;
103
104 let mut tampered_receipt = receipt.clone();
106 tampered_receipt.signature_or_hash[0] ^= 1;
107 assert!(tampered_receipt.verify().is_err());
108
109 let hook_bob = KnowledgeHook::new(
111 "has_bob".to_string(),
112 "ASK WHERE { GRAPH ?g { ?s <http://example.org/name> \"Bob\" } }".to_string(),
113 );
114 assert!(hook_bob.execute(&graph)?);
115
116 let hook_no_charlie = KnowledgeHook::new(
118 "no_charlie".to_string(),
119 "ASK WHERE { FILTER NOT EXISTS { GRAPH ?g { ?s <http://example.org/name> \"Charlie\" } } }".to_string(),
120 );
121 assert!(hook_no_charlie.execute(&graph)?);
122
123 let target_charlie = DeterministicGraph::new()?;
125 target_charlie.insert_quad(&q1)?;
126 target_charlie.insert_quad(&q2)?;
127 let q3_str = "<http://example.org/charlie> <http://example.org/name> \"Charlie\" <http://example.org/graph> .";
128 let q3 = parse_nquad(q3_str)?;
129 target_charlie.insert_quad(&q3)?;
130
131 let delta_charlie = RdfDelta::compute(&graph, &target_charlie)?;
132 let apply_res = graph.apply_delta(&delta_charlie, &[hook_no_charlie]);
133
134 assert!(apply_res.is_err());
136
137 assert!(graph.contains_quad(&q1)?);
139 assert!(graph.contains_quad(&q2)?);
140 assert!(!graph.contains_quad(&q3)?);
141 assert_eq!(graph.state_hash()?, hash2);
142
143 Ok(())
144 }
145}