use std::{
collections::HashMap,
error::Error,
fmt, fs,
path::{Path, PathBuf},
};
use lazy_static::lazy_static;
use path_clean::clean;
use crate::{
cache::{Cache, CdiOption},
spec::{read_spec, Spec},
utils::is_cdi_spec,
};
const DEFAULT_STATIC_DIR: &str = "/etc/cdi";
const DEFAULT_DYNAMIC_DIR: &str = "/var/run/cdi";
lazy_static! {
pub static ref DEFAULT_SPEC_DIRS: &'static [&'static str] = &[
DEFAULT_STATIC_DIR,
DEFAULT_DYNAMIC_DIR,
];
}
#[derive(Debug)]
pub struct SpecError {
message: String,
}
impl SpecError {
pub fn new(info: &str) -> Self {
Self {
message: info.to_owned(),
}
}
}
impl fmt::Display for SpecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "spec error message {}", self.message)
}
}
impl Error for SpecError {}
pub fn convert_errors(
spec_errors: &HashMap<String, Vec<Box<dyn Error>>>,
) -> HashMap<String, Vec<Box<dyn Error + Send + Sync + 'static>>> {
spec_errors
.iter()
.map(|(key, value)| {
(
key.clone(),
value
.iter()
.map(|error| {
Box::new(SpecError::new(&error.to_string()))
as Box<dyn Error + Send + Sync + 'static>
})
.collect(),
)
})
.collect()
}
pub fn with_spec_dirs(dirs: &[&str]) -> CdiOption {
let cleaned_dirs: Vec<String> = dirs
.iter()
.map(|dir| {
clean(PathBuf::from(*dir))
.into_os_string()
.into_string()
.unwrap()
})
.collect();
Box::new(move |cache: &mut Cache| {
cache.spec_dirs.clone_from(&cleaned_dirs);
})
}
#[allow(dead_code)]
fn traverse_dir<F>(dir_path: &Path, traverse_fn: &mut F) -> Result<(), Box<dyn Error>>
where
F: FnMut(&Path) -> Result<(), Box<dyn Error>>,
{
if let Ok(entries) = fs::read_dir(dir_path) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
traverse_dir(&path, traverse_fn)?;
} else {
traverse_fn(&path)?;
}
}
}
Ok(())
}
#[allow(dead_code)]
pub(crate) fn scan_spec_dirs<P: AsRef<Path>>(dirs: &[P]) -> Result<Vec<Spec>, Box<dyn Error>> {
let mut scaned_specs = Vec::new();
for (priority, dir) in dirs.iter().enumerate() {
let dir_path = dir.as_ref();
if !dir_path.is_dir() {
continue;
}
let mut operation = |path: &Path| -> Result<(), Box<dyn Error>> {
if !path.is_dir() && is_cdi_spec(path) {
let spec = match read_spec(&path.to_path_buf(), priority as i32) {
Ok(spec) => spec,
Err(err) => {
return Err(Box::new(SpecError::new(&err.to_string())));
}
};
scaned_specs.push(spec);
}
Ok(())
};
if let Err(e) = traverse_dir(dir_path, &mut operation) {
return Err(Box::new(SpecError::new(&e.to_string())));
}
}
Ok(scaned_specs)
}
#[cfg(test)]
mod tests {
}