1use std::collections::{HashMap, HashSet};
2use std::hash::{BuildHasherDefault, Hasher};
3
4#[derive(Default, Clone, Copy)]
18pub struct FastHasher {
19 hash: u64,
20}
21
22const SEED: u64 = 0xf135_7aea_2e62_a9c5;
23
24impl FastHasher {
25 #[inline]
26 fn add(&mut self, word: u64) {
27 self.hash = self.hash.wrapping_add(word).wrapping_mul(SEED);
28 }
29}
30
31impl Hasher for FastHasher {
32 #[inline]
33 fn write(&mut self, bytes: &[u8]) {
34 let mut chunks = bytes.chunks_exact(8);
35 for c in &mut chunks {
36 self.add(u64::from_le_bytes(c.try_into().expect("chunk of 8")));
37 }
38 let rest = chunks.remainder();
39 if !rest.is_empty() {
40 let mut buf = [0u8; 8];
41 buf[..rest.len()].copy_from_slice(rest);
42 self.add(u64::from_le_bytes(buf));
43 }
44 }
45 #[inline]
46 fn write_u8(&mut self, i: u8) {
47 self.add(i as u64);
48 }
49 #[inline]
50 fn write_u32(&mut self, i: u32) {
51 self.add(i as u64);
52 }
53 #[inline]
54 fn write_u64(&mut self, i: u64) {
55 self.add(i);
56 }
57 #[inline]
58 fn write_usize(&mut self, i: usize) {
59 self.add(i as u64);
60 }
61 #[inline]
62 fn finish(&self) -> u64 {
63 self.hash.rotate_left(26)
64 }
65}
66
67pub type FastBuildHasher = BuildHasherDefault<FastHasher>;
68pub type FastMap<K, V> = HashMap<K, V, FastBuildHasher>;
69pub type FastSet<T> = HashSet<T, FastBuildHasher>;
70pub type FieldMap = FastMap<&'static str, FieldValue>;
74
75pub fn intern(key: &str) -> &'static str {
80 use std::cell::RefCell;
81 use std::sync::{Mutex, OnceLock};
82 thread_local! {
83 static LOCAL: RefCell<FastSet<&'static str>> = RefCell::new(FastSet::default());
84 }
85 static GLOBAL: OnceLock<Mutex<FastSet<&'static str>>> = OnceLock::new();
86 if let Some(k) = LOCAL.with(|l| l.borrow().get(key).copied()) {
87 return k;
88 }
89 let k = {
90 let mut global = GLOBAL.get_or_init(Default::default).lock().unwrap();
91 match global.get(key) {
92 Some(k) => *k,
93 None => {
94 let k: &'static str = Box::leak(key.to_owned().into_boxed_str());
95 global.insert(k);
96 k
97 }
98 }
99 };
100 LOCAL.with(|l| l.borrow_mut().insert(k));
101 k
102}
103
104pub fn foreign_class(ns: &str, local: &str) -> &'static ClassDef {
109 use std::sync::{Mutex, OnceLock};
110 static CLASSES: OnceLock<Mutex<FastMap<String, &'static ClassDef>>> = OnceLock::new();
111 let iri = format!("{ns}{local}");
112 let mut classes = CLASSES.get_or_init(Default::default).lock().unwrap();
113 if let Some(c) = classes.get(&iri) {
114 return c;
115 }
116 let qualified: &'static str = Box::leak(iri.clone().into_boxed_str());
117 let class: &'static ClassDef = Box::leak(Box::new(ClassDef {
118 ns: intern(ns),
119 local: &qualified[ns.len()..],
120 qualified,
121 super_class: None,
122 concrete: true,
123 attrs: &[],
124 origins: &[],
125 comment: "",
126 }));
127 classes.insert(iri, class);
128 class
129}
130
131#[derive(Debug, Clone)]
132pub enum FieldValue {
133 Text(String),
134 TextList(Vec<String>),
137 Resource(String),
138 ResourceList(Vec<String>),
139}
140
141#[derive(Debug, Clone)]
151pub struct Element {
152 class: &'static ClassDef,
153 mrid: String,
154 fields: FieldMap,
155 duplicate_fields: FastSet<&'static str>,
158 langs: Option<Box<Langs>>,
162 also: Option<Box<Types>>,
168 described: bool,
172}
173
174type Langs = Vec<(&'static str, Box<str>)>;
176
177type Types = Vec<&'static ClassDef>;
179
180impl Element {
181 pub fn new(class: &'static ClassDef, mrid: String) -> Self {
182 Self { class, mrid, fields: FieldMap::default(), duplicate_fields: FastSet::default(), langs: None, also: None, described: false }
183 }
184
185 pub fn with_fields(class: &'static ClassDef, mrid: String, fields: FieldMap) -> Self {
186 Self { class, mrid, fields, duplicate_fields: FastSet::default(), langs: None, also: None, described: false }
187 }
188
189 pub fn class(&self) -> &'static ClassDef {
190 self.class
191 }
192
193 pub fn types(&self) -> impl Iterator<Item = &'static ClassDef> + '_ {
196 std::iter::once(self.class).chain(self.also.iter().flat_map(|v| v.iter().copied()))
197 }
198
199 pub(crate) fn add_type(&mut self, other: &'static ClassDef, reg: &TypeRegistry) -> bool {
204 if self.types().any(|c| c.qualified == other.qualified) {
205 return false;
206 }
207 let promote = reg.chain(other).iter().any(|c| c.qualified == self.class.qualified);
208 let kept = if promote { std::mem::replace(&mut self.class, other) } else { other };
209 self.also.get_or_insert_with(Default::default).push(kept);
210 promote
211 }
212
213 pub fn defined(&self) -> bool {
216 !self.described
217 }
218
219 pub fn mark_described(&mut self) {
221 self.described = true;
222 }
223
224 pub fn mrid(&self) -> &str {
225 &self.mrid
226 }
227
228 pub fn type_name(&self) -> &'static str {
231 self.class.qualified
232 }
233
234 pub fn local_name(&self) -> &'static str {
236 self.class.local
237 }
238
239 pub fn type_ns(&self) -> &'static str {
241 self.class.ns
242 }
243
244 pub fn fields(&self) -> &FieldMap {
245 &self.fields
246 }
247
248 pub fn duplicate_fields(&self) -> &FastSet<&'static str> {
249 &self.duplicate_fields
250 }
251
252 pub fn langs(&self, key: &str) -> impl Iterator<Item = &str> {
256 self.langs.iter().flat_map(|l| l.iter()).filter(move |(k, _)| *k == key).map(|(_, l)| &**l)
257 }
258
259 pub fn add_lang(&mut self, key: &'static str, lang: &str) {
261 self.langs.get_or_insert_with(Default::default).push((key, lang.into()));
262 }
263
264 pub fn retype(&mut self, key: &str, reference: bool) {
267 if let Some(value) = self.fields.get_mut(key) {
268 *value = match (std::mem::replace(value, FieldValue::Text(String::new())), reference) {
269 (FieldValue::Text(s), true) => FieldValue::Resource(s),
270 (FieldValue::TextList(v), true) => FieldValue::ResourceList(v),
271 (FieldValue::Resource(s), false) => FieldValue::Text(s),
272 (FieldValue::ResourceList(v), false) => FieldValue::TextList(v),
273 (other, _) => other,
274 };
275 }
276 }
277
278 pub fn get(&self, attr: &str) -> Option<&FieldValue> {
279 self.fields.get(attr)
280 }
281
282 pub fn get_str(&self, attr: &str) -> Option<&str> {
283 match self.fields.get(attr) {
284 Some(FieldValue::Text(s)) => Some(s.as_str()),
285 Some(FieldValue::TextList(v)) => v.first().map(String::as_str),
286 _ => None,
287 }
288 }
289
290 pub fn get_f64(&self, attr: &str) -> Option<f64> {
291 self.get_str(attr)?.parse().ok()
292 }
293
294 pub fn get_bool(&self, attr: &str) -> Option<bool> {
295 match self.get_str(attr)? {
296 "true" | "1" => Some(true),
297 "false" | "0" => Some(false),
298 _ => None,
299 }
300 }
301
302 pub fn get_refs(&self, attr: &str) -> &[String] {
308 match self.fields.get(attr) {
309 Some(FieldValue::Resource(s)) => std::slice::from_ref(s),
310 Some(FieldValue::ResourceList(v)) => v.as_slice(),
311 _ => &[],
312 }
313 }
314
315 pub fn get_ref(&self, attr: &str) -> Option<&str> {
317 self.get_refs(attr).first().map(String::as_str)
318 }
319
320 pub fn add_field(&mut self, key: &'static str, val: FieldValue) {
323 match &val {
324 FieldValue::Resource(new_ref) => match self.fields.get_mut(key) {
325 Some(FieldValue::ResourceList(list)) => {
326 list.push(new_ref.clone());
327 self.duplicate_fields.insert(key);
328 return;
329 }
330 Some(existing @ FieldValue::Resource(_)) => {
331 let FieldValue::Resource(old) = std::mem::replace(existing, FieldValue::ResourceList(Vec::new())) else {
332 unreachable!()
333 };
334 *existing = FieldValue::ResourceList(vec![old, new_ref.clone()]);
335 self.duplicate_fields.insert(key);
336 return;
337 }
338 _ => {}
339 },
340 FieldValue::Text(new_text) => match self.fields.get_mut(key) {
341 Some(FieldValue::TextList(list)) => {
342 list.push(new_text.clone());
343 self.duplicate_fields.insert(key);
344 return;
345 }
346 Some(existing @ FieldValue::Text(_)) => {
347 let FieldValue::Text(old) = std::mem::replace(existing, FieldValue::TextList(Vec::new())) else {
348 unreachable!()
349 };
350 *existing = FieldValue::TextList(vec![old, new_text.clone()]);
351 self.duplicate_fields.insert(key);
352 return;
353 }
354 _ => {}
355 },
356 _ => {
357 if self.fields.contains_key(key) {
358 self.duplicate_fields.insert(key);
359 }
360 }
361 }
362 self.fields.insert(key, val);
363 }
364
365 pub fn merge_from(&mut self, other: &Element) {
368 self.described &= other.described;
369 for (k, v) in &other.fields {
370 match v {
371 FieldValue::ResourceList(new_list) => match self.fields.get_mut(k) {
372 Some(FieldValue::ResourceList(existing)) => existing.extend(new_list.iter().cloned()),
373 _ => {
374 self.fields.insert(*k, v.clone());
375 }
376 },
377 _ => {
378 self.fields.insert(*k, v.clone());
379 if let Some(l) = self.langs.as_mut() {
381 l.retain(|(key, _)| key != k);
382 }
383 for lang in other.langs(k) {
384 self.add_lang(k, lang);
385 }
386 }
387 }
388 }
389 }
390
391 pub fn to_json_value(&self) -> serde_json::Value {
395 let mut map = serde_json::Map::with_capacity(self.fields.len() + 2);
396 map.insert("id".to_string(), serde_json::Value::String(self.mrid.clone()));
397 for (k, v) in &self.fields {
398 map.insert(k.to_string(), field_to_json(v));
399 }
400 if let Some(langs) = &self.langs {
401 let mut tags = serde_json::Map::new();
402 for (key, lang) in langs.iter() {
403 if let serde_json::Value::Array(list) =
404 tags.entry(key.to_string()).or_insert_with(|| serde_json::Value::Array(Vec::new()))
405 {
406 list.push(serde_json::Value::String(lang.to_string()));
407 }
408 }
409 map.insert(LANG_KEY.to_string(), serde_json::Value::Object(tags));
410 }
411 serde_json::Value::Object(map)
412 }
413
414 pub fn from_json(
418 class: &'static ClassDef,
419 reg: &TypeRegistry,
420 value: &serde_json::Value,
421 ) -> Result<Self, String> {
422 let obj = value.as_object().ok_or("element is not a JSON object")?;
423 let mrid = obj.get("id").and_then(|v| v.as_str()).ok_or("element has no \"id\"")?.to_string();
424 let mut fields = FieldMap::default();
425 for (k, v) in obj {
426 if k == "id" || k == "_type" || k == LANG_KEY {
427 continue;
428 }
429 let is_ref = reg.attr(class, k).is_some_and(|a| a.kind != AttrKind::Literal);
430 let value = match v {
431 serde_json::Value::String(s) if is_ref => FieldValue::Resource(s.clone()),
432 serde_json::Value::String(s) => FieldValue::Text(s.clone()),
433 serde_json::Value::Array(items) => {
434 let items: Vec<String> = items
435 .iter()
436 .map(|i| i.as_str().map(str::to_string).ok_or(format!("{k}: list item is not a string")))
437 .collect::<Result<_, _>>()?;
438 if is_ref { FieldValue::ResourceList(items) } else { FieldValue::TextList(items) }
439 }
440 other => return Err(format!("{k}: expected a string or an array, got {other}")),
441 };
442 fields.insert(reg.field_key(class, k), value);
443 }
444 let mut element = Self::with_fields(class, mrid, fields);
445 if let Some(tags) = obj.get(LANG_KEY).and_then(|t| t.as_object()) {
446 for (k, list) in tags {
447 let key = reg.field_key(class, k);
448 for lang in list.as_array().into_iter().flatten().filter_map(|l| l.as_str()) {
449 element.add_lang(key, lang);
450 }
451 }
452 }
453 Ok(element)
454 }
455}
456
457pub const LANG_KEY: &str = "_lang";
459
460fn field_to_json(v: &FieldValue) -> serde_json::Value {
461 match v {
462 FieldValue::Text(s) | FieldValue::Resource(s) => serde_json::Value::String(s.clone()),
463 FieldValue::TextList(v) | FieldValue::ResourceList(v) => {
464 serde_json::Value::Array(v.iter().cloned().map(serde_json::Value::String).collect())
465 }
466 }
467}
468
469#[derive(Debug, Clone, Copy, PartialEq, Eq)]
473pub enum AttrKind {
474 Literal,
476 Association,
478 Enum,
480}
481
482#[derive(Debug)]
484pub struct AttrDef {
485 pub id: &'static str,
487 pub ns: &'static str,
489 pub kind: AttrKind,
490 pub range: &'static str,
493 pub is_list: bool,
494 pub used: bool,
498 pub xsd: &'static str,
501 pub value_ns: &'static str,
504 pub origins: &'static [&'static str],
506 pub comment: &'static str,
509}
510
511#[derive(Debug)]
515pub struct Schema {
516 pub classes: &'static [ClassDef],
517 pub profiles: &'static [(&'static str, &'static str)],
520 pub namespaces: &'static [(&'static str, &'static str)],
522 pub enums: &'static [EnumDef],
524}
525
526impl Schema {
527 pub fn enumeration(&self, name: &str) -> Option<&'static EnumDef> {
529 let enums: &'static [EnumDef] = self.enums;
530 enums.binary_search_by_key(&name, |e| e.local).ok().map(|i| &enums[i])
531 }
532
533 pub fn enum_value(&self, id: &str) -> Option<(&'static EnumDef, &'static EnumValueDef)> {
536 let e = self.enumeration(id.split_once('.')?.0)?;
537 Some((e, e.values.iter().find(|v| v.id == id)?))
538 }
539}
540
541#[derive(Debug)]
543pub struct EnumDef {
544 pub ns: &'static str,
545 pub local: &'static str,
546 pub comment: &'static str,
548 pub values: &'static [EnumValueDef],
550}
551
552#[derive(Debug)]
554pub struct EnumValueDef {
555 pub id: &'static str,
557 pub comment: &'static str,
559}
560
561#[derive(Debug)]
563pub struct ClassDef {
564 pub ns: &'static str,
565 pub local: &'static str,
566 pub qualified: &'static str,
568 pub super_class: Option<usize>,
570 pub concrete: bool,
571 pub attrs: &'static [AttrDef],
575 pub origins: &'static [&'static str],
576 pub comment: &'static str,
579}
580
581impl ClassDef {
582 pub fn attr(&self, id: &str) -> Option<&'static AttrDef> {
584 self.attrs.iter().find(|a| a.id == id)
585 }
586}
587
588#[derive(Default)]
596pub struct TypeRegistry {
597 by_ns: FastMap<&'static str, FastMap<&'static str, &'static ClassDef>>,
598 bare: FastMap<&'static str, &'static ClassDef>,
599 by_type_name: FastMap<&'static str, &'static ClassDef>,
600 declared: FastMap<&'static str, FastMap<&'static str, &'static AttrDef>>,
603 chain: FastMap<&'static str, Vec<&'static ClassDef>>,
605 families: Vec<FamilyTable>,
607}
608
609struct FamilyTable {
612 id: &'static str,
613 prefix: &'static str,
614 schema: &'static Schema,
615 attrs: FastMap<&'static str, &'static AttrDef>,
618}
619
620impl TypeRegistry {
621 pub fn new() -> Self {
622 Self::default()
623 }
624
625 pub fn add_family(&mut self, id: &'static str, schema: &'static Schema, bare_fallback: bool) {
629 let classes = schema.classes;
630 let prefix = classes
631 .first()
632 .and_then(|c| c.qualified.strip_suffix(c.local))
633 .unwrap_or("");
634 let mut by_id = FastMap::default();
635 for class in classes {
636 for a in class.attrs {
637 by_id.entry(a.id).or_insert(a);
638 }
639 }
640 self.families.push(FamilyTable { id, prefix, schema, attrs: by_id });
641 for class in classes {
642 self.by_ns.entry(class.ns).or_default().insert(class.local, class);
643 self.by_type_name.insert(class.qualified, class);
644 if bare_fallback {
645 self.bare.insert(class.local, class);
646 }
647 let mut attrs = FastMap::default();
648 let mut chain = Vec::new();
649 let mut next = Some(class);
650 while let Some(c) = next {
651 for a in c.attrs {
652 attrs.entry(a.id).or_insert(a);
653 }
654 chain.push(c);
655 next = c.super_class.map(|i| &classes[i]);
656 }
657 chain.reverse();
658 self.declared.insert(class.qualified, attrs);
659 self.chain.insert(class.qualified, chain);
660 }
661 }
662
663 pub fn ns_table(&self, ns: &str) -> Option<&FastMap<&'static str, &'static ClassDef>> {
665 self.by_ns.get(ns)
666 }
667
668 pub fn bare(&self) -> &FastMap<&'static str, &'static ClassDef> {
669 &self.bare
670 }
671
672 pub fn by_type_name(&self, name: &str) -> Option<&'static ClassDef> {
673 self.by_type_name.get(name).copied()
674 }
675
676 pub fn attr(&self, class: &ClassDef, id: &str) -> Option<&'static AttrDef> {
678 self.declared.get(class.qualified)?.get(id).copied()
679 }
680
681 pub fn declared(&self, class: &ClassDef) -> Option<&FastMap<&'static str, &'static AttrDef>> {
684 self.declared.get(class.qualified)
685 }
686
687 pub fn field_key(&self, class: &ClassDef, id: &str) -> &'static str {
689 self.attr(class, id).map_or_else(|| intern(id), |a| a.id)
690 }
691
692 pub fn chain(&self, class: &ClassDef) -> &[&'static ClassDef] {
694 self.chain.get(class.qualified).map_or(&[], Vec::as_slice)
695 }
696
697 pub fn concrete_descendants(&self, qualified: &str) -> Vec<&'static str> {
700 let mut out: Vec<&'static str> = self
701 .chain
702 .iter()
703 .filter(|(_, chain)| chain.iter().any(|c| c.qualified == qualified))
704 .filter_map(|(q, _)| self.by_type_name.get(q).filter(|c| c.concrete).map(|c| c.qualified))
705 .collect();
706 out.sort_unstable();
707 out
708 }
709
710 pub fn attrs(&self, class: &ClassDef) -> impl Iterator<Item = &'static AttrDef> + '_ {
712 self.declared.get(class.qualified).into_iter().flat_map(|m| m.values().copied())
713 }
714
715 pub fn schema(&self, family: &str) -> Option<&'static Schema> {
717 self.family(family).map(|f| f.schema)
718 }
719
720 fn family(&self, id: &str) -> Option<&FamilyTable> {
721 self.families.iter().find(|f| f.id == id)
722 }
723
724 pub fn enum_value(&self, class: &ClassDef, id: &str) -> Option<(&'static EnumDef, &'static EnumValueDef)> {
727 self.schema(self.family_of(class.qualified)?)?.enum_value(id)
728 }
729
730 pub fn family_of(&self, type_name: &str) -> Option<&'static str> {
732 self.families
734 .iter()
735 .filter(|f| type_name.starts_with(f.prefix))
736 .max_by_key(|f| f.prefix.len())
737 .map(|f| f.id)
738 }
739
740 pub fn family_attr(&self, family: &str, id: &str) -> Option<&'static AttrDef> {
743 self.family(family)?.attrs.get(id).copied()
744 }
745
746 pub fn attr_of(&self, class: &ClassDef, key: &str) -> Option<&'static AttrDef> {
749 self.attr(class, key)
750 .or_else(|| self.family_attr(self.family_of(class.qualified)?, key))
751 }
752}