use crate::model::{entity::EntityModel, field::FieldModel, index::IndexModel};
pub(crate) const fn entity_model_from_static(
path: &'static str,
entity_name: &'static str,
primary_key: &'static FieldModel,
primary_key_slot: usize,
fields: &'static [FieldModel],
indexes: &'static [&'static IndexModel],
) -> EntityModel {
EntityModel::generated(
path,
entity_name,
primary_key,
primary_key_slot,
fields,
indexes,
)
}
const fn str_eq(left: &str, right: &str) -> bool {
let left = left.as_bytes();
let right = right.as_bytes();
if left.len() != right.len() {
return false;
}
let mut index = 0;
while index < left.len() {
if left[index] != right[index] {
return false;
}
index += 1;
}
true
}
#[doc(hidden)]
pub(crate) const fn field_model_by_name(
fields: &'static [FieldModel],
name: &'static str,
) -> &'static FieldModel {
let mut index = 0;
while index < fields.len() {
if str_eq(fields[index].name(), name) {
return &fields[index];
}
index += 1;
}
panic!("test relation references a field that is not declared on the entity")
}
#[doc(hidden)]
pub(crate) const fn field_model_index_by_name(
fields: &'static [FieldModel],
name: &'static str,
) -> usize {
let mut index = 0;
while index < fields.len() {
if str_eq(fields[index].name(), name) {
return index;
}
index += 1;
}
panic!("test primary key references a field that is not declared on the entity")
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_entity_markers {
($entity:ty) => {
impl $crate::traits::SanitizeAuto for $entity {}
impl $crate::traits::SanitizeCustom for $entity {}
impl $crate::traits::ValidateAuto for $entity {}
impl $crate::traits::ValidateCustom for $entity {}
impl $crate::traits::Visitable for $entity {}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_canister_model {
(
vis = $vis:vis,
ident = $canister:ident,
commit_memory_id = $commit_memory_id:expr $(,)?
) => {
$vis struct $canister;
impl $crate::traits::Path for $canister {
const PATH: &'static str = concat!(module_path!(), "::", stringify!($canister));
}
impl $crate::traits::CanisterKind for $canister {
const COMMIT_MEMORY_ID: u8 = $commit_memory_id;
const COMMIT_STABLE_KEY: &'static str = "icydb.test.commit.v1";
}
};
}
#[macro_export]
macro_rules! test_canister {
(
ident = $canister:ident $(,)?
) => {
compile_error!("test_canister! requires `commit_memory_id = <u8>`");
};
(
vis = $vis:vis,
ident = $canister:ident,
commit_memory_id = $commit_memory_id:expr $(,)?
) => {
$crate::__icydb_test_canister_model! {
vis = $vis,
ident = $canister,
commit_memory_id = $commit_memory_id,
}
};
(
ident = $canister:ident,
commit_memory_id = $commit_memory_id:expr $(,)?
) => {
$crate::__icydb_test_canister_model! {
vis =,
ident = $canister,
commit_memory_id = $commit_memory_id,
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_store_model {
(
vis = $vis:vis,
ident = $store:ident,
canister = $canister:ty $(,)?
) => {
$vis struct $store;
impl $crate::traits::Path for $store {
const PATH: &'static str = concat!(module_path!(), "::", stringify!($store));
}
impl $crate::traits::StoreKind for $store {
type Canister = $canister;
}
};
}
#[macro_export]
macro_rules! test_store {
(
vis = $vis:vis,
ident = $store:ident,
canister = $canister:ty $(,)?
) => {
$crate::__icydb_test_store_model! {
vis = $vis,
ident = $store,
canister = $canister,
}
};
(
ident = $store:ident,
canister = $canister:ty $(,)?
) => {
$crate::__icydb_test_store_model! {
vis =,
ident = $store,
canister = $canister,
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_entity_runtime_surface {
($entity:ident, $id_ty:ty, $entity_name:expr, $model_ident:ident) => {
impl $crate::traits::EntityKey for $entity {
type Key = $id_ty;
}
impl $crate::traits::Path for $entity {
const PATH: &'static str = concat!(module_path!(), "::", stringify!($entity));
}
impl $crate::traits::EntitySchema for $entity {
const NAME: &'static str = $entity_name;
const MODEL: &'static $crate::model::entity::EntityModel = &Self::$model_ident;
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_entity_model {
(
$entity:ident,
$entity_name:expr,
primary_key = [ $pk_field:ident ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
) => {
impl $entity {
const FIELD_MODELS: [$crate::model::field::FieldModel;
$crate::__icydb_test_entity_model!(@count $( $field_model ),+)
] = [
$( $field_model, )+
];
const INDEXES_DEF: [&'static $crate::model::index::IndexModel;
$crate::__icydb_test_entity_model!(@count $( $index ),*)
] = [
$( $index, )*
];
const RELATIONS_DEF: [$crate::model::entity::RelationEdgeModel;
$crate::__icydb_test_entity_model!(@count $( $relation ),*)
] = [
$( $relation, )*
];
const MODEL_DEF: $crate::model::entity::EntityModel =
$crate::model::entity::EntityModel::generated_with_primary_key_model_and_relations(
concat!(module_path!(), "::", stringify!($entity)),
$entity_name,
$crate::model::entity::PrimaryKeyModel::scalar(
$crate::testing::field_model_by_name(
&Self::FIELD_MODELS,
stringify!($pk_field),
),
),
$crate::testing::field_model_index_by_name(
&Self::FIELD_MODELS,
stringify!($pk_field),
),
&Self::FIELD_MODELS,
&Self::INDEXES_DEF,
&Self::RELATIONS_DEF,
);
}
};
(
$entity:ident,
$entity_name:expr,
primary_key = [ $pk_field_0:ident, $( $pk_field_rest:ident ),+ $(,)? ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
) => {
impl $entity {
const FIELD_MODELS: [$crate::model::field::FieldModel;
$crate::__icydb_test_entity_model!(@count $( $field_model ),+)
] = [
$( $field_model, )+
];
const PRIMARY_KEY_FIELDS: [&'static $crate::model::field::FieldModel;
$crate::__icydb_test_entity_model!(@count $pk_field_0, $( $pk_field_rest ),+)
] = [
$crate::testing::field_model_by_name(
&Self::FIELD_MODELS,
stringify!($pk_field_0),
),
$(
$crate::testing::field_model_by_name(
&Self::FIELD_MODELS,
stringify!($pk_field_rest),
),
)+
];
const INDEXES_DEF: [&'static $crate::model::index::IndexModel;
$crate::__icydb_test_entity_model!(@count $( $index ),*)
] = [
$( $index, )*
];
const RELATIONS_DEF: [$crate::model::entity::RelationEdgeModel;
$crate::__icydb_test_entity_model!(@count $( $relation ),*)
] = [
$( $relation, )*
];
const MODEL_DEF: $crate::model::entity::EntityModel =
$crate::model::entity::EntityModel::generated_with_primary_key_model_and_relations(
concat!(module_path!(), "::", stringify!($entity)),
$entity_name,
$crate::model::entity::PrimaryKeyModel::ordered(&Self::PRIMARY_KEY_FIELDS),
0,
&Self::FIELD_MODELS,
&Self::INDEXES_DEF,
&Self::RELATIONS_DEF,
);
}
};
(@count $( $value:expr ),* ) => {
<[()]>::len(&[ $( $crate::__icydb_test_entity_model!(@unit $value) ),* ])
};
(@unit $value:expr) => {
()
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_entity_traits {
(
ident = $entity:ident,
entity_name = $entity_name:expr,
tag = $entity_tag:expr,
store = $store_ty:ty,
canister = $canister_ty:ty,
key_type = $key_ty:ty,
model = $model_ident:ident,
entity_value = { $($entity_value:tt)+ } $(,)?
) => {
$crate::__icydb_test_entity_runtime_surface!($entity, $key_ty, $entity_name, $model_ident);
impl $crate::traits::EntityPlacement for $entity {
type Store = $store_ty;
type Canister = $canister_ty;
}
impl $crate::traits::EntityKind for $entity {
const ENTITY_TAG: $crate::types::EntityTag = $entity_tag;
}
$crate::__icydb_test_entity_value!($entity, $($entity_value)+);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_entity_value {
($entity:ident, none $(,)?) => {};
($entity:ident, id_field($id_field:ident) $(,)?) => {
impl $crate::traits::EntityValue for $entity {
fn id(&self) -> $crate::types::Id<Self> {
$crate::types::Id::from_key(self.$id_field)
}
}
};
($entity:ident, key($key_expr:expr) $(,)?) => {
impl $crate::traits::EntityValue for $entity {
fn id(&self) -> $crate::types::Id<Self> {
$crate::types::Id::from_key(($key_expr)(self))
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_entity_singleton {
($entity:ident, true) => {
impl $crate::traits::SingletonEntity for $entity {}
};
($entity:ident, false) => {};
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct TestFieldModelOptions {
storage_decode: crate::model::field::FieldStorageDecode,
nullable: bool,
insert_generation: Option<crate::model::field::FieldInsertGeneration>,
write_management: Option<crate::model::field::FieldWriteManagement>,
database_default: crate::model::field::FieldDatabaseDefault,
nested_fields: &'static [crate::model::field::FieldModel],
}
impl TestFieldModelOptions {
pub(crate) const DEFAULT: Self = Self {
storage_decode: crate::model::field::FieldStorageDecode::ByKind,
nullable: false,
insert_generation: None,
write_management: None,
database_default: crate::model::field::FieldDatabaseDefault::None,
nested_fields: &[],
};
pub(crate) const fn with_storage_decode(
mut self,
storage_decode: crate::model::field::FieldStorageDecode,
) -> Self {
self.storage_decode = storage_decode;
self
}
pub(crate) const fn with_nullable(mut self, nullable: bool) -> Self {
self.nullable = nullable;
self
}
pub(crate) const fn with_insert_generation(
mut self,
insert_generation: crate::model::field::FieldInsertGeneration,
) -> Self {
self.insert_generation = Some(insert_generation);
self
}
pub(crate) const fn with_write_management(
mut self,
write_management: crate::model::field::FieldWriteManagement,
) -> Self {
self.write_management = Some(write_management);
self
}
pub(crate) const fn with_database_default(
mut self,
database_default: crate::model::field::FieldDatabaseDefault,
) -> Self {
self.database_default = database_default;
self
}
pub(crate) const fn with_nested_fields(
mut self,
nested_fields: &'static [crate::model::field::FieldModel],
) -> Self {
self.nested_fields = nested_fields;
self
}
}
pub(crate) const fn test_field_model(
name: &'static str,
kind: crate::model::field::FieldKind,
options: TestFieldModelOptions,
) -> crate::model::field::FieldModel {
crate::model::field::FieldModel::generated_with_storage_decode_nullability_write_policies_database_default_and_nested_fields(
name,
kind,
options.storage_decode,
options.nullable,
options.insert_generation,
options.write_management,
options.database_default,
options.nested_fields,
)
}
#[macro_export]
macro_rules! test_field {
(
name = $field_name:expr,
ty = $field_ty:ty,
kind = $field_kind:expr $(,)?
) => {
$crate::testing::test_field_model(
$field_name,
$field_kind,
$crate::testing::TestFieldModelOptions::DEFAULT,
)
};
(
name = $field_name:expr,
ty = $field_ty:ty,
kind = $field_kind:expr,
options = $options:expr $(,)?
) => {
$crate::testing::test_field_model($field_name, $field_kind, $options)
};
($field:ident : $field_ty:ty => $field_kind:expr $(,)?) => {
$crate::testing::test_field_model(
stringify!($field),
$field_kind,
$crate::testing::TestFieldModelOptions::DEFAULT,
)
};
(
$field:ident : $field_ty:ty => $field_kind:expr,
options = $options:expr $(,)?
) => {
$crate::testing::test_field_model(stringify!($field), $field_kind, $options)
};
}
#[macro_export]
macro_rules! test_relation {
(
name = $relation_name:expr,
target = $relation_target:ty,
fields = [ $( $relation_field:ident ),+ $(,)? ] $(,)?
) => {
$crate::model::entity::RelationEdgeModel::generated(
$relation_name,
<$relation_target as $crate::traits::Path>::PATH,
&[
$(
$crate::testing::field_model_by_name(
&Self::FIELD_MODELS,
stringify!($relation_field),
),
)+
],
)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __icydb_test_emit_entity {
(
ident = $entity:ident,
entity_name = $entity_name:expr,
tag = $entity_tag:expr,
store = $store_ty:ty,
canister = $canister_ty:ty,
runtime = full,
singleton = $singleton:tt,
key_type = $key_ty:ty,
primary_key = [ $( $pk_field:ident ),+ $(,)? ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
entity_value = { $($entity_value:tt)+ } $(,)?
) => {
$crate::__icydb_test_entity_markers!($entity);
$crate::__icydb_test_entity_model!(
$entity,
$entity_name,
primary_key = [ $( $pk_field ),+ ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [ $( $relation ),* ],
);
$crate::__icydb_test_entity_traits! {
ident = $entity,
entity_name = $entity_name,
tag = $entity_tag,
store = $store_ty,
canister = $canister_ty,
key_type = $key_ty,
model = MODEL_DEF,
entity_value = { $($entity_value)+ },
}
$crate::__icydb_test_entity_singleton!($entity, $singleton);
};
(
ident = $entity:ident,
entity_name = $entity_name:expr,
runtime = schema_only,
key_type = $key_ty:ty,
primary_key = [ $( $pk_field:ident ),+ $(,)? ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
) => {
struct $entity;
$crate::__icydb_test_entity_model!(
$entity,
$entity_name,
primary_key = [ $( $pk_field ),+ ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [ $( $relation ),* ],
);
$crate::__icydb_test_entity_runtime_surface!($entity, $key_ty, $entity_name, MODEL_DEF);
};
}
#[macro_export]
macro_rules! test_entity {
(
ident = $entity:ident,
entity_name = $entity_name:expr,
tag = $entity_tag:expr,
store = $store_ty:ty,
canister = $canister_ty:ty,
key_type = $key_ty:ty,
primary_key = [ $( $pk_field:ident ),+ $(,)? ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
entity_value = $($entity_value:tt)+
) => {
$crate::__icydb_test_emit_entity! {
ident = $entity,
entity_name = $entity_name,
tag = $entity_tag,
store = $store_ty,
canister = $canister_ty,
runtime = full,
singleton = false,
key_type = $key_ty,
primary_key = [ $( $pk_field ),+ ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [ $( $relation ),* ],
entity_value = { $($entity_value)+ },
}
};
(
ident = $entity:ident,
entity_name = $entity_name:expr,
tag = $entity_tag:expr,
store = $store_ty:ty,
canister = $canister_ty:ty,
key_type = $key_ty:ty,
primary_key = [ $pk_field:ident ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
) => {
$crate::__icydb_test_emit_entity! {
ident = $entity,
entity_name = $entity_name,
tag = $entity_tag,
store = $store_ty,
canister = $canister_ty,
runtime = full,
singleton = false,
key_type = $key_ty,
primary_key = [ $pk_field ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [ $( $relation ),* ],
entity_value = { id_field($pk_field) },
}
};
(
ident = $entity:ident,
entity_name = $entity_name:expr,
tag = $entity_tag:expr,
store = $store_ty:ty,
canister = $canister_ty:ty,
key_type = $key_ty:ty,
primary_key = [ $pk_field:ident ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
) => {
$crate::test_entity! {
ident = $entity,
entity_name = $entity_name,
tag = $entity_tag,
store = $store_ty,
canister = $canister_ty,
key_type = $key_ty,
primary_key = [ $pk_field ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [],
}
};
}
#[macro_export]
macro_rules! test_singleton_entity {
(
ident = $entity:ident,
entity_name = $entity_name:expr,
tag = $entity_tag:expr,
store = $store_ty:ty,
canister = $canister_ty:ty,
key_type = $key_ty:ty,
primary_key = [ $pk_field:ident ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
) => {
$crate::__icydb_test_emit_entity! {
ident = $entity,
entity_name = $entity_name,
tag = $entity_tag,
store = $store_ty,
canister = $canister_ty,
runtime = full,
singleton = true,
key_type = $key_ty,
primary_key = [ $pk_field ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [ $( $relation ),* ],
entity_value = { id_field($pk_field) },
}
};
(
ident = $entity:ident,
entity_name = $entity_name:expr,
tag = $entity_tag:expr,
store = $store_ty:ty,
canister = $canister_ty:ty,
key_type = $key_ty:ty,
primary_key = [ $pk_field:ident ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
) => {
$crate::test_singleton_entity! {
ident = $entity,
entity_name = $entity_name,
tag = $entity_tag,
store = $store_ty,
canister = $canister_ty,
key_type = $key_ty,
primary_key = [ $pk_field ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [],
}
};
}
#[macro_export]
macro_rules! test_schema_entity {
(
ident = $entity:ident,
entity_name = $entity_name:expr,
key_type = $key_ty:ty,
primary_key = [ $( $pk_field:ident ),+ $(,)? ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
relations = [ $( $relation:expr ),* $(,)? ],
) => {
$crate::__icydb_test_emit_entity! {
ident = $entity,
entity_name = $entity_name,
runtime = schema_only,
key_type = $key_ty,
primary_key = [ $( $pk_field ),+ ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [ $( $relation ),* ],
}
};
(
ident = $entity:ident,
entity_name = $entity_name:expr,
key_type = $key_ty:ty,
primary_key = [ $( $pk_field:ident ),+ $(,)? ],
fields = [ $( $field_model:expr ),+ $(,)? ],
indexes = [ $( $index:expr ),* $(,)? ],
) => {
$crate::test_schema_entity! {
ident = $entity,
entity_name = $entity_name,
key_type = $key_ty,
primary_key = [ $( $pk_field ),+ ],
fields = [ $( $field_model ),+ ],
indexes = [ $( $index ),* ],
relations = [],
}
};
}