use crate::{cmd::plural, collective::fellowship::FellowshipEvidenceReport, types::prelude::*};
use anyhow::{bail, Context};
use std::path::PathBuf;
type Result<T> = anyhow::Result<T>;
#[derive(Debug, clap::Parser)]
pub struct IndexEvidenceCommand {
#[clap(long, default_value = "evidence")]
evidence: PathBuf,
#[clap(long, default_value = "date,reporter", value_delimiter = ',')]
by: Vec<IndexMetric>,
#[clap(long)]
reindex: bool,
}
#[derive(Debug, Clone, PartialEq, clap::ValueEnum)]
pub enum IndexMetric {
Date,
Reporter,
}
impl IndexEvidenceCommand {
pub fn run(&self) -> Result<()> {
self.check_root_folder()?;
self.create_date_index()?;
self.create_reporter_index()?;
let evidences = self.parse_files()?;
let (mut scanned, mut created) = (0, 0);
for (path, evidence) in evidences.iter() {
let file_name = path.file_name().context("No file name")?;
log::debug!("Processing '{}'", path.display());
if self.by_reporter() {
let sub =
self.create_sub_folder(&["by_reporter", &evidence.member.canonical_name()])?;
let from = PathBuf::from("..").join("..").join(file_name);
let to = sub.join(file_name);
if std::fs::symlink_metadata(&to).is_ok() {
log::info!("Link '{}' already exists", to.display());
} else {
log::info!("Linking to '{}' as '{}'", to.display(), from.display());
symlink::symlink_file(&from, &to)?;
created += 1;
}
scanned += 1;
}
if self.by_date() {
let splits = evidence.date.split('-').collect::<Vec<_>>();
let sub =
self.create_sub_folder(&["by_date", (splits[0]), (splits[1]), (splits[2])])?;
let from = PathBuf::from("..").join("..").join("..").join("..").join(file_name);
let to = sub.join(file_name);
if std::fs::symlink_metadata(&to).is_ok() {
log::info!("Link '{}' already exists", to.display());
} else {
log::info!("Linking to '{}' as '{}'", to.display(), from.display());
symlink::symlink_file(&from, &to)?;
created += 1;
}
scanned += 1;
}
}
println!("Scanned {} and created {} new link{}.", scanned, created, plural(created));
Ok(())
}
fn check_root_folder(&self) -> Result<()> {
let root = PathBuf::from(&self.evidence);
if !root.exists() {
bail!("Folder '{}' does not exist", root.display())
}
if !root.is_dir() {
bail!("Folder '{}' is not a directory", root.display())
}
Ok(())
}
fn parse_files(&self) -> Result<Vec<(PathBuf, FellowshipEvidenceReport)>> {
let mut reports = vec![];
for entry in std::fs::read_dir(&self.evidence)? {
let entry = entry?;
let path = entry.path();
if path.is_file() && path.extension() == Some("evidence".as_ref()) {
let file = std::fs::File::open(&path)?;
let report: FellowshipEvidenceReport = serde_yaml::from_reader(file)?;
log::debug!("Parsed evidence report from '{}'", path.display());
reports.push((path, report));
}
}
Ok(reports)
}
fn create_date_index(&self) -> Result<()> {
if !self.by_date() {
return Ok(());
}
if self.reindex {
self.delete_sub_folder(&["by_date"])?;
}
let _folder = self.create_sub_folder(&["by_date"])?;
Ok(())
}
fn create_reporter_index(&self) -> Result<()> {
if !self.by_reporter() {
return Ok(());
}
if self.reindex {
self.delete_sub_folder(&["by_reporter"])?;
}
let _folder = self.create_sub_folder(&["by_reporter"])?;
Ok(())
}
fn delete_sub_folder(&self, subs: &[&str]) -> Result<()> {
let folder = self.create_sub_folder(subs)?;
log::info!("Deleting index folder at '{}'", folder.display());
std::fs::remove_dir_all(&folder)?;
Ok(())
}
fn create_sub_folder(&self, subs: &[&str]) -> Result<PathBuf> {
let mut folder = self.evidence.clone();
for sub in subs {
folder = folder.join(sub);
}
if folder.exists() {
log::debug!("Index folder '{}' already exists", folder.display());
if !folder.is_dir() {
bail!("Path {} is not a directory", folder.display());
}
} else {
log::info!("Creating index folder at '{}'", folder.display());
std::fs::create_dir_all(&folder)?;
}
Ok(folder)
}
fn by_date(&self) -> bool {
self.by.contains(&IndexMetric::Date)
}
fn by_reporter(&self) -> bool {
self.by.contains(&IndexMetric::Reporter)
}
}