Skip to content

Commit

Permalink
Process Asset File Extensions With Multiple Dots
Browse files Browse the repository at this point in the history
Fixes #1276
  • Loading branch information
zicklag committed Jan 22, 2021
1 parent a880b54 commit f31af20
Showing 1 changed file with 137 additions and 8 deletions.
145 changes: 137 additions & 8 deletions crates/bevy_asset/src/asset_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use thiserror::Error;
pub enum AssetServerError {
#[error("asset folder path is not a directory")]
AssetFolderNotADirectory(String),
#[error("no AssetLoader found for the given extension")]
MissingAssetLoader(Option<String>),
#[error("no AssetLoader found{}", format_missing_asset_ext(.extensions))]
MissingAssetLoader { extensions: Vec<String> },
#[error("the given type does not match the type of the loaded asset")]
IncorrectHandleType,
#[error("encountered an error while loading an asset")]
Expand All @@ -28,6 +28,18 @@ pub enum AssetServerError {
PathLoaderError(#[from] AssetIoError),
}

fn format_missing_asset_ext(exts: &[String]) -> String {
if exts.is_empty() {
format!(
" for the following extension{}: {}",
if exts.len() > 1 { "s" } else { "" },
exts.join(", ")
)
} else {
String::new()
}
}

#[derive(Default)]
pub(crate) struct AssetRefCounter {
pub(crate) channel: Arc<RefChangeChannel>,
Expand Down Expand Up @@ -125,18 +137,38 @@ impl AssetServer {
.read()
.get(extension)
.map(|index| self.server.loaders.read()[*index].clone())
.ok_or_else(|| AssetServerError::MissingAssetLoader(Some(extension.to_string())))
.ok_or_else(|| AssetServerError::MissingAssetLoader {
extensions: vec![extension.to_string()],
})
}

fn get_path_asset_loader<P: AsRef<Path>>(
&self,
path: P,
) -> Result<Arc<Box<dyn AssetLoader>>, AssetServerError> {
path.as_ref()
.extension()
.and_then(|e| e.to_str())
.ok_or(AssetServerError::MissingAssetLoader(None))
.and_then(|extension| self.get_asset_loader(extension))
let s = path
.as_ref()
.file_name()
.ok_or(AssetServerError::MissingAssetLoader {
extensions: Vec::new(),
})?
.to_str()
.ok_or(AssetServerError::MissingAssetLoader {
extensions: Vec::new(),
})?;

let mut exts = Vec::new();
let mut ext = s;
while let Some(idx) = ext.find('.') {
ext = &ext[idx + 1..];
exts.push(ext);
if let Ok(loader) = self.get_asset_loader(ext) {
return Ok(loader);
}
}
Err(AssetServerError::MissingAssetLoader {
extensions: exts.into_iter().map(String::from).collect(),
})
}

pub fn get_handle_path<H: Into<HandleId>>(&self, handle: H) -> Option<AssetPath<'_>> {
Expand Down Expand Up @@ -444,3 +476,100 @@ impl AssetServer {
pub fn free_unused_assets_system(asset_server: Res<AssetServer>) {
asset_server.free_unused_assets();
}

#[cfg(test)]
mod test {
use super::*;
use bevy_utils::BoxedFuture;

struct FakePngLoader;
impl AssetLoader for FakePngLoader {
fn load<'a>(
&'a self,
_: &'a [u8],
_: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
Box::pin(async move { Ok(()) })
}

fn extensions(&self) -> &[&str] {
&["png"]
}
}

struct FakeMultipleDotLoader;
impl AssetLoader for FakeMultipleDotLoader {
fn load<'a>(
&'a self,
_: &'a [u8],
_: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
Box::pin(async move { Ok(()) })
}

fn extensions(&self) -> &[&str] {
&["test.png"]
}
}

fn setup() -> AssetServer {
use crate::FileAssetIo;

let asset_server = AssetServer {
server: Arc::new(AssetServerInternal {
loaders: Default::default(),
extension_to_loader_index: Default::default(),
asset_sources: Default::default(),
asset_ref_counter: Default::default(),
handle_to_path: Default::default(),
asset_lifecycles: Default::default(),
task_pool: Default::default(),
asset_io: Box::new(FileAssetIo::new(&".")),
}),
};
asset_server.add_loader::<FakePngLoader>(FakePngLoader);
asset_server.add_loader::<FakeMultipleDotLoader>(FakeMultipleDotLoader);
asset_server
}

#[test]
fn extensions() {
let asset_server = setup();
let t = asset_server.get_path_asset_loader("test.png");
assert_eq!(t.unwrap().extensions()[0], "png");
}

#[test]
fn no_loader() {
let asset_server = setup();
let t = asset_server.get_path_asset_loader("test.pong");
assert!(t.is_err());
}

#[test]
fn multiple_extensions_no_loader() {
let asset_server = setup();

assert!(
match asset_server.get_path_asset_loader("test.v1.2.3.pong") {
Err(AssetServerError::MissingAssetLoader { extensions }) =>
extensions == vec!["v1.2.3.pong", "2.3.pong", "3.pong", "pong"],
_ => false,
}
)
}

#[test]
fn filename_with_dots() {
let asset_server = setup();
let t = asset_server.get_path_asset_loader("test-v1.2.3.png");
assert_eq!(t.unwrap().extensions()[0], "png");
}

#[test]
fn multiple_extensions() {
let asset_server = setup();
let t = asset_server.get_path_asset_loader("test.test.png");
assert_eq!(t.unwrap().extensions()[0], "test.png");
}
}

0 comments on commit f31af20

Please sign in to comment.