use std::{ fmt::Debug, hash::{Hash, Hasher}, }; use bevy_property::{Properties, Property}; use serde::{Deserialize, Serialize}; use std::{any::TypeId, marker::PhantomData}; use uuid::Uuid; /// The ID of the "default" asset pub(crate) const DEFAULT_HANDLE_ID: HandleId = HandleId(Uuid::from_u128(240940089166493627844978703213080810552)); /// A unique id that corresponds to a specific asset in the [Assets](crate::Assets) collection. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, Property)] pub struct HandleId(pub Uuid); impl HandleId { pub fn new() -> HandleId { HandleId(Uuid::new_v4()) } } /// A handle into a specific Asset of type `T` /// /// Handles contain a unique id that corresponds to a specific asset in the [Assets](crate::Assets) collection. #[derive(Properties)] pub struct Handle where T: 'static, { pub id: HandleId, #[property(ignore)] marker: PhantomData, } impl Handle { pub fn new() -> Self { Handle { id: HandleId::new(), marker: PhantomData, } } /// Gets a handle for the given type that has this handle's id. This is useful when an /// asset is derived from another asset. In this case, a common handle can be used to /// correlate them. /// NOTE: This pattern might eventually be replaced by a more formal asset dependency system. pub fn as_handle(&self) -> Handle { Handle::from_id(self.id) } pub const fn from_id(id: HandleId) -> Self { Handle { id, marker: PhantomData, } } pub const fn from_u128(value: u128) -> Self { Handle { id: HandleId(Uuid::from_u128(value)), marker: PhantomData, } } pub const fn from_bytes(bytes: [u8; 16]) -> Self { Handle { id: HandleId(Uuid::from_bytes(bytes)), marker: PhantomData, } } pub fn from_untyped(untyped_handle: HandleUntyped) -> Option> where T: 'static, { if TypeId::of::() == untyped_handle.type_id { Some(Handle::from_id(untyped_handle.id)) } else { None } } } impl From for Handle { fn from(value: HandleId) -> Self { Handle::from_id(value) } } impl From for Handle { fn from(value: u128) -> Self { Handle::from_u128(value) } } impl From<[u8; 16]> for Handle { fn from(value: [u8; 16]) -> Self { Handle::from_bytes(value) } } impl From for Handle where T: 'static, { fn from(handle: HandleUntyped) -> Self { if TypeId::of::() == handle.type_id { Handle { id: handle.id, marker: PhantomData::default(), } } else { panic!("attempted to convert untyped handle to incorrect typed handle") } } } impl Hash for Handle { fn hash(&self, state: &mut H) { self.id.hash(state); } } impl PartialEq for Handle { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl Eq for Handle {} impl Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { let name = std::any::type_name::().split("::").last().unwrap(); write!(f, "Handle<{}>({:?})", name, self.id.0) } } impl Default for Handle { fn default() -> Self { Handle { id: DEFAULT_HANDLE_ID, marker: PhantomData, } } } impl Clone for Handle { fn clone(&self) -> Self { Handle { id: self.id.clone(), marker: PhantomData, } } } impl Copy for Handle {} // SAFE: T is phantom data and Handle::id is an integer unsafe impl Send for Handle {} unsafe impl Sync for Handle {} /// A non-generic version of [Handle] /// /// This allows handles to be mingled in a cross asset context. For example, storing `Handle` and `Handle` in the same `HashSet`. #[derive(Hash, Copy, Clone, Eq, PartialEq, Debug)] pub struct HandleUntyped { pub id: HandleId, pub type_id: TypeId, } impl HandleUntyped { pub fn is_handle(untyped: &HandleUntyped) -> bool { TypeId::of::() == untyped.type_id } } impl From> for HandleUntyped where T: 'static, { fn from(handle: Handle) -> Self { HandleUntyped { id: handle.id, type_id: TypeId::of::(), } } }