Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make it possible to extend type data of schemas after registration. #250

Merged
merged 1 commit into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions framework_crates/bones_asset/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ impl UntypedHandle {
#[schema(opaque, no_default)]
pub struct SchemaAssetHandle {
/// The schema of the type pointed to by the handle, if this is not an [`UntypedHandle`].
pub schema: Option<&'static Schema>,
schema: Option<&'static Schema>,
}

impl SchemaAssetHandle {
/// Returns the schema of the type pointed to by the handle, if this is not an [`UntypedHandle`].
pub fn inner_schema(&self) -> Option<&'static Schema> {
self.schema
}
}

// SAFE: We return a valid schema.
Expand Down Expand Up @@ -129,10 +136,11 @@ unsafe impl<T: HasSchema> HasSchema for Handle<T> {
eq_fn: Some(<Self as RawEq>::raw_eq),
hash_fn: Some(<Self as RawHash>::raw_hash),
type_data: {
let mut td = bones_schema::alloc::SchemaTypeMap::default();
let td = bones_schema::alloc::TypeDatas::default();
td.insert(SchemaAssetHandle {
schema: Some(T::schema()),
});
})
.unwrap();
td
},
});
Expand Down Expand Up @@ -177,8 +185,8 @@ unsafe impl HasSchema for UntypedHandle {
eq_fn: Some(<Self as RawEq>::raw_eq),
hash_fn: Some(<Self as RawHash>::raw_hash),
type_data: {
let mut td = bones_schema::alloc::SchemaTypeMap::default();
td.insert(SchemaAssetHandle { schema: None });
let td = bones_schema::alloc::TypeDatas::default();
td.insert(SchemaAssetHandle { schema: None }).unwrap();
td
},
})
Expand Down
2 changes: 1 addition & 1 deletion framework_crates/bones_asset/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ impl std::fmt::Display for LoaderNotFound {
write!(
f,
"Schema/loader not found for schema/extension: {}\n\
You may need to register the asset with asset_server.register_asset::<AssetType>()",
You may need to register the asset by calling `MyAsset::schema()`",
self.name
)
}
Expand Down
6 changes: 3 additions & 3 deletions framework_crates/bones_ecs/src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,10 +514,10 @@ mod tests {
c: Option<ResMut<u32>>,
d: Option<ResMut<u64>>,
) {
assert!(a.as_deref() == None);
assert!(a.as_deref().is_none());
assert!(b.as_deref() == Some(&1));
assert!(c.as_deref() == None);
assert!(d.as_deref() == Some(&mut 2));
assert!(c.as_deref().is_none());
assert!(d.as_deref() == Some(&2));
}

let mut world = World::new();
Expand Down
2 changes: 1 addition & 1 deletion framework_crates/bones_framework/src/localization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ impl<T: HasSchema> SystemParam for Localization<'_, T> {
.enumerate()
{
if let Some(handle_data) = field.schema.type_data.get::<SchemaAssetHandle>() {
if let Some(schema) = handle_data.schema {
if let Some(schema) = handle_data.inner_schema() {
if schema == LocalizationAsset::schema() {
idx = Some(i);
break;
Expand Down
6 changes: 3 additions & 3 deletions framework_crates/bones_schema/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream {
let add_derive_type_datas = derive_type_data_flags.into_iter().map(|ty| {
let ty = format_ident!("{ty}");
quote! {
tds.insert(<#ty as #schema_mod::FromType<#name>>::from_type());
tds.insert(<#ty as #schema_mod::FromType<#name>>::from_type()).unwrap();
}
});
let add_type_datas = input
Expand All @@ -106,10 +106,10 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream {

quote! {
{
let mut tds = #schema_mod::alloc::SchemaTypeMap::default();
let tds = #schema_mod::alloc::TypeDatas::default();
#(#add_derive_type_datas),*
#(
tds.insert(#add_type_datas);
tds.insert(#add_type_datas).unwrap();
),*
tds
}
Expand Down
4 changes: 2 additions & 2 deletions framework_crates/bones_schema/src/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ mod vec;
pub use map::*;
mod map;

pub use type_set::*;
mod type_set;
pub use type_datas::*;
mod type_datas;
102 changes: 102 additions & 0 deletions framework_crates/bones_schema/src/alloc/type_datas.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use append_only_vec::AppendOnlyVec;

use crate::prelude::*;

/// A `TypeMap`-like structure, that does not allow removing entries or updating exisintg
/// entries.
///
/// This structure doesn't require a mutable reference to insert records
#[derive(Debug)]
pub struct TypeDatas(AppendOnlyVec<SchemaBox>);
impl Default for TypeDatas {
fn default() -> Self {
Self(AppendOnlyVec::new())
}
}
impl Clone for TypeDatas {
fn clone(&self) -> Self {
let clone = TypeDatas::default();
for entry in self.0.iter() {
clone.insert(entry.clone()).unwrap();
}
clone
}
}

impl TypeDatas {
/// Insert data into the store.
pub fn insert<T: HasSchema>(&self, data: T) -> Result<(), TypeDataAlreadyInserted> {
self.insert_box(SchemaBox::new(data))
}

/// Insert boxed data into the store.
pub fn insert_box(&self, data: SchemaBox) -> Result<(), TypeDataAlreadyInserted> {
let schema = data.schema();
for entry in self.0.iter() {
if entry.schema() == schema {
return Err(TypeDataAlreadyInserted(schema));
}
}
self.0.push(data);
Ok(())
}

/// Borrow data from the store, if it exists.
pub fn get<T: HasSchema>(&self) -> Option<&T> {
let id = T::schema().id();
for data in self.0.iter() {
if data.schema().id() == id {
return Some(data.cast_ref());
}
}
None
}

/// Borrow data from the store, if it exists.
pub fn get_ref(&self, id: SchemaId) -> Option<SchemaRef> {
for data in self.0.iter() {
if data.schema().id() == id {
return Some(data.as_ref());
}
}
None
}

/// Iterate over type datas.
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &SchemaBox> {
self.0.iter()
}
}

/// Error type for [`TypeDatas`]
#[derive(Debug)]
pub struct TypeDataAlreadyInserted(&'static Schema);

impl std::fmt::Display for TypeDataAlreadyInserted {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"Type data already contains an entry of type: {}",
self.0.full_name,
))
}
}
impl std::error::Error for TypeDataAlreadyInserted {}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn smoke() {
let tds = TypeDatas::default();

tds.insert(String::from("hi")).unwrap();
assert_eq!(Some("hi"), tds.get::<String>().map(|x| x.as_str()));

tds.insert(7u32).unwrap();
assert_eq!(Some(&7), tds.get::<u32>());

let result = tds.insert(String::from("bye"));
assert!(matches!(result, Err(TypeDataAlreadyInserted(_))));
}
}
25 changes: 0 additions & 25 deletions framework_crates/bones_schema/src/alloc/type_set.rs

This file was deleted.

4 changes: 2 additions & 2 deletions framework_crates/bones_schema/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::{alloc::Layout, any::TypeId, borrow::Cow};

use crate::{alloc::SchemaTypeMap, prelude::*};
use crate::{alloc::TypeDatas, prelude::*};

/// Trait implemented for types that have a [`Schema`].
///
Expand Down Expand Up @@ -165,7 +165,7 @@ pub struct SchemaData {
/// #[derive_type_data(SomeTypeData)]
/// struct MyData;
/// ```
pub type_data: SchemaTypeMap,
pub type_data: TypeDatas,

// NOTE: The fields below could be implemented as type datas, and it would be nicely elegant to
// do so, but for performance reasons, we put them right in the [`Schema`] struct because
Expand Down
12 changes: 7 additions & 5 deletions framework_crates/bones_schema/src/std_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bones_utils::{fxhash::FxHasher, Ustr};
#[cfg(feature = "serde")]
use serde::{de::Error, Deserialize};

use crate::{alloc::SchemaTypeMap, prelude::*, raw_fns::*};
use crate::{alloc::TypeDatas, prelude::*, raw_fns::*};

use std::{alloc::Layout, any::TypeId, hash::Hasher, sync::OnceLock, time::Duration};

Expand Down Expand Up @@ -142,7 +142,7 @@ unsafe impl HasSchema for Ustr {
hash_fn: Some(<Self as RawHash>::raw_hash),
eq_fn: Some(<Self as RawEq>::raw_eq),
type_data: {
let mut td = SchemaTypeMap::default();
let td = TypeDatas::default();
#[cfg(feature = "serde")]
td.insert(SchemaDeserialize {
deserialize_fn: |reference, deserializer| {
Expand All @@ -156,7 +156,8 @@ unsafe impl HasSchema for Ustr {

Ok(())
},
});
})
.unwrap();
td
},
})
Expand All @@ -183,7 +184,7 @@ unsafe impl HasSchema for Duration {
hash_fn: Some(<Self as RawHash>::raw_hash),
eq_fn: Some(<Self as RawEq>::raw_eq),
type_data: {
let mut td = SchemaTypeMap::default();
let td = TypeDatas::default();
#[cfg(feature = "serde")]
td.insert(SchemaDeserialize {
deserialize_fn: |reference, deserializer| {
Expand All @@ -209,7 +210,8 @@ unsafe impl HasSchema for Duration {

Ok(())
},
});
})
.unwrap();
td
},
})
Expand Down
Loading