#![doc = include_str!("../README.md")]
use std::any::type_name;
use std::fmt::Display;
use bevy_ecs::{component::Component, system::Resource, world::{EntityRef, EntityWorldMut}};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
mod from_world;
pub use from_world::{NoContext, WorldAccess, FromWorldAccess, from_world, from_world_mut};
mod extractors;
pub use extractors::{Null, Object, DefaultInit, Maybe, Child, ChildUnchecked, ChildVec, ChildMap};
mod save_load;
pub use save_load::{WorldExtension, Join, SaveLoad, BindResource};
mod macros;
pub mod typetagged;
pub mod asset;
pub mod interning;
pub mod entity;
mod projections;
mod filter;
pub use projections::{ProjectOption, ProjectMap, ProjectVec, Map};
pub use bevy_serde_project_derive::SerdeProject;
pub use filter::EntityFilter;
#[allow(unused)]
use bevy_asset::Handle;
#[allow(unused)]
use bevy_hierarchy::Children;
#[doc(hidden)]
pub use bevy_ecs::{world::World, entity::Entity, query::With};
use bevy_ecs::world::Mut;
#[doc(hidden)]
pub use serde;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("More than one children of {parent:?} found with extractor Child<{ty}>.")]
MoreThenOne{
parent: Entity,
ty: &'static str,
},
#[error("Entity {0:?} missing.")]
EntityMissing(Entity),
#[error("Component {ty} not found for Entity {entity:?}.")]
ComponentNotFound{
entity: Entity,
ty: &'static str,
},
#[error("ChildMap<{key}, {value}> found key with missing value.")]
KeyNoValue{
key: &'static str,
value: &'static str,
},
#[error("Resource {ty} not found.")]
ResourceNotFound{
ty: &'static str,
},
#[error("Field {field} missing in BevyObject {ty}.")]
FieldMissing{
field: &'static str,
ty: &'static str,
},
#[error("Unregistered type {name} of trait object {ty}.")]
UnregisteredTraitObjectType {
ty: &'static str,
name: String,
},
#[error("Serialization Error: {0}")]
SerializationError(String),
#[error("Serialization Error: {0}")]
DeserializationError(String),
#[error("Cannot serialize a skipped enum variant \"{0}\".")]
SkippedVariant(&'static str),
#[error("{0}")]
CustomError(String),
#[error("Handle<{ty}> does not have an associated path.")]
PathlessHandle{
ty: &'static str
},
#[error("Associated Asset of Handle<{ty}> missing.")]
AssetMissing{
ty: &'static str
},
#[error("'__Phantom' branch deserialized, this is impossible.")]
PhantomBranch,
#[error("Trying to serialize/deserialize a enum with no valid variants.")]
NoValidVariants,
}
impl Error {
pub fn boxed(self) -> Box<Self> {
Box::new(self)
}
pub fn custom(err: impl Display) -> Box<Self> {
Box::new(Self::CustomError(err.to_string()))
}
}
type BoxError = Box<Error>;
pub trait SerdeProject: Sized {
type Ctx: FromWorldAccess;
type Ser<'t>: Serialize + 't where Self: 't;
type De<'de>: Deserialize<'de>;
fn to_ser<'t>(&'t self, ctx: &<Self::Ctx as FromWorldAccess>::Ref<'t>) -> Result<Self::Ser<'t>, Box<Error>>;
fn from_de(ctx: &mut <Self::Ctx as FromWorldAccess>::Mut<'_>, de: Self::De<'_>) -> Result<Self, Box<Error>>;
}
pub type Ser<'t, T> = <T as SerdeProject>::Ser<'t>;
pub type De<'de, T> = <T as SerdeProject>::De<'de>;
impl<T> SerdeProject for T where T: Serialize + DeserializeOwned + 'static {
type Ctx = NoContext;
type Ser<'t> = &'t Self;
type De<'de> = Self;
fn to_ser(&self, _: &()) -> Result<Self::Ser<'_>, BoxError> {
Ok(self)
}
fn from_de(_: &mut (), de: Self::De<'_>) -> Result<Self, BoxError> {
Ok(de)
}
}
pub trait Named {
fn name() -> &'static str;
}
pub trait BindBevyObject {
type BevyObject: BevyObject;
type Filter: EntityFilter;
#[allow(unused)]
fn get_root(world: &mut World) -> Option<Entity> {
None
}
fn name() -> &'static str;
}
pub trait BevyObject {
type Ser<'t>: Serialize + 't where Self: 't;
type De<'de>: Deserialize<'de>;
#[allow(clippy::wrong_self_convention)]
fn to_ser(world: &World, entity: Entity) -> Result<Option<Self::Ser<'_>>, Box<Error>>;
fn from_de(world: &mut World, entity: Entity, de: Self::De<'_>) -> Result<(), Box<Error>>;
}
impl<T> BevyObject for T where T: SerdeProject + Component {
type Ser<'t> = T::Ser<'t> where T: 't;
type De<'de> = T::De<'de>;
#[allow(clippy::wrong_self_convention)]
fn to_ser(world: &World, entity: Entity) -> Result<Option<Self::Ser<'_>>, BoxError> {
let state = T::Ctx::from_world(world)?;
match world.get_entity(entity).and_then(|e| e.get::<T>()) {
Some(component) => component.to_ser(&state).map(Some),
None => Ok(None),
}
}
fn from_de(world: &mut World, entity: Entity, de: Self::De<'_>) -> Result<(), BoxError> {
let mut state = T::Ctx::from_world_mut(world)?;
let result = T::from_de(&mut state, de)?;
drop(state);
world.entity_mut_ok(entity)?.insert(result);
Ok(())
}
}
pub trait Convert<In> {
fn ser(input: &In) -> &Self;
fn de(self) -> In;
}
impl<T> Convert<T> for T {
fn ser(input: &T) -> &Self {
input
}
fn de(self) -> T {
self
}
}
trait WorldUtil {
fn entity_ok(&self, entity: Entity) -> Result<EntityRef, BoxError>;
fn entity_mut_ok(&mut self, entity: Entity) -> Result<EntityWorldMut, BoxError>;
fn resource_ok<R: Resource>(&self) -> Result<&R, BoxError>;
fn resource_mut_ok<R: Resource>(&mut self) -> Result<Mut<'_, R>, BoxError>;
}
impl WorldUtil for World {
fn entity_ok(&self, entity: Entity) -> Result<EntityRef, BoxError> {
self.get_entity(entity)
.ok_or_else(||Error::EntityMissing(entity).boxed())
}
fn entity_mut_ok(&mut self, entity: Entity) -> Result<EntityWorldMut, BoxError> {
self.get_entity_mut(entity)
.ok_or_else(||Error::EntityMissing(entity).boxed())
}
fn resource_ok<R: Resource>(&self) -> Result<&R, BoxError> {
self.get_resource::<R>()
.ok_or_else(||Error::ResourceNotFound { ty: type_name::<R>() }.boxed())
}
fn resource_mut_ok<R: Resource>(&mut self) -> Result<Mut<'_, R>, BoxError> {
self.get_resource_mut::<R>()
.ok_or_else(||Error::ResourceNotFound { ty: type_name::<R>() }.boxed())
}
}