use std::sync::atomic;
use ahash::{HashMap, HashMapExt};
use iri_string::types::{IriStr, IriString};
use xot::Xot;
use super::document_order::DocumentOrderAnnotations;
use super::DocumentOrderAccess;
static DOCUMENTS_COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
fn get_documents_id() -> usize {
DOCUMENTS_COUNTER.fetch_add(1, atomic::Ordering::Relaxed)
}
#[derive(Debug)]
pub enum DocumentsError {
DuplicateUri(String),
Parse(xot::ParseError),
}
impl std::error::Error for DocumentsError {}
impl std::fmt::Display for DocumentsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DocumentsError::DuplicateUri(uri) => write!(f, "Duplicate URI: {}", uri),
DocumentsError::Parse(e) => write!(f, "Parse error: {}", e),
}
}
}
impl From<xot::ParseError> for DocumentsError {
fn from(e: xot::ParseError) -> Self {
DocumentsError::Parse(e)
}
}
#[derive(Debug, Clone)]
pub struct Document {
pub(crate) uri: Option<IriString>,
root: xot::Node,
}
impl Document {
pub fn root(&self) -> xot::Node {
self.root
}
pub(crate) fn cleanup(&self, xot: &mut Xot) {
xot.remove(self.root).unwrap();
}
}
#[derive(Debug, Clone)]
pub struct Documents {
id: usize,
annotations: DocumentOrderAnnotations,
documents: Vec<Document>,
by_uri: HashMap<IriString, DocumentHandle>,
uri_by_document_node: HashMap<xot::Node, IriString>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct DocumentHandle {
pub(crate) documents_id: usize,
pub(crate) id: usize,
}
impl Documents {
pub fn new() -> Self {
Self {
id: get_documents_id(),
annotations: DocumentOrderAnnotations::new(),
documents: Vec::new(),
by_uri: HashMap::new(),
uri_by_document_node: HashMap::new(),
}
}
pub fn cleanup(&mut self, xot: &mut Xot) {
for document in &self.documents {
document.cleanup(xot);
}
self.documents.clear();
self.by_uri.clear();
}
pub fn add_string(
&mut self,
xot: &mut Xot,
uri: Option<&IriStr>,
xml: &str,
) -> Result<DocumentHandle, DocumentsError> {
let root = xot.parse(xml)?;
self.add_root(uri, root)
}
pub fn add_fragment_string(
&mut self,
xot: &mut Xot,
xml: &str,
) -> Result<DocumentHandle, DocumentsError> {
let root = xot.parse_fragment(xml)?;
self.add_root(None, root)
}
pub fn add_root(
&mut self,
uri: Option<&IriStr>,
root: xot::Node,
) -> Result<DocumentHandle, DocumentsError> {
if let Some(uri) = uri {
if self.by_uri.contains_key(uri) {
return Err(DocumentsError::DuplicateUri(uri.as_str().to_string()));
}
}
let id = self.documents.len();
let handle = DocumentHandle {
documents_id: self.id,
id,
};
self.documents.push(Document {
uri: uri.map(|uri| uri.to_owned()),
root,
});
if let Some(uri) = uri {
self.by_uri.insert(uri.to_owned(), handle);
self.uri_by_document_node.insert(root, uri.to_owned());
}
Ok(handle)
}
pub fn get_by_handle(&self, handle: DocumentHandle) -> Option<&Document> {
if handle.documents_id != self.id {
return None;
}
self.documents.get(handle.id)
}
pub fn get_node_by_handle(&self, handle: DocumentHandle) -> Option<xot::Node> {
Some(self.get_by_handle(handle)?.root)
}
pub fn get_by_uri(&self, uri: &IriStr) -> Option<&Document> {
let handle = self.by_uri.get(uri)?;
self.get_by_handle(*handle)
}
pub fn get_node_by_uri(&self, uri: &IriStr) -> Option<xot::Node> {
Some(self.get_by_uri(uri)?.root)
}
pub fn get_uri_by_document_node(&self, node: xot::Node) -> Option<IriString> {
self.uri_by_document_node.get(&node).cloned()
}
pub fn len(&self) -> usize {
self.documents.len()
}
pub fn is_empty(&self) -> bool {
self.documents.is_empty()
}
pub(crate) fn annotations(&self) -> &DocumentOrderAnnotations {
&self.annotations
}
pub(crate) fn document_order_access<'a>(&'a self, xot: &'a Xot) -> DocumentOrderAccess<'a> {
self.annotations.access(xot)
}
}
impl Default for Documents {
fn default() -> Self {
Self::new()
}
}