use super::*;
use regex::Regex;
static ENTITY_REGISTRY: OnceLock<RwLock<Vec<EntityRegistrationFn>>> = OnceLock::new();
static MODEL_SCHEMAS: OnceLock<RwLock<Vec<ModelSchema>>> = OnceLock::new();
#[doc(hidden)]
pub struct CompiledModelRegistration {
pub source_path: &'static str,
pub sync_schema: fn() -> ModelSchema,
}
inventory::collect!(CompiledModelRegistration);
pub(super) fn get_entity_registry() -> &'static RwLock<Vec<EntityRegistrationFn>> {
ENTITY_REGISTRY.get_or_init(|| RwLock::new(Vec::new()))
}
pub(super) fn get_model_schemas() -> &'static RwLock<Vec<ModelSchema>> {
MODEL_SCHEMAS.get_or_init(|| RwLock::new(Vec::new()))
}
pub(super) fn register_compiled_models_matching(pattern: &str) -> usize {
let Some(compiled_pattern) = compile_glob_pattern(pattern) else {
return 0;
};
let mut registered = 0;
for model in inventory::iter::<CompiledModelRegistration> {
if glob_path_matches(&compiled_pattern, model.source_path) {
SyncRegistry::register_schema((model.sync_schema)());
registered += 1;
}
}
registered
}
fn compile_glob_pattern(pattern: &str) -> Option<Regex> {
let normalized_pattern = normalize_path(pattern);
let regex = glob_to_regex(&normalized_pattern);
Regex::new(®ex).ok()
}
fn glob_path_matches(pattern: &Regex, candidate: &str) -> bool {
let normalized_candidate = normalize_path(candidate);
pattern.is_match(&normalized_candidate)
}
fn normalize_path(path: &str) -> String {
path.replace('\\', "/")
}
fn glob_to_regex(pattern: &str) -> String {
let mut regex = String::from("^");
let mut chars = pattern.chars().peekable();
while let Some(ch) = chars.next() {
match ch {
'*' => {
if chars.peek() == Some(&'*') {
chars.next();
if chars.peek() == Some(&'/') {
chars.next();
regex.push_str("(?:[^/]+/)*");
} else {
regex.push_str(".*");
}
} else {
regex.push_str("[^/]*");
}
}
'?' => regex.push_str("[^/]"),
'.' | '+' | '(' | ')' | '|' | '^' | '$' | '{' | '}' | '[' | ']' | '\\' => {
regex.push('\\');
regex.push(ch);
}
_ => regex.push(ch),
}
}
regex.push('$');
regex
}
pub trait SyncModel {
fn sync_schema() -> ModelSchema;
fn register_for_sync() {
SyncRegistry::register_schema(Self::sync_schema());
}
}
pub trait RegisterModels {
fn register_all();
}
impl RegisterModels for () {
fn register_all() {}
}
macro_rules! impl_register_models_tuples {
($first:ident) => {
impl<$first: SyncModel> RegisterModels for ($first,) {
fn register_all() {
$first::register_for_sync();
}
}
};
($first:ident, $($rest:ident),+) => {
impl_register_models_tuples!($($rest),+);
impl<$first: SyncModel, $($rest: SyncModel),+> RegisterModels for ($first, $($rest),+) {
fn register_all() {
$first::register_for_sync();
$($rest::register_for_sync();)+
}
}
};
}
impl_register_models_tuples!(
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21,
T22, T23, T24, T25, T26, T27, T28, T29, T30, T31, T32, T33, T34, T35, T36, T37, T38, T39, T40,
T41, T42, T43, T44, T45, T46, T47, T48, T49, T50, T51, T52, T53, T54, T55, T56, T57, T58, T59,
T60, T61, T62, T63, T64, T65, T66, T67, T68, T69, T70, T71, T72, T73, T74, T75, T76, T77, T78,
T79, T80, T81, T82, T83, T84, T85, T86, T87, T88, T89, T90, T91, T92, T93, T94, T95, T96, T97,
T98, T99, T100, T101, T102, T103, T104, T105, T106, T107, T108, T109, T110, T111, T112, T113,
T114, T115, T116, T117, T118, T119, T120, T121, T122, T123, T124, T125, T126, T127, T128, T129,
T130, T131, T132, T133, T134, T135, T136, T137, T138, T139, T140, T141, T142, T143, T144, T145,
T146, T147, T148, T149, T150, T151, T152, T153, T154, T155, T156, T157, T158, T159, T160, T161,
T162, T163, T164, T165, T166, T167, T168, T169, T170, T171, T172, T173, T174, T175, T176, T177,
T178, T179, T180, T181, T182, T183, T184, T185, T186, T187, T188, T189, T190, T191, T192, T193,
T194, T195, T196, T197, T198, T199, T200
);