use std::{
any::{Any, TypeId},
borrow::Cow,
fmt, hash,
sync::Arc,
};
use crate::{
Types,
datatype::{Generic, Map, NamedDataType, Primitive},
};
use super::DataType;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Reference {
Named(NamedReference),
Opaque(OpaqueReference),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct NamedReference {
pub(crate) id: NamedId,
pub inner: NamedReferenceType,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum NamedReferenceType {
Recursive(RecursiveInlineType),
#[non_exhaustive]
Inline {
dt: Box<DataType>,
},
#[non_exhaustive]
Reference {
generics: Vec<(Generic, DataType)>,
},
}
#[derive(Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct RecursiveInlineType {
cycle: Vec<RecursiveInlineFrame>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct RecursiveInlineFrame {
type_name: Cow<'static, str>,
generics: Vec<Cow<'static, str>>,
}
impl RecursiveInlineType {
pub(crate) fn from_cycle(cycle: Vec<RecursiveInlineFrame>) -> Self {
Self { cycle }
}
fn last_frame(&self) -> Option<&RecursiveInlineFrame> {
self.cycle.last()
}
}
impl fmt::Debug for RecursiveInlineType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (idx, ty) in self.cycle.iter().enumerate() {
if idx != 0 {
f.write_str(" -> ")?;
}
write!(f, "{ty:?}")?;
}
Ok(())
}
}
impl RecursiveInlineFrame {
pub(crate) fn new(
types: &Types,
ndt: &NamedDataType,
generics: &[(Generic, DataType)],
) -> Self {
Self::new_inner(types, named_type_path(ndt), generics)
}
pub(crate) fn from_type_path(
types: &Types,
type_name: Cow<'static, str>,
generics: &[(Generic, DataType)],
) -> Self {
Self::new_inner(types, type_name, generics)
}
fn new_inner(
types: &Types,
type_name: Cow<'static, str>,
generics: &[(Generic, DataType)],
) -> Self {
Self {
type_name,
generics: generics
.iter()
.map(|(_, dt)| render_recursive_inline_generic(types, dt))
.collect(),
}
}
}
impl fmt::Debug for RecursiveInlineFrame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.type_name)?;
if self.generics.is_empty() {
return Ok(());
}
f.write_str("<")?;
for (idx, generic) in self.generics.iter().enumerate() {
if idx != 0 {
f.write_str(", ")?;
}
f.write_str(generic)?;
}
f.write_str(">")
}
}
fn named_type_path(ndt: &NamedDataType) -> Cow<'static, str> {
if ndt.module_path.is_empty() {
ndt.name.clone()
} else {
Cow::Owned(format!("{}::{}", ndt.module_path, ndt.name))
}
}
fn render_recursive_inline_generic(types: &Types, dt: &DataType) -> Cow<'static, str> {
match dt {
DataType::Primitive(primitive) => Cow::Borrowed(primitive_type_name(primitive)),
DataType::Generic(generic) => generic.name().clone(),
DataType::Reference(Reference::Named(reference)) => {
render_recursive_inline_named(types, reference)
}
DataType::Reference(Reference::Opaque(reference)) => Cow::Borrowed(reference.type_name()),
DataType::List(list) => {
let ty = render_recursive_inline_generic(types, &list.ty);
match list.length {
Some(length) => Cow::Owned(format!("[{ty}; {length}]")),
None => Cow::Owned(format!("Vec<{ty}>")),
}
}
DataType::Map(map) => render_recursive_inline_map(types, map),
DataType::Nullable(inner) => Cow::Owned(format!(
"Option<{}>",
render_recursive_inline_generic(types, inner)
)),
DataType::Tuple(tuple) => Cow::Owned(format!(
"({})",
tuple
.elements
.iter()
.map(|dt| render_recursive_inline_generic(types, dt).into_owned())
.collect::<Vec<_>>()
.join(", ")
)),
dt => Cow::Owned(format!("{dt:?}")),
}
}
fn render_recursive_inline_named(types: &Types, reference: &NamedReference) -> Cow<'static, str> {
if let NamedReferenceType::Recursive(cycle) = &reference.inner
&& let Some(ty) = cycle.last_frame()
{
return Cow::Owned(format!("{ty:?}"));
}
let Some(ndt) = types.get(reference) else {
return Cow::Owned(format!("{reference:?}"));
};
let mut out = named_type_path(ndt).into_owned();
if let NamedReferenceType::Reference { generics } = &reference.inner
&& !generics.is_empty()
{
out.push('<');
out.push_str(
&generics
.iter()
.map(|(_, dt)| render_recursive_inline_generic(types, dt).into_owned())
.collect::<Vec<_>>()
.join(", "),
);
out.push('>');
}
Cow::Owned(out)
}
fn render_recursive_inline_map(types: &Types, map: &Map) -> Cow<'static, str> {
Cow::Owned(format!(
"HashMap<{}, {}>",
render_recursive_inline_generic(types, map.key_ty()),
render_recursive_inline_generic(types, map.value_ty())
))
}
fn primitive_type_name(primitive: &Primitive) -> &'static str {
match primitive {
Primitive::i8 => "i8",
Primitive::i16 => "i16",
Primitive::i32 => "i32",
Primitive::i64 => "i64",
Primitive::i128 => "i128",
Primitive::u8 => "u8",
Primitive::u16 => "u16",
Primitive::u32 => "u32",
Primitive::u64 => "u64",
Primitive::u128 => "u128",
Primitive::isize => "isize",
Primitive::usize => "usize",
Primitive::f16 => "f16",
Primitive::f32 => "f32",
Primitive::f64 => "f64",
Primitive::f128 => "f128",
Primitive::bool => "bool",
Primitive::str => "String",
Primitive::char => "char",
}
}
#[derive(Clone)]
pub struct OpaqueReference(Arc<dyn DynOpaqueReference>);
trait DynOpaqueReference: Any + Send + Sync {
fn type_name(&self) -> &'static str;
fn hash(&self, hasher: &mut dyn hash::Hasher);
fn eq(&self, other: &dyn Any) -> bool;
fn as_any(&self) -> &dyn Any;
}
#[derive(Debug)]
struct OpaqueReferenceInner<T>(T);
impl<T: hash::Hash + Eq + Send + Sync + 'static> DynOpaqueReference for OpaqueReferenceInner<T> {
fn type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
fn hash(&self, mut hasher: &mut dyn hash::Hasher) {
self.0.hash(&mut hasher)
}
fn eq(&self, other: &dyn Any) -> bool {
other
.downcast_ref::<T>()
.map(|other| self.0 == *other)
.unwrap_or_default()
}
fn as_any(&self) -> &dyn Any {
&self.0
}
}
impl fmt::Debug for OpaqueReference {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OpaqueReference")
.field(&self.0.type_name())
.finish()
}
}
impl PartialEq for OpaqueReference {
fn eq(&self, other: &Self) -> bool {
self.0.eq(other.0.as_any())
}
}
impl Eq for OpaqueReference {}
impl hash::Hash for OpaqueReference {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl OpaqueReference {
pub fn type_name(&self) -> &'static str {
self.0.type_name()
}
pub fn type_id(&self) -> TypeId {
self.0.as_any().type_id()
}
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.0.as_any().downcast_ref::<T>()
}
}
impl Reference {
pub fn opaque<T: hash::Hash + Eq + Send + Sync + 'static>(state: T) -> Self {
Self::Opaque(OpaqueReference(Arc::new(OpaqueReferenceInner(state))))
}
pub fn ty_eq(&self, other: &Reference) -> bool {
match (self, other) {
(Reference::Named(a), Reference::Named(b)) => a.id == b.id,
(Reference::Opaque(a), Reference::Opaque(b)) => *a == *b,
_ => false,
}
}
}
impl From<Reference> for DataType {
fn from(r: Reference) -> Self {
Self::Reference(r)
}
}
#[derive(Clone)]
pub(crate) enum NamedId {
Static(&'static str),
Dynamic(Arc<()>),
}
impl PartialEq for NamedId {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(NamedId::Static(a), NamedId::Static(b)) => a == b,
(NamedId::Dynamic(a), NamedId::Dynamic(b)) => Arc::ptr_eq(a, b),
_ => false,
}
}
}
impl Eq for NamedId {}
impl hash::Hash for NamedId {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
match self {
NamedId::Static(s) => s.hash(state),
NamedId::Dynamic(p) => std::ptr::hash(Arc::as_ptr(p), state),
}
}
}
impl fmt::Debug for NamedId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NamedId::Static(s) => write!(f, "s:{}", s),
NamedId::Dynamic(p) => write!(f, "d{:p}", Arc::as_ptr(p)),
}
}
}