use super::{Profile, Verdict, construct_verdict, verdict::ConstructKind};
use crate::error::AstError;
use crate::model::Ast;
use crate::node::AnyId;
use crate::node::type_::TypeKind;
use alloc::collections::BTreeSet;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use zerodds_ast_core::{Id, NodeId};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Dropped {
pub name: String,
pub construct: &'static str,
pub spec_reference: &'static str,
pub detail: &'static str,
}
const fn has_no_data_form(kind: &TypeKind) -> bool {
matches!(
kind,
TypeKind::Any | TypeKind::ValueBase | TypeKind::Native | TypeKind::ObjectRef { .. }
)
}
struct Seed {
node: NodeId,
construct: ConstructKind,
spec_reference: &'static str,
detail: &'static str,
}
impl Ast {
pub fn drop_outside_profile(&mut self, profile: Profile) -> Result<Vec<Dropped>, AstError> {
let mut seeds: Vec<Seed> = Vec::new();
for (_, node) in self.types() {
if !has_no_data_form(&node.kind) {
continue;
}
let construct = ConstructKind::of(&node.kind);
if let Verdict::Rejected {
spec_reference,
detail,
..
} = construct_verdict(profile, construct)
{
seeds.push(Seed {
node: node.node_id,
construct,
spec_reference,
detail,
});
}
}
if seeds.is_empty() {
return Ok(Vec::new());
}
let named_elsewhere = self.types_named_outside_the_type_graph();
let mut doomed: Vec<(Id<crate::TypeNode>, NodeId, usize)> = Vec::new();
for (id, node) in self.types() {
if seeds.iter().any(|seed| seed.node == node.node_id) {
continue;
}
let reach = self.reachable_types(id);
if let Some(seed) = seeds.iter().position(|seed| reach.contains(seed.node)) {
if !named_elsewhere.contains(&node.node_id) {
doomed.push((id, node.node_id, seed));
}
}
}
let names: Vec<Option<String>> = doomed
.iter()
.map(|(id, node, _)| {
if self.types.get(*id).map_or(true, |t| t.name.is_empty()) {
return None;
}
self.scoped_name_of(AnyId::Type(*id, *node))
.map(|scoped| scoped.to_string())
})
.collect();
let mut dropped = Vec::new();
for ((id, node, seed_at), name) in doomed.into_iter().zip(names) {
let seed = &seeds[seed_at];
self.remove(AnyId::Type(id, node))?;
if let Some(name) = name {
dropped.push(Dropped {
name,
construct: seed.construct.as_str(),
spec_reference: seed.spec_reference,
detail: seed.detail,
});
}
}
Ok(dropped)
}
fn types_named_outside_the_type_graph(&self) -> BTreeSet<NodeId> {
let mut named: BTreeSet<NodeId> = BTreeSet::new();
let mut note = |reference: &zerodds_ast_core::Ref<crate::TypeNode>| {
if let Some(id) = reference.resolved() {
if let Ok(node) = self.types.get(id) {
named.insert(node.node_id);
}
}
};
for (_, constant) in self.constants.iter() {
note(&constant.type_);
}
for (_, operation) in self.operations.iter() {
if let Some(result) = &operation.return_type {
note(result);
}
}
for (_, parameter) in self.params.iter() {
note(¶meter.type_);
}
for (_, attribute) in self.attributes.iter() {
note(&attribute.type_);
}
for (_, member) in self.annotation_members.iter() {
note(&member.type_);
}
for (_, topic) in self.topics.iter() {
note(&topic.type_);
}
named
}
}