use std::{collections::HashMap, fmt::Display};
use crate::{
builtins::{ARR_TYPEID, BUILT_INS_NAMES, MAP_TYPEID},
errors::ImportError,
};
#[derive(Debug)]
pub struct DeclFile {
pub name: String,
pub id: u64,
#[doc(hidden)]
pub items: HashMap<u16, DeclItem>,
pub(crate) items_by_name: HashMap<String, u16>,
}
#[derive(Debug)]
pub struct TypeId {
pub ns: u64,
pub id: u16,
pub variant: u16,
pub item: Option<Box<TypeId>>,
pub metadata: Option<Box<Vec<(String, String)>>>,
}
#[derive(Debug)]
pub struct Field {
pub name: String,
pub tag: u32,
pub typeid: TypeId,
pub is_optional: bool,
}
#[derive(Default, Debug)]
pub struct StructDef {
pub fields: HashMap<u32, Field>,
pub fields_by_name: HashMap<String, u32>,
pub required_fields: u32,
}
#[derive(Debug)]
pub struct EnumVariant {
pub name: String,
pub tag: u32,
pub def: Option<StructDef>,
}
#[derive(Debug)]
pub enum DeclItem {
Struct {
name: String,
typeid: u16,
def: StructDef,
},
Enum {
name: String,
typeid: u16,
variants: Vec<EnumVariant>,
variants_by_id: HashMap<u32, usize>,
variants_by_name: HashMap<String, usize>,
},
}
pub trait DeclProvider {
fn get(&self, id: u64) -> &DeclFile;
fn load<'a>(&'a self, name: &str) -> Result<&'a DeclFile, ImportError>;
}
impl DeclFile {
pub(crate) fn new(name: String) -> Self {
static mut DECLARE_ID_COUNTER: u64 = 0;
let id = unsafe {
DECLARE_ID_COUNTER += 1;
DECLARE_ID_COUNTER
};
DeclFile { name, id, items: HashMap::new(), items_by_name: HashMap::new() }
}
pub(crate) fn add_item(&mut self, item: DeclItem) {
self.items_by_name.insert(item.name().to_string(), item.typeid());
self.items.insert(item.typeid(), item);
}
#[doc(hidden)]
pub fn get_by_name(&self, name: &str) -> Option<&DeclItem> {
self.items.get(self.items_by_name.get(name)?)
}
#[doc(hidden)]
pub fn get_by_id(&self, id: u16) -> Option<&DeclItem> {
self.items.get(&id)
}
}
impl PartialEq<DeclFile> for DeclFile {
fn eq(&self, other: &DeclFile) -> bool {
self.id == other.id
}
}
impl DeclItem {
pub fn name(&self) -> &str {
match self {
Self::Struct { name, .. } => name,
Self::Enum { name, .. } => name,
}
}
pub fn typeid(&self) -> u16 {
match self {
Self::Struct { typeid, .. } => *typeid,
Self::Enum { typeid, .. } => *typeid,
}
}
pub fn new_enum(name: String, typeid: u16) -> Self {
Self::Enum {
name,
typeid,
variants: Vec::new(),
variants_by_id: HashMap::new(),
variants_by_name: HashMap::new(),
}
}
pub fn add_variant(&mut self, variant: EnumVariant) {
match self {
Self::Enum { variants, variants_by_id, variants_by_name, .. } => {
let ind = variants.len();
variants_by_name.insert(variant.name.to_string(), ind);
variants_by_id.insert(variant.tag, ind);
variants.push(variant);
}
_ => panic!("why"),
}
}
pub fn get_variant_by_name(&self, name: &str) -> Option<&EnumVariant> {
match self {
Self::Enum { variants, variants_by_name, .. } => {
variants.get(*variants_by_name.get(name)?)
}
_ => None,
}
}
pub fn get_variant_by_id(&self, tag: u32) -> Option<&EnumVariant> {
match self {
Self::Enum { variants, variants_by_id, .. } => variants.get(*variants_by_id.get(&tag)?),
_ => None,
}
}
}
impl StructDef {
pub fn add_field(&mut self, field: Field) {
self.required_fields += !field.is_optional as u32;
self.fields_by_name.insert(field.name.to_string(), field.tag);
self.fields.insert(field.tag, field);
}
pub fn get_field_by_name(&self, name: &str) -> Option<&Field> {
self.fields.get(self.fields_by_name.get(name)?)
}
pub fn get_field_by_id(&self, tag: u32) -> Option<&Field> {
self.fields.get(&tag)
}
}
impl TypeId {
pub fn new(ns: u64, id: u16, metadata: Option<Vec<(String, String)>>) -> Self {
Self { ns, id, variant: 0, item: None, metadata: metadata.map(Box::new) }
}
pub fn with_variant(
ns: u64, id: u16, variant: u16, item: Option<TypeId>,
metadata: Option<Vec<(String, String)>>,
) -> Self {
Self { ns, id, variant, item: item.map(Box::new), metadata: metadata.map(Box::new) }
}
pub const ANY: Self = Self { ns: 0, id: 1, variant: 0, item: None, metadata: None };
pub fn item(&self) -> &Self {
self.item.as_ref().unwrap()
}
pub fn is_any(&self) -> bool {
self.ns == 0 && self.id == 1
}
pub fn is_builtin(&self) -> bool {
self.ns == 0
}
pub fn arr(item: TypeId, metadata: Option<Vec<(String, String)>>) -> Self {
Self::with_variant(0, ARR_TYPEID, 0, Some(item), metadata)
}
pub fn map(key: u16, value: TypeId, metadata: Option<Vec<(String, String)>>) -> Self {
Self::with_variant(0, MAP_TYPEID, key, Some(value), metadata)
}
pub fn name<'a>(&'a self, provider: &'a dyn DeclProvider) -> TypeIdFormatter<'a> {
TypeIdFormatter(self, provider)
}
}
pub struct TypeIdFormatter<'a>(&'a TypeId, &'a dyn DeclProvider);
impl Display for TypeIdFormatter<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(typeid, provider) = *self;
if typeid.is_builtin() {
match typeid.id {
ARR_TYPEID => write!(f, "arr<{}>", typeid.item().name(provider)),
MAP_TYPEID => {
let key = BUILT_INS_NAMES[&typeid.variant];
write!(f, "map<{key}, {}>", typeid.item().name(provider))
}
id => write!(f, "{}", BUILT_INS_NAMES[&id]),
}
} else {
let file = provider.get(typeid.ns);
let item = file.get_by_id(typeid.id).unwrap();
write!(f, "`{}`.{}", file.name, item.name())
}
}
}
impl PartialEq for TypeId {
fn eq(&self, other: &Self) -> bool {
if self.is_any() {
return true;
}
self.ns == other.ns
&& self.id == other.id
&& self.variant == other.variant
&& self.item == other.item
}
}
pub fn resolve_typeid<'a>(typeid: &TypeId, provider: &'a dyn DeclProvider) -> &'a DeclItem {
provider.get(typeid.ns).get_by_id(typeid.id).unwrap()
}
#[derive(Debug, Default)]
pub struct VoidProvider {}
impl DeclProvider for VoidProvider {
fn get(&self, _id: u64) -> &DeclFile {
panic!("how did we get here")
}
fn load<'a>(&'a self, _name: &str) -> Result<&'a DeclFile, ImportError> {
Err(ImportError::NotFound)
}
}
#[derive(Debug, Clone)]
pub struct FixedSetProviderRef<'a> {
files: HashMap<u64, &'a DeclFile>,
files_by_name: HashMap<&'a str, &'a DeclFile>,
}
impl<'a> FixedSetProviderRef<'a> {
pub fn new(declarations: &[&'a DeclFile]) -> Self {
let mut files = HashMap::new();
let mut files_by_name = HashMap::new();
for &file in declarations {
files.insert(file.id, file);
files_by_name.insert(file.name.as_str(), file);
}
Self { files, files_by_name }
}
}
impl DeclProvider for FixedSetProviderRef<'_> {
fn get(&self, id: u64) -> &DeclFile {
self.files.get(&id).unwrap()
}
fn load<'a>(&'a self, name: &str) -> Result<&'a DeclFile, ImportError> {
self.files_by_name.get(name).copied().ok_or(ImportError::NotFound)
}
}
#[derive(Debug)]
pub struct FixedSetProvider {
files: HashMap<u64, DeclFile>,
files_by_name: HashMap<String, u64>,
}
impl FixedSetProvider {
pub fn new(declarations: Vec<DeclFile>) -> Self {
let mut files = HashMap::new();
let mut files_by_name = HashMap::new();
for file in declarations.into_iter() {
files_by_name.insert(file.name.clone(), file.id);
files.insert(file.id, file);
}
Self { files, files_by_name }
}
}
impl DeclProvider for FixedSetProvider {
fn get(&self, id: u64) -> &DeclFile {
self.files.get(&id).unwrap()
}
fn load<'a>(&'a self, name: &str) -> Result<&'a DeclFile, ImportError> {
let Some(file) = self.files_by_name.get(name) else {
return Err(ImportError::NotFound);
};
self.files.get(file).ok_or(ImportError::NotFound)
}
}