use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use super::descriptor::{MemberDescriptor, MemberId, TypeDescriptor, TypeKind};
use super::error::DynamicError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DynamicTypeMember {
pub(super) descriptor: MemberDescriptor,
pub(super) member_type: DynamicType,
}
impl DynamicTypeMember {
#[must_use]
pub fn descriptor(&self) -> &MemberDescriptor {
&self.descriptor
}
#[must_use]
pub fn name(&self) -> &str {
&self.descriptor.name
}
#[must_use]
pub fn id(&self) -> MemberId {
self.descriptor.id
}
#[must_use]
pub fn index(&self) -> u32 {
self.descriptor.index
}
#[must_use]
pub fn dynamic_type(&self) -> &DynamicType {
&self.member_type
}
#[must_use]
pub fn equals(&self, other: &Self) -> bool {
self.descriptor == other.descriptor && self.member_type.equals(&other.member_type)
}
}
#[derive(Debug)]
pub(super) struct DynamicTypeInner {
pub(super) descriptor: TypeDescriptor,
pub(super) members: Vec<DynamicTypeMember>,
}
impl PartialEq for DynamicTypeInner {
fn eq(&self, other: &Self) -> bool {
self.descriptor == other.descriptor && self.members == other.members
}
}
impl Eq for DynamicTypeInner {}
#[derive(Debug, Clone)]
pub struct DynamicType {
pub(super) inner: Arc<DynamicTypeInner>,
}
impl PartialEq for DynamicType {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner) || *self.inner == *other.inner
}
}
impl Eq for DynamicType {}
impl DynamicType {
pub(super) fn from_inner(inner: DynamicTypeInner) -> Self {
Self {
inner: Arc::new(inner),
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.inner.descriptor.name
}
#[must_use]
pub fn kind(&self) -> TypeKind {
self.inner.descriptor.kind
}
#[must_use]
pub fn descriptor(&self) -> &TypeDescriptor {
&self.inner.descriptor
}
#[must_use]
pub fn member_count(&self) -> u32 {
u32::try_from(self.inner.members.len()).unwrap_or(u32::MAX)
}
#[must_use]
pub fn member_by_index(&self, index: u32) -> Option<&DynamicTypeMember> {
self.inner.members.get(index as usize)
}
#[must_use]
pub fn member_by_id(&self, id: MemberId) -> Option<&DynamicTypeMember> {
self.inner.members.iter().find(|m| m.descriptor.id == id)
}
#[must_use]
pub fn member_by_name(&self, name: &str) -> Option<&DynamicTypeMember> {
self.inner
.members
.iter()
.find(|m| m.descriptor.name == name)
}
pub fn members(&self) -> impl Iterator<Item = &DynamicTypeMember> {
self.inner.members.iter()
}
#[must_use]
pub fn equals(&self, other: &Self) -> bool {
if Arc::ptr_eq(&self.inner, &other.inner) {
return true;
}
if self.inner.descriptor != other.inner.descriptor
|| self.inner.members.len() != other.inner.members.len()
{
return false;
}
self.inner
.members
.iter()
.zip(other.inner.members.iter())
.all(|(a, b)| a.equals(b))
}
#[must_use]
pub fn is_aggregable(&self) -> bool {
self.kind().is_aggregable()
}
pub fn is_consistent(&self) -> Result<(), DynamicError> {
self.inner
.descriptor
.is_consistent()
.map_err(DynamicError::inconsistent)?;
for m in &self.inner.members {
m.descriptor
.is_consistent()
.map_err(DynamicError::inconsistent)?;
}
Ok(())
}
#[must_use]
pub fn new_primitive(kind: TypeKind) -> Self {
let name = primitive_name(kind);
Self::from_inner(DynamicTypeInner {
descriptor: TypeDescriptor::primitive(kind, String::from(name)),
members: Vec::new(),
})
}
}
pub(super) const fn primitive_name(kind: TypeKind) -> &'static str {
match kind {
TypeKind::Boolean => "boolean",
TypeKind::Byte => "octet",
TypeKind::Int8 => "int8",
TypeKind::UInt8 => "uint8",
TypeKind::Int16 => "int16",
TypeKind::UInt16 => "uint16",
TypeKind::Int32 => "int32",
TypeKind::UInt32 => "uint32",
TypeKind::Int64 => "int64",
TypeKind::UInt64 => "uint64",
TypeKind::Float32 => "float",
TypeKind::Float64 => "double",
TypeKind::Float128 => "long double",
TypeKind::Char8 => "char",
TypeKind::Char16 => "wchar",
_ => "<non-primitive>",
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn primitive_dynamic_type_has_correct_kind_and_name() {
let t = DynamicType::new_primitive(TypeKind::Int32);
assert_eq!(t.kind(), TypeKind::Int32);
assert_eq!(t.name(), "int32");
assert_eq!(t.member_count(), 0);
}
#[test]
fn equals_is_reflexive_and_value_based() {
let a = DynamicType::new_primitive(TypeKind::Int32);
let b = DynamicType::new_primitive(TypeKind::Int32);
assert!(a.equals(&a));
assert!(a.equals(&b));
assert_eq!(a, b);
}
#[test]
fn equals_distinguishes_different_kinds() {
let a = DynamicType::new_primitive(TypeKind::Int32);
let b = DynamicType::new_primitive(TypeKind::Int64);
assert!(!a.equals(&b));
}
}