forked from Cutieguwu/raven
152 lines
3.9 KiB
Rust
152 lines
3.9 KiB
Rust
use std::collections::HashMap;
|
|
use std::path::{Path, PathBuf};
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
//TODO: Clean this up. Shouldn't need duplicated DIR_SRC consts about the workspace.
|
|
|
|
const DIR_SRC: &str = "src/";
|
|
const DIR_TARGET: &str = "target/";
|
|
|
|
const DIR_MAIN: &str = const_format::concatcp!(DIR_SRC, "main/");
|
|
const DIR_TEST: &str = const_format::concatcp!(DIR_SRC, "test/");
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct PathHandler {
|
|
root_path: PathBuf,
|
|
// This is a short-living binary. This doesn't need an LRU like Moka.
|
|
derived_path_cache: HashMap<String, PathBuf>,
|
|
}
|
|
|
|
impl PathHandler {
|
|
pub fn new(root_path: PathBuf) -> Self {
|
|
Self::from(root_path)
|
|
}
|
|
|
|
pub fn root_path(&self) -> PathBuf {
|
|
self.root_path.clone()
|
|
}
|
|
|
|
/// This is a readability helper.
|
|
/// Make sure to set the root of this `PathHandler` to the project root.
|
|
/// This simply calls upon the root_path() of the `PathHandler`.
|
|
pub fn project_root(&self) -> PathBuf {
|
|
self.root_path()
|
|
}
|
|
|
|
pub fn dir_src(&mut self) -> PathBuf {
|
|
self.get_path(DIR_SRC)
|
|
}
|
|
|
|
pub fn dir_target(&mut self) -> PathBuf {
|
|
self.get_path(DIR_TARGET)
|
|
}
|
|
|
|
pub fn dir_main(&mut self) -> PathBuf {
|
|
self.get_path(DIR_MAIN)
|
|
}
|
|
|
|
pub fn dir_test(&mut self) -> PathBuf {
|
|
self.get_path(DIR_TEST)
|
|
}
|
|
|
|
/// Attempts to load from cache, else generates the path and clones it to the cache.
|
|
/// Returns the requested path.
|
|
fn get_path<S>(&mut self, k: S) -> PathBuf
|
|
where
|
|
S: ToString + AsRef<str>,
|
|
{
|
|
self.from_cache(k.as_ref())
|
|
.unwrap_or_else(|| {
|
|
self.gen_key(k.to_string(), self.root_path().join(k.to_string()));
|
|
self.get_path(k.as_ref())
|
|
})
|
|
.to_path_buf()
|
|
}
|
|
|
|
/// Attempts to pull the value for the given key from the cache.
|
|
fn from_cache<S: AsRef<str>>(&self, path_key: S) -> Option<PathBuf> {
|
|
self.derived_path_cache
|
|
.get(path_key.as_ref())
|
|
.and_then(|v| Some(v.to_owned()))
|
|
}
|
|
|
|
/// Tries to generate a new key-value pair in the cache
|
|
fn gen_key<P, S>(&mut self, k: S, v: P) -> Option<PathBuf>
|
|
where
|
|
P: AsRef<Path>,
|
|
S: ToString,
|
|
{
|
|
self.derived_path_cache
|
|
.insert(k.to_string(), v.as_ref().to_path_buf())
|
|
}
|
|
}
|
|
|
|
impl<P> From<P> for PathHandler
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
fn from(value: P) -> Self {
|
|
Self {
|
|
root_path: value.as_ref().to_path_buf(),
|
|
derived_path_cache: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait PathHandled {
|
|
fn set_path_handler(&mut self, ph: Arc<Mutex<PathHandler>>) {}
|
|
|
|
fn with_path_handler(&mut self, ph: Arc<Mutex<PathHandler>>) -> &mut Self {
|
|
self
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const ROOT: &str = "/root";
|
|
|
|
#[test]
|
|
fn ph_get_path() {
|
|
let root = PathBuf::from(ROOT);
|
|
let expected = root.join(DIR_SRC);
|
|
|
|
let mut ph = PathHandler::from(root);
|
|
|
|
assert!(ph.dir_src() == expected);
|
|
}
|
|
|
|
#[test]
|
|
fn ph_cache_gen() {
|
|
let root = PathBuf::from(ROOT);
|
|
let expected = root.join(DIR_SRC);
|
|
|
|
let ph = PathHandler::from(root);
|
|
|
|
assert!(
|
|
ph.derived_path_cache
|
|
.get(DIR_SRC)
|
|
.is_some_and(|v| *v == expected)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn ph_cache_pull() {
|
|
let faux_path = "faux/path";
|
|
|
|
let mut ph = PathHandler::from("false/root");
|
|
ph.derived_path_cache
|
|
.insert(faux_path.to_string(), PathBuf::from(faux_path));
|
|
|
|
// Use the method that attempts a fallback.
|
|
// By using a false root, this will create a different path to the injected one,
|
|
// making it possible to determine if the cache load fails.
|
|
//
|
|
// I.e.
|
|
// Expected: faux/path as PathBuf
|
|
// Failed: false/root/faux/path as PathBuf
|
|
assert!(ph.get_path(faux_path) == PathBuf::from(faux_path));
|
|
}
|
|
}
|