amethystate_core/
scheme.rs1use crate::path::StorePath;
2use std::fmt::Write;
3
4pub fn event_channel(path: &StorePath) -> String {
6 let mut name = String::from("amethystate://");
7 for (at, level) in path.segments().enumerate() {
8 if at > 0 {
9 name.push(':');
10 }
11 for byte in level.as_str().bytes() {
12 match byte {
13 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' => name.push(byte as char),
14 other => {
15 let _ = write!(name, "_{other:02x}");
16 }
17 }
18 }
19 }
20 name
21}
22
23#[derive(Debug, Clone, Copy)]
24pub enum FieldKind {
25 Plain,
26 Nested {
28 entry: &'static SchemaExportEntry,
29 },
30 Volatile,
31 ReactiveMap {
32 key_type: &'static str,
33 value_type: &'static str,
34 key_rust_type: &'static str,
35 value_rust_type: &'static str,
36 accepts_key: fn(&str) -> bool,
38 },
39}
40
41#[derive(Debug, Clone, Copy)]
42pub struct FieldExportMeta {
43 pub name: &'static str,
44 pub stored: &'static str,
47 pub ts_type: &'static str,
48 pub full_ts_type: &'static str,
49 pub rust_type: &'static str,
50 pub kind: FieldKind,
51 pub accepts: fn(&str) -> bool,
54 pub default: fn() -> Option<String>,
57 pub resets: Option<bool>,
60}
61
62#[derive(Debug, Clone, Copy)]
63pub struct SchemaExportEntry {
64 pub prefix: Option<&'static str>,
65 pub struct_name: &'static str,
66 pub module_path: &'static str,
68 pub id: Option<&'static str>,
70 pub version: u32,
71 pub resets: Option<bool>,
75 pub fields: &'static [FieldExportMeta],
76}
77
78impl SchemaExportEntry {
79 pub fn is(&self, other: &SchemaExportEntry) -> bool {
81 self.module_path == other.module_path && self.struct_name == other.struct_name
82 }
83}
84
85pub trait Exported {
90 const EXPORT: SchemaExportEntry;
91}
92
93inventory::collect!(SchemaExportEntry);
94
95pub fn exports() -> &'static [&'static SchemaExportEntry] {
102 static COMPILED: std::sync::OnceLock<Vec<&'static SchemaExportEntry>> =
103 std::sync::OnceLock::new();
104
105 COMPILED.get_or_init(|| {
106 let mut newest: Vec<&'static SchemaExportEntry> = Vec::new();
107 for entry in inventory::iter::<SchemaExportEntry> {
108 let line = |other: &&mut &'static SchemaExportEntry| match entry.id {
109 Some(id) => other.id == Some(id),
110 None => other.id.is_none() && other.prefix == entry.prefix,
111 };
112 match newest.iter_mut().find(line) {
113 Some(kept) if kept.version < entry.version => *kept = entry,
114 Some(kept) if kept.version == entry.version && !kept.is(entry) => {
115 newest.push(entry)
116 }
117 Some(_) => {}
118 None => newest.push(entry),
119 }
120 }
121 newest
122 })
123}