use bimap::BiMap;
use smallvec::{smallvec, SmallVec};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use thiserror::Error;
type SourceGroup<P> = SmallVec<[P; 1]>;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ManifestError {
#[error("detected collision in route destination path: `{0}`")]
PathCollision(PathBuf),
}
pub struct Route<M, P>
where
P: AsRef<Path>,
{
sources: SourceGroup<P>,
destination: P,
phantom: PhantomData<M>,
}
impl<M, P> Route<M, P>
where
P: AsRef<Path>,
{
pub fn sources(&self) -> impl ExactSizeIterator<Item = &'_ P> {
self.sources.iter()
}
pub fn destination(&self) -> &P {
&self.destination
}
}
#[derive(Default)]
pub struct Manifest<M>
where
M: Routing,
{
router: M,
}
impl<M> Manifest<M>
where
M: Routing,
{
pub fn insert(
&mut self,
source: impl Into<PathBuf>,
destination: impl Into<PathBuf>,
) -> Result<(), ManifestError> {
self.router.insert(source.into(), destination.into())
}
pub fn routes(&self) -> impl ExactSizeIterator<Item = Route<M, &'_ Path>> {
self.router.paths().map(|(sources, destination)| Route {
sources,
destination,
phantom: PhantomData,
})
}
}
pub trait Routing: Default {
fn insert(&mut self, source: PathBuf, destination: PathBuf) -> Result<(), ManifestError>;
fn paths(&self) -> Box<dyn '_ + ExactSizeIterator<Item = (SourceGroup<&'_ Path>, &'_ Path)>>;
}
#[derive(Clone, Debug, Default)]
pub struct Bijective {
inner: BiMap<PathBuf, PathBuf>,
}
impl Routing for Bijective {
fn insert(&mut self, source: PathBuf, destination: PathBuf) -> Result<(), ManifestError> {
if self.inner.contains_right(&destination) {
Err(ManifestError::PathCollision(destination))
}
else {
self.inner.insert_no_overwrite(source, destination).unwrap();
Ok(())
}
}
fn paths(&self) -> Box<dyn '_ + ExactSizeIterator<Item = (SourceGroup<&'_ Path>, &'_ Path)>> {
Box::new(
self.inner
.iter()
.map(|(source, destination)| (smallvec![source.as_ref()], destination.as_ref())),
)
}
}