vyre_foundation/dispatch/
extern_registry.rs1#![forbid(unsafe_code)]
8
9use rustc_hash::{FxHashMap, FxHashSet};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13#[non_exhaustive]
14pub struct ExternDialect {
15 pub name: &'static str,
17 pub version: &'static str,
19 pub crate_repo: &'static str,
21}
22
23impl ExternDialect {
24 #[must_use]
26 pub const fn new(name: &'static str, version: &'static str, crate_repo: &'static str) -> Self {
27 Self {
28 name,
29 version,
30 crate_repo,
31 }
32 }
33}
34
35inventory::collect!(ExternDialect);
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39#[non_exhaustive]
40pub struct ExternOp {
41 pub dialect: &'static str,
43 pub op_id: &'static str,
45}
46
47impl ExternOp {
48 #[must_use]
50 pub const fn new(dialect: &'static str, op_id: &'static str) -> Self {
51 Self { dialect, op_id }
52 }
53}
54
55inventory::collect!(ExternOp);
56
57#[must_use]
59pub fn dialects() -> Vec<&'static ExternDialect> {
60 let iter = inventory::iter::<ExternDialect>();
61 let (lo, hi) = iter.size_hint();
62 let mut out = Vec::with_capacity(hi.unwrap_or(lo));
63 out.extend(iter);
64 out
65}
66
67#[must_use]
69pub fn ops_in_dialect(dialect: &str) -> Vec<&'static ExternOp> {
70 let iter = inventory::iter::<ExternOp>().filter(|op| op.dialect == dialect);
71 let (lo, hi) = iter.size_hint();
72 let mut out = Vec::with_capacity(hi.unwrap_or(lo));
73 out.extend(iter);
74 out
75}
76
77#[must_use]
79pub fn all_ops() -> Vec<&'static ExternOp> {
80 let iter = inventory::iter::<ExternOp>();
81 let (lo, hi) = iter.size_hint();
82 let mut out = Vec::with_capacity(hi.unwrap_or(lo));
83 out.extend(iter);
84 out
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
89#[non_exhaustive]
90pub enum ExternVerifyError {
91 #[error("duplicate dialect name `{name}`: {count} entries registered. Fix: pick a unique crates.io name for each community pack.")]
93 DuplicateDialect {
94 name: &'static str,
96 count: usize,
98 },
99
100 #[error("dialect name `{name}` does not start with `vyre-libs-`. Fix: rename the pack crate and its ExternDialect::name to begin with `vyre-libs-`.")]
102 MalformedDialectName {
103 name: &'static str,
105 },
106
107 #[error("orphan op `{op_id}` references dialect `{dialect}`, which is not registered. Fix: make sure the dialect's crate registers an `ExternDialect` entry with this name.")]
110 OrphanOp {
111 dialect: &'static str,
113 op_id: &'static str,
115 },
116
117 #[error("op registered with empty op_id under dialect `{dialect}`. Fix: every op must carry a fully-qualified id like `<dialect>::<op_name>`.")]
119 EmptyOpId {
120 dialect: &'static str,
122 },
123}
124
125pub fn verify() -> Result<(), Vec<ExternVerifyError>> {
131 let mut errors = Vec::new();
132
133 let mut counts: FxHashMap<&'static str, usize> = FxHashMap::default();
136 let mut known: FxHashSet<&'static str> = FxHashSet::default();
137 for dialect in inventory::iter::<ExternDialect>() {
138 *counts.entry(dialect.name).or_insert(0) += 1;
139 known.insert(dialect.name);
140 if !dialect.name.starts_with("vyre-libs-") {
141 errors.push(ExternVerifyError::MalformedDialectName { name: dialect.name });
142 }
143 }
144 for (name, count) in counts {
145 if count > 1 {
146 errors.push(ExternVerifyError::DuplicateDialect { name, count });
147 }
148 }
149
150 for op in inventory::iter::<ExternOp>() {
151 if op.op_id.is_empty() {
152 errors.push(ExternVerifyError::EmptyOpId {
153 dialect: op.dialect,
154 });
155 }
156 if !known.contains(op.dialect) {
157 errors.push(ExternVerifyError::OrphanOp {
158 dialect: op.dialect,
159 op_id: op.op_id,
160 });
161 }
162 }
163
164 if errors.is_empty() {
165 Ok(())
166 } else {
167 Err(errors)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn extern_dialect_construction() {
177 let d = ExternDialect::new("vyre-libs-quant", "0.1.0", "https://github.com/example");
178 assert_eq!(d.name, "vyre-libs-quant");
179 assert_eq!(d.version, "0.1.0");
180 assert_eq!(d.crate_repo, "https://github.com/example");
181 }
182
183 #[test]
184 fn extern_op_construction() {
185 let op = ExternOp::new("vyre-libs-quant", "vyre-libs-quant::int8::matmul");
186 assert_eq!(op.dialect, "vyre-libs-quant");
187 assert_eq!(op.op_id, "vyre-libs-quant::int8::matmul");
188 }
189
190 #[test]
191 fn duplicate_dialect_error_display() {
192 let err = ExternVerifyError::DuplicateDialect {
193 name: "vyre-libs-x",
194 count: 3,
195 };
196 let msg = err.to_string();
197 assert!(msg.contains("vyre-libs-x"));
198 assert!(msg.contains("3"));
199 }
200
201 #[test]
202 fn malformed_name_error_display() {
203 let err = ExternVerifyError::MalformedDialectName { name: "bad-name" };
204 let msg = err.to_string();
205 assert!(msg.contains("bad-name"));
206 assert!(msg.contains("vyre-libs-"));
207 }
208
209 #[test]
210 fn orphan_op_error_display() {
211 let err = ExternVerifyError::OrphanOp {
212 dialect: "missing-dialect",
213 op_id: "missing::op",
214 };
215 assert!(err.to_string().contains("orphan"));
216 }
217
218 #[test]
219 fn empty_op_id_error_display() {
220 let err = ExternVerifyError::EmptyOpId {
221 dialect: "vyre-libs-x",
222 };
223 assert!(err.to_string().contains("empty"));
224 }
225
226 #[test]
227 fn verify_empty_registry_succeeds() {
228 let result = verify();
231 if let Err(errors) = &result {
233 for e in errors {
235 let _ = e.to_string();
236 }
237 }
238 }
239}