//!
//! Functionality for loading any type of asset runtime on both desktop and web.
//!
use crate::{
io::{is_data_url, RawAssets},
Error, Result,
};
use std::collections::HashSet;
use std::path::{Path, PathBuf};
///
/// Loads all of the resources in the given paths and returns the [RawAssets] resources.
///
/// Supported functionality:
/// - Loading from disk (relative and absolute paths)
/// - Parsing from data URLs (requires the `data-url` feature flag)
///
/// If downloading resources is also needed, use the [load_async] method instead.
///
#[cfg(not(target_arch = "wasm32"))]
pub fn load(paths: &[impl AsRef<Path>]) -> Result<RawAssets> {
let mut raw_assets = load_single(paths)?;
let mut dependencies = super::get_dependencies(&raw_assets);
while !dependencies.is_empty() {
let deps = load_single(&dependencies)?;
dependencies = super::get_dependencies(&deps);
raw_assets.extend(deps);
}
Ok(raw_assets)
}
#[cfg(not(target_arch = "wasm32"))]
fn load_single(paths: &[impl AsRef<Path>]) -> Result<RawAssets> {
let mut data_urls = HashSet::new();
let mut local_paths = HashSet::new();
for path in paths.iter() {
let path = path.as_ref().to_path_buf();
if is_data_url(&path) {
data_urls.insert(path);
} else {
local_paths.insert(path);
}
}
let mut raw_assets = RawAssets::new();
load_from_disk(local_paths, &mut raw_assets)?;
parse_data_urls(data_urls, &mut raw_assets)?;
Ok(raw_assets)
}
///
/// Async loads all of the resources in the given paths and returns the [RawAssets] resources.
///
/// Supported functionality:
/// - Downloading from URLs relative to the base URL and absolute urls (requires the `http` or `reqwest` feature flag)
/// - Parsing from data URLs (requires the `data-url` feature flag)
/// - *** Native only *** Loading from disk (relative and absolute paths)
///
pub async fn load_async(paths: &[impl AsRef<Path>]) -> Result<RawAssets> {
let mut raw_assets = load_async_single(paths).await?;
let mut dependencies = super::get_dependencies(&raw_assets);
while !dependencies.is_empty() {
let deps = load_async_single(&dependencies).await?;
dependencies = super::get_dependencies(&deps);
raw_assets.extend(deps);
}
Ok(raw_assets)
}
#[cfg(target_arch = "wasm32")]
async fn load_async_single(paths: &[impl AsRef<Path>]) -> Result<RawAssets> {
let base_path = base_path();
let mut urls = HashSet::new();
let mut data_urls = HashSet::new();
for path in paths.iter() {
let path = path.as_ref().to_path_buf();
if is_data_url(&path) {
data_urls.insert(path);
} else if is_absolute_url(&path) {
urls.insert(path);
} else {
urls.insert(base_path.join(path));
}
}
let mut raw_assets = RawAssets::new();
load_urls(urls, &mut raw_assets).await?;
parse_data_urls(data_urls, &mut raw_assets)?;
Ok(raw_assets)
}
#[cfg(not(target_arch = "wasm32"))]
async fn load_async_single(paths: &[impl AsRef<Path>]) -> Result<RawAssets> {
let mut urls = HashSet::new();
let mut data_urls = HashSet::new();
let mut local_paths = HashSet::new();
for path in paths.iter() {
let path = path.as_ref().to_path_buf();
if is_data_url(&path) {
data_urls.insert(path);
} else if is_absolute_url(&path) {
urls.insert(path);
} else {
local_paths.insert(path);
}
}
let mut raw_assets = RawAssets::new();
load_urls(urls, &mut raw_assets).await?;
load_from_disk(local_paths, &mut raw_assets)?;
parse_data_urls(data_urls, &mut raw_assets)?;
Ok(raw_assets)
}
#[cfg(not(target_arch = "wasm32"))]
fn load_from_disk(paths: HashSet<PathBuf>, raw_assets: &mut RawAssets) -> Result<()> {
let mut handles = Vec::new();
for path in paths {
handles.push((
path.clone(),
std::thread::spawn(move || std::fs::read(path)),
));
}
for (path, handle) in handles.drain(..) {
let bytes = handle
.join()
.unwrap()
.map_err(|e| Error::FailedLoading(path.to_str().unwrap().to_string(), e))?;
raw_assets.insert(path, bytes);
}
Ok(())
}
#[allow(unused_variables)]
async fn load_urls(paths: HashSet<PathBuf>, raw_assets: &mut RawAssets) -> Result<()> {
#[cfg(feature = "reqwest")]
if !paths.is_empty() {
let mut handles = Vec::new();
let client = reqwest::Client::new();
for path in paths {
let url = reqwest::Url::parse(path.to_str().unwrap())
.map_err(|_| Error::FailedParsingUrl(path.to_str().unwrap().to_string()))?;
handles.push((path, client.get(url).send().await));
}
for (path, handle) in handles.drain(..) {
let bytes = handle
.map_err(|e| {
Error::FailedLoadingUrlWithReqwest(path.to_str().unwrap().to_string(), e)
})?
.bytes()
.await
.map_err(|e| {
Error::FailedLoadingUrlWithReqwest(path.to_str().unwrap().to_string(), e)
})?
.to_vec();
#[cfg(target_arch = "wasm32")]
{
if std::str::from_utf8(&bytes[0..15])
.map(|r| r.starts_with("<!DOCTYPE html>"))
.unwrap_or(false)
{
Err(Error::FailedLoadingUrl(
path.to_str().unwrap().to_string(),
std::str::from_utf8(&bytes).unwrap().to_string(),
))?;
}
}
raw_assets.insert(path, bytes);
}
}
#[cfg(not(feature = "reqwest"))]
if !paths.is_empty() {
return Err(Error::FeatureMissing("reqwest".to_string()));
}
Ok(())
}
fn parse_data_urls(paths: HashSet<PathBuf>, raw_assets: &mut RawAssets) -> Result<()> {
for path in paths {
let bytes = super::parse_data_url(path.to_str().unwrap())?;
raw_assets.insert(path, bytes);
}
Ok(())
}
fn is_absolute_url(path: &Path) -> bool {
path.to_str()
.map(|s| {
s.find("://").map(|i| i > 0).unwrap_or(false)
|| s.find("//").map(|i| i == 0).unwrap_or(false)
})
.unwrap_or(false)
}
#[cfg(target_arch = "wasm32")]
fn base_path() -> PathBuf {
let base_url = web_sys::window()
.unwrap()
.document()
.unwrap()
.url()
.unwrap();
if !base_url.ends_with('/') {
PathBuf::from(base_url).parent().unwrap().to_path_buf()
} else {
PathBuf::from(base_url)
}
}
#[cfg(test)]
mod test {
#[cfg(feature = "data-url")]
#[test]
pub fn load_data_url() {
use super::*;
use crate::Texture2D;
let data_url = "data:image/png;base64,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";
let mut loaded = load(&[data_url, "test_data/data_url.png"]).unwrap();
let loaded_data_url: Texture2D = loaded.deserialize(data_url).unwrap();
let mut loaded_image: Texture2D = loaded.deserialize(".png").unwrap();
loaded_image.name = "default".to_owned();
assert_eq!(loaded_data_url, loaded_image);
}
}