#[cfg(feature = "macros")]
pub use bevy_elf_macros::{FromDef, asset_spec};
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::io;
use std::marker::PhantomData;
use bevy_app::prelude::*;
use bevy_asset::io::Reader;
use bevy_asset::prelude::*;
use bevy_asset::{AssetLoader, AssetPath, LoadContext, ParseAssetPathError};
use bevy_reflect::TypePath;
use ron::de::SpannedError;
use serde::de::DeserializeOwned;
use thiserror::Error;
mod from_def_impls;
type Phantom<L> = PhantomData<fn() -> L>;
pub trait AssetResolver {
fn resolve(&self, asset_id: &str) -> Result<AssetPath<'static>, ResolveError>;
}
pub trait StaticAssetResolver {
fn resolve(asset_id: &str) -> Result<AssetPath<'static>, ResolveError>;
}
pub struct PathResolver;
impl AssetResolver for PathResolver {
fn resolve(&self, asset_path: &str) -> Result<AssetPath<'static>, ResolveError> {
Ok(AssetPath::from(asset_path.to_string()))
}
}
pub fn extract_id_from(asset_path: AssetPath) -> Result<String, ResolveError> {
asset_path
.path()
.file_prefix()
.ok_or_else(|| ResolveError::MissingFileName(asset_path.clone_owned()))
.map(|v| v.to_string_lossy().to_string())
}
#[derive(Error, Debug)]
pub enum FromDefError {
#[error("{0}")]
Resolve(#[from] ResolveError),
#[error("{0}")]
InvalidDef(String),
}
impl From<ParseAssetPathError> for FromDefError {
fn from(value: ParseAssetPathError) -> Self {
Self::from(ResolveError::from(value))
}
}
#[derive(Error, Debug)]
pub enum ResolveError {
#[error("{0}")]
Parse(#[from] ParseAssetPathError),
#[error("missing file name in asset path {0}")]
MissingFileName(AssetPath<'static>),
#[error("invalid asset link \"{0}\"")]
InvalidAssetLink(String),
}
pub struct StaticResolverAdapter<S>(Phantom<S>);
impl<S> Default for StaticResolverAdapter<S> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<S: StaticAssetResolver> AssetResolver for StaticResolverAdapter<S> {
fn resolve(&self, asset_id: &str) -> Result<AssetPath<'static>, ResolveError> {
S::resolve(asset_id)
}
}
pub trait HasResolver {
type Resolver: AssetResolver;
fn resolver() -> Self::Resolver;
}
impl<T: AssetResolver + Default> HasResolver for T {
type Resolver = Self;
fn resolver() -> Self::Resolver {
Self::default()
}
}
pub trait AssetPathSpec {
const BASE_PATH: &'static str;
const EXTENSION: Option<&'static str> = None;
}
pub struct ResolverSpec<S>(Phantom<S>);
impl<S> Default for ResolverSpec<S> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<S: AssetPathSpec> AssetPathSpecProvider for ResolverSpec<S> {
fn base_path(&self) -> Cow<'static, str> {
Cow::Borrowed(S::BASE_PATH)
}
fn extension(&self) -> Option<&'static str> {
S::EXTENSION
}
}
pub struct DynamicPathResolver {
pub base_path: String,
pub extension: Option<&'static str>,
}
impl DynamicPathResolver {
pub fn resolve_sub_path(
load_context: &mut LoadContext,
sub_path: &str,
extension: Option<&'static str>,
) -> Result<Self, ParseAssetPathError> {
let sub_path = AssetPath::parse(sub_path);
let base_path = load_context
.path()
.parent()
.map(|p| p.resolve(&sub_path))
.unwrap_or_else(|| sub_path)
.to_string();
Ok(Self {
base_path,
extension,
})
}
}
impl AssetPathSpecProvider for DynamicPathResolver {
fn base_path(&self) -> Cow<'static, str> {
Cow::Owned(self.base_path.clone())
}
fn extension(&self) -> Option<&'static str> {
self.extension
}
}
pub struct SpecResolver<S>(Phantom<S>);
impl<S> Default for SpecResolver<S> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<S: HasSpecProvider> AssetPathSpecProvider for SpecResolver<S> {
fn base_path(&self) -> Cow<'static, str> {
S::provider().base_path()
}
fn extension(&self) -> Option<&'static str> {
S::provider().extension()
}
}
pub trait AssetPathSpecProvider {
fn base_path(&self) -> Cow<'static, str>;
fn extension(&self) -> Option<&'static str> {
None
}
}
pub trait HasSpecProvider {
type Provider: AssetPathSpecProvider;
fn provider() -> Self::Provider;
}
impl<T> HasSpecProvider for T
where
T: AssetPathSpecProvider + Default,
{
type Provider = Self;
fn provider() -> Self::Provider {
Self::default()
}
}
impl<T> AssetResolver for T
where
T: AssetPathSpecProvider,
{
fn resolve(&self, asset_id: &str) -> Result<AssetPath<'static>, ResolveError> {
let file_name = if let Some(ext) = self.extension() {
Cow::Owned(asset_id.to_string() + "." + ext)
} else {
Cow::Borrowed(asset_id)
};
Ok(AssetPath::from(self.base_path().to_string())
.resolve(&AssetPath::parse(file_name.as_ref())))
}
}
pub trait FromDef {
type Def: DeserializeOwned;
fn from_def(def: Self::Def, ctx: &mut LoadContext) -> Result<Self, FromDefError>
where
Self: Sized;
}
pub trait FromDefWithResolver {
type Def: DeserializeOwned;
type Error;
fn from_def_with_resolver<R: AssetResolver>(
def: Self::Def,
resolver: &R,
ctx: &mut LoadContext,
) -> Result<Self, Self::Error>
where
Self: Sized;
}
#[derive(Debug, Eq)]
pub struct AssetRef<A: Asset> {
id: String,
handle: Handle<A>,
}
impl<A: Asset> AssetRef<A> {
pub fn new(id: String, handle: Handle<A>) -> Self {
Self { id, handle }
}
pub fn id(&self) -> &str {
&self.id
}
pub fn handle(&self) -> &Handle<A> {
&self.handle
}
}
impl<A: Asset + HasResolver> FromDef for AssetRef<A> {
type Def = String;
fn from_def(def: Self::Def, ctx: &mut LoadContext) -> Result<Self, FromDefError>
where
Self: Sized,
{
let handle = ctx.load(A::resolver().resolve(&def)?);
Ok(Self { id: def, handle })
}
}
impl<A: Asset> FromDefWithResolver for AssetRef<A> {
type Def = String;
type Error = ResolveError;
fn from_def_with_resolver<R: AssetResolver>(
def: Self::Def,
resolver: &R,
ctx: &mut LoadContext,
) -> Result<Self, Self::Error> {
Ok(Self {
handle: ctx.load(resolver.resolve(&def)?),
id: def,
})
}
}
impl<A: Asset + HasResolver> FromDef for Handle<A> {
type Def = String;
fn from_def(def: Self::Def, ctx: &mut LoadContext) -> Result<Self, FromDefError> {
Ok(ctx.load(A::resolver().resolve(&def)?))
}
}
impl<A: Asset> FromDefWithResolver for Handle<A> {
type Def = String;
type Error = FromDefError;
fn from_def_with_resolver<R: AssetResolver>(
def: Self::Def,
resolver: &R,
ctx: &mut LoadContext,
) -> Result<Self, Self::Error> {
Ok(ctx.load(resolver.resolve(&def)?))
}
}
impl<T, D> FromDef for Option<T>
where
T: FromDef<Def = D>,
D: DeserializeOwned,
{
type Def = Option<D>;
fn from_def(def: Self::Def, ctx: &mut LoadContext) -> Result<Self, FromDefError> {
def.map(|d| T::from_def(d, ctx)).transpose()
}
}
impl<T, D> FromDefWithResolver for Option<T>
where
T: FromDefWithResolver<Def = D>,
D: DeserializeOwned,
{
type Def = Option<D>;
type Error = T::Error;
fn from_def_with_resolver<R: AssetResolver>(
def: Self::Def,
resolver: &R,
ctx: &mut LoadContext,
) -> Result<Self, Self::Error> {
def.map(|d| T::from_def_with_resolver(d, resolver, ctx))
.transpose()
}
}
impl<T, D> FromDef for Vec<T>
where
T: FromDef<Def = D>,
D: DeserializeOwned,
{
type Def = Vec<D>;
fn from_def(def: Self::Def, ctx: &mut LoadContext) -> Result<Self, FromDefError> {
def.into_iter().map(|d| T::from_def(d, ctx)).collect()
}
}
impl<T, D> FromDefWithResolver for Vec<T>
where
T: FromDefWithResolver<Def = D>,
D: DeserializeOwned,
{
type Def = Vec<D>;
type Error = T::Error;
fn from_def_with_resolver<R: AssetResolver>(
def: Self::Def,
resolver: &R,
ctx: &mut LoadContext,
) -> Result<Self, Self::Error> {
def.into_iter()
.map(|d| T::from_def_with_resolver(d, resolver, ctx))
.collect()
}
}
impl<A, K, D> FromDef for HashMap<K, A>
where
A: FromDef<Def = D>,
K: DeserializeOwned + Eq + Hash,
D: DeserializeOwned,
{
type Def = HashMap<K, D>;
fn from_def(def: Self::Def, ctx: &mut LoadContext) -> Result<Self, FromDefError> {
def.into_iter()
.map(|(k, d)| Ok((k, A::from_def(d, ctx)?)))
.collect()
}
}
impl<A, K, D> FromDefWithResolver for HashMap<K, A>
where
A: FromDefWithResolver<Def = D>,
K: DeserializeOwned + Eq + Hash,
D: DeserializeOwned,
{
type Def = HashMap<K, D>;
type Error = A::Error;
fn from_def_with_resolver<R: AssetResolver>(
def: Self::Def,
resolver: &R,
ctx: &mut LoadContext,
) -> Result<Self, Self::Error> {
def.into_iter()
.map(|(k, d)| Ok((k, A::from_def_with_resolver(d, resolver, ctx)?)))
.collect()
}
}
pub struct RonAssetPlugin<A>(Phantom<A>);
impl<A> Default for RonAssetPlugin<A> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<A> Plugin for RonAssetPlugin<A>
where
A: Asset + FromDef + 'static,
{
fn build(&self, app: &mut App) {
app.init_asset::<A>()
.init_asset_loader::<RonAssetLoader<A>>();
}
}
#[derive(TypePath)]
pub struct RonAssetLoader<A>(Phantom<A>);
impl<A> AssetLoader for RonAssetLoader<A>
where
A: FromDef + Asset,
{
type Asset = A;
type Error = RonAssetLoadError;
type Settings = ();
async fn load(
&self,
reader: &mut dyn Reader,
_: &Self::Settings,
load_context: &mut LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
let def: A::Def = ron::de::from_bytes(&bytes)?;
Ok(A::from_def(def, load_context)?)
}
}
impl<A> Default for RonAssetLoader<A> {
fn default() -> Self {
Self(PhantomData)
}
}
#[derive(Error, Debug)]
pub enum RonAssetLoadError {
#[error("{0}")]
Io(#[from] io::Error),
#[error("{0}")]
Spanned(#[from] SpannedError),
#[error("{0}")]
FromDef(#[from] FromDefError),
}
impl<A: Asset> PartialEq for AssetRef<A> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.handle.id() == other.handle.id()
}
}
impl<A: Asset> PartialEq<Handle<A>> for AssetRef<A> {
fn eq(&self, other: &Handle<A>) -> bool {
self.handle.id() == other.id()
}
}
impl<A: Asset> Clone for AssetRef<A> {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
handle: self.handle.clone(),
}
}
}