#[macro_export]
#[doc(hidden)]
macro_rules! __cn_surviving_tag {
($variant:ident;) => { Some($crate::ecs::ComponentTag::$variant) };
($variant:ident; consumed: $surviving:ident $($rest:tt)*) => {
Some($crate::ecs::ComponentTag::$surviving)
};
($variant:ident; consumed $($rest:tt)*) => { None };
($variant:ident; $skip:tt $($rest:tt)*) => { $crate::__cn_surviving_tag!($variant; $($rest)*) };
}
#[macro_export]
#[doc(hidden)]
macro_rules! __cn_validate {
($val:expr, $p:expr;) => { $val };
($val:expr, $p:expr; validate: $f:ident $($r:tt)*) => {
$crate::components::validate::$f($val)
};
($val:expr, $p:expr; validate_for: $f:ident $($r:tt)*) => {
$crate::components::validate::$f($val, $p)
};
($val:expr, $p:expr; $t:tt $($r:tt)*) => { $crate::__cn_validate!($val, $p; $($r)*) };
}
#[macro_export]
#[doc(hidden)]
macro_rules! __cn_resource_kind {
(resource: $kind:ident $($r:tt)*) => { $crate::ecs::ResourceKind::$kind };
($t:tt $($r:tt)*) => { $crate::__cn_resource_kind!($($r)*) };
}
#[macro_export]
#[doc(hidden)]
macro_rules! __define_asset_kind {
(
asset_enum: $asset_enum:ident,
asset_kind: $kind_variant:ident,
$( $variant:ident => $ty:path, $disc:expr_2021 ),+ $(,)?
) => {
#[derive(Debug)]
#[expect(missing_docs, reason = "one variant per component type, each named for the component it wraps")]
pub enum $asset_enum {
$( $variant($ty) ),+
}
$( impl From<$ty> for $asset_enum { fn from(c: $ty) -> Self { $asset_enum::$variant(c) } } )+
};
}
#[macro_export]
macro_rules! define_components {
(
stored: { $( $variant:ident => $ty:path { $($meta:tt)* } ),+ $(,)? },
resource: { $( $rvariant:ident => $rty:path { $($rmeta:tt)* } ),+ $(,)? } $(,)?
) => {
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[expect(missing_docs, reason = "one variant per component type, named for that component")]
pub enum ComponentTag {
$( $variant ),+
}
impl ComponentTag {
pub fn as_str(self) -> &'static str {
match self {
$( ComponentTag::$variant => stringify!($variant) ),+
}
}
pub fn surviving_tag(self) -> Option<ComponentTag> {
match self {
$( ComponentTag::$variant => $crate::__cn_surviving_tag!($variant; $($meta)*) ),+
}
}
pub fn parse(name: &str) -> Option<ComponentTag> {
$(
if name == stringify!($variant) {
return Some(ComponentTag::$variant);
}
)+
None
}
}
$crate::__define_asset_kind! {
asset_enum: ComponentAsset,
asset_kind: Component,
$( $variant => $ty, ComponentTag::$variant as u8 ),+
}
impl ComponentAsset {
pub fn from_baked(def: &BlobAssetDef) -> Result<Self, CnResult> {
$(
if def.discriminant == ComponentTag::$variant as u8 {
let mut c = <$ty as Component>::from_baked(&def.args_bytes)?;
if let Some(id) = def.name {
<$ty as Component>::inject_name(&mut c, id);
}
return Ok(ComponentAsset::$variant(c));
}
)+
Err(CnResult::AssetInvalidType)
}
pub fn inject_locator(&mut self, locator: PayloadLocator) {
match self {
$( ComponentAsset::$variant(c) => c.inject_locator(locator) ),+
}
}
pub fn inject_name(&mut self, id: $crate::ecs::asset_id::AssetId) {
match self {
$( ComponentAsset::$variant(c) => c.inject_name(id) ),+
}
}
pub fn validated(self, platform: $crate::platform::Platform) -> Self {
match self {
$(
ComponentAsset::$variant(c) => ComponentAsset::$variant(
$crate::__cn_validate!(c, platform; $($meta)*)
),
)+
}
}
pub fn type_name(&self) -> &'static str {
match self {
$( ComponentAsset::$variant(_) => stringify!($variant) ),+
}
}
pub fn tag(&self) -> ComponentTag {
match self {
$( ComponentAsset::$variant(_) => ComponentTag::$variant ),+
}
}
}
impl $crate::ecs::ResourceKind {
pub fn parse(name: &str) -> Option<$crate::ecs::ResourceKind> {
$(
if name == stringify!($rvariant) {
return Some($crate::__cn_resource_kind!($($rmeta)*));
}
)+
None
}
}
$crate::define_component_storage! {
storage: ComponentStorage,
slot: ComponentSlot,
$( $variant => $ty, ComponentTag::$variant as u8 ),+
}
impl ComponentStorage {
pub fn push(&mut self, asset: ComponentAsset) -> $crate::ecs::Entity {
match asset {
$( ComponentAsset::$variant(c) => self.push_typed(c), )+
}
}
pub fn replace(&mut self, entity: $crate::ecs::Entity, asset: ComponentAsset) -> bool {
match asset {
$(
ComponentAsset::$variant(c) => match self.get_mut::<$ty>(entity) {
Some(slot) => {
*slot = c;
true
}
None => false,
},
)+
}
}
pub fn entities_with_tag(&self, tag: u8) -> &[$crate::ecs::Entity] {
$(
if tag == ComponentTag::$variant as u8 {
return self.$variant.entities();
}
)+
&[]
}
pub fn component_census(&self) -> ::alloc::vec::Vec<(u8, u32)> {
let mut out = ::alloc::vec::Vec::new();
$(
let count = self.$variant.len();
if count > 0 {
out.push((ComponentTag::$variant as u8, count as u32));
}
)+
out
}
}
$( impl $crate::ecs::RuntimeComponent for $ty {} )+
$( impl $crate::ecs::ResourceAsset for $rty {} )+
};
}