//! Utilities for working with files use std::{ collections::{BTreeMap, BTreeSet}, fs::{self, DirEntry}, ops::Add, path::PathBuf, }; /// The current distribution's "c89-compatible" folder pub const DIST_C89: &str = "vendored/v0.4.5-dist/c89-compatible"; /// The current distribution's KaRaMeL include folder pub const DIST_KARAMEL_INCLUDE: &str = "vendored/v0.4.5-dist/kremlin/include"; /// The current distribution's KaRaMeL mini-dist include folder pub const DIST_KARAMEL_MINIMAL_INCLUDE: &str = "vendored/v0.4.5-dist/kremlin/kremlib/dist/minimal"; /// A pattern #[derive(Debug, Clone)] pub enum Pattern { /// Whether a string starts with the pattern Start(T), /// Whether a string ends with the pattern End(T), /// Whether a string contains the pattern Contains(T), /// Whether the string is exactly the same as the pattern Exact(T), /// An aggregated pattern Multi(Vec>), } impl Pattern { /// Whether `self` matches the given string pub fn matches(&self, string: &str) -> bool where T: AsRef, { match self { Self::Start(pat) => string.starts_with(pat.as_ref()), Self::End(pat) => string.ends_with(pat.as_ref()), Self::Contains(pat) => string.contains(pat.as_ref()), Self::Exact(pat) => string.eq(pat.as_ref()), Self::Multi(pats) => pats.iter().all(|pat| pat.matches(string)), } } /// Canonicalizes `self` fn canonical(self) -> Vec> where T: ToString, { match self { Self::Multi(pats) => pats, Self::Start(pat) => vec![Pattern::Start(pat.to_string())], Self::End(pat) => vec![Pattern::End(pat.to_string())], Self::Contains(pat) => vec![Pattern::Contains(pat.to_string())], Self::Exact(pat) => vec![Pattern::Exact(pat.to_string())], } } } impl Add for Pattern where T: ToString, { type Output = Pattern; fn add(self, other: Self) -> Self::Output { let mut pats = self.canonical(); pats.extend(other.canonical()); Pattern::Multi(pats) } } /// A file list #[derive(Debug, Default)] pub struct FileList { /// The names pub names: BTreeSet, /// The entries pub entries: BTreeMap, } impl FileList { /// Creates a new empty file list pub fn new() -> Self { Self { names: BTreeSet::new(), entries: BTreeMap::new() } } /// Adds all files within `dir` for which `pat` matches pub fn add(&mut self, dir: &str, pat: Pattern) -> &mut Self where T: AsRef, { // Gather files for maybe_entry in fs::read_dir(dir).expect("Failed to open directory") { // Unwrap the entry and get the file name let entry = maybe_entry.expect("Failed to iterate over directory entry"); let name = entry.file_name().into_string().expect("Non-UTF-8 file name"); // Collect the entry if the pattern is satisfied if pat.matches(&name) { self.names.insert(name.clone()); self.entries.insert(name, entry); } } self } /// Removes all files for which `pat` matches pub fn remove(&mut self, pat: Pattern) -> &mut Self where T: AsRef, { self.names.retain(|name| !pat.matches(name)); self.entries.retain(|name, _| !pat.matches(name)); self } /// Returns an iterator over all item paths pub fn paths(&self) -> impl Iterator + '_ { self.entries.values().map(|entry| entry.path()) } }