use crate::lang::TypeId;
use crate::lang::types::kind::wire::WireOnly as CsWireOnly;
use crate::lang::types::kind::{Composite, Field, TypeKind};
use crate::pass::Outcome::Unchanged;
use crate::pass::{ModelResult, PassInfo, model};
use interoptopus::inventory::{TypeId as RsTypeId, Types as RsTypes};
use interoptopus::lang::meta::Visibility as RsVisibility;
use interoptopus::lang::types::{Repr, Struct, TypeKind as RsTypeKind, WireOnly as RsWireOnly};
use std::collections::HashSet;
#[derive(Default)]
pub struct Config {}
pub struct Pass {
info: PassInfo,
done: bool,
}
impl Pass {
#[must_use]
pub fn new(_: Config) -> Self {
Self { info: PassInfo { name: file!() }, done: false }
}
pub fn process(
&mut self,
_pass_meta: &mut crate::pass::PassMeta,
id_map: &model::common::id_map::Pass,
type_kinds: &mut model::common::types::kind::Pass,
type_names: &mut model::common::types::names::Pass,
rs_types: &RsTypes,
) -> ModelResult {
if self.done {
return Ok(Unchanged);
}
let mut outcome = Unchanged;
for (rust_id, rust_ty) in rs_types {
let RsTypeKind::Struct(rust_s) = &rust_ty.kind else { continue };
let Some(cs_id) = id_map.ty(*rust_id) else { continue };
if type_kinds.contains(&cs_id) {
continue;
}
if !contains_wireonly_transitive(*rust_id, rs_types, &mut HashSet::new()) {
continue;
}
let cs_fields: Vec<Field> = rust_s
.fields
.iter()
.filter_map(|f| {
let cs_field_ty = id_map.ty(f.ty)?;
Some(Field { name: f.name.clone(), docs: f.docs.clone(), visibility: map_visibility(f.visibility), ty: cs_field_ty })
})
.collect();
let composite = Composite { fields: cs_fields, repr: Repr::c() };
type_kinds.set(cs_id, TypeKind::WireOnly(CsWireOnly::Composite(composite)));
type_names.set(cs_id, rust_ty.name.clone());
outcome.changed();
}
let mut registered: HashSet<RsTypeId> = HashSet::new();
for rust_ty in rs_types.values() {
let inner_rust_id = match &rust_ty.kind {
RsTypeKind::TypePattern(interoptopus::lang::types::TypePattern::Wire(inner)) => *inner,
_ => continue,
};
let Some(inner_rust_ty) = rs_types.get(&inner_rust_id) else { continue };
let mut nested = Vec::new();
let mut visited = HashSet::new();
match &inner_rust_ty.kind {
RsTypeKind::Struct(s) => collect_nested_structs(rs_types, s, &mut nested, &mut visited),
_ => collect_nested_from_type(rs_types, inner_rust_id, &mut nested, &mut visited),
}
nested.retain(|id| *id != inner_rust_id);
for nested_id in nested {
if !registered.insert(nested_id) {
continue;
}
let Some(cs_id) = id_map.ty(nested_id) else { continue };
if type_kinds.contains(&cs_id) {
continue;
}
let Some(nested_ty) = rs_types.get(&nested_id) else { continue };
let RsTypeKind::Struct(nested_s) = &nested_ty.kind else { continue };
let cs_fields: Vec<Field> = nested_s
.fields
.iter()
.filter_map(|f| {
let cs_field_ty = id_map.ty(f.ty)?;
Some(Field { name: f.name.clone(), docs: f.docs.clone(), visibility: map_visibility(f.visibility), ty: cs_field_ty })
})
.collect();
let composite = Composite { fields: cs_fields, repr: Repr::c() };
type_kinds.set(cs_id, TypeKind::WireOnly(CsWireOnly::Composite(composite)));
type_names.set(cs_id, nested_ty.name.clone());
outcome.changed();
}
}
self.done = true;
Ok(outcome)
}
}
fn map_visibility(vis: RsVisibility) -> crate::lang::meta::Visibility {
match vis {
RsVisibility::Public => crate::lang::meta::Visibility::Public,
RsVisibility::Private => crate::lang::meta::Visibility::Private,
}
}
fn collect_nested_structs(rs_types: &RsTypes, s: &Struct, out: &mut Vec<RsTypeId>, visited: &mut HashSet<RsTypeId>) {
for f in &s.fields {
collect_nested_from_type(rs_types, f.ty, out, visited);
}
}
fn collect_nested_from_type(rs_types: &RsTypes, ty_id: RsTypeId, out: &mut Vec<RsTypeId>, visited: &mut HashSet<RsTypeId>) {
if !visited.insert(ty_id) {
return;
}
let Some(ty) = rs_types.get(&ty_id) else { return };
match &ty.kind {
RsTypeKind::Struct(s) => {
if contains_wireonly_transitive(ty_id, rs_types, &mut HashSet::new()) {
out.push(ty_id);
}
for f in &s.fields {
collect_nested_from_type(rs_types, f.ty, out, visited);
}
}
RsTypeKind::WireOnly(RsWireOnly::Vec(inner)) => {
collect_nested_from_type(rs_types, *inner, out, visited);
}
RsTypeKind::WireOnly(RsWireOnly::Map(k, v)) => {
collect_nested_from_type(rs_types, *k, out, visited);
collect_nested_from_type(rs_types, *v, out, visited);
}
RsTypeKind::WireOnly(RsWireOnly::Option(inner)) => {
collect_nested_from_type(rs_types, *inner, out, visited);
}
RsTypeKind::TypePattern(interoptopus::lang::types::TypePattern::Option(inner)) => {
collect_nested_from_type(rs_types, *inner, out, visited);
}
RsTypeKind::TypePattern(interoptopus::lang::types::TypePattern::Result(ok, err)) => {
collect_nested_from_type(rs_types, *ok, out, visited);
collect_nested_from_type(rs_types, *err, out, visited);
}
RsTypeKind::Array(arr) => {
collect_nested_from_type(rs_types, arr.ty, out, visited);
}
_ => {}
}
}
fn contains_wireonly_transitive(ty_id: RsTypeId, rs_types: &RsTypes, visited: &mut HashSet<RsTypeId>) -> bool {
if !visited.insert(ty_id) {
return false;
}
let Some(ty) = rs_types.get(&ty_id) else { return false };
match &ty.kind {
RsTypeKind::WireOnly(_) => true,
RsTypeKind::Struct(s) => s.fields.iter().any(|f| contains_wireonly_transitive(f.ty, rs_types, visited)),
RsTypeKind::TypePattern(interoptopus::lang::types::TypePattern::Option(inner)) => contains_wireonly_transitive(*inner, rs_types, visited),
RsTypeKind::TypePattern(interoptopus::lang::types::TypePattern::Result(ok, err)) => {
contains_wireonly_transitive(*ok, rs_types, visited) || contains_wireonly_transitive(*err, rs_types, visited)
}
_ => false,
}
}