use std::path::Component;
use std::path::PathBuf;
pub struct MediaLoader {
root: PathBuf,
}
#[derive(Debug, PartialEq)]
pub enum MediaLoaderError {
Absolute,
NotFound,
NotFile,
SymbolicLink,
ParentComponent,
}
impl MediaLoader {
pub fn new(path: PathBuf) -> Self {
assert!(path.is_absolute());
Self { root: path }
}
pub fn validate(&self, path: &str) -> Result<PathBuf, MediaLoaderError> {
let path: PathBuf = PathBuf::from(path);
if path.components().any(|c| c == Component::ParentDir) {
return Err(MediaLoaderError::ParentComponent);
}
if path.is_absolute() {
return Err(MediaLoaderError::Absolute);
}
let path: PathBuf = self.root.join(path);
if !path.exists() {
return Err(MediaLoaderError::NotFound);
}
if path.is_symlink() {
return Err(MediaLoaderError::SymbolicLink);
}
if !path.is_file() {
return Err(MediaLoaderError::NotFile);
}
Ok(path)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Fallible;
use crate::helper::create_tmp_directory;
#[cfg(not(target_os = "windows"))]
#[test]
fn test_abs_rejected() -> Fallible<()> {
let root = create_tmp_directory()?;
let loader = MediaLoader::new(root);
assert_eq!(
loader.validate("/etc/passwd"),
Err(MediaLoaderError::Absolute)
);
Ok(())
}
#[test]
fn test_parent() -> Fallible<()> {
let root = create_tmp_directory()?;
let loader = MediaLoader::new(root);
assert_eq!(
loader.validate("../../../../../../../../../../etc/passwd"),
Err(MediaLoaderError::ParentComponent)
);
Ok(())
}
#[test]
fn test_non_existent() -> Fallible<()> {
let root = create_tmp_directory()?;
let loader = MediaLoader::new(root);
assert_eq!(
loader.validate("does_not_exist.txt"),
Err(MediaLoaderError::NotFound)
);
Ok(())
}
#[cfg(not(target_os = "windows"))]
#[test]
fn test_symlink() -> Fallible<()> {
use std::fs::File;
use std::os::unix::fs::symlink;
let root = create_tmp_directory()?;
let loader = MediaLoader::new(root.clone());
let real_file = root.join("real.txt");
File::create(&real_file)?;
let link_path = root.join("link.txt");
symlink(&real_file, &link_path)?;
assert_eq!(
loader.validate("link.txt"),
Err(MediaLoaderError::SymbolicLink)
);
Ok(())
}
#[test]
fn test_dir() -> Fallible<()> {
use std::fs::create_dir;
let root = create_tmp_directory()?;
let loader = MediaLoader::new(root.clone());
let subdir = root.join("subdir");
create_dir(&subdir)?;
assert_eq!(loader.validate("subdir"), Err(MediaLoaderError::NotFile));
Ok(())
}
}