use crate::path::StorePath;
use std::fmt::Write;
pub fn event_channel(path: &StorePath) -> String {
let mut name = String::from("amethystate://");
for (at, level) in path.segments().enumerate() {
if at > 0 {
name.push(':');
}
for byte in level.as_str().bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' => name.push(byte as char),
other => {
let _ = write!(name, "_{other:02x}");
}
}
}
}
name
}
#[derive(Debug, Clone, Copy)]
pub enum FieldKind {
Plain,
Nested {
entry: &'static SchemaExportEntry,
},
Volatile,
ReactiveMap {
key_type: &'static str,
value_type: &'static str,
key_rust_type: &'static str,
value_rust_type: &'static str,
accepts_key: fn(&str) -> bool,
},
}
#[derive(Debug, Clone, Copy)]
pub struct FieldExportMeta {
pub name: &'static str,
pub stored: &'static str,
pub ts_type: &'static str,
pub full_ts_type: &'static str,
pub rust_type: &'static str,
pub kind: FieldKind,
pub accepts: fn(&str) -> bool,
pub default: fn() -> Option<String>,
pub resets: Option<bool>,
}
#[derive(Debug, Clone, Copy)]
pub struct SchemaExportEntry {
pub prefix: Option<&'static str>,
pub struct_name: &'static str,
pub module_path: &'static str,
pub id: Option<&'static str>,
pub version: u32,
pub resets: Option<bool>,
pub fields: &'static [FieldExportMeta],
}
impl SchemaExportEntry {
pub fn is(&self, other: &SchemaExportEntry) -> bool {
self.module_path == other.module_path && self.struct_name == other.struct_name
}
}
pub trait Exported {
const EXPORT: SchemaExportEntry;
}
inventory::collect!(SchemaExportEntry);
pub fn exports() -> &'static [&'static SchemaExportEntry] {
static COMPILED: std::sync::OnceLock<Vec<&'static SchemaExportEntry>> =
std::sync::OnceLock::new();
COMPILED.get_or_init(|| {
let mut newest: Vec<&'static SchemaExportEntry> = Vec::new();
for entry in inventory::iter::<SchemaExportEntry> {
let line = |other: &&mut &'static SchemaExportEntry| match entry.id {
Some(id) => other.id == Some(id),
None => other.id.is_none() && other.prefix == entry.prefix,
};
match newest.iter_mut().find(line) {
Some(kept) if kept.version < entry.version => *kept = entry,
Some(kept) if kept.version == entry.version && !kept.is(entry) => {
newest.push(entry)
}
Some(_) => {}
None => newest.push(entry),
}
}
newest
})
}