tensorlogic_oxirs_bridge/
blank_node.rs1use std::collections::HashMap;
21
22pub struct BlankNodeManager {
24 counter: u64,
25 base_iri: String,
26 pub mapping: HashMap<String, String>,
28}
29
30impl BlankNodeManager {
31 pub fn new(base_iri: impl Into<String>) -> Self {
33 Self {
34 counter: 0,
35 base_iri: base_iri.into(),
36 mapping: HashMap::new(),
37 }
38 }
39
40 pub fn mint(&mut self, blank_id: &str) -> String {
45 if let Some(existing) = self.mapping.get(blank_id) {
46 return existing.clone();
47 }
48 let iri = format!("{}_blank_{}", self.base_iri, self.counter);
49 self.counter += 1;
50 self.mapping.insert(blank_id.to_owned(), iri.clone());
51 iri
52 }
53
54 pub fn resolve(&mut self, s: &str) -> String {
57 if Self::is_blank(s) {
58 self.mint(s)
59 } else {
60 s.to_owned()
61 }
62 }
63
64 pub fn is_blank(s: &str) -> bool {
66 s.starts_with("_:")
67 }
68
69 pub fn mapping_count(&self) -> usize {
71 self.mapping.len()
72 }
73
74 pub fn get_minted(&self, blank_id: &str) -> Option<&str> {
76 self.mapping.get(blank_id).map(|s| s.as_str())
77 }
78}
79
80#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn test_is_blank_node_detection() {
88 assert!(BlankNodeManager::is_blank("_:b0"));
89 assert!(BlankNodeManager::is_blank("_:xyz"));
90 assert!(!BlankNodeManager::is_blank("http://example.org/"));
91 assert!(!BlankNodeManager::is_blank(""));
92 assert!(!BlankNodeManager::is_blank("notblank"));
93 }
94
95 #[test]
96 fn test_mint_returns_unique_iris() {
97 let mut mgr = BlankNodeManager::new("http://base/");
98 let a = mgr.mint("_:a");
99 let b = mgr.mint("_:b");
100 assert_ne!(a, b, "different blank ids must yield different IRIs");
101 }
102
103 #[test]
104 fn test_mint_increments_counter() {
105 let mut mgr = BlankNodeManager::new("http://base/");
106 mgr.mint("_:x");
107 mgr.mint("_:y");
108 assert_eq!(mgr.mapping_count(), 2);
110 }
111
112 #[test]
113 fn test_resolve_blank_returns_minted() {
114 let mut mgr = BlankNodeManager::new("http://base/");
115 let first = mgr.resolve("_:node1");
116 let second = mgr.resolve("_:node1");
117 assert_eq!(first, second, "resolve must be idempotent");
118 assert!(first.starts_with("http://base/"));
119 }
120
121 #[test]
122 fn test_resolve_named_unchanged() {
123 let mut mgr = BlankNodeManager::new("http://base/");
124 let named = "http://example.org/Alice";
125 assert_eq!(mgr.resolve(named), named);
126 assert_eq!(mgr.mapping_count(), 0, "no blank node should be recorded");
127 }
128
129 #[test]
130 fn test_mapping_stored() {
131 let mut mgr = BlankNodeManager::new("http://base/");
132 let iri = mgr.mint("_:b42");
133 assert!(mgr.mapping.contains_key("_:b42"));
134 assert_eq!(mgr.mapping["_:b42"], iri);
135 }
136
137 #[test]
138 fn test_get_minted_returns_correct_iri() {
139 let mut mgr = BlankNodeManager::new("http://base/");
140 let iri = mgr.mint("_:foo");
141 assert_eq!(mgr.get_minted("_:foo"), Some(iri.as_str()));
142 assert_eq!(mgr.get_minted("_:unknown"), None);
143 }
144}