46 lines
1.7 KiB
Rust
46 lines
1.7 KiB
Rust
// file: crates/common/game-assets-lib/src/resolver.rs
|
|
// version: 1
|
|
|
|
/// Resolves logical asset URIs against separate common and game roots.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct AssetResolver {
|
|
common_root: std::path::PathBuf,
|
|
game_root: std::path::PathBuf,
|
|
}
|
|
|
|
impl AssetResolver {
|
|
/// Creates a resolver from physical common and game asset roots.
|
|
#[must_use]
|
|
pub fn new(common_root: impl Into<std::path::PathBuf>, game_root: impl Into<std::path::PathBuf>) -> Self {
|
|
return Self { common_root: common_root.into(), game_root: game_root.into() };
|
|
}
|
|
|
|
/// Resolves `common://` or `game://` URI into a physical path without touching the filesystem.
|
|
pub fn resolve(&self, uri: &str) -> Result<std::path::PathBuf, crate::AssetError> {
|
|
let (root, relative) = if let Some(relative) = uri.strip_prefix("common://") {
|
|
(&self.common_root, relative)
|
|
} else if let Some(relative) = uri.strip_prefix("game://") {
|
|
(&self.game_root, relative)
|
|
} else {
|
|
return Err(crate::AssetError::UnsupportedScheme);
|
|
};
|
|
if relative.is_empty() {
|
|
return Err(crate::AssetError::EmptyPath);
|
|
}
|
|
let relative_path = std::path::Path::new(relative);
|
|
if relative_path.is_absolute() || relative.contains('\\') {
|
|
return Err(crate::AssetError::InvalidPath);
|
|
}
|
|
for component in relative_path.components() {
|
|
if !matches!(component, std::path::Component::Normal(_)) {
|
|
return Err(crate::AssetError::InvalidPath);
|
|
}
|
|
}
|
|
return Ok(root.join(relative_path));
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "../unit_tests/resolver.rs"]
|
|
mod tests;
|