use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use glob::Pattern;
use crate::engine::{Map, Provenance};
use crate::error::HauchiwaError;
#[derive(Clone, Default, Debug)]
pub struct IterationState {
pub count: usize,
pub exhausted: bool,
}
#[derive(Clone, Default, Debug)]
pub struct TrackerState {
pub accessed: HashMap<String, Provenance>,
pub globs: HashMap<String, IterationState>,
pub iterated: IterationState,
}
#[derive(Clone, Default)]
pub struct TrackerPtr {
pub(crate) ptr: Arc<Mutex<TrackerState>>,
}
#[derive(Default)]
pub struct Tracking {
pub edges: Vec<Option<TrackerPtr>>,
}
impl Tracking {
pub(crate) fn unwrap(self) -> Vec<Option<TrackerState>> {
self.edges
.into_iter()
.map(|edge| edge.map(|item| Arc::try_unwrap(item.ptr).unwrap().into_inner().unwrap()))
.collect()
}
}
pub struct Tracker<'a, T> {
pub(crate) map: &'a Map<T>,
pub(crate) tracker: TrackerPtr,
}
impl<'a, T> Tracker<'a, T> {
pub fn get<K>(&self, key: K) -> Result<&T, HauchiwaError>
where
K: AsRef<str>,
{
match self.map.map.get(key.as_ref()) {
Some((item, provenance)) => {
let mut tracker = self.tracker.ptr.lock().unwrap();
tracker
.accessed
.insert(key.as_ref().to_string(), *provenance);
Ok(item)
}
None => Err(HauchiwaError::AssetNotFound(
key.as_ref().to_string().into(),
)),
}
}
pub fn glob<P>(&self, pattern: P) -> Result<impl Iterator<Item = (&str, &T)>, HauchiwaError>
where
P: AsRef<str>,
{
let pattern_str = pattern.as_ref().to_string();
let matcher = Pattern::new(&pattern_str)?;
let tracker = self.tracker.ptr.clone();
let iter = Box::new(
self.map
.map
.iter()
.filter(move |(key, _)| matcher.matches(key)),
);
Ok(TrackerGlobIter {
iter,
tracker,
pattern: pattern_str,
count: 0,
})
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &T)> {
TrackerIter {
iter: self.map.map.iter(),
tracker: self.tracker.ptr.clone(),
count: 0,
}
}
pub fn values(&self) -> Box<dyn Iterator<Item = &T> + '_> {
Box::new(
TrackerIter {
iter: self.map.map.iter(),
tracker: self.tracker.ptr.clone(),
count: 0,
}
.map(|(_, item)| item),
)
}
}
pub struct TrackerIter<'a, T> {
iter: std::collections::btree_map::Iter<'a, String, (T, Provenance)>,
tracker: Arc<Mutex<TrackerState>>,
count: usize,
}
impl<'a, T> Iterator for TrackerIter<'a, T> {
type Item = (&'a String, &'a T);
fn next(&mut self) -> Option<Self::Item> {
let next = self.iter.next();
let mut tracker = self.tracker.lock().unwrap();
match next {
Some((key, (item, provenance))) => {
self.count += 1;
tracker.iterated.count = tracker.iterated.count.max(self.count);
tracker.accessed.insert(key.clone(), *provenance);
Some((key, item))
}
None => {
tracker.iterated.exhausted = true;
None
}
}
}
}
pub struct TrackerGlobIter<'a, T> {
iter: Box<dyn Iterator<Item = (&'a String, &'a (T, Provenance))> + 'a>,
tracker: Arc<Mutex<TrackerState>>,
pattern: String,
count: usize,
}
impl<'a, T> Iterator for TrackerGlobIter<'a, T> {
type Item = (&'a str, &'a T);
fn next(&mut self) -> Option<Self::Item> {
let next = self.iter.next();
let mut tracker = self.tracker.lock().unwrap();
match next {
Some((key, (item, provenance))) => {
self.count += 1;
let state = tracker.globs.entry(self.pattern.clone()).or_default();
state.count = state.count.max(self.count);
tracker.accessed.insert(key.clone(), *provenance);
Some((key.as_str(), item))
}
None => {
let state = tracker.globs.entry(self.pattern.clone()).or_default();
state.exhausted = true;
None
}
}
}
}
impl<'a, T> IntoIterator for Tracker<'a, T> {
type Item = &'a T;
type IntoIter = Box<dyn Iterator<Item = &'a T> + 'a>;
fn into_iter(self) -> Self::IntoIter {
Box::new(
TrackerIter {
iter: self.map.map.iter(),
tracker: self.tracker.ptr.clone(),
count: 0,
}
.map(|(_, item)| item),
)
}
}
impl<'a, 'b, T> IntoIterator for &'b Tracker<'a, T> {
type Item = &'a T;
type IntoIter = Box<dyn Iterator<Item = &'a T> + 'b>;
fn into_iter(self) -> Self::IntoIter {
Box::new(
TrackerIter {
iter: self.map.map.iter(),
tracker: self.tracker.ptr.clone(),
count: 0,
}
.map(|(_, item)| item),
)
}
}