use std::cmp::Ordering;
use std::collections::HashSet;
use std::fmt::{self, Display, Formatter};
use std::hash::{Hash, Hasher};
use amplify::confinement::{NonEmptyOrdMap, TinyOrdSet};
use baid64::DisplayBaid64;
use encoding::StrictDumb;
use strict_encoding::{LibName, TypeName, STRICT_TYPES_LIB};
use crate::typelib::compile::CompileError;
use crate::typelib::id::TypeLibId;
use crate::typelib::ExternTypes;
use crate::{SemId, Ty, TypeRef};
pub trait LibSubref: TypeRef {}
impl LibSubref for LibRef {}
impl LibSubref for InlineRef {}
impl LibSubref for InlineRef1 {}
impl LibSubref for InlineRef2 {}
#[derive(Clone, Eq, PartialEq, Debug, Display)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB)]
#[display("{lib_id}.{sem_id}")]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub struct ExternRef {
pub lib_id: TypeLibId,
pub sem_id: SemId,
}
impl ExternRef {
pub fn with(lib_id: TypeLibId, sem_id: SemId) -> ExternRef { ExternRef { lib_id, sem_id } }
}
impl StrictDumb for Box<Ty<InlineRef1>> {
fn strict_dumb() -> Self { Box::new(Ty::UNIT) }
}
#[derive(Clone, Eq, PartialEq, Debug, From)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB, tags = order, dumb = { InlineRef::Inline(Box::new(Ty::strict_dumb())) })]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub enum InlineRef {
#[from]
Inline(Box<Ty<InlineRef1>>),
Named(SemId),
Extern(ExternRef),
}
impl TypeRef for InlineRef {
fn is_compound(&self) -> bool {
match self {
InlineRef::Inline(ty) => ty.is_compound(),
_ => false,
}
}
fn is_byte(&self) -> bool {
match self {
InlineRef::Inline(ty) => ty.is_byte(),
_ => false,
}
}
fn is_unicode_char(&self) -> bool {
match self {
InlineRef::Inline(ty) => ty.is_unicode_char(),
_ => false,
}
}
}
impl Display for InlineRef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
InlineRef::Named(sem_id) => write!(f, "{sem_id}"),
InlineRef::Extern(ext) => Display::fmt(ext, f),
InlineRef::Inline(ty) => Display::fmt(ty, f),
}
}
}
#[derive(Clone, Eq, PartialEq, Debug, From)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB, tags = order, dumb = { InlineRef1::Inline(Ty::strict_dumb()) })]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub enum InlineRef1 {
#[from]
Inline(Ty<InlineRef2>),
Named(SemId),
Extern(ExternRef),
}
impl TypeRef for InlineRef1 {
fn is_compound(&self) -> bool {
match self {
InlineRef1::Inline(ty) => ty.is_compound(),
_ => false,
}
}
fn is_byte(&self) -> bool {
match self {
InlineRef1::Inline(ty) => ty.is_byte(),
_ => false,
}
}
fn is_unicode_char(&self) -> bool {
match self {
InlineRef1::Inline(ty) => ty.is_unicode_char(),
_ => false,
}
}
}
impl Display for InlineRef1 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
InlineRef1::Named(sem_id) => write!(f, "{sem_id}"),
InlineRef1::Extern(ext) => Display::fmt(ext, f),
InlineRef1::Inline(ty) => Display::fmt(ty, f),
}
}
}
#[derive(Clone, Eq, PartialEq, Debug, From)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB, tags = order, dumb = { InlineRef2::Named(SemId::strict_dumb()) })]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub enum InlineRef2 {
Named(SemId),
Extern(ExternRef),
}
impl TypeRef for InlineRef2 {
fn is_compound(&self) -> bool { false }
fn is_byte(&self) -> bool { false }
fn is_unicode_char(&self) -> bool { false }
}
impl Display for InlineRef2 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
InlineRef2::Named(sem_id) => write!(f, "{sem_id}"),
InlineRef2::Extern(ext) => Display::fmt(ext, f),
}
}
}
impl StrictDumb for Box<Ty<InlineRef>> {
fn strict_dumb() -> Self { Box::new(Ty::UNIT) }
}
#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB, tags = order, dumb = { LibRef::Inline(Box::new(Ty::strict_dumb())) })]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
pub enum LibRef {
Inline(Box<Ty<InlineRef>>),
Named(SemId),
Extern(ExternRef),
}
impl TypeRef for LibRef {
fn is_compound(&self) -> bool {
match self {
LibRef::Inline(ty) => ty.is_compound(),
_ => false,
}
}
fn is_byte(&self) -> bool {
match self {
LibRef::Inline(ty) => ty.is_byte(),
_ => false,
}
}
fn is_unicode_char(&self) -> bool {
match self {
LibRef::Inline(ty) => ty.is_unicode_char(),
_ => false,
}
}
}
impl Display for LibRef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
LibRef::Named(sem_id) => Display::fmt(sem_id, f),
LibRef::Inline(ty) => Display::fmt(ty, f),
LibRef::Extern(ext) => Display::fmt(ext, f),
}
}
}
#[derive(Clone, Eq, Debug)]
#[derive(StrictDumb, StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Dependency {
pub id: TypeLibId,
pub name: LibName,
}
impl Hash for Dependency {
fn hash<H: Hasher>(&self, state: &mut H) { self.id.hash(state) }
}
impl PartialEq for Dependency {
fn eq(&self, other: &Self) -> bool { self.id == other.id || self.name == other.name }
}
impl PartialOrd for Dependency {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
impl Ord for Dependency {
fn cmp(&self, other: &Self) -> Ordering { self.id.cmp(&other.id) }
}
impl Dependency {
pub fn with(id: TypeLibId, name: LibName) -> Self { Dependency { id, name } }
}
impl From<&TypeLib> for Dependency {
fn from(lib: &TypeLib) -> Self {
Dependency {
id: lib.id(),
name: lib.name.clone(),
}
}
}
impl Display for Dependency {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}#{}", self.name, self.id.to_baid64_mnemonic())
}
}
pub type TypeMap = NonEmptyOrdMap<TypeName, Ty<LibRef>, { u16::MAX as usize }>;
#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = STRICT_TYPES_LIB)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TypeLib {
pub name: LibName,
pub dependencies: TinyOrdSet<Dependency>,
pub extern_types: ExternTypes,
pub types: TypeMap,
}
impl StrictDumb for TypeLib {
fn strict_dumb() -> Self {
TypeLib {
name: LibName::strict_dumb(),
dependencies: default!(),
extern_types: default!(),
types: TypeMap::with_key_value(tn!("DumbType"), Ty::strict_dumb()),
}
}
}
impl TypeLib {
pub fn to_dependency(&self) -> Dependency { Dependency::with(self.id(), self.name.clone()) }
pub fn to_dependency_types(&self) -> (Dependency, HashSet<SemId>) {
let dependency = Dependency::with(self.id(), self.name.clone());
let types = self.types.iter().map(|(name, ty)| ty.sem_id_named(name)).collect();
(dependency, types)
}
pub fn import(&mut self, dependency: Dependency) -> Result<(), CompileError> {
if self.dependencies.contains(&dependency) {
return Err(CompileError::DuplicatedDependency(dependency));
}
self.dependencies.push(dependency).map_err(|_| CompileError::TooManyDependencies)?;
Ok(())
}
pub fn populate(&mut self, name: TypeName, ty: Ty<LibRef>) -> Result<(), CompileError> {
if self.types.contains_key(&name) {
return Err(CompileError::DuplicateName(name));
}
self.types.insert(name, ty).map_err(|_| CompileError::TooManyTypes)?;
Ok(())
}
}