use std::collections::{HashMap, HashSet};
use crate::schema::{
datum::{Datum, DatumDiscriminator},
entity::{Entity, EntityPart, EntityPartList},
DatabaseItemVisitor, Schema,
};
mod detail;
pub(crate) use detail::extract_signature_for;
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Debug, serde::Serialize, serde::Deserialize,
)]
pub enum PoDType {
Unit,
Bool,
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
Char,
String,
Bytes,
}
impl std::fmt::Display for PoDType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Unit => "unit",
Self::Bool => "bool",
Self::U8 => "u8",
Self::U16 => "u16",
Self::U32 => "u32",
Self::U64 => "u64",
Self::U128 => "u128",
Self::I8 => "i8",
Self::I16 => "i16",
Self::I32 => "i32",
Self::I64 => "i64",
Self::I128 => "i128",
Self::F32 => "f32",
Self::F64 => "f64",
Self::Char => "char",
Self::String => "string",
Self::Bytes => "bytes",
})
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, serde::Serialize, serde::Deserialize)]
pub enum TypeSignature {
PoD(PoDType),
Option(Box<Self>),
Enum(Vec<(String, Self)>),
Struct(Vec<(String, Self)>),
Tuple(Vec<Self>),
List(Box<Self>),
EntityID(String),
SqlField(String),
}
impl TypeSignature {
fn normalize(&mut self) {
match self {
Self::PoD(_) | Self::EntityID(_) | Self::SqlField(_) => (),
Self::Option(ts) => ts.normalize(),
Self::Enum(fs) | Self::Struct(fs) => {
fs.sort_by_cached_key(|v| v.0.clone());
for f in fs.iter_mut() {
f.1.normalize();
}
},
Self::Tuple(ts) => {
for t in ts.iter_mut() {
t.normalize();
}
},
Self::List(ts) => ts.normalize(),
}
}
}
impl std::fmt::Display for TypeSignature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::PoD(pod) => pod.fmt(f),
Self::Option(ts) => {
f.write_str("Opt<")?;
ts.fmt(f)?;
f.write_str(">")
},
Self::Enum(fs) => {
f.write_str("Enum[")?;
for (n, ts) in fs {
f.write_str(n)?;
f.write_str(": ")?;
ts.fmt(f)?;
f.write_str(",")?;
}
f.write_str("]")
},
Self::Struct(fs) => {
f.write_str("Struct[")?;
for (n, ts) in fs {
f.write_str(n)?;
f.write_str(": ")?;
ts.fmt(f)?;
f.write_str(",")?;
}
f.write_str("]")
},
Self::Tuple(ts) => {
f.write_str("(")?;
for t in ts {
t.fmt(f)?;
f.write_str(",")?;
}
f.write_str(")")
},
Self::List(ts) => {
f.write_str("[")?;
ts.fmt(f)?;
f.write_str("]")
},
Self::EntityID(ename) => {
f.write_str("ID[")?;
f.write_str(ename.as_str())?;
f.write_str("]")
},
Self::SqlField(ty) => {
f.write_str("Sql[")?;
f.write_str(ty.as_str())?;
f.write_str("]")
},
}
}
}
struct Collector {
encountered: HashSet<std::any::TypeId>,
collected: HashMap<String, TypeSignature>,
active_entity: Vec<&'static str>,
active_field: Vec<&'static str>,
}
impl Collector {
fn iterate_part_list<EPL: EntityPartList>(&mut self) {
if EPL::IS_EMPTY {
return;
}
self.active_field
.push(<EPL::ListHead as EntityPart>::part_name());
<<EPL::ListHead as EntityPart>::Datum as Datum>::accept_discriminator(self);
self.active_field.pop();
self.iterate_part_list::<EPL::ListTail>();
}
fn active_key(&self) -> String {
format!(
"{}.{}",
self.active_entity.last().expect("no active entity"),
self.active_field.last().expect("no active field")
)
}
}
impl DatumDiscriminator for Collector {
fn visit_entity_id<E: Entity>(&mut self) {
self.collected.insert(
self.active_key(),
TypeSignature::EntityID(E::entity_name().into()),
);
}
fn visit_bare_field<T: Datum>(&mut self) {
self.collected.insert(
self.active_key(),
TypeSignature::SqlField(T::sql_type().into()),
);
}
fn visit_relation_map<E: Entity>(&mut self) {
self.visit_idmap::<E>();
}
fn visit_relation_range<R: crate::Relation>(&mut self) {
self.visit_idmap::<R::Range>();
}
fn visit_relation_domain<R: crate::Relation>(&mut self) {
self.visit_idmap::<R::Domain>();
}
fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(&mut self) {
self.visit_value::<T>();
}
fn visit_value<T: serde::de::DeserializeOwned>(&mut self) {
log::trace!(
"collecting type signature for {}",
std::any::type_name::<T>()
);
self.collected
.insert(self.active_key(), extract_signature_for::<T>());
}
}
impl DatabaseItemVisitor for Collector {
fn visit_idmap<T: Entity>(&mut self)
where
Self: Sized,
{
let tid = std::any::TypeId::of::<T>();
if !self.encountered.contains(&tid) {
self.encountered.insert(tid);
log::trace!("starting processing of entity {}", T::entity_name());
self.active_entity.push(T::entity_name());
self.iterate_part_list::<T::Parts>();
self.active_entity.pop();
log::trace!("stopping processing of entity {}", T::entity_name());
}
}
fn visit_index<const U: bool, T: Entity, PL: EntityPartList<Entity = T>>(&mut self)
where
Self: Sized,
{
}
}
pub const TYPE_SIGNATURES_KEY: &str = "type_signatures";
pub type SignatureMap = HashMap<String, TypeSignature>;
pub type BorrowedSignatureMap<'l> = HashMap<&'l str, TypeSignature>;
pub fn collect_type_info<S: Schema>() -> HashMap<String, TypeSignature> {
log::trace!("collecting type info...");
let mut c = Collector {
encountered: Default::default(),
collected: Default::default(),
active_field: vec![],
active_entity: vec![],
};
S::accept_item_visitor(&mut c);
c.collected
}
#[derive(Debug)]
enum CollectorError {
TypeFeedback(TypeSignature),
VariantFeedback(&'static str, TypeSignature),
FieldFeedback(&'static str, TypeSignature),
InProgress,
}
impl serde::de::Error for CollectorError {
fn custom<T>(_msg: T) -> Self
where
T: std::fmt::Display,
{
unreachable!()
}
}
impl serde::de::StdError for CollectorError {}
impl std::fmt::Display for CollectorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
<Self as std::fmt::Debug>::fmt(self, f)
}
}