use super::fs;
use super::prelude::{Error, File, Pid, Relation};
use std::collections::hash_set::{IntoIter, Iter};
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::iter::FromIterator;
use std::path::{Path, PathBuf};
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Index {
pid: Pid,
path: PathBuf,
}
impl Index {
pub fn new(pid: Pid, path: impl Into<PathBuf>) -> Self {
let path = path.into();
Self { pid, path }
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn pid(&self) -> &Pid {
&self.pid
}
pub fn is_similar(&self, other: &Self) -> bool {
self.path == other.path
}
pub fn rename<F>(&self, format: F) -> Result<Self, Error>
where
F: Fn(&Pid, &str) -> String,
{
let path = self.path();
let parent = fs::parent(path)?;
let stem = fs::file_stem(path).map(|name| format(&self.pid, name))?;
let mut new_path = parent.join(stem);
if let Some(extension) = self.path.extension() {
new_path.set_extension(extension);
}
let result = Self::new(self.pid, new_path);
Ok(result)
}
pub fn prefix(&self, root: &Path) -> PathBuf {
root.join(&self.path)
}
pub fn with_pid(&self, pid: Pid) -> Self {
Self::new(pid, &self.path)
}
}
impl fmt::Debug for Index {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Index {{ {} @ {} }}", self.path.display(), self.pid)
}
}
impl fmt::Display for Index {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} ({})", self.path.display(), self.pid)
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct IndexList<'a> {
indexes: HashSet<&'a Index>,
}
impl<'a> IndexList<'a> {
fn indexes(&self) -> impl Iterator<Item = &Index> {
self.indexes.iter().copied()
}
pub fn new(indexes: HashSet<&'a Index>) -> Self {
Self { indexes }
}
pub fn get_different_pid(&self, index: &Index) -> Option<&Index> {
self.indexes()
.find(|i| i.is_similar(index) && i.pid() != index.pid())
}
pub fn get(&self, index: &Index) -> Option<&Index> {
self.indexes().find(|i| i.is_similar(index))
}
pub fn get_exact(&self, index: &Index) -> Option<&Index> {
self.indexes.get(index).copied()
}
pub fn inner(&self) -> &HashSet<&Index> {
&self.indexes
}
pub fn iter(&self) -> Iter<&Index> {
self.indexes.iter()
}
}
impl<'a, 'b> IntoIterator for &'a IndexList<'b> {
type Item = &'a &'b Index;
type IntoIter = Iter<'a, &'b Index>;
fn into_iter(self) -> Self::IntoIter {
self.indexes.iter()
}
}
impl<'a> IntoIterator for IndexList<'a> {
type Item = &'a Index;
type IntoIter = IntoIter<&'a Index>;
fn into_iter(self) -> Self::IntoIter {
self.indexes.into_iter()
}
}
impl<'a> FromIterator<&'a Index> for IndexList<'a> {
fn from_iter<T: IntoIterator<Item = &'a Index>>(iter: T) -> Self {
let indexes = iter.into_iter().collect();
Self::new(indexes)
}
}
#[derive(Debug, Default, Clone)]
pub struct IndexMapping<'a> {
map: HashMap<&'a Index, Index>,
}
impl<'a> IndexMapping<'a> {
pub fn new(map: HashMap<&'a Index, Index>) -> Self {
Self { map }
}
pub fn get(&self, index: &'a Index) -> Option<&Index> {
self.map.get(index)
}
pub fn apply_mapping<F: File>(&self, file: F) -> F {
let modify_if_exists = |acc: F, ref from| match self.get(from) {
Some(to) => acc.modify_relation(from, to),
None => acc,
};
file.relation()
.into_iter()
.map(Relation::index)
.fold(file, modify_if_exists)
}
}
impl<'a> FromIterator<(&'a Index, Index)> for IndexMapping<'a> {
fn from_iter<T: IntoIterator<Item = (&'a Index, Index)>>(iter: T) -> Self {
let map = iter.into_iter().collect();
Self::new(map)
}
}
#[cfg(test)]
#[allow(clippy::blacklisted_name)]
mod tests {
use super::*;
fn formatter(pid: &Pid, filename: &str) -> String {
format!("{}_{}", filename, pid.value())
}
#[test]
fn is_similar_index() {
let foo = Index::new(Pid::new(0), "./foo/bar");
let bar = Index::new(Pid::new(1), "./foo/bar");
assert!(foo.is_similar(&bar))
}
#[test]
fn rename_index() {
let index = Index::new(Pid::new(42), "./foo");
let result = index.rename(formatter).unwrap();
let expect = Index::new(Pid::new(42), "./foo_42");
assert_eq!(result, expect);
}
#[test]
fn rename_index_2() {
let index = Index::new(Pid::new(1), "./foo/bar.json");
let result = index.rename(formatter).unwrap();
let expect = Index::new(Pid::new(1), "./foo/bar_1.json");
assert_eq!(result, expect);
}
#[test]
fn rename_index_3() {
let index = Index::new(Pid::new(1000), "./bar/");
let result = index.rename(formatter).unwrap();
let expect = Index::new(Pid::new(1000), "./bar_1000");
assert_eq!(result, expect);
}
}