use std::cell::RefCell;
use energyml_domain::attachments::{self, CacheStats, Listing};
use energyml_domain::package_index::{PackageIndex, Progress};
use energyml_storage::EpcStore;
pub mod colours;
pub mod index;
pub use index::{Consumer, Disturbed, IndexSlot};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
Store(#[from] energyml_storage::Error),
#[error("{0}")]
Domain(#[from] energyml_domain::Error),
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Layers {
pub index: bool,
pub colours: bool,
pub attachments: bool,
}
impl Layers {
pub const fn none() -> Self {
Self {
index: false,
colours: false,
attachments: false,
}
}
pub const fn all() -> Self {
Self {
index: true,
colours: true,
attachments: true,
}
}
pub const fn with_index(mut self) -> Self {
self.index = true;
self
}
pub const fn with_colours(mut self) -> Self {
self.colours = true;
self
}
pub const fn with_attachments(mut self) -> Self {
self.attachments = true;
self
}
pub const fn normalised(mut self) -> Self {
self.index = self.index || self.colours || self.attachments;
self
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Held {
pub index_nodes: usize,
pub colour_walks: usize,
pub listings: usize,
}
impl Held {
pub fn is_empty(&self) -> bool {
*self == Held::default()
}
}
pub struct Cache {
layers: Layers,
pub(crate) index: IndexSlot,
pub(crate) colours: RefCell<colours::ColourSlot>,
listings: RefCell<attachments::Cache>,
}
impl Cache {
pub fn off() -> Self {
Self::new(Layers::none())
}
pub fn new(layers: Layers) -> Self {
Self {
layers: layers.normalised(),
index: IndexSlot::default(),
colours: RefCell::default(),
listings: RefCell::default(),
}
}
pub fn layers(&self) -> Layers {
self.layers
}
pub fn held(&self) -> Held {
Held {
index_nodes: self.index.node_count().unwrap_or(0),
colour_walks: self.colours.try_borrow().map(|slot| slot.len()).unwrap_or(0),
listings: self.listings.try_borrow().map(|cache| cache.len()).unwrap_or(0),
}
}
pub fn with_index<R>(&self, store: &EpcStore, f: impl FnOnce(&PackageIndex) -> R) -> R {
if self.layers.index {
self.index.with(store, f)
} else {
f(&PackageIndex::build(store, store.revision()))
}
}
pub fn index_step(&self, store: &EpcStore, n: usize) -> Progress {
if self.layers.index {
self.index.step(store, n)
} else {
Progress { done: 0, total: 0 }
}
}
#[doc(hidden)]
pub fn index_slot(&self) -> &IndexSlot {
&self.index
}
pub fn set_partial(&self, partial: bool) {
self.index.set_partial(partial);
}
pub fn attachments_of(&self, store: &EpcStore, uuids: &[&str]) -> Vec<Listing> {
let mut fresh = attachments::Cache::new();
let mut held = self.listings.try_borrow_mut();
let (cache, kept): (&mut attachments::Cache, bool) = match held.as_deref_mut() {
Ok(cache) if self.layers.attachments => (cache, true),
_ => (&mut fresh, false),
};
self.with_index(store, |index| {
if kept {
let dirty = self.index.take_disturbed(Consumer::Attachments);
if dirty.everything {
cache.clear();
} else {
cache.disturb(&dirty.uuids);
}
}
cache.listings(store, store.files_revision(), index, uuids)
})
}
pub fn attachment_stats(&self) -> CacheStats {
self.listings
.try_borrow()
.map(|cache| cache.stats())
.unwrap_or_default()
}
#[doc(hidden)]
pub fn attachment_listing_held(&self, uuid: &str) -> bool {
self.listings
.try_borrow()
.map(|cache| cache.holds(uuid))
.unwrap_or(false)
}
}