0.1.0-0-pre.9
This commit is contained in:
45
crates/common/game-assets-lib/src/resolver.rs
Normal file
45
crates/common/game-assets-lib/src/resolver.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user