use std::collections::{BTreeMap, BTreeSet};
use anyhow::{Context, Result};
use rspyts::ir::{
BufferElement, ConstantDef, DefinitionId, ErrorDef, FunctionDef, Manifest, Namespace,
ResourceDef, TypeDef, TypeRef, TypeShape,
};
#[derive(Default)]
pub(super) struct NamespaceItems<'a> {
pub(super) types: Vec<&'a TypeDef>,
pub(super) errors: Vec<&'a ErrorDef>,
pub(super) functions: Vec<&'a FunctionDef>,
pub(super) resources: Vec<&'a ResourceDef>,
pub(super) constants: Vec<&'a ConstantDef>,
}
pub(super) fn namespaces(manifest: &Manifest) -> BTreeMap<Namespace, NamespaceItems<'_>> {
let mut result = BTreeMap::<Namespace, NamespaceItems<'_>>::new();
result.entry(Namespace::root()).or_default();
for item in &manifest.types {
result
.entry(manifest.namespace(&item.owner, &item.rust_module))
.or_default()
.types
.push(item);
}
for item in &manifest.errors {
result
.entry(manifest.namespace(&item.owner, &item.rust_module))
.or_default()
.errors
.push(item);
}
for item in &manifest.functions {
result
.entry(manifest.namespace(&item.owner, &item.rust_module))
.or_default()
.functions
.push(item);
}
for item in &manifest.resources {
result
.entry(manifest.namespace(&item.owner, &item.rust_module))
.or_default()
.resources
.push(item);
}
for item in &manifest.constants {
result
.entry(manifest.namespace(&item.owner, &item.rust_module))
.or_default()
.constants
.push(item);
}
let declared = result.keys().cloned().collect::<Vec<_>>();
for namespace in declared {
if namespace.package.is_some() {
result
.entry(Namespace {
package: namespace.package.clone(),
modules: Vec::new(),
})
.or_default();
}
for length in 1..namespace.modules.len() {
result
.entry(Namespace {
package: namespace.package.clone(),
modules: namespace.modules[..length].to_vec(),
})
.or_default();
}
}
result
}
pub(super) fn tagged_variant_name(type_name: &str, variant_name: &str) -> String {
format!("{type_name}{variant_name}")
}
pub(super) fn type_namespace(identity: &DefinitionId, manifest: &Manifest) -> Result<Namespace> {
let definition = type_definition(identity, manifest)?;
Ok(manifest.namespace(&definition.owner, &definition.rust_module))
}
pub(super) fn type_definition<'a>(
identity: &DefinitionId,
manifest: &'a Manifest,
) -> Result<&'a TypeDef> {
manifest
.types
.iter()
.find(|item| item.owner == identity.owner && item.id == identity.id)
.with_context(|| format!("missing type `{identity}`"))
}
pub(super) fn error_definition<'a>(
identity: &DefinitionId,
manifest: &'a Manifest,
) -> Result<&'a ErrorDef> {
manifest
.errors
.iter()
.find(|item| item.owner == identity.owner && item.id == identity.id)
.with_context(|| format!("missing error `{identity}`"))
}
pub(super) fn definition_key(identity: &DefinitionId) -> String {
format!("{}::{}", identity.owner, identity.id)
}
pub(super) fn buffer_elements(manifest: &Manifest) -> BTreeSet<BufferElement> {
let mut result = BTreeSet::new();
for reference in contract_refs(manifest) {
collect_buffers(reference, &mut result);
}
result
}
pub(super) fn uses_buffer(manifest: &Manifest) -> bool {
!buffer_elements(manifest).is_empty()
}
pub(super) fn collect_buffers(reference: &TypeRef, result: &mut BTreeSet<BufferElement>) {
match reference {
TypeRef::Buffer { element } => {
result.insert(*element);
}
TypeRef::Option { item } | TypeRef::List { item } => collect_buffers(item, result),
TypeRef::Map { value } => collect_buffers(value, result),
TypeRef::Tuple { items } => {
for item in items {
collect_buffers(item, result);
}
}
_ => {}
}
}
pub(super) fn reference_contains(
reference: &TypeRef,
predicate: &impl Fn(&TypeRef) -> bool,
) -> bool {
if predicate(reference) {
return true;
}
match reference {
TypeRef::Option { item } | TypeRef::List { item } => reference_contains(item, predicate),
TypeRef::Map { value } => reference_contains(value, predicate),
TypeRef::Tuple { items } => items.iter().any(|item| reference_contains(item, predicate)),
_ => false,
}
}
pub(super) fn contract_refs(manifest: &Manifest) -> Vec<&TypeRef> {
let mut result = Vec::new();
for definition in &manifest.types {
match &definition.shape {
TypeShape::Struct { fields } => result.extend(fields.iter().map(|field| &field.ty)),
TypeShape::TaggedEnum { variants, .. } => result.extend(
variants
.iter()
.flat_map(|variant| variant.fields.iter().map(|field| &field.ty)),
),
TypeShape::Alias { target } => result.push(target),
TypeShape::StringEnum { .. } => {}
}
}
for function in &manifest.functions {
result.extend(function.params.iter().map(|param| ¶m.ty));
result.push(&function.returns);
}
for resource in &manifest.resources {
for constructor in &resource.constructors {
result.extend(constructor.params.iter().map(|param| ¶m.ty));
}
for method in &resource.methods {
result.extend(method.params.iter().map(|param| ¶m.ty));
result.push(&method.returns);
}
}
result.extend(manifest.constants.iter().map(|constant| &constant.ty));
result
}
pub(super) fn namespace_refs<'a>(items: &'a NamespaceItems<'a>) -> Vec<&'a TypeRef> {
let mut result = Vec::new();
for definition in &items.types {
result.extend(type_refs(definition));
}
for function in &items.functions {
result.extend(function.params.iter().map(|param| ¶m.ty));
result.push(&function.returns);
}
for resource in &items.resources {
for constructor in &resource.constructors {
result.extend(constructor.params.iter().map(|param| ¶m.ty));
}
for method in &resource.methods {
result.extend(method.params.iter().map(|param| ¶m.ty));
result.push(&method.returns);
}
}
result.extend(items.constants.iter().map(|constant| &constant.ty));
result
}
pub(super) fn type_refs(definition: &TypeDef) -> Vec<&TypeRef> {
match &definition.shape {
TypeShape::Struct { fields } => fields.iter().map(|field| &field.ty).collect(),
TypeShape::TaggedEnum { variants, .. } => variants
.iter()
.flat_map(|variant| variant.fields.iter().map(|field| &field.ty))
.collect(),
TypeShape::Alias { target } => vec![target],
TypeShape::StringEnum { .. } => Vec::new(),
}
}
pub(super) fn named_identities<'a>(reference: &'a TypeRef, result: &mut Vec<&'a DefinitionId>) {
match reference {
TypeRef::Named { identity } => result.push(identity),
TypeRef::Option { item } | TypeRef::List { item } => named_identities(item, result),
TypeRef::Map { value } => named_identities(value, result),
TypeRef::Tuple { items } => {
for item in items {
named_identities(item, result);
}
}
_ => {}
}
}