1use std::{
2 alloc::Layout,
3 any::{type_name, TypeId},
4 marker::PhantomData,
5 sync::OnceLock,
6};
7
8use bones_schema::{prelude::*, raw_fns::*};
9use bones_utils::HashMap;
10use parking_lot::RwLock;
11use ulid::Ulid;
12
13use crate::{AssetServer, NetworkHandle};
14
15#[repr(C)]
19pub struct Handle<T> {
20 pub id: Ulid,
22 phantom: PhantomData<T>,
23}
24
25impl<T> Clone for Handle<T> {
28 fn clone(&self) -> Self {
29 *self
30 }
31}
32impl<T> Copy for Handle<T> {}
33impl<T> PartialEq for Handle<T> {
34 fn eq(&self, other: &Self) -> bool {
35 self.id == other.id
36 }
37}
38impl<T> Eq for Handle<T> {}
39impl<T> std::hash::Hash for Handle<T> {
40 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
41 self.id.hash(state);
42 }
43}
44impl<T> Default for Handle<T> {
45 fn default() -> Self {
46 Self {
47 id: Default::default(),
48 phantom: Default::default(),
49 }
50 }
51}
52
53impl<T> std::fmt::Debug for Handle<T> {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_struct("Handle").field("id", &self.id).finish()
56 }
57}
58
59impl<T> Handle<T> {
60 pub fn untyped(self) -> UntypedHandle {
62 UntypedHandle { rid: self.id }
63 }
64
65 pub fn network_handle(&self, asset_server: &AssetServer) -> NetworkHandle<T> {
68 let content_id = *asset_server.get_untyped(self.untyped()).key();
70
71 NetworkHandle::<T>::from_cid(content_id)
72 }
73}
74
75#[derive(Default, Clone, Debug, Hash, PartialEq, Eq, Copy, PartialOrd, Ord)]
77#[repr(C)]
78pub struct UntypedHandle {
79 pub rid: Ulid,
81}
82
83impl UntypedHandle {
84 pub fn typed<T>(self) -> Handle<T> {
86 Handle {
87 id: self.rid,
88 phantom: PhantomData,
89 }
90 }
91}
92
93#[derive(HasSchema, Clone, Copy, Debug)]
102#[schema(opaque, no_default)]
103pub struct SchemaAssetHandle {
104 schema: Option<&'static Schema>,
106}
107
108impl SchemaAssetHandle {
109 pub fn inner_schema(&self) -> Option<&'static Schema> {
111 self.schema
112 }
113}
114
115unsafe impl<T: HasSchema> HasSchema for Handle<T> {
117 fn schema() -> &'static bones_schema::Schema {
118 static S: OnceLock<RwLock<HashMap<TypeId, &'static Schema>>> = OnceLock::new();
119 assert_eq!(
125 Layout::new::<Ulid>(),
126 Layout::new::<u128>(),
127 "ULID memory layout is unexpected! Bad Rust compiler! 😡"
128 );
129
130 let map = S.get_or_init(|| RwLock::new(HashMap::default()));
131
132 let existing_schema = { map.read().get(&TypeId::of::<T>()).copied() };
133
134 if let Some(existing_schema) = existing_schema {
135 existing_schema
136 } else {
137 let schema = SCHEMA_REGISTRY.register(SchemaData {
138 name: type_name::<Self>().into(),
139 full_name: format!("{}{}", module_path!(), type_name::<Self>()).into(),
140 type_id: Some(TypeId::of::<Self>()),
141 kind: SchemaKind::Struct(StructSchemaInfo {
142 fields: vec![StructFieldInfo {
143 name: Some("id".into()),
144 schema: u128::schema(),
145 }],
146 }),
147 clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
148 drop_fn: None,
149 default_fn: Some(<Self as RawDefault>::raw_default_cb()),
150 eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
151 hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
152 type_data: {
153 let td = bones_schema::alloc::TypeDatas::default();
154 td.insert(SchemaAssetHandle {
155 schema: Some(T::schema()),
156 })
157 .unwrap();
158 td
159 },
160 });
161
162 {
163 let mut map = map.write();
164 map.insert(TypeId::of::<T>(), schema);
165 }
166
167 schema
168 }
169 }
170}
171unsafe impl HasSchema for UntypedHandle {
173 fn schema() -> &'static bones_schema::Schema {
174 static S: OnceLock<&'static Schema> = OnceLock::new();
175 assert_eq!(
181 Layout::new::<Ulid>(),
182 Layout::new::<u128>(),
183 "ULID memory layout is unexpected! Bad Rust compiler! 😡"
184 );
185 S.get_or_init(|| {
186 SCHEMA_REGISTRY.register(SchemaData {
187 name: "UntypedHandle".into(),
188 full_name: format!("{}::{}", module_path!(), "UntypedHandle").into(),
189 type_id: Some(TypeId::of::<Self>()),
190 kind: SchemaKind::Struct(StructSchemaInfo {
191 fields: vec![StructFieldInfo {
192 name: Some("id".into()),
193 schema: u128::schema(),
194 }],
195 }),
196 clone_fn: Some(<Self as RawClone>::raw_clone_cb()),
197 drop_fn: None,
198 default_fn: Some(<Self as RawDefault>::raw_default_cb()),
199 eq_fn: Some(<Self as RawEq>::raw_eq_cb()),
200 hash_fn: Some(<Self as RawHash>::raw_hash_cb()),
201 type_data: {
202 let td = bones_schema::alloc::TypeDatas::default();
203 td.insert(SchemaAssetHandle { schema: None }).unwrap();
204 td
205 },
206 })
207 })
208 }
209}