use std::fs;
use std::path::Path;
use doublets::data::LinkReference;
use crate::error::LinkError;
use crate::link::GenericLink;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct StorageRevision {
pub len: u64,
pub modified_nanos: u128,
}
impl StorageRevision {
pub fn of<P: AsRef<Path>>(path: P) -> Result<Self, LinkError> {
let metadata = match fs::metadata(path.as_ref()) {
Ok(metadata) => metadata,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return Ok(Self::default())
}
Err(error) => return Err(LinkError::Io(error)),
};
let modified_nanos = metadata
.modified()
.ok()
.and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
.map(|duration| duration.as_nanos())
.unwrap_or(0);
Ok(Self {
len: metadata.len(),
modified_nanos,
})
}
}
pub trait LinksStorage<T: LinkReference> {
fn create_link(&mut self, source: T, target: T) -> Result<T, LinkError>;
fn ensure_link_created(&mut self, index: T) -> Result<T, LinkError>;
fn get_link(&self, index: T) -> Option<GenericLink<T>>;
fn link_exists(&self, index: T) -> bool {
self.get_link(index).is_some()
}
fn update_link(&mut self, index: T, source: T, target: T) -> Result<GenericLink<T>, LinkError>;
fn delete_link(&mut self, index: T) -> Result<GenericLink<T>, LinkError>;
fn update_link_observed(
&mut self,
index: T,
source: T,
target: T,
observer: &mut dyn FnMut(GenericLink<T>, GenericLink<T>),
) -> Result<GenericLink<T>, LinkError> {
let previous = self.update_link(index, source, target)?;
let after = self
.get_link(index)
.unwrap_or_else(|| GenericLink::new(index, source, target));
observer(previous, after);
Ok(previous)
}
fn delete_link_observed(
&mut self,
index: T,
observer: &mut dyn FnMut(GenericLink<T>, GenericLink<T>),
) -> Result<GenericLink<T>, LinkError> {
let deleted = self.delete_link(index)?;
observer(deleted, GenericLink::null());
Ok(deleted)
}
fn all_links(&self) -> Vec<GenericLink<T>>;
fn query_links(
&self,
index: Option<T>,
source: Option<T>,
target: Option<T>,
) -> Vec<GenericLink<T>> {
self.all_links()
.into_iter()
.filter(|link| {
index.is_none_or(|value| value == link.index)
&& source.is_none_or(|value| value == link.source)
&& target.is_none_or(|value| value == link.target)
})
.collect()
}
fn search_link(&self, source: T, target: T) -> Option<T> {
self.all_links()
.into_iter()
.find(|link| link.source == source && link.target == target)
.map(|link| link.index)
}
fn get_or_create_link(&mut self, source: T, target: T) -> Result<T, LinkError> {
match self.search_link(source, target) {
Some(index) => Ok(index),
None => self.create_link(source, target),
}
}
fn links_count(&self) -> usize {
self.all_links().len()
}
fn flush(&mut self) -> Result<(), LinkError>;
fn has_external_changes(&self) -> Result<bool, LinkError> {
Ok(false)
}
fn reload(&mut self) -> Result<(), LinkError> {
Ok(())
}
}
pub trait LinksStorageRef<T: LinkReference>: LinksStorage<T> {
fn get_link_ref(&self, index: T) -> Option<&GenericLink<T>>;
fn all_link_refs(&self) -> Vec<&GenericLink<T>>;
fn query_link_refs(
&self,
index: Option<T>,
source: Option<T>,
target: Option<T>,
) -> Vec<&GenericLink<T>> {
self.all_link_refs()
.into_iter()
.filter(|link| {
index.is_none_or(|value| value == link.index)
&& source.is_none_or(|value| value == link.source)
&& target.is_none_or(|value| value == link.target)
})
.collect()
}
}