use std::collections::{HashMap, HashSet};
use std::hash::{BuildHasherDefault, Hasher};
#[derive(Default, Clone, Copy)]
pub struct FastHasher {
hash: u64,
}
const SEED: u64 = 0xf135_7aea_2e62_a9c5;
impl FastHasher {
#[inline]
fn add(&mut self, word: u64) {
self.hash = self.hash.wrapping_add(word).wrapping_mul(SEED);
}
}
impl Hasher for FastHasher {
#[inline]
fn write(&mut self, bytes: &[u8]) {
let mut chunks = bytes.chunks_exact(8);
for c in &mut chunks {
self.add(u64::from_le_bytes(c.try_into().expect("chunk of 8")));
}
let rest = chunks.remainder();
if !rest.is_empty() {
let mut buf = [0u8; 8];
buf[..rest.len()].copy_from_slice(rest);
self.add(u64::from_le_bytes(buf));
}
}
#[inline]
fn write_u8(&mut self, i: u8) {
self.add(i as u64);
}
#[inline]
fn write_u32(&mut self, i: u32) {
self.add(i as u64);
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.add(i);
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.add(i as u64);
}
#[inline]
fn finish(&self) -> u64 {
self.hash.rotate_left(26)
}
}
pub type FastBuildHasher = BuildHasherDefault<FastHasher>;
pub type FastMap<K, V> = HashMap<K, V, FastBuildHasher>;
pub type FastSet<T> = HashSet<T, FastBuildHasher>;
pub type FieldMap = FastMap<&'static str, FieldValue>;
pub fn intern(key: &str) -> &'static str {
use std::cell::RefCell;
use std::sync::{Mutex, OnceLock};
thread_local! {
static LOCAL: RefCell<FastSet<&'static str>> = RefCell::new(FastSet::default());
}
static GLOBAL: OnceLock<Mutex<FastSet<&'static str>>> = OnceLock::new();
if let Some(k) = LOCAL.with(|l| l.borrow().get(key).copied()) {
return k;
}
let k = {
let mut global = GLOBAL.get_or_init(Default::default).lock().unwrap();
match global.get(key) {
Some(k) => *k,
None => {
let k: &'static str = Box::leak(key.to_owned().into_boxed_str());
global.insert(k);
k
}
}
};
LOCAL.with(|l| l.borrow_mut().insert(k));
k
}
pub fn foreign_class(ns: &str, local: &str) -> &'static ClassDef {
use std::sync::{Mutex, OnceLock};
static CLASSES: OnceLock<Mutex<FastMap<String, &'static ClassDef>>> = OnceLock::new();
let iri = format!("{ns}{local}");
let mut classes = CLASSES.get_or_init(Default::default).lock().unwrap();
if let Some(c) = classes.get(&iri) {
return c;
}
let qualified: &'static str = Box::leak(iri.clone().into_boxed_str());
let class: &'static ClassDef = Box::leak(Box::new(ClassDef {
ns: intern(ns),
local: &qualified[ns.len()..],
qualified,
super_class: None,
concrete: true,
attrs: &[],
origins: &[],
comment: "",
}));
classes.insert(iri, class);
class
}
#[derive(Debug, Clone)]
pub enum FieldValue {
Text(String),
TextList(Vec<String>),
Resource(String),
ResourceList(Vec<String>),
}
#[derive(Debug, Clone)]
pub struct Element {
class: &'static ClassDef,
mrid: String,
fields: FieldMap,
duplicate_fields: FastSet<&'static str>,
langs: Option<Box<Langs>>,
also: Option<Box<Types>>,
described: bool,
}
type Langs = Vec<(&'static str, Box<str>)>;
type Types = Vec<&'static ClassDef>;
impl Element {
pub fn new(class: &'static ClassDef, mrid: String) -> Self {
Self { class, mrid, fields: FieldMap::default(), duplicate_fields: FastSet::default(), langs: None, also: None, described: false }
}
pub fn with_fields(class: &'static ClassDef, mrid: String, fields: FieldMap) -> Self {
Self { class, mrid, fields, duplicate_fields: FastSet::default(), langs: None, also: None, described: false }
}
pub fn class(&self) -> &'static ClassDef {
self.class
}
pub fn types(&self) -> impl Iterator<Item = &'static ClassDef> + '_ {
std::iter::once(self.class).chain(self.also.iter().flat_map(|v| v.iter().copied()))
}
pub(crate) fn add_type(&mut self, other: &'static ClassDef, reg: &TypeRegistry) -> bool {
if self.types().any(|c| c.qualified == other.qualified) {
return false;
}
let promote = reg.chain(other).iter().any(|c| c.qualified == self.class.qualified);
let kept = if promote { std::mem::replace(&mut self.class, other) } else { other };
self.also.get_or_insert_with(Default::default).push(kept);
promote
}
pub fn defined(&self) -> bool {
!self.described
}
pub fn mark_described(&mut self) {
self.described = true;
}
pub fn mrid(&self) -> &str {
&self.mrid
}
pub fn type_name(&self) -> &'static str {
self.class.qualified
}
pub fn local_name(&self) -> &'static str {
self.class.local
}
pub fn type_ns(&self) -> &'static str {
self.class.ns
}
pub fn fields(&self) -> &FieldMap {
&self.fields
}
pub fn duplicate_fields(&self) -> &FastSet<&'static str> {
&self.duplicate_fields
}
pub fn langs(&self, key: &str) -> impl Iterator<Item = &str> {
self.langs.iter().flat_map(|l| l.iter()).filter(move |(k, _)| *k == key).map(|(_, l)| &**l)
}
pub fn add_lang(&mut self, key: &'static str, lang: &str) {
self.langs.get_or_insert_with(Default::default).push((key, lang.into()));
}
pub fn retype(&mut self, key: &str, reference: bool) {
if let Some(value) = self.fields.get_mut(key) {
*value = match (std::mem::replace(value, FieldValue::Text(String::new())), reference) {
(FieldValue::Text(s), true) => FieldValue::Resource(s),
(FieldValue::TextList(v), true) => FieldValue::ResourceList(v),
(FieldValue::Resource(s), false) => FieldValue::Text(s),
(FieldValue::ResourceList(v), false) => FieldValue::TextList(v),
(other, _) => other,
};
}
}
pub fn get(&self, attr: &str) -> Option<&FieldValue> {
self.fields.get(attr)
}
pub fn get_str(&self, attr: &str) -> Option<&str> {
match self.fields.get(attr) {
Some(FieldValue::Text(s)) => Some(s.as_str()),
Some(FieldValue::TextList(v)) => v.first().map(String::as_str),
_ => None,
}
}
pub fn get_f64(&self, attr: &str) -> Option<f64> {
self.get_str(attr)?.parse().ok()
}
pub fn get_bool(&self, attr: &str) -> Option<bool> {
match self.get_str(attr)? {
"true" | "1" => Some(true),
"false" | "0" => Some(false),
_ => None,
}
}
pub fn get_refs(&self, attr: &str) -> &[String] {
match self.fields.get(attr) {
Some(FieldValue::Resource(s)) => std::slice::from_ref(s),
Some(FieldValue::ResourceList(v)) => v.as_slice(),
_ => &[],
}
}
pub fn get_ref(&self, attr: &str) -> Option<&str> {
self.get_refs(attr).first().map(String::as_str)
}
pub fn add_field(&mut self, key: &'static str, val: FieldValue) {
match &val {
FieldValue::Resource(new_ref) => match self.fields.get_mut(key) {
Some(FieldValue::ResourceList(list)) => {
list.push(new_ref.clone());
self.duplicate_fields.insert(key);
return;
}
Some(existing @ FieldValue::Resource(_)) => {
let FieldValue::Resource(old) = std::mem::replace(existing, FieldValue::ResourceList(Vec::new())) else {
unreachable!()
};
*existing = FieldValue::ResourceList(vec![old, new_ref.clone()]);
self.duplicate_fields.insert(key);
return;
}
_ => {}
},
FieldValue::Text(new_text) => match self.fields.get_mut(key) {
Some(FieldValue::TextList(list)) => {
list.push(new_text.clone());
self.duplicate_fields.insert(key);
return;
}
Some(existing @ FieldValue::Text(_)) => {
let FieldValue::Text(old) = std::mem::replace(existing, FieldValue::TextList(Vec::new())) else {
unreachable!()
};
*existing = FieldValue::TextList(vec![old, new_text.clone()]);
self.duplicate_fields.insert(key);
return;
}
_ => {}
},
_ => {
if self.fields.contains_key(key) {
self.duplicate_fields.insert(key);
}
}
}
self.fields.insert(key, val);
}
pub fn merge_from(&mut self, other: &Element) {
self.described &= other.described;
for (k, v) in &other.fields {
match v {
FieldValue::ResourceList(new_list) => match self.fields.get_mut(k) {
Some(FieldValue::ResourceList(existing)) => existing.extend(new_list.iter().cloned()),
_ => {
self.fields.insert(*k, v.clone());
}
},
_ => {
self.fields.insert(*k, v.clone());
if let Some(l) = self.langs.as_mut() {
l.retain(|(key, _)| key != k);
}
for lang in other.langs(k) {
self.add_lang(k, lang);
}
}
}
}
}
pub fn to_json_value(&self) -> serde_json::Value {
let mut map = serde_json::Map::with_capacity(self.fields.len() + 2);
map.insert("id".to_string(), serde_json::Value::String(self.mrid.clone()));
for (k, v) in &self.fields {
map.insert(k.to_string(), field_to_json(v));
}
if let Some(langs) = &self.langs {
let mut tags = serde_json::Map::new();
for (key, lang) in langs.iter() {
if let serde_json::Value::Array(list) =
tags.entry(key.to_string()).or_insert_with(|| serde_json::Value::Array(Vec::new()))
{
list.push(serde_json::Value::String(lang.to_string()));
}
}
map.insert(LANG_KEY.to_string(), serde_json::Value::Object(tags));
}
serde_json::Value::Object(map)
}
pub fn from_json(
class: &'static ClassDef,
reg: &TypeRegistry,
value: &serde_json::Value,
) -> Result<Self, String> {
let obj = value.as_object().ok_or("element is not a JSON object")?;
let mrid = obj.get("id").and_then(|v| v.as_str()).ok_or("element has no \"id\"")?.to_string();
let mut fields = FieldMap::default();
for (k, v) in obj {
if k == "id" || k == "_type" || k == LANG_KEY {
continue;
}
let is_ref = reg.attr(class, k).is_some_and(|a| a.kind != AttrKind::Literal);
let value = match v {
serde_json::Value::String(s) if is_ref => FieldValue::Resource(s.clone()),
serde_json::Value::String(s) => FieldValue::Text(s.clone()),
serde_json::Value::Array(items) => {
let items: Vec<String> = items
.iter()
.map(|i| i.as_str().map(str::to_string).ok_or(format!("{k}: list item is not a string")))
.collect::<Result<_, _>>()?;
if is_ref { FieldValue::ResourceList(items) } else { FieldValue::TextList(items) }
}
other => return Err(format!("{k}: expected a string or an array, got {other}")),
};
fields.insert(reg.field_key(class, k), value);
}
let mut element = Self::with_fields(class, mrid, fields);
if let Some(tags) = obj.get(LANG_KEY).and_then(|t| t.as_object()) {
for (k, list) in tags {
let key = reg.field_key(class, k);
for lang in list.as_array().into_iter().flatten().filter_map(|l| l.as_str()) {
element.add_lang(key, lang);
}
}
}
Ok(element)
}
}
pub const LANG_KEY: &str = "_lang";
fn field_to_json(v: &FieldValue) -> serde_json::Value {
match v {
FieldValue::Text(s) | FieldValue::Resource(s) => serde_json::Value::String(s.clone()),
FieldValue::TextList(v) | FieldValue::ResourceList(v) => {
serde_json::Value::Array(v.iter().cloned().map(serde_json::Value::String).collect())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttrKind {
Literal,
Association,
Enum,
}
#[derive(Debug)]
pub struct AttrDef {
pub id: &'static str,
pub ns: &'static str,
pub kind: AttrKind,
pub range: &'static str,
pub is_list: bool,
pub used: bool,
pub xsd: &'static str,
pub value_ns: &'static str,
pub origins: &'static [&'static str],
pub comment: &'static str,
}
#[derive(Debug)]
pub struct Schema {
pub classes: &'static [ClassDef],
pub profiles: &'static [(&'static str, &'static str)],
pub namespaces: &'static [(&'static str, &'static str)],
pub enums: &'static [EnumDef],
}
impl Schema {
pub fn enumeration(&self, name: &str) -> Option<&'static EnumDef> {
let enums: &'static [EnumDef] = self.enums;
enums.binary_search_by_key(&name, |e| e.local).ok().map(|i| &enums[i])
}
pub fn enum_value(&self, id: &str) -> Option<(&'static EnumDef, &'static EnumValueDef)> {
let e = self.enumeration(id.split_once('.')?.0)?;
Some((e, e.values.iter().find(|v| v.id == id)?))
}
}
#[derive(Debug)]
pub struct EnumDef {
pub ns: &'static str,
pub local: &'static str,
pub comment: &'static str,
pub values: &'static [EnumValueDef],
}
#[derive(Debug)]
pub struct EnumValueDef {
pub id: &'static str,
pub comment: &'static str,
}
#[derive(Debug)]
pub struct ClassDef {
pub ns: &'static str,
pub local: &'static str,
pub qualified: &'static str,
pub super_class: Option<usize>,
pub concrete: bool,
pub attrs: &'static [AttrDef],
pub origins: &'static [&'static str],
pub comment: &'static str,
}
impl ClassDef {
pub fn attr(&self, id: &str) -> Option<&'static AttrDef> {
self.attrs.iter().find(|a| a.id == id)
}
}
#[derive(Default)]
pub struct TypeRegistry {
by_ns: FastMap<&'static str, FastMap<&'static str, &'static ClassDef>>,
bare: FastMap<&'static str, &'static ClassDef>,
by_type_name: FastMap<&'static str, &'static ClassDef>,
declared: FastMap<&'static str, FastMap<&'static str, &'static AttrDef>>,
chain: FastMap<&'static str, Vec<&'static ClassDef>>,
families: Vec<FamilyTable>,
}
struct FamilyTable {
id: &'static str,
prefix: &'static str,
schema: &'static Schema,
attrs: FastMap<&'static str, &'static AttrDef>,
}
impl TypeRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn add_family(&mut self, id: &'static str, schema: &'static Schema, bare_fallback: bool) {
let classes = schema.classes;
let prefix = classes
.first()
.and_then(|c| c.qualified.strip_suffix(c.local))
.unwrap_or("");
let mut by_id = FastMap::default();
for class in classes {
for a in class.attrs {
by_id.entry(a.id).or_insert(a);
}
}
self.families.push(FamilyTable { id, prefix, schema, attrs: by_id });
for class in classes {
self.by_ns.entry(class.ns).or_default().insert(class.local, class);
self.by_type_name.insert(class.qualified, class);
if bare_fallback {
self.bare.insert(class.local, class);
}
let mut attrs = FastMap::default();
let mut chain = Vec::new();
let mut next = Some(class);
while let Some(c) = next {
for a in c.attrs {
attrs.entry(a.id).or_insert(a);
}
chain.push(c);
next = c.super_class.map(|i| &classes[i]);
}
chain.reverse();
self.declared.insert(class.qualified, attrs);
self.chain.insert(class.qualified, chain);
}
}
pub fn ns_table(&self, ns: &str) -> Option<&FastMap<&'static str, &'static ClassDef>> {
self.by_ns.get(ns)
}
pub fn bare(&self) -> &FastMap<&'static str, &'static ClassDef> {
&self.bare
}
pub fn by_type_name(&self, name: &str) -> Option<&'static ClassDef> {
self.by_type_name.get(name).copied()
}
pub fn attr(&self, class: &ClassDef, id: &str) -> Option<&'static AttrDef> {
self.declared.get(class.qualified)?.get(id).copied()
}
pub fn declared(&self, class: &ClassDef) -> Option<&FastMap<&'static str, &'static AttrDef>> {
self.declared.get(class.qualified)
}
pub fn field_key(&self, class: &ClassDef, id: &str) -> &'static str {
self.attr(class, id).map_or_else(|| intern(id), |a| a.id)
}
pub fn chain(&self, class: &ClassDef) -> &[&'static ClassDef] {
self.chain.get(class.qualified).map_or(&[], Vec::as_slice)
}
pub fn concrete_descendants(&self, qualified: &str) -> Vec<&'static str> {
let mut out: Vec<&'static str> = self
.chain
.iter()
.filter(|(_, chain)| chain.iter().any(|c| c.qualified == qualified))
.filter_map(|(q, _)| self.by_type_name.get(q).filter(|c| c.concrete).map(|c| c.qualified))
.collect();
out.sort_unstable();
out
}
pub fn attrs(&self, class: &ClassDef) -> impl Iterator<Item = &'static AttrDef> + '_ {
self.declared.get(class.qualified).into_iter().flat_map(|m| m.values().copied())
}
pub fn schema(&self, family: &str) -> Option<&'static Schema> {
self.family(family).map(|f| f.schema)
}
fn family(&self, id: &str) -> Option<&FamilyTable> {
self.families.iter().find(|f| f.id == id)
}
pub fn enum_value(&self, class: &ClassDef, id: &str) -> Option<(&'static EnumDef, &'static EnumValueDef)> {
self.schema(self.family_of(class.qualified)?)?.enum_value(id)
}
pub fn family_of(&self, type_name: &str) -> Option<&'static str> {
self.families
.iter()
.filter(|f| type_name.starts_with(f.prefix))
.max_by_key(|f| f.prefix.len())
.map(|f| f.id)
}
pub fn family_attr(&self, family: &str, id: &str) -> Option<&'static AttrDef> {
self.family(family)?.attrs.get(id).copied()
}
pub fn attr_of(&self, class: &ClassDef, key: &str) -> Option<&'static AttrDef> {
self.attr(class, key)
.or_else(|| self.family_attr(self.family_of(class.qualified)?, key))
}
}