use std::collections::HashMap;
pub struct BlankNodeManager {
counter: u64,
base_iri: String,
pub mapping: HashMap<String, String>,
}
impl BlankNodeManager {
pub fn new(base_iri: impl Into<String>) -> Self {
Self {
counter: 0,
base_iri: base_iri.into(),
mapping: HashMap::new(),
}
}
pub fn mint(&mut self, blank_id: &str) -> String {
if let Some(existing) = self.mapping.get(blank_id) {
return existing.clone();
}
let iri = format!("{}_blank_{}", self.base_iri, self.counter);
self.counter += 1;
self.mapping.insert(blank_id.to_owned(), iri.clone());
iri
}
pub fn resolve(&mut self, s: &str) -> String {
if Self::is_blank(s) {
self.mint(s)
} else {
s.to_owned()
}
}
pub fn is_blank(s: &str) -> bool {
s.starts_with("_:")
}
pub fn mapping_count(&self) -> usize {
self.mapping.len()
}
pub fn get_minted(&self, blank_id: &str) -> Option<&str> {
self.mapping.get(blank_id).map(|s| s.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_blank_node_detection() {
assert!(BlankNodeManager::is_blank("_:b0"));
assert!(BlankNodeManager::is_blank("_:xyz"));
assert!(!BlankNodeManager::is_blank("http://example.org/"));
assert!(!BlankNodeManager::is_blank(""));
assert!(!BlankNodeManager::is_blank("notblank"));
}
#[test]
fn test_mint_returns_unique_iris() {
let mut mgr = BlankNodeManager::new("http://base/");
let a = mgr.mint("_:a");
let b = mgr.mint("_:b");
assert_ne!(a, b, "different blank ids must yield different IRIs");
}
#[test]
fn test_mint_increments_counter() {
let mut mgr = BlankNodeManager::new("http://base/");
mgr.mint("_:x");
mgr.mint("_:y");
assert_eq!(mgr.mapping_count(), 2);
}
#[test]
fn test_resolve_blank_returns_minted() {
let mut mgr = BlankNodeManager::new("http://base/");
let first = mgr.resolve("_:node1");
let second = mgr.resolve("_:node1");
assert_eq!(first, second, "resolve must be idempotent");
assert!(first.starts_with("http://base/"));
}
#[test]
fn test_resolve_named_unchanged() {
let mut mgr = BlankNodeManager::new("http://base/");
let named = "http://example.org/Alice";
assert_eq!(mgr.resolve(named), named);
assert_eq!(mgr.mapping_count(), 0, "no blank node should be recorded");
}
#[test]
fn test_mapping_stored() {
let mut mgr = BlankNodeManager::new("http://base/");
let iri = mgr.mint("_:b42");
assert!(mgr.mapping.contains_key("_:b42"));
assert_eq!(mgr.mapping["_:b42"], iri);
}
#[test]
fn test_get_minted_returns_correct_iri() {
let mut mgr = BlankNodeManager::new("http://base/");
let iri = mgr.mint("_:foo");
assert_eq!(mgr.get_minted("_:foo"), Some(iri.as_str()));
assert_eq!(mgr.get_minted("_:unknown"), None);
}
}