use crate::{GodoruError, GodoruResult, Shader};
use std::path::{Path, PathBuf};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AssetLoader {
root: PathBuf,
}
impl AssetLoader {
pub fn new(root: impl Into<PathBuf>) -> Self {
Self { root: root.into() }
}
pub fn image(&self, path: impl AsRef<Path>) -> GodoruResult<ImageHandle> {
let path = assetPath(path.as_ref())?;
Ok(ImageHandle {
path: self.root.join(path),
})
}
pub fn font(&self, path: impl AsRef<Path>) -> GodoruResult<FontHandle> {
let path = assetPath(path.as_ref())?;
Ok(FontHandle {
path: self.root.join(path),
})
}
pub fn shader(&self, path: impl AsRef<Path>) -> GodoruResult<Shader> {
let path = assetPath(path.as_ref())?;
Ok(Shader::fromFile(self.root.join(path)))
}
}
impl Default for AssetLoader {
fn default() -> Self {
Self::new("")
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ImageHandle {
pub path: PathBuf,
}
impl ImageHandle {
pub fn new(path: impl Into<PathBuf>) -> Self {
Self { path: path.into() }
}
pub fn runtimePath(&self) -> String {
runtimePath(&self.path)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FontHandle {
pub path: PathBuf,
}
impl FontHandle {
pub fn new(path: impl Into<PathBuf>) -> Self {
Self { path: path.into() }
}
pub fn runtimePath(&self) -> String {
runtimePath(&self.path)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AssetKind {
Image,
Font,
Shader,
}
fn assetPath(path: &Path) -> GodoruResult<PathBuf> {
if path.as_os_str().is_empty() {
Err(GodoruError::AssetPath("asset path is empty".to_string()))
} else if path.is_absolute() {
Err(GodoruError::AssetPath(format!(
"asset path must be relative: {}",
path.display()
)))
} else {
Ok(path.to_path_buf())
}
}
pub(crate) fn runtimePath(path: &Path) -> String {
let mut value = String::from("res://assets");
for item in path.components() {
if let std::path::Component::Normal(item) = item {
value.push('/');
value.push_str(&item.to_string_lossy().replace('\\', "/"));
}
}
value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn assetRuntimePathUsesResAssets() {
let image = AssetLoader::default().image("images/logo.webp").unwrap();
assert_eq!(image.runtimePath(), "res://assets/images/logo.webp");
}
#[test]
fn absoluteAssetsAreRejected() {
let err = AssetLoader::default().image("/tmp/logo.png").unwrap_err();
assert!(matches!(err, GodoruError::AssetPath(_)));
}
}