use crate::{
typespace::TypeRefError, AlgebraicType, AlgebraicTypeRef, ArrayType, ProductType, ProductTypeElement, SumType,
SumTypeVariant, WithTypespace,
};
#[derive(Default)]
pub struct ResolveRefState {
stack: Vec<AlgebraicTypeRef>,
}
pub trait ResolveRefs {
type Output;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError>;
}
impl ResolveRefs for AlgebraicTypeRef {
type Output = AlgebraicType;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {
if state.stack.contains(this.ty()) {
return Err(TypeRefError::RecursiveTypeRef(*this.ty()));
}
state.stack.push(*this.ty());
let ret = this
.typespace()
.get(*this.ty())
.ok_or(TypeRefError::InvalidTypeRef(*this.ty()))
.and_then(|at| this.with(at)._resolve_refs(state));
state.stack.pop();
ret
}
}
impl ResolveRefs for AlgebraicType {
type Output = Self;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {
match this.ty() {
Self::Ref(r) => this.with(r)._resolve_refs(state),
Self::Sum(sum) => this.with(sum)._resolve_refs(state).map(Into::into),
Self::Product(prod) => this.with(prod)._resolve_refs(state).map(Into::into),
Self::Array(ty) => this.with(ty)._resolve_refs(state).map(Into::into),
x => Ok(x.clone()),
}
}
}
impl ResolveRefs for ArrayType {
type Output = Self;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {
Ok(Self {
elem_ty: Box::new(this.map(|m| &*m.elem_ty)._resolve_refs(state)?),
})
}
}
impl ResolveRefs for ProductType {
type Output = Self;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {
let elements = this
.ty()
.elements
.iter()
.map(|el| this.with(el)._resolve_refs(state))
.collect::<Result<_, _>>()?;
Ok(ProductType { elements })
}
}
impl ResolveRefs for ProductTypeElement {
type Output = Self;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {
Ok(Self {
algebraic_type: this.map(|e| &e.algebraic_type)._resolve_refs(state)?,
name: this.ty().name.clone(),
})
}
}
impl ResolveRefs for SumType {
type Output = Self;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {
let variants = this
.ty()
.variants
.iter()
.map(|v| this.with(v)._resolve_refs(state))
.collect::<Result<_, _>>()?;
Ok(Self { variants })
}
}
impl ResolveRefs for SumTypeVariant {
type Output = Self;
fn resolve_refs(this: WithTypespace<'_, Self>, state: &mut ResolveRefState) -> Result<Self::Output, TypeRefError> {
Ok(Self {
algebraic_type: this.map(|v| &v.algebraic_type)._resolve_refs(state)?,
name: this.ty().name.clone(),
})
}
}
impl<T: ResolveRefs> WithTypespace<'_, T> {
pub fn resolve_refs(self) -> Result<T::Output, TypeRefError> {
T::resolve_refs(self, &mut ResolveRefState::default())
}
fn _resolve_refs(self, state: &mut ResolveRefState) -> Result<T::Output, TypeRefError> {
T::resolve_refs(self, state)
}
}